intc.c 18.9 KB
Newer Older
1 2 3
/*
 * Shared interrupt handling code for IPR and INTC2 types of IRQs.
 *
M
Magnus Damm 已提交
4
 * Copyright (C) 2007, 2008 Magnus Damm
5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
 *
 * Based on intc2.c and ipr.c
 *
 * Copyright (C) 1999  Niibe Yutaka & Takeshi Yaegashi
 * Copyright (C) 2000  Kazumoto Kojima
 * Copyright (C) 2001  David J. Mckay (david.mckay@st.com)
 * Copyright (C) 2003  Takashi Kusuda <kusuda-takashi@hitachi-ul.co.jp>
 * Copyright (C) 2005, 2006  Paul Mundt
 *
 * 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.
 */
#include <linux/init.h>
#include <linux/irq.h>
#include <linux/module.h>
#include <linux/io.h>
#include <linux/interrupt.h>
M
Magnus Damm 已提交
23
#include <linux/bootmem.h>
24
#include <linux/sh_intc.h>
M
Magnus Damm 已提交
25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40

#define _INTC_MK(fn, mode, addr_e, addr_d, width, shift) \
	((shift) | ((width) << 5) | ((fn) << 9) | ((mode) << 13) | \
	 ((addr_e) << 16) | ((addr_d << 24)))

#define _INTC_SHIFT(h) (h & 0x1f)
#define _INTC_WIDTH(h) ((h >> 5) & 0xf)
#define _INTC_FN(h) ((h >> 9) & 0xf)
#define _INTC_MODE(h) ((h >> 13) & 0x7)
#define _INTC_ADDR_E(h) ((h >> 16) & 0xff)
#define _INTC_ADDR_D(h) ((h >> 24) & 0xff)

struct intc_handle_int {
	unsigned int irq;
	unsigned long handle;
};
41

M
Magnus Damm 已提交
42 43
struct intc_desc_int {
	unsigned long *reg;
M
Magnus Damm 已提交
44 45 46
#ifdef CONFIG_SMP
	unsigned long *smp;
#endif
M
Magnus Damm 已提交
47 48 49 50 51 52 53
	unsigned int nr_reg;
	struct intc_handle_int *prio;
	unsigned int nr_prio;
	struct intc_handle_int *sense;
	unsigned int nr_sense;
	struct irq_chip chip;
};
54

M
Magnus Damm 已提交
55 56 57 58 59 60 61 62 63 64
#ifdef CONFIG_SMP
#define IS_SMP(x) x.smp
#define INTC_REG(d, x, c) (d->reg[(x)] + ((d->smp[(x)] & 0xff) * c))
#define SMP_NR(d, x) ((d->smp[(x)] >> 8) ? (d->smp[(x)] >> 8) : 1)
#else
#define IS_SMP(x) 0
#define INTC_REG(d, x, c) (d->reg[(x)])
#define SMP_NR(d, x) 1
#endif

M
Magnus Damm 已提交
65
static unsigned int intc_prio_level[NR_IRQS]; /* for now */
66
#if defined(CONFIG_CPU_SH3) || defined(CONFIG_CPU_SH4A)
M
Magnus Damm 已提交
67 68
static unsigned long ack_handle[NR_IRQS];
#endif
69

M
Magnus Damm 已提交
70
static inline struct intc_desc_int *get_intc_desc(unsigned int irq)
71 72
{
	struct irq_chip *chip = get_irq_chip(irq);
M
Magnus Damm 已提交
73
	return (void *)((char *)chip - offsetof(struct intc_desc_int, chip));
74 75 76 77
}

static inline unsigned int set_field(unsigned int value,
				     unsigned int field_value,
M
Magnus Damm 已提交
78
				     unsigned int handle)
79
{
M
Magnus Damm 已提交
80 81 82
	unsigned int width = _INTC_WIDTH(handle);
	unsigned int shift = _INTC_SHIFT(handle);

83 84 85 86 87
	value &= ~(((1 << width) - 1) << shift);
	value |= field_value << shift;
	return value;
}

M
Magnus Damm 已提交
88
static void write_8(unsigned long addr, unsigned long h, unsigned long data)
89
{
90
	__raw_writeb(set_field(0, data, h), addr);
91 92
}

