intc.c 20.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>
23
#include <linux/sh_intc.h>
M
Magnus Damm 已提交
24 25
#include <linux/sysdev.h>
#include <linux/list.h>
P
Paul Mundt 已提交
26
#include <linux/topology.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;
47
	pm_message_t state;
M
Magnus Damm 已提交
48
	unsigned long *reg;
M
Magnus Damm 已提交
49 50 51
#ifdef CONFIG_SMP
	unsigned long *smp;
#endif
M
Magnus Damm 已提交
52 53 54 55 56 57 58
	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;
};
59

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

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

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

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

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

M
Magnus Damm 已提交
93
static void write_8(unsigned long addr, unsigned long h, unsigned long data)
94
{
95
	__raw_writeb(set_field(0, data, h), addr);
96
	(void)__raw_readb(addr);	/* Defeat write posting */
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
	(void)__raw_readw(addr);	/* Defeat write posting */
103 104
}

M
Magnus Damm 已提交
105
static void write_32(unsigned long addr, unsigned long h, unsigned long data)
106
{
107
	__raw_writel(set_field(0, data, h), addr);
108
	(void)__raw_readl(addr);	/* Defeat write posting */
109 110
}

M
Magnus Damm 已提交
111
static void modify_8(unsigned long addr, unsigned long h, unsigned long data)
112
{
M
Magnus Damm 已提交
113 114
	unsigned long flags;
	local_irq_save(flags);
115
	__raw_writeb(set_field(__raw_readb(addr), data, h), addr);
116
	(void)__raw_readb(addr);	/* Defeat write posting */
M
Magnus Damm 已提交
117
	local_irq_restore(flags);
118 119
}

M
Magnus Damm 已提交
120
static void modify_16(unsigned long addr, unsigned long h, unsigned long data)
121
{
M
Magnus Damm 已提交
122 123
	unsigned long flags;
	local_irq_save(flags);
124
	__raw_writew(set_field(__raw_readw(addr), data, h), addr);
125
	(void)__raw_readw(addr);	/* Defeat write posting */
M
Magnus Damm 已提交
126
	local_irq_restore(flags);
127 128
}

M
Magnus Damm 已提交
129
static void modify_32(unsigned long addr, unsigned long h, unsigned long data)
130
{
M
Magnus Damm 已提交
131 132
	unsigned long flags;
	local_irq_save(flags);
133
	__raw_writel(set_field(__raw_readl(addr), data, h), addr);
134
	(void)__raw_readl(addr);	/* Defeat write posting */
M
Magnus Damm 已提交
135
	local_irq_restore(flags);
136 137
}

M
Magnus Damm 已提交
138 139 140 141 142 143 144 145 146 147 148 149
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,
};
150

M
Magnus Damm 已提交
151 152 153 154 155 156
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 */
};
157

M
Magnus Damm 已提交
158 159 160 161 162 163
static void intc_mode_field(unsigned long addr,
			    unsigned long handle,
			    void (*fn)(unsigned long,
				       unsigned long,
				       unsigned long),
			    unsigned int irq)
164
{
M
Magnus Damm 已提交
165
	fn(addr, handle, ((1 << _INTC_WIDTH(handle)) - 1));
166 167
}

M
Magnus Damm 已提交
168 169 170 171 172 173
static void intc_mode_zero(unsigned long addr,
			   unsigned long handle,
			   void (*fn)(unsigned long,
				       unsigned long,
				       unsigned long),
			   unsigned int irq)
174
{
M
Magnus Damm 已提交
175
	fn(addr, handle, 0);
176 177
}

M
Magnus Damm 已提交
178 179 180 181 182 183
static void intc_mode_prio(unsigned long addr,
			   unsigned long handle,
			   void (*fn)(unsigned long,
				       unsigned long,
				       unsigned long),
			   unsigned int irq)
184
{
M
Magnus Damm 已提交
185
	fn(addr, handle, intc_prio_level[irq]);
186 187
}

M
Magnus Damm 已提交
188 189 190 191 192 193 194 195 196 197 198 199
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,
};
200

