intc.c 20.3 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
#include <linux/sysdev.h>
#include <linux/list.h>
M
Magnus Damm 已提交
27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42

#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;
};
43

M
Magnus Damm 已提交
44
struct intc_desc_int {
M
Magnus Damm 已提交
45 46
	struct list_head list;
	struct sys_device sysdev;
M
Magnus Damm 已提交
47
	unsigned long *reg;
M
Magnus Damm 已提交
48 49 50
#ifdef CONFIG_SMP
	unsigned long *smp;
#endif
M
Magnus Damm 已提交
51 52 53 54 55 56 57
	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;
};
58

M
Magnus Damm 已提交
59 60
static LIST_HEAD(intc_list);

M
Magnus Damm 已提交
61 62 63 64 65 66 67 68 69 70
#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 已提交
71
static unsigned int intc_prio_level[NR_IRQS]; /* for now */
72
#if defined(CONFIG_CPU_SH3) || defined(CONFIG_CPU_SH4A)
M
Magnus Damm 已提交
73 74
static unsigned long ack_handle[NR_IRQS];
#endif
75

M
Magnus Damm 已提交
76
static inline struct intc_desc_int *get_intc_desc(unsigned int irq)
77 78
{
	struct irq_chip *chip = get_irq_chip(irq);
M
Magnus Damm 已提交
79
	return (void *)((char *)chip - offsetof(struct intc_desc_int, chip));
80 81 82 83
}

static inline unsigned int set_field(unsigned int value,
				     unsigned int field_value,
M
Magnus Damm 已提交
84
				     unsigned int handle)
85
{
M
Magnus Damm 已提交
86 87 88
	unsigned int width = _INTC_WIDTH(handle);
	unsigned int shift = _INTC_SHIFT(handle);

89 90 91 92 93
	value &= ~(((1 << width) - 1) << shift);
	value |= field_value << shift;
	return value;
}

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

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

M
Magnus Damm 已提交
104
static void write_32(unsigned long addr, unsigned long h, unsigned long data)
105
{
106
	__raw_writel(set_field(0, data, h), addr);
107 108
}

M
Magnus Damm 已提交
109
static void modify_8(unsigned long addr, unsigned long h, unsigned long data)
110
{
M
Magnus Damm 已提交
111 112
	unsigned long flags;
	local_irq_save(flags);
113
	__raw_writeb(set_field(__raw_readb(addr), data, h), addr);
M
Magnus Damm 已提交
114
	local_irq_restore(flags);
115 116
}

M
Magnus Damm 已提交
117
static void modify_16(unsigned long addr, unsigned long h, unsigned long data)
118
{
M
Magnus Damm 已提交
119 120
	unsigned long flags;
	local_irq_save(flags);
121
	__raw_writew(set_field(__raw_readw(addr), data, h), addr);
M
Magnus Damm 已提交
122
	local_irq_restore(flags);
123 124
}

M
Magnus Damm 已提交
125
static void modify_32(unsigned long addr, unsigned long h, unsigned long data)
126
{
M
Magnus Damm 已提交
127 128
	unsigned long flags;
	local_irq_save(flags);
129
	__raw_writel(set_field(__raw_readl(addr), data, h), addr);
M
Magnus Damm 已提交
130
	local_irq_restore(flags);
131 132
}

M
Magnus Damm 已提交
133 134 135 136 137 138 139 140 141 142 143 144
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,
};
145

M
Magnus Damm 已提交
146 147 148 149 150 151
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 */
};
152

M
Magnus Damm 已提交
153 154 155 156 157 158
static void intc_mode_field(unsigned long addr,
			    unsigned long handle,
			    void (*fn)(unsigned long,
				       unsigned long,
				       unsigned long),
			    unsigned int irq)
159
{
M
Magnus Damm 已提交
160
	fn(addr, handle, ((1 << _INTC_WIDTH(handle)) - 1));
161 162
}