M
Magnus Damm 已提交
93
static void write_16(unsigned long addr, unsigned long h, unsigned long data)
94
{
95
	__raw_writew(set_field(0, data, h), addr);
96 97
}

M
Magnus Damm 已提交
98
static void write_32(unsigned long addr, unsigned long h, unsigned long data)
99
{
100
	__raw_writel(set_field(0, data, h), addr);
101 102
}

M
Magnus Damm 已提交
103
static void modify_8(unsigned long addr, unsigned long h, unsigned long data)
104
{
M
Magnus Damm 已提交
105 106
	unsigned long flags;
	local_irq_save(flags);
107
	__raw_writeb(set_field(__raw_readb(addr), data, h), addr);
M
Magnus Damm 已提交
108
	local_irq_restore(flags);
109 110
}

M
Magnus Damm 已提交
111
static void modify_16(unsigned long addr, unsigned long h, unsigned long data)
112
{
M
Magnus Damm 已提交
113 114
	unsigned long flags;
	local_irq_save(flags);
115
	__raw_writew(set_field(__raw_readw(addr), data, h), addr);
M
Magnus Damm 已提交
116
	local_irq_restore(flags);
117 118
}

M
Magnus Damm 已提交
119
static void modify_32(unsigned long addr, unsigned long h, unsigned long data)
120
{
M
Magnus Damm 已提交
121 122
	unsigned long flags;
	local_irq_save(flags);
123
	__raw_writel(set_field(__raw_readl(addr), data, h), addr);
M
Magnus Damm 已提交
124
	local_irq_restore(flags);
125 126
}

M
Magnus Damm 已提交
127 128 129 130 131 132 133 134 135 136 137 138
enum {	REG_FN_ERR = 0, REG_FN_WRITE_BASE = 1, REG_FN_MODIFY_BASE = 5 };

static void (*intc_reg_fns[])(unsigned long addr,
			      unsigned long h,
			      unsigned long data) = {
	[REG_FN_WRITE_BASE + 0] = write_8,
	[REG_FN_WRITE_BASE + 1] = write_16,
	[REG_FN_WRITE_BASE + 3] = write_32,
	[REG_FN_MODIFY_BASE + 0] = modify_8,
	[REG_FN_MODIFY_BASE + 1] = modify_16,
	[REG_FN_MODIFY_BASE + 3] = modify_32,
};
139

M
Magnus Damm 已提交
140 141 142 143 144 145
enum {	MODE_ENABLE_REG = 0, /* Bit(s) set -> interrupt enabled */
	MODE_MASK_REG,       /* Bit(s) set -> interrupt disabled */
	MODE_DUAL_REG,       /* Two registers, set bit to enable / disable */
	MODE_PRIO_REG,       /* Priority value written to enable interrupt */
	MODE_PCLR_REG,       /* Above plus all bits set to disable interrupt */
};
146

M
Magnus Damm 已提交
147 148 149 150 151 152
static void intc_mode_field(unsigned long addr,
			    unsigned long handle,
			    void (*fn)(unsigned long,
				       unsigned long,
				       unsigned long),
			    unsigned int irq)
153
{
M
Magnus Damm 已提交
154
	fn(addr, handle, ((1 << _INTC_WIDTH(handle)) - 1));
155 156
}

M
Magnus Damm 已提交
157 158 159 160 161 162
static void intc_mode_zero(unsigned long addr,
			   unsigned long handle,
			   void (*fn)(unsigned long,
				       unsigned long,
				       unsigned long),
			   unsigned int irq)
163
{
M
Magnus Damm 已提交
164
	fn(addr, handle, 0);
165 166
}

M
Magnus Damm 已提交
167 168 169 170 171 172
static void intc_mode_prio(unsigned long addr,
			   unsigned long handle,
			   void (*fn)(unsigned long,
				       unsigned long,
				       unsigned long),
			   unsigned int irq)
173
{
M
Magnus Damm 已提交
174
	fn(addr, handle, intc_prio_level[irq]);
175 176
}