M
Magnus Damm 已提交
201 202 203 204 205 206 207 208 209 210 211 212
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,
};
213

M
Magnus Damm 已提交
214
static inline void _intc_enable(unsigned int irq, unsigned long handle)
215
{
M
Magnus Damm 已提交
216
	struct intc_desc_int *d = get_intc_desc(irq);
M
Magnus Damm 已提交
217 218
	unsigned long addr;
	unsigned int cpu;
219

M
Magnus Damm 已提交
220 221 222 223 224
	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);
	}
225 226
}

227 228
static void intc_enable(unsigned int irq)
{
M
Magnus Damm 已提交
229
	_intc_enable(irq, (unsigned long)get_irq_chip_data(irq));
230 231 232 233
}

static void intc_disable(unsigned int irq)
{
M
Magnus Damm 已提交
234
	struct intc_desc_int *d = get_intc_desc(irq);
M
Magnus Damm 已提交
235
	unsigned long handle = (unsigned long) get_irq_chip_data(irq);
M
Magnus Damm 已提交
236 237
	unsigned long addr;
	unsigned int cpu;
238

M
Magnus Damm 已提交
239 240 241 242 243
	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);
	}
244 245
}

M
Magnus Damm 已提交
246 247 248 249 250
static int intc_set_wake(unsigned int irq, unsigned int on)
{
	return 0; /* allow wakeup, but setup hardware in intc_suspend() */
}

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

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

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

		return hp + i;
	}
307

M
Magnus Damm 已提交
308
	return NULL;
309 310
}

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

M
Magnus Damm 已提交
324 325 326 327 328 329 330 331
		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()
		 */

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

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

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

M
Magnus Damm 已提交
359
	if (!value)
360 361
		return -EINVAL;

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

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

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

	BUG();
M
Magnus Damm 已提交
381
	return 0;
382 383
}

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

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

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

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

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

M
Magnus Damm 已提交
437 438 439 440 441 442
			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);
443 444 445
		}
	}

M
Magnus Damm 已提交
446
	if (do_grps)
M
Magnus Damm 已提交
447
		return intc_mask_data(desc, d, intc_grp_id(desc, enum_id), 0);
M
Magnus Damm 已提交
448

449 450 451 452
	return 0;
}

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

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

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

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

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

483 484 485
			BUG_ON((j + 1) * pr->field_width > pr->reg_width);

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

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

M
Magnus Damm 已提交
494
	if (do_grps)
M
Magnus Damm 已提交
495 496 497 498 499
		return intc_prio_data(desc, d, intc_grp_id(desc, enum_id), 0);

	return 0;
}

M
Magnus Damm 已提交
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 531
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;
}

M
Magnus Damm 已提交
532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548
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;

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

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

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

558 559 560
	return 0;
}

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

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

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

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

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

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

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

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

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

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

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

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

	if (desc->ack_regs)
		ack_handle[irq] = intc_ack_data(desc, d, enum_id);
640 641
}

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

658
static void intc_redirect_irq(unsigned int irq, struct irq_desc *desc)
659
{
660
	generic_handle_irq((unsigned int)get_irq_data(irq));
661
}
M
Magnus Damm 已提交
662

