xref: /OK3568_Linux_fs/kernel/drivers/sh/intc/balancing.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
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