M
Magnus Damm 已提交
177 178 179 180 181 182 183 184 185 186 187 188
static void (*intc_enable_fns[])(unsigned long addr,
				 unsigned long handle,
				 void (*fn)(unsigned long,
					    unsigned long,
					    unsigned long),
				 unsigned int irq) = {
	[MODE_ENABLE_REG] = intc_mode_field,
	[MODE_MASK_REG] = intc_mode_zero,
	[MODE_DUAL_REG] = intc_mode_field,
	[MODE_PRIO_REG] = intc_mode_prio,
	[MODE_PCLR_REG] = intc_mode_prio,
};
189

M
Magnus Damm 已提交
190 191 192 193 194 195 196 197 198 199 200 201
static void (*intc_disable_fns[])(unsigned long addr,
				  unsigned long handle,
				  void (*fn)(unsigned long,
					     unsigned long,
					     unsigned long),
				  unsigned int irq) = {
	[MODE_ENABLE_REG] = intc_mode_zero,
	[MODE_MASK_REG] = intc_mode_field,
	[MODE_DUAL_REG] = intc_mode_field,
	[MODE_PRIO_REG] = intc_mode_zero,
	[MODE_PCLR_REG] = intc_mode_field,
};
202

M
Magnus Damm 已提交
203
static inline void _intc_enable(unsigned int irq, unsigned long handle)
204
{
M
Magnus Damm 已提交
205
	struct intc_desc_int *d = get_intc_desc(irq);
M
Magnus Damm 已提交
206 207
	unsigned long addr;
	unsigned int cpu;
208

M
Magnus Damm 已提交
209 210 211 212 213
	for (cpu = 0; cpu < SMP_NR(d, _INTC_ADDR_E(handle)); cpu++) {
		addr = INTC_REG(d, _INTC_ADDR_E(handle), cpu);
		intc_enable_fns[_INTC_MODE(handle)](addr, handle, intc_reg_fns\
						    [_INTC_FN(handle)], irq);
	}
214 215
}

216 217
static void intc_enable(unsigned int irq)
{
M
Magnus Damm 已提交
218
	_intc_enable(irq, (unsigned long)get_irq_chip_data(irq));
219 220 221 222
}

static void intc_disable(unsigned int irq)
{
M
Magnus Damm 已提交
223
	struct intc_desc_int *d = get_intc_desc(irq);
M
Magnus Damm 已提交
224
	unsigned long handle = (unsigned long) get_irq_chip_data(irq);
M
Magnus Damm 已提交
225 226
	unsigned long addr;
	unsigned int cpu;
227

M
Magnus Damm 已提交
228 229 230 231 232
	for (cpu = 0; cpu < SMP_NR(d, _INTC_ADDR_D(handle)); cpu++) {
		addr = INTC_REG(d, _INTC_ADDR_D(handle), cpu);
		intc_disable_fns[_INTC_MODE(handle)](addr, handle,intc_reg_fns\
						     [_INTC_FN(handle)], irq);
	}
233 234
}

235
#if defined(CONFIG_CPU_SH3) || defined(CONFIG_CPU_SH4A)
M
Magnus Damm 已提交
236 237 238 239 240 241 242 243 244 245 246 247
static void intc_mask_ack(unsigned int irq)
{
	struct intc_desc_int *d = get_intc_desc(irq);
	unsigned long handle = ack_handle[irq];
	unsigned long addr;

	intc_disable(irq);

	/* read register and write zero only to the assocaited bit */

	if (handle) {
		addr = INTC_REG(d, _INTC_ADDR_D(handle), 0);
248 249
		switch (_INTC_FN(handle)) {
		case REG_FN_MODIFY_BASE + 0:	/* 8bit */
250 251
			__raw_readb(addr);
			__raw_writeb(0xff ^ set_field(0, 1, handle), addr);
252 253
			break;
		case REG_FN_MODIFY_BASE + 1:	/* 16bit */
254 255
			__raw_readw(addr);
			__raw_writew(0xffff ^ set_field(0, 1, handle), addr);
256 257
			break;
		case REG_FN_MODIFY_BASE + 3:	/* 32bit */
258 259
			__raw_readl(addr);
			__raw_writel(0xffffffff ^ set_field(0, 1, handle), addr);
260 261 262 263 264
			break;
		default:
			BUG();
			break;
		}
M
Magnus Damm 已提交
265 266 267 268
	}
}
#endif