M
Magnus Damm 已提交
163 164 165 166 167 168
static void intc_mode_zero(unsigned long addr,
			   unsigned long handle,
			   void (*fn)(unsigned long,
				       unsigned long,
				       unsigned long),
			   unsigned int irq)
169
{
M
Magnus Damm 已提交
170
	fn(addr, handle, 0);
171 172
}

M
Magnus Damm 已提交
173 174 175 176 177 178
static void intc_mode_prio(unsigned long addr,
			   unsigned long handle,
			   void (*fn)(unsigned long,
				       unsigned long,
				       unsigned long),
			   unsigned int irq)
179
{
M
Magnus Damm 已提交
180
	fn(addr, handle, intc_prio_level[irq]);
181 182
}

M
Magnus Damm 已提交
183 184 185 186 187 188 189 190 191 192 193 194
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,
};
195

M
Magnus Damm 已提交
196 197 198 199 200 201 202 203 204 205 206 207
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,
};
208

M
Magnus Damm 已提交
209
static inline void _intc_enable(unsigned int irq, unsigned long handle)
210
{
M
Magnus Damm 已提交
211
	struct intc_desc_int *d = get_intc_desc(irq);
M
Magnus Damm 已提交
212 213
	unsigned long addr;
	unsigned int cpu;
214

M
Magnus Damm 已提交
215 216 217 218 219
	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);
	}
220 221
}

222 223
static void intc_enable(unsigned int irq)
{
M
Magnus Damm 已提交
224
	_intc_enable(irq, (unsigned long)get_irq_chip_data(irq));
225 226 227 228
}

static void intc_disable(unsigned int irq)
{
M
Magnus Damm 已提交
229
	struct intc_desc_int *d = get_intc_desc(irq);
M
Magnus Damm 已提交
230
	unsigned long handle = (unsigned long) get_irq_chip_data(irq);
M
Magnus Damm 已提交
231 232
	unsigned long addr;
	unsigned int cpu;
233

M
Magnus Damm 已提交
234 235 236 237 238
	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);
	}
239 240
}

M
Magnus Damm 已提交
241 242 243 244 245
static int intc_set_wake(unsigned int irq, unsigned int on)
{
	return 0; /* allow wakeup, but setup hardware in intc_suspend() */
}

246
#if defined(CONFIG_CPU_SH3) || defined(CONFIG_CPU_SH4A)
M
Magnus Damm 已提交
247 248 249 250 251 252 253 254 255 256 257 258
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);
259 260
		switch (_INTC_FN(handle)) {
		case REG_FN_MODIFY_BASE + 0:	/* 8bit */
261 262
			__raw_readb(addr);
			__raw_writeb(0xff ^ set_field(0, 1, handle), addr);
263 264
			break;
		case REG_FN_MODIFY_BASE + 1:	/* 16bit */
265 266
			__raw_readw(addr);
			__raw_writew(0xffff ^ set_field(0, 1, handle), addr);
267 268
			break;
		case REG_FN_MODIFY_BASE + 3:	/* 32bit */
269 270
			__raw_readl(addr);
			__raw_writel(0xffffffff ^ set_field(0, 1, handle), addr);
271 272 273 274 275
			break;
		default:
			BUG();
			break;
		}
M
Magnus Damm 已提交
276 277 278 279
	}
}
#endif

M
Magnus Damm 已提交
280 281 282
static struct intc_handle_int *intc_find_irq(struct intc_handle_int *hp,
					     unsigned int nr_hp,
					     unsigned int irq)
