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