M
Magnus Damm 已提交
269 270 271
static struct intc_handle_int *intc_find_irq(struct intc_handle_int *hp,
					     unsigned int nr_hp,
					     unsigned int irq)
272
{
M
Magnus Damm 已提交
273 274
	int i;

275 276 277 278 279 280 281 282 283 284 285 286
	/* this doesn't scale well, but...
	 *
	 * this function should only be used for cerain uncommon
	 * operations such as intc_set_priority() and intc_set_sense()
	 * and in those rare cases performance doesn't matter that much.
	 * keeping the memory footprint low is more important.
	 *
	 * one rather simple way to speed this up and still keep the
	 * memory footprint down is to make sure the array is sorted
	 * and then perform a bisect to lookup the irq.
	 */

M
Magnus Damm 已提交
287 288 289 290 291 292
	for (i = 0; i < nr_hp; i++) {
		if ((hp + i)->irq != irq)
			continue;

		return hp + i;
	}
293

M
Magnus Damm 已提交
294
	return NULL;
295 296
}

M
Magnus Damm 已提交
297
int intc_set_priority(unsigned int irq, unsigned int prio)
298
{
M
Magnus Damm 已提交
299 300 301 302 303 304 305 306
	struct intc_desc_int *d = get_intc_desc(irq);
	struct intc_handle_int *ihp;

	if (!intc_prio_level[irq] || prio <= 1)
		return -EINVAL;

	ihp = intc_find_irq(d->prio, d->nr_prio, irq);
	if (ihp) {
307
		if (prio >= (1 << _INTC_WIDTH(ihp->handle)))
M
Magnus Damm 已提交
308
			return -EINVAL;
309

M
Magnus Damm 已提交
310 311 312 313 314 315 316 317
		intc_prio_level[irq] = prio;

		/*
		 * only set secondary masking method directly
		 * primary masking method is using intc_prio_level[irq]
		 * priority level will be set during next enable()
		 */

318
		if (_INTC_FN(ihp->handle) != REG_FN_ERR)
M
Magnus Damm 已提交
319 320 321
			_intc_enable(irq, ihp->handle);
	}
	return 0;
322 323 324 325 326 327 328 329
}

#define VALID(x) (x | 0x80)

static unsigned char intc_irq_sense_table[IRQ_TYPE_SENSE_MASK + 1] = {
	[IRQ_TYPE_EDGE_FALLING] = VALID(0),
	[IRQ_TYPE_EDGE_RISING] = VALID(1),
	[IRQ_TYPE_LEVEL_LOW] = VALID(2),
330 331 332 333
	/* SH7706, SH7707 and SH7709 do not support high level triggered */
#if !defined(CONFIG_CPU_SUBTYPE_SH7706) && \
    !defined(CONFIG_CPU_SUBTYPE_SH7707) && \
    !defined(CONFIG_CPU_SUBTYPE_SH7709)
334
	[IRQ_TYPE_LEVEL_HIGH] = VALID(3),
335
#endif
336 337 338 339
};

static int intc_set_sense(unsigned int irq, unsigned int type)
{
M
Magnus Damm 已提交
340
	struct intc_desc_int *d = get_intc_desc(irq);
341
	unsigned char value = intc_irq_sense_table[type & IRQ_TYPE_SENSE_MASK];
M
Magnus Damm 已提交
342 343
	struct intc_handle_int *ihp;
	unsigned long addr;
344

M
Magnus Damm 已提交
345
	if (!value)
346 347
		return -EINVAL;

M
Magnus Damm 已提交
348 349
	ihp = intc_find_irq(d->sense, d->nr_sense, irq);
	if (ihp) {
M
Magnus Damm 已提交
350
		addr = INTC_REG(d, _INTC_ADDR_E(ihp->handle), 0);
M
Magnus Damm 已提交
351
		intc_reg_fns[_INTC_FN(ihp->handle)](addr, ihp->handle, value);
352
	}
M
Magnus Damm 已提交
353
	return 0;
354 355
}

M
Magnus Damm 已提交
356 357
static unsigned int __init intc_get_reg(struct intc_desc_int *d,
				 unsigned long address)