283
{
M
Magnus Damm 已提交
284 285
	int i;

286 287 288 289 290 291 292 293 294 295 296 297
	/* 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 已提交
298 299 300 301 302 303
	for (i = 0; i < nr_hp; i++) {
		if ((hp + i)->irq != irq)
			continue;

		return hp + i;
	}
304

M
Magnus Damm 已提交
305
	return NULL;
306 307
}

M
Magnus Damm 已提交
308
int intc_set_priority(unsigned int irq, unsigned int prio)
309
{
M
Magnus Damm 已提交
310 311 312 313 314 315 316 317
	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) {
318
		if (prio >= (1 << _INTC_WIDTH(ihp->handle)))
M
Magnus Damm 已提交
319
			return -EINVAL;
320

M
Magnus Damm 已提交
321 322 323 324 325 326 327 328
		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()
		 */

329
		if (_INTC_FN(ihp->handle) != REG_FN_ERR)
M
Magnus Damm 已提交
330 331 332
			_intc_enable(irq, ihp->handle);
	}
	return 0;
333 334 335 336 337 338 339 340
}

#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),
341 342 343 344
	/* 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)
345
	[IRQ_TYPE_LEVEL_HIGH] = VALID(3),
346
#endif
347 348 349 350
};

static int intc_set_sense(unsigned int irq, unsigned int type)
{
M
Magnus Damm 已提交
351
	struct intc_desc_int *d = get_intc_desc(irq);
352
	unsigned char value = intc_irq_sense_table[type & IRQ_TYPE_SENSE_MASK];
M
Magnus Damm 已提交
353 354
	struct intc_handle_int *ihp;
	unsigned long addr;
355

M
Magnus Damm 已提交
356
	if (!value)
357 358
		return -EINVAL;

M
Magnus Damm 已提交
359 360
	ihp = intc_find_irq(d->sense, d->nr_sense, irq);
	if (ihp) {
M
Magnus Damm 已提交
361
		addr = INTC_REG(d, _INTC_ADDR_E(ihp->handle), 0);
M
Magnus Damm 已提交
362
		intc_reg_fns[_INTC_FN(ihp->handle)](addr, ihp->handle, value);
363
	}
M
Magnus Damm 已提交
364
	return 0;
365 366
}

M
Magnus Damm 已提交
367 368
static unsigned int __init intc_get_reg(struct intc_desc_int *d,
				 unsigned long address)
369
{
M
Magnus Damm 已提交
370
	unsigned int k;
371

M
Magnus Damm 已提交
372 373 374
	for (k = 0; k < d->nr_reg; k++) {
		if (d->reg[k] == address)
			return k;
375 376 377
	}

	BUG();
M
Magnus Damm 已提交
378
	return 0;
379 380
}

M
Magnus Damm 已提交
381 382
static intc_enum __init intc_grp_id(struct intc_desc *desc,
				    intc_enum enum_id)
M
Magnus Damm 已提交
383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400
{
	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;
}

401
static unsigned int __init intc_mask_data(struct intc_desc *desc,
M
Magnus Damm 已提交
402
					  struct intc_desc_int *d,
M
Magnus Damm 已提交
403
					  intc_enum enum_id, int do_grps)
404
{
M
Magnus Damm 已提交
405
	struct intc_mask_reg *mr = desc->mask_regs;
M
Magnus Damm 已提交
406 407
	unsigned int i, j, fn, mode;
	unsigned long reg_e, reg_d;
408

M
Magnus Damm 已提交
409 410
	for (i = 0; mr && enum_id && i < desc->nr_mask_regs; i++) {
		mr = desc->mask_regs + i;
411 412 413 414 415

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

M
Magnus Damm 已提交
416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431
			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;
				}
432 433
			}

M
Magnus Damm 已提交
434 435 436 437 438 439
			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);
440 441 442
		}
	}

M
Magnus Damm 已提交
443
	if (do_grps)
M
Magnus Damm 已提交
444
		return intc_mask_data(desc, d, intc_grp_id(desc, enum_id), 0);
M
Magnus Damm 已提交
445

446 447 448 449
	return 0;
}

static unsigned int __init intc_prio_data(struct intc_desc *desc,
M
Magnus Damm 已提交
450
					  struct intc_desc_int *d,
M
Magnus Damm 已提交
451
					  intc_enum enum_id, int do_grps)
452
{
M
Magnus Damm 已提交
453
	struct intc_prio_reg *pr = desc->prio_regs;
M
Magnus Damm 已提交
454 455
	unsigned int i, j, fn, mode, bit;
	unsigned long reg_e, reg_d;
456

M
Magnus Damm 已提交
457 458
	for (i = 0; pr && enum_id && i < desc->nr_prio_regs; i++) {
		pr = desc->prio_regs + i;
459 460 461 462 463

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

M
Magnus Damm 已提交
464 465 466 467 468 469 470 471 472 473 474 475 476
			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;
			}
477

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

480 481 482
			BUG_ON((j + 1) * pr->field_width > pr->reg_width);

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

M
Magnus Damm 已提交
484 485 486 487
			return _INTC_MK(fn, mode,
					intc_get_reg(d, reg_e),
					intc_get_reg(d, reg_d),
					pr->field_width, bit);
488 489 490
		}
	}

M
Magnus Damm 已提交
491
	if (do_grps)
M
Magnus Damm 已提交
492 493 494 495 496
		return intc_prio_data(desc, d, intc_grp_id(desc, enum_id), 0);

	return 0;
}

497
#if defined(CONFIG_CPU_SH3) || defined(CONFIG_CPU_SH4A)
M
Magnus Damm 已提交
498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530
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 已提交
531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547
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;

548 549 550
			BUG_ON((j + 1) * sr->field_width > sr->reg_width);

			bit = sr->reg_width - ((j + 1) * sr->field_width);
M
Magnus Damm 已提交
551 552 553 554 555

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

557 558 559
	return 0;
}

M
Magnus Damm 已提交
560 561 562
static void __init intc_register_irq(struct intc_desc *desc,
				     struct intc_desc_int *d,
				     intc_enum enum_id,
563 564
				     unsigned int irq)
{
565
	struct intc_handle_int *hp;
M
Magnus Damm 已提交
566 567 568 569 570 571 572 573
	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)
	 */