663 664
void __init register_intc_controller(struct intc_desc *desc)
{
P
Paul Mundt 已提交
665
	unsigned int i, k, smp;
M
Magnus Damm 已提交
666 667
	struct intc_desc_int *d;

668
	d = kzalloc(sizeof(*d), GFP_NOWAIT);
M
Magnus Damm 已提交
669

M
Magnus Damm 已提交
670 671 672
	INIT_LIST_HEAD(&d->list);
	list_add(&d->list, &intc_list);

M
Magnus Damm 已提交
673 674 675
	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;
M
Magnus Damm 已提交
676
	d->nr_reg += desc->ack_regs ? desc->nr_ack_regs : 0;
677

678
	d->reg = kzalloc(d->nr_reg * sizeof(*d->reg), GFP_NOWAIT);
M
Magnus Damm 已提交
679
#ifdef CONFIG_SMP
680
	d->smp = kzalloc(d->nr_reg * sizeof(*d->smp), GFP_NOWAIT);
M
Magnus Damm 已提交
681
#endif
M
Magnus Damm 已提交
682 683 684 685
	k = 0;

	if (desc->mask_regs) {
		for (i = 0; i < desc->nr_mask_regs; i++) {
M
Magnus Damm 已提交
686 687 688
			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 已提交
689 690 691 692
		}
	}

	if (desc->prio_regs) {
693
		d->prio = kzalloc(desc->nr_vectors * sizeof(*d->prio), GFP_NOWAIT);
M
Magnus Damm 已提交
694 695

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

	if (desc->sense_regs) {
703
		d->sense = kzalloc(desc->nr_vectors * sizeof(*d->sense), GFP_NOWAIT);
M
Magnus Damm 已提交
704 705

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

	d->chip.name = desc->name;
	d->chip.mask = intc_disable;
	d->chip.unmask = intc_enable;
	d->chip.mask_ack = intc_disable;
714 715 716
	d->chip.enable = intc_enable;
	d->chip.disable = intc_disable;
	d->chip.shutdown = intc_disable;
M
Magnus Damm 已提交
717
	d->chip.set_type = intc_set_sense;
M
Magnus Damm 已提交
718
	d->chip.set_wake = intc_set_wake;
719

M
Magnus Damm 已提交
720 721 722 723 724 725 726 727 728
	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;
	}

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

729
	/* register the vectors one by one */
730 731
	for (i = 0; i < desc->nr_vectors; i++) {
		struct intc_vect *vect = desc->vectors + i;
732 733
		unsigned int irq = evt2irq(vect->vect);
		struct irq_desc *irq_desc;
P
Paul Mundt 已提交
734

735 736 737
		if (!vect->enum_id)
			continue;

P
Paul Mundt 已提交
738
		irq_desc = irq_to_desc_alloc_node(irq, numa_node_id());
739
		if (unlikely(!irq_desc)) {
740
			pr_info("can't get irq_desc for %d\n", irq);
741 742 743 744
			continue;
		}

		intc_register_irq(desc, d, vect->enum_id, irq);
745 746 747 748 749 750 751 752

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

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

753 754 755 756 757 758 759 760 761 762 763
			/*
			 * In the case of multi-evt handling and sparse
			 * IRQ support, each vector still needs to have
			 * its own backing irq_desc.
			 */
			irq_desc = irq_to_desc_alloc_node(irq2, numa_node_id());
			if (unlikely(!irq_desc)) {
				pr_info("can't get irq_desc for %d\n", irq2);
				continue;
			}

764 765 766 767 768 769 770
			vect2->enum_id = 0;

			/* redirect this interrupts to the first one */
			set_irq_chip_and_handler_name(irq2, &d->chip,
					intc_redirect_irq, "redirect");
			set_irq_data(irq2, (void *)irq);
		}
771 772
	}
}
M
Magnus Damm 已提交
773 774 775 776 777 778 779 780 781 782

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);

783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805
	switch (state.event) {
	case PM_EVENT_ON:
		if (d->state.event != PM_EVENT_FREEZE)
			break;
		for_each_irq_desc(irq, desc) {
			if (desc->chip != &d->chip)
				continue;
			if (desc->status & IRQ_DISABLED)
				intc_disable(irq);
			else
				intc_enable(irq);
		}
		break;
	case PM_EVENT_FREEZE:
		/* nothing has to be done */
		break;
	case PM_EVENT_SUSPEND:
		/* 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);
		}
		break;
M
Magnus Damm 已提交
806
	}
807
	d->state = state;
M
Magnus Damm 已提交
808 809 810 811

	return 0;
}

812 813 814 815 816
static int intc_resume(struct sys_device *dev)
{
	return intc_suspend(dev, PMSG_ON);
}

M
Magnus Damm 已提交
817 818 819
static struct sysdev_class intc_sysdev_class = {
	.name = "intc",
	.suspend = intc_suspend,
820
	.resume = intc_resume,
M
Magnus Damm 已提交
821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848
};

/* 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);