358
{
M
Magnus Damm 已提交
359
	unsigned int k;
360

M
Magnus Damm 已提交
361 362 363
	for (k = 0; k < d->nr_reg; k++) {
		if (d->reg[k] == address)
			return k;
364 365 366
	}

	BUG();
M
Magnus Damm 已提交
367
	return 0;
368 369
}

M
Magnus Damm 已提交
370 371
static intc_enum __init intc_grp_id(struct intc_desc *desc,
				    intc_enum enum_id)
M
Magnus Damm 已提交
372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389
{
	struct intc_group *g = desc->groups;
	unsigned int i, j;

	for (i = 0; g && enum_id && i < desc->nr_groups; i++) {
		g = desc->groups + i;

		for (j = 0; g->enum_ids[j]; j++) {
			if (g->enum_ids[j] != enum_id)
				continue;

			return g->enum_id;
		}
	}

	return 0;
}

390
static unsigned int __init intc_mask_data(struct intc_desc *desc,
M
Magnus Damm 已提交
391
					  struct intc_desc_int *d,
M
Magnus Damm 已提交
392
					  intc_enum enum_id, int do_grps)
393
{
M
Magnus Damm 已提交
394
	struct intc_mask_reg *mr = desc->mask_regs;
M
Magnus Damm 已提交
395 396
	unsigned int i, j, fn, mode;
	unsigned long reg_e, reg_d;
397

M
Magnus Damm 已提交
398 399
	for (i = 0; mr && enum_id && i < desc->nr_mask_regs; i++) {
		mr = desc->mask_regs + i;
400 401 402 403 404

		for (j = 0; j < ARRAY_SIZE(mr->enum_ids); j++) {
			if (mr->enum_ids[j] != enum_id)
				continue;

M
Magnus Damm 已提交
405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420
			if (mr->set_reg && mr->clr_reg) {
				fn = REG_FN_WRITE_BASE;
				mode = MODE_DUAL_REG;
				reg_e = mr->clr_reg;
				reg_d = mr->set_reg;
			} else {
				fn = REG_FN_MODIFY_BASE;
				if (mr->set_reg) {
					mode = MODE_ENABLE_REG;
					reg_e = mr->set_reg;
					reg_d = mr->set_reg;
				} else {
					mode = MODE_MASK_REG;
					reg_e = mr->clr_reg;
					reg_d = mr->clr_reg;
				}
421 422
			}

M
Magnus Damm 已提交
423 424 425 426 427 428
			fn += (mr->reg_width >> 3) - 1;
			return _INTC_MK(fn, mode,
					intc_get_reg(d, reg_e),
					intc_get_reg(d, reg_d),
					1,
					(mr->reg_width - 1) - j);
429 430 431
		}
	}

M
Magnus Damm 已提交
432
	if (do_grps)
M
Magnus Damm 已提交
433
		return intc_mask_data(desc, d, intc_grp_id(desc, enum_id), 0);
M
Magnus Damm 已提交
434

435 436 437 438
	return 0;
}

static unsigned int __init intc_prio_data(struct intc_desc *desc,
M
Magnus Damm 已提交
439
					  struct intc_desc_int *d,
M
Magnus Damm 已提交
440
					  intc_enum enum_id, int do_grps)
441
{
M
Magnus Damm 已提交
442
	struct intc_prio_reg *pr = desc->prio_regs;
M
Magnus Damm 已提交
443 444
	unsigned int i, j, fn, mode, bit;
	unsigned long reg_e, reg_d;
445

M
Magnus Damm 已提交
446 447
	for (i = 0; pr && enum_id && i < desc->nr_prio_regs; i++) {
		pr = desc->prio_regs + i;
448 449 450 451 452

		for (j = 0; j < ARRAY_SIZE(pr->enum_ids); j++) {
			if (pr->enum_ids[j] != enum_id)
				continue;

M
Magnus Damm 已提交
453 454 455 456 457 458 459 460 461 462 463 464 465
			if (pr->set_reg && pr->clr_reg) {
				fn = REG_FN_WRITE_BASE;
				mode = MODE_PCLR_REG;
				reg_e = pr->set_reg;
				reg_d = pr->clr_reg;
			} else {
				fn = REG_FN_MODIFY_BASE;
				mode = MODE_PRIO_REG;
				if (!pr->set_reg)
					BUG();
				reg_e = pr->set_reg;
				reg_d = pr->set_reg;
			}
466

M
Magnus Damm 已提交
467
			fn += (pr->reg_width >> 3) - 1;
468

469 470 471
			BUG_ON((j + 1) * pr->field_width > pr->reg_width);

			bit = pr->reg_width - ((j + 1) * pr->field_width);
472

M
Magnus Damm 已提交
473 474 475 476
			return _INTC_MK(fn, mode,
					intc_get_reg(d, reg_e),
					intc_get_reg(d, reg_d),
					pr->field_width, bit);
477 478 479
		}
	}

M
Magnus Damm 已提交
480
	if (do_grps)
M
Magnus Damm 已提交
481 482 483 484 485
		return intc_prio_data(desc, d, intc_grp_id(desc, enum_id), 0);

	return 0;
}