574

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

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

582
	if (!data[0] && !data[1])
583 584
		pr_warning("intc: missing unique irq mask for "
			   "irq %d (vect 0x%04x)\n", irq, irq2evt(irq));
585

M
Magnus Damm 已提交
586 587
	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 已提交
588 589 590 591 592

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

	BUG_ON(!data[primary]); /* must have primary masking method */
593 594

	disable_irq_nosync(irq);
M
Magnus Damm 已提交
595
	set_irq_chip_and_handler_name(irq, &d->chip,
596
				      handle_level_irq, "level");
M
Magnus Damm 已提交
597
	set_irq_chip_data(irq, (void *)data[primary]);
598

599 600 601 602
	/* set priority level
	 * - this needs to be at least 2 for 5-bit priorities on 7780
	 */
	intc_prio_level[irq] = 2;
M
Magnus Damm 已提交
603

M
Magnus Damm 已提交
604 605
	/* enable secondary masking method if present */
	if (data[!primary])
M
Magnus Damm 已提交
606 607 608 609
		_intc_enable(irq, data[!primary]);

	/* add irq to d->prio list if priority is available */
	if (data[1]) {
610 611 612 613 614 615 616 617 618 619 620 621 622
		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 已提交
623 624 625 626 627 628 629 630 631 632
		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++;
	}
633 634

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

637
#if defined(CONFIG_CPU_SH3) || defined(CONFIG_CPU_SH4A)
M
Magnus Damm 已提交
638 639 640
	if (desc->ack_regs)
		ack_handle[irq] = intc_ack_data(desc, d, enum_id);
#endif
641 642
}

M
Magnus Damm 已提交
643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658
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;
}

659 660 661 662 663 664 665 666 667 668 669
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 已提交
670

