1 /* 2 * IRQ chip definitions for INTC IRQs. 3 * 4 * Copyright (C) 2007, 2008 Magnus Damm 5 * Copyright (C) 2009, 2010 Paul Mundt 6 * 7 * This file is subject to the terms and conditions of the GNU General Public 8 * License. See the file "COPYING" in the main directory of this archive 9 * for more details. 10 */ 11 #include <linux/cpumask.h> 12 #include <linux/io.h> 13 #include "internals.h" 14 15 void _intc_enable(unsigned int irq, unsigned long handle) 16 { 17 struct intc_desc_int *d = get_intc_desc(irq); 18 unsigned long addr; 19 unsigned int cpu; 20 21 for (cpu = 0; cpu < SMP_NR(d, _INTC_ADDR_E(handle)); cpu++) { 22 #ifdef CONFIG_SMP 23 if (!cpumask_test_cpu(cpu, irq_to_desc(irq)->affinity)) 24 continue; 25 #endif 26 addr = INTC_REG(d, _INTC_ADDR_E(handle), cpu); 27 intc_enable_fns[_INTC_MODE(handle)](addr, handle, intc_reg_fns\ 28 [_INTC_FN(handle)], irq); 29 } 30 31 intc_balancing_enable(irq); 32 } 33 34 static void intc_enable(unsigned int irq) 35 { 36 _intc_enable(irq, (unsigned long)get_irq_chip_data(irq)); 37 } 38 39 static void intc_disable(unsigned int irq) 40 { 41 struct intc_desc_int *d = get_intc_desc(irq); 42 unsigned long handle = (unsigned long)get_irq_chip_data(irq); 43 unsigned long addr; 44 unsigned int cpu; 45 46 intc_balancing_disable(irq); 47 48 for (cpu = 0; cpu < SMP_NR(d, _INTC_ADDR_D(handle)); cpu++) { 49 #ifdef CONFIG_SMP 50 if (!cpumask_test_cpu(cpu, irq_to_desc(irq)->affinity)) 51 continue; 52 #endif 53 addr = INTC_REG(d, _INTC_ADDR_D(handle), cpu); 54 intc_disable_fns[_INTC_MODE(handle)](addr, handle,intc_reg_fns\ 55 [_INTC_FN(handle)], irq); 56 } 57 } 58 59 static int intc_set_wake(unsigned int irq, unsigned int on) 60 { 61 return 0; /* allow wakeup, but setup hardware in intc_suspend() */ 62 } 63 64 #ifdef CONFIG_SMP 65 /* 66 * This is held with the irq desc lock held, so we don't require any 67 * additional locking here at the intc desc level. The affinity mask is 68 * later tested in the enable/disable paths. 69 */ 70 static int intc_set_affinity(unsigned int irq, const struct cpumask *cpumask) 71 { 72 if (!cpumask_intersects(cpumask, cpu_online_mask)) 73 return -1; 74 75 cpumask_copy(irq_to_desc(irq)->affinity, cpumask); 76 77 return 0; 78 } 79 #endif 80 81 static void intc_mask_ack(unsigned int irq) 82 { 83 struct intc_desc_int *d = get_intc_desc(irq); 84 unsigned long handle = intc_get_ack_handle(irq); 85 unsigned long addr; 86 87 intc_disable(irq); 88 89 /* read register and write zero only to the associated bit */ 90 if (handle) { 91 unsigned int value; 92 93 addr = INTC_REG(d, _INTC_ADDR_D(handle), 0); 94 value = intc_set_field_from_handle(0, 1, handle); 95 96 switch (_INTC_FN(handle)) { 97 case REG_FN_MODIFY_BASE + 0: /* 8bit */ 98 __raw_readb(addr); 99 __raw_writeb(0xff ^ value, addr); 100 break; 101 case REG_FN_MODIFY_BASE + 1: /* 16bit */ 102 __raw_readw(addr); 103 __raw_writew(0xffff ^ value, addr); 104 break; 105 case REG_FN_MODIFY_BASE + 3: /* 32bit */ 106 __raw_readl(addr); 107 __raw_writel(0xffffffff ^ value, addr); 108 break; 109 default: 110 BUG(); 111 break; 112 } 113 } 114 } 115 116 static struct intc_handle_int *intc_find_irq(struct intc_handle_int *hp, 117 unsigned int nr_hp, 118 unsigned int irq) 119 { 120 int i; 121 122 /* 123 * this doesn't scale well, but... 124 * 125 * this function should only be used for cerain uncommon 126 * operations such as intc_set_priority() and intc_set_type() 127 * and in those rare cases performance doesn't matter that much. 128 * keeping the memory footprint low is more important. 129 * 130 * one rather simple way to speed this up and still keep the 131 * memory footprint down is to make sure the array is sorted 132 * and then perform a bisect to lookup the irq. 133 */ 134 for (i = 0; i < nr_hp; i++) { 135 if ((hp + i)->irq != irq) 136 continue; 137 138 return hp + i; 139 } 140 141 return NULL; 142 } 143 144 int intc_set_priority(unsigned int irq, unsigned int prio) 145 { 146 struct intc_desc_int *d = get_intc_desc(irq); 147 struct intc_handle_int *ihp; 148 149 if (!intc_get_prio_level(irq) || prio <= 1) 150 return -EINVAL; 151 152 ihp = intc_find_irq(d->prio, d->nr_prio, irq); 153 if (ihp) { 154 if (prio >= (1 << _INTC_WIDTH(ihp->handle))) 155 return -EINVAL; 156 157 intc_set_prio_level(irq, prio); 158 159 /* 160 * only set secondary masking method directly 161 * primary masking method is using intc_prio_level[irq] 162 * priority level will be set during next enable() 163 */ 164 if (_INTC_FN(ihp->handle) != REG_FN_ERR) 165 _intc_enable(irq, ihp->handle); 166 } 167 return 0; 168 } 169 170 #define VALID(x) (x | 0x80) 171 172 static unsigned char intc_irq_sense_table[IRQ_TYPE_SENSE_MASK + 1] = { 173 [IRQ_TYPE_EDGE_FALLING] = VALID(0), 174 [IRQ_TYPE_EDGE_RISING] = VALID(1), 175 [IRQ_TYPE_LEVEL_LOW] = VALID(2), 176 /* SH7706, SH7707 and SH7709 do not support high level triggered */ 177 #if !defined(CONFIG_CPU_SUBTYPE_SH7706) && \ 178 !defined(CONFIG_CPU_SUBTYPE_SH7707) && \ 179 !defined(CONFIG_CPU_SUBTYPE_SH7709) 180 [IRQ_TYPE_LEVEL_HIGH] = VALID(3), 181 #endif 182 }; 183 184 static int intc_set_type(unsigned int irq, unsigned int type) 185 { 186 struct intc_desc_int *d = get_intc_desc(irq); 187 unsigned char value = intc_irq_sense_table[type & IRQ_TYPE_SENSE_MASK]; 188 struct intc_handle_int *ihp; 189 unsigned long addr; 190 191 if (!value) 192 return -EINVAL; 193 194 ihp = intc_find_irq(d->sense, d->nr_sense, irq); 195 if (ihp) { 196 addr = INTC_REG(d, _INTC_ADDR_E(ihp->handle), 0); 197 intc_reg_fns[_INTC_FN(ihp->handle)](addr, ihp->handle, value); 198 } 199 200 return 0; 201 } 202 203 struct irq_chip intc_irq_chip = { 204 .mask = intc_disable, 205 .unmask = intc_enable, 206 .mask_ack = intc_mask_ack, 207 .enable = intc_enable, 208 .disable = intc_disable, 209 .shutdown = intc_disable, 210 .set_type = intc_set_type, 211 .set_wake = intc_set_wake, 212 #ifdef CONFIG_SMP 213 .set_affinity = intc_set_affinity, 214 #endif 215 }; 216