486
#if defined(CONFIG_CPU_SH3) || defined(CONFIG_CPU_SH4A)
M
Magnus Damm 已提交
487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519
static unsigned int __init intc_ack_data(struct intc_desc *desc,
					  struct intc_desc_int *d,
					  intc_enum enum_id)
{
	struct intc_mask_reg *mr = desc->ack_regs;
	unsigned int i, j, fn, mode;
	unsigned long reg_e, reg_d;

	for (i = 0; mr && enum_id && i < desc->nr_ack_regs; i++) {
		mr = desc->ack_regs + i;

		for (j = 0; j < ARRAY_SIZE(mr->enum_ids); j++) {
			if (mr->enum_ids[j] != enum_id)
				continue;

			fn = REG_FN_MODIFY_BASE;
			mode = MODE_ENABLE_REG;
			reg_e = mr->set_reg;
			reg_d = mr->set_reg;

			fn += (mr->reg_width >> 3) - 1;
			return _INTC_MK(fn, mode,
					intc_get_reg(d, reg_e),
					intc_get_reg(d, reg_d),
					1,
					(mr->reg_width - 1) - j);
		}
	}

	return 0;
}
#endif

M
Magnus Damm 已提交
520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536
static unsigned int __init intc_sense_data(struct intc_desc *desc,
					   struct intc_desc_int *d,
					   intc_enum enum_id)
{
	struct intc_sense_reg *sr = desc->sense_regs;
	unsigned int i, j, fn, bit;

	for (i = 0; sr && enum_id && i < desc->nr_sense_regs; i++) {
		sr = desc->sense_regs + i;

		for (j = 0; j < ARRAY_SIZE(sr->enum_ids); j++) {
			if (sr->enum_ids[j] != enum_id)
				continue;

			fn = REG_FN_MODIFY_BASE;
			fn += (sr->reg_width >> 3) - 1;

537 538 539
			BUG_ON((j + 1) * sr->field_width > sr->reg_width);

			bit = sr->reg_width - ((j + 1) * sr->field_width);
M
Magnus Damm 已提交
540 541 542 543 544

			return _INTC_MK(fn, 0, intc_get_reg(d, sr->reg),
					0, sr->field_width, bit);
		}
	}
M
Magnus Damm 已提交
545

546 547 548
	return 0;
}