671 672
void __init register_intc_controller(struct intc_desc *desc)
{
M
Magnus Damm 已提交
673
	unsigned int i, k, smp;
M
Magnus Damm 已提交
674 675 676 677
	struct intc_desc_int *d;

	d = alloc_bootmem(sizeof(*d));

M
Magnus Damm 已提交
678 679 680
	INIT_LIST_HEAD(&d->list);
	list_add(&d->list, &intc_list);

M
Magnus Damm 已提交
681 682 683 684
	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;

685
#if defined(CONFIG_CPU_SH3) || defined(CONFIG_CPU_SH4A)
M
Magnus Damm 已提交
686 687
	d->nr_reg += desc->ack_regs ? desc->nr_ack_regs : 0;
#endif
M
Magnus Damm 已提交
688
	d->reg = alloc_bootmem(d->nr_reg * sizeof(*d->reg));
M
Magnus Damm 已提交
689 690 691
#ifdef CONFIG_SMP
	d->smp = alloc_bootmem(d->nr_reg * sizeof(*d->smp));
#endif
M
Magnus Damm 已提交
692 693 694 695
	k = 0;

	if (desc->mask_regs) {
		for (i = 0; i < desc->nr_mask_regs; i++) {
M
Magnus Damm 已提交
696 697 698
			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 已提交
699 700 701 702 703 704 705
		}
	}

	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 已提交
706 707 708
			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 已提交
709 710 711 712 713 714 715
		}
	}

	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 已提交
716
			k += save_reg(d, k, desc->sense_regs[i].reg, 0);
M
Magnus Damm 已提交
717 718 719 720 721 722 723
		}
	}

	d->chip.name = desc->name;
	d->chip.mask = intc_disable;
	d->chip.unmask = intc_enable;
	d->chip.mask_ack = intc_disable;
724 725 726
	d->chip.enable = intc_enable;
	d->chip.disable = intc_disable;
	d->chip.shutdown = intc_disable;
M
Magnus Damm 已提交
727
	d->chip.set_type = intc_set_sense;
M
Magnus Damm 已提交
728
	d->chip.set_wake = intc_set_wake;
729

730
#if defined(CONFIG_CPU_SH3) || defined(CONFIG_CPU_SH4A)
M
Magnus Damm 已提交
731 732 733 734 735 736 737 738 739 740
	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 */

741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769
	/* 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 */
770 771 772
	for (i = 0; i < desc->nr_vectors; i++) {
		struct intc_vect *vect = desc->vectors + i;

773 774 775
		if (!vect->enum_id)
			continue;

M
Magnus Damm 已提交
776
		intc_register_irq(desc, d, vect->enum_id, evt2irq(vect->vect));
777 778
	}
}
M
Magnus Damm 已提交
779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828

static int intc_suspend(struct sys_device *dev, pm_message_t state)
{
	struct intc_desc_int *d;
	struct irq_desc *desc;
	int irq;

	/* get intc controller associated with this sysdev */
	d = container_of(dev, struct intc_desc_int, sysdev);

	/* enable wakeup irqs belonging to this intc controller */
	for_each_irq_desc(irq, desc) {
		if ((desc->status & IRQ_WAKEUP) && (desc->chip == &d->chip))
			intc_enable(irq);
	}

	return 0;
}

static struct sysdev_class intc_sysdev_class = {
	.name = "intc",
	.suspend = intc_suspend,
};

/* register this intc as sysdev to allow suspend/resume */
static int __init register_intc_sysdevs(void)
{
	struct intc_desc_int *d;
	int error;
	int id = 0;

	error = sysdev_class_register(&intc_sysdev_class);
	if (!error) {
		list_for_each_entry(d, &intc_list, list) {
			d->sysdev.id = id;
			d->sysdev.cls = &intc_sysdev_class;
			error = sysdev_register(&d->sysdev);
			if (error)
				break;
			id++;
		}
	}

	if (error)
		pr_warning("intc: sysdev registration error\n");

	return error;
}

device_initcall(register_intc_sysdevs);