diff options
Diffstat (limited to 'drivers/sh/intc/balancing.c')
-rw-r--r-- | drivers/sh/intc/balancing.c | 97 |
1 files changed, 97 insertions, 0 deletions
diff --git a/drivers/sh/intc/balancing.c b/drivers/sh/intc/balancing.c new file mode 100644 index 000000000000..cec7a96f2c09 --- /dev/null +++ b/drivers/sh/intc/balancing.c | |||
@@ -0,0 +1,97 @@ | |||
1 | /* | ||
2 | * Support for hardware-managed IRQ auto-distribution. | ||
3 | * | ||
4 | * Copyright (C) 2010 Paul Mundt | ||
5 | * | ||
6 | * This file is subject to the terms and conditions of the GNU General Public | ||
7 | * License. See the file "COPYING" in the main directory of this archive | ||
8 | * for more details. | ||
9 | */ | ||
10 | #include "internals.h" | ||
11 | |||
12 | static unsigned long dist_handle[NR_IRQS]; | ||
13 | |||
14 | void intc_balancing_enable(unsigned int irq) | ||
15 | { | ||
16 | struct intc_desc_int *d = get_intc_desc(irq); | ||
17 | unsigned long handle = dist_handle[irq]; | ||
18 | unsigned long addr; | ||
19 | |||
20 | if (irq_balancing_disabled(irq) || !handle) | ||
21 | return; | ||
22 | |||
23 | addr = INTC_REG(d, _INTC_ADDR_D(handle), 0); | ||
24 | intc_reg_fns[_INTC_FN(handle)](addr, handle, 1); | ||
25 | } | ||
26 | |||
27 | void intc_balancing_disable(unsigned int irq) | ||
28 | { | ||
29 | struct intc_desc_int *d = get_intc_desc(irq); | ||
30 | unsigned long handle = dist_handle[irq]; | ||
31 | unsigned long addr; | ||
32 | |||
33 | if (irq_balancing_disabled(irq) || !handle) | ||
34 | return; | ||
35 | |||
36 | addr = INTC_REG(d, _INTC_ADDR_D(handle), 0); | ||
37 | intc_reg_fns[_INTC_FN(handle)](addr, handle, 0); | ||
38 | } | ||
39 | |||
40 | static unsigned int intc_dist_data(struct intc_desc *desc, | ||
41 | struct intc_desc_int *d, | ||
42 | intc_enum enum_id) | ||
43 | { | ||
44 | struct intc_mask_reg *mr = desc->hw.mask_regs; | ||
45 | unsigned int i, j, fn, mode; | ||
46 | unsigned long reg_e, reg_d; | ||
47 | |||
48 | for (i = 0; mr && enum_id && i < desc->hw.nr_mask_regs; i++) { | ||
49 | mr = desc->hw.mask_regs + i; | ||
50 | |||
51 | /* | ||
52 | * Skip this entry if there's no auto-distribution | ||
53 | * register associated with it. | ||
54 | */ | ||
55 | if (!mr->dist_reg) | ||
56 | continue; | ||
57 | |||
58 | for (j = 0; j < ARRAY_SIZE(mr->enum_ids); j++) { | ||
59 | if (mr->enum_ids[j] != enum_id) | ||
60 | continue; | ||
61 | |||
62 | fn = REG_FN_MODIFY_BASE; | ||
63 | mode = MODE_ENABLE_REG; | ||
64 | reg_e = mr->dist_reg; | ||
65 | reg_d = mr->dist_reg; | ||
66 | |||
67 | fn += (mr->reg_width >> 3) - 1; | ||
68 | return _INTC_MK(fn, mode, | ||
69 | intc_get_reg(d, reg_e), | ||
70 | intc_get_reg(d, reg_d), | ||
71 | 1, | ||
72 | (mr->reg_width - 1) - j); | ||
73 | } | ||
74 | } | ||
75 | |||
76 | /* | ||
77 | * It's possible we've gotten here with no distribution options | ||
78 | * available for the IRQ in question, so we just skip over those. | ||
79 | */ | ||
80 | return 0; | ||
81 | } | ||
82 | |||
83 | void intc_set_dist_handle(unsigned int irq, struct intc_desc *desc, | ||
84 | struct intc_desc_int *d, intc_enum id) | ||
85 | { | ||
86 | unsigned long flags; | ||
87 | |||
88 | /* | ||
89 | * Nothing to do for this IRQ. | ||
90 | */ | ||
91 | if (!desc->hw.mask_regs) | ||
92 | return; | ||
93 | |||
94 | raw_spin_lock_irqsave(&intc_big_lock, flags); | ||
95 | dist_handle[irq] = intc_dist_data(desc, d, id); | ||
96 | raw_spin_unlock_irqrestore(&intc_big_lock, flags); | ||
97 | } | ||