M
Magnus Damm 已提交
549 550 551
static void __init intc_register_irq(struct intc_desc *desc,
				     struct intc_desc_int *d,
				     intc_enum enum_id,
552 553
				     unsigned int irq)
{
554
	struct intc_handle_int *hp;
M
Magnus Damm 已提交
555 556 557 558 559 560 561 562
	unsigned int data[2], primary;

	/* Prefer single interrupt source bitmap over other combinations:
	 * 1. bitmap, single interrupt source
	 * 2. priority, single interrupt source
	 * 3. bitmap, multiple interrupt sources (groups)
	 * 4. priority, multiple interrupt sources (groups)
	 */
563

M
Magnus Damm 已提交
564 565
	data[0] = intc_mask_data(desc, d, enum_id, 0);
	data[1] = intc_prio_data(desc, d, enum_id, 0);
M
Magnus Damm 已提交
566 567 568 569 570

	primary = 0;
	if (!data[0] && data[1])
		primary = 1;

571
	if (!data[0] && !data[1])
572 573
		pr_warning("intc: missing unique irq mask for "
			   "irq %d (vect 0x%04x)\n", irq, irq2evt(irq));
574

M
Magnus Damm 已提交
575 576
	data[0] = data[0] ? data[0] : intc_mask_data(desc, d, enum_id, 1);
	data[1] = data[1] ? data[1] : intc_prio_data(desc, d, enum_id, 1);
M
Magnus Damm 已提交
577 578 579 580 581

	if (!data[primary])
		primary ^= 1;

	BUG_ON(!data[primary]); /* must have primary masking method */
582 583

	disable_irq_nosync(irq);
M
Magnus Damm 已提交
584
	set_irq_chip_and_handler_name(irq, &d->chip,
585
				      handle_level_irq, "level");
M
Magnus Damm 已提交
586
	set_irq_chip_data(irq, (void *)data[primary]);
587

588 589 590 591
	/* set priority level
	 * - this needs to be at least 2 for 5-bit priorities on 7780
	 */
	intc_prio_level[irq] = 2;
M
Magnus Damm 已提交
592

M
Magnus Damm 已提交
593 594
	/* enable secondary masking method if present */
	if (data[!primary])
M
Magnus Damm 已提交
595 596 597 598
		_intc_enable(irq, data[!primary]);

	/* add irq to d->prio list if priority is available */
	if (data[1]) {
599 600 601 602 603 604 605 606 607 608 609 610 611
		hp = d->prio + d->nr_prio;
		hp->irq = irq;
		hp->handle = data[1];

		if (primary) {
			/*
			 * only secondary priority should access registers, so
			 * set _INTC_FN(h) = REG_FN_ERR for intc_set_priority()
			 */

			hp->handle &= ~_INTC_MK(0x0f, 0, 0, 0, 0, 0);
			hp->handle |= _INTC_MK(REG_FN_ERR, 0, 0, 0, 0, 0);
		}
M
Magnus Damm 已提交
612 613 614 615 616 617 618 619 620 621
		d->nr_prio++;
	}

	/* add irq to d->sense list if sense is available */
	data[0] = intc_sense_data(desc, d, enum_id);
	if (data[0]) {
		(d->sense + d->nr_sense)->irq = irq;
		(d->sense + d->nr_sense)->handle = data[0];
		d->nr_sense++;
	}
622 623

	/* irq should be disabled by default */
M
Magnus Damm 已提交
624
	d->chip.mask(irq);
M
Magnus Damm 已提交
625

626
#if defined(CONFIG_CPU_SH3) || defined(CONFIG_CPU_SH4A)
M
Magnus Damm 已提交
627 628 629
	if (desc->ack_regs)
		ack_handle[irq] = intc_ack_data(desc, d, enum_id);
#endif
630 631
}

M
Magnus Damm 已提交
632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647
static unsigned int __init save_reg(struct intc_desc_int *d,
				    unsigned int cnt,
				    unsigned long value,
				    unsigned int smp)
{
	if (value) {
		d->reg[cnt] = value;
#ifdef CONFIG_SMP
		d->smp[cnt] = smp;
#endif
		return 1;
	}

	return 0;
}

648 649 650 651 652 653 654 655 656 657 658
static unsigned char *intc_evt2irq_table;

unsigned int intc_evt2irq(unsigned int vector)
{
	unsigned int irq = evt2irq(vector);

	if (intc_evt2irq_table && intc_evt2irq_table[irq])
		irq = intc_evt2irq_table[irq];

	return irq;
}
M
Magnus Damm 已提交
659

660 661
void __init register_intc_controller(struct intc_desc *desc)
{
M
Magnus Damm 已提交
662
	unsigned int i, k, smp;
M
Magnus Damm 已提交
663 664 665 666 667 668 669 670
	struct intc_desc_int *d;

	d = alloc_bootmem(sizeof(*d));

	d->nr_reg = desc->mask_regs ? desc->nr_mask_regs * 2 : 0;
	d->nr_reg += desc->prio_regs ? desc->nr_prio_regs * 2 : 0;
	d->nr_reg += desc->sense_regs ? desc->nr_sense_regs : 0;

671
#if defined(CONFIG_CPU_SH3) || defined(CONFIG_CPU_SH4A)
M
Magnus Damm 已提交
672 673
	d->nr_reg += desc->ack_regs ? desc->nr_ack_regs : 0;
#endif
M
Magnus Damm 已提交
674
	d->reg = alloc_bootmem(d->nr_reg * sizeof(*d->reg));
M
Magnus Damm 已提交
675 676 677
#ifdef CONFIG_SMP
	d->smp = alloc_bootmem(d->nr_reg * sizeof(*d->smp));
#endif
M
Magnus Damm 已提交
678 679 680 681
	k = 0;

	if (desc->mask_regs) {
		for (i = 0; i < desc->nr_mask_regs; i++) {
M
Magnus Damm 已提交
682 683 684
			smp = IS_SMP(desc->mask_regs[i]);
			k += save_reg(d, k, desc->mask_regs[i].set_reg, smp);
			k += save_reg(d, k, desc->mask_regs[i].clr_reg, smp);
M
Magnus Damm 已提交
685 686 687 688 689 690 691
		}
	}

	if (desc->prio_regs) {
		d->prio = alloc_bootmem(desc->nr_vectors * sizeof(*d->prio));

		for (i = 0; i < desc->nr_prio_regs; i++) {
M
Magnus Damm 已提交
692 693 694
			smp = IS_SMP(desc->prio_regs[i]);
			k += save_reg(d, k, desc->prio_regs[i].set_reg, smp);
			k += save_reg(d, k, desc->prio_regs[i].clr_reg, smp);
M
Magnus Damm 已提交
695 696 697 698 699 700 701
		}
	}

	if (desc->sense_regs) {
		d->sense = alloc_bootmem(desc->nr_vectors * sizeof(*d->sense));

		for (i = 0; i < desc->nr_sense_regs; i++) {
M
Magnus Damm 已提交
702
			k += save_reg(d, k, desc->sense_regs[i].reg, 0);
M
Magnus Damm 已提交
703 704 705 706 707 708 709
		}
	}

	d->chip.name = desc->name;
	d->chip.mask = intc_disable;
	d->chip.unmask = intc_enable;
	d->chip.mask_ack = intc_disable;
710 711 712
	d->chip.enable = intc_enable;
	d->chip.disable = intc_disable;
	d->chip.shutdown = intc_disable;
M
Magnus Damm 已提交
713
	d->chip.set_type = intc_set_sense;
714

715
#if defined(CONFIG_CPU_SH3) || defined(CONFIG_CPU_SH4A)
M
Magnus Damm 已提交
716 717 718 719 720 721 722 723 724 725
	if (desc->ack_regs) {
		for (i = 0; i < desc->nr_ack_regs; i++)
			k += save_reg(d, k, desc->ack_regs[i].set_reg, 0);

		d->chip.mask_ack = intc_mask_ack;
	}
#endif

	BUG_ON(k > 256); /* _INTC_ADDR_E() and _INTC_ADDR_D() are 8 bits */

726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754
	/* keep the first vector only if same enum is used multiple times */
	for (i = 0; i < desc->nr_vectors; i++) {
		struct intc_vect *vect = desc->vectors + i;
		int first_irq = evt2irq(vect->vect);

		if (!vect->enum_id)
			continue;

		for (k = i + 1; k < desc->nr_vectors; k++) {
			struct intc_vect *vect2 = desc->vectors + k;

			if (vect->enum_id != vect2->enum_id)
				continue;

			vect2->enum_id = 0;

			if (!intc_evt2irq_table)
				intc_evt2irq_table = alloc_bootmem(NR_IRQS);

			if (!intc_evt2irq_table) {
				pr_warning("intc: cannot allocate evt2irq!\n");
				continue;
			}

			intc_evt2irq_table[evt2irq(vect2->vect)] = first_irq;
		}
	}

	/* register the vectors one by one */
755 756 757
	for (i = 0; i < desc->nr_vectors; i++) {
		struct intc_vect *vect = desc->vectors + i;

758 759 760
		if (!vect->enum_id)
			continue;

M
Magnus Damm 已提交
761
		intc_register_irq(desc, d, vect->enum_id, evt2irq(vect->vect));
762 763
	}
}