1 /* 2 * TI Common Platform Interrupt Controller (cp_intc) driver 3 * 4 * Author: Steve Chen <schen@mvista.com> 5 * Copyright (C) 2008-2009, MontaVista Software, Inc. <source@mvista.com> 6 * 7 * This file is licensed under the terms of the GNU General Public License 8 * version 2. This program is licensed "as is" without any warranty of any 9 * kind, whether express or implied. 10 */ 11 12 #include <linux/export.h> 13 #include <linux/init.h> 14 #include <linux/irq.h> 15 #include <linux/irqchip.h> 16 #include <linux/irqdomain.h> 17 #include <linux/io.h> 18 #include <linux/of.h> 19 #include <linux/of_address.h> 20 #include <linux/of_irq.h> 21 22 #include <mach/common.h> 23 #include "cp_intc.h" 24 25 static inline unsigned int cp_intc_read(unsigned offset) 26 { 27 return __raw_readl(davinci_intc_base + offset); 28 } 29 30 static inline void cp_intc_write(unsigned long value, unsigned offset) 31 { 32 __raw_writel(value, davinci_intc_base + offset); 33 } 34 35 static void cp_intc_ack_irq(struct irq_data *d) 36 { 37 cp_intc_write(d->hwirq, CP_INTC_SYS_STAT_IDX_CLR); 38 } 39 40 /* Disable interrupt */ 41 static void cp_intc_mask_irq(struct irq_data *d) 42 { 43 /* XXX don't know why we need to disable nIRQ here... */ 44 cp_intc_write(1, CP_INTC_HOST_ENABLE_IDX_CLR); 45 cp_intc_write(d->hwirq, CP_INTC_SYS_ENABLE_IDX_CLR); 46 cp_intc_write(1, CP_INTC_HOST_ENABLE_IDX_SET); 47 } 48 49 /* Enable interrupt */ 50 static void cp_intc_unmask_irq(struct irq_data *d) 51 { 52 cp_intc_write(d->hwirq, CP_INTC_SYS_ENABLE_IDX_SET); 53 } 54 55 static int cp_intc_set_irq_type(struct irq_data *d, unsigned int flow_type) 56 { 57 unsigned reg = BIT_WORD(d->hwirq); 58 unsigned mask = BIT_MASK(d->hwirq); 59 unsigned polarity = cp_intc_read(CP_INTC_SYS_POLARITY(reg)); 60 unsigned type = cp_intc_read(CP_INTC_SYS_TYPE(reg)); 61 62 switch (flow_type) { 63 case IRQ_TYPE_EDGE_RISING: 64 polarity |= mask; 65 type |= mask; 66 break; 67 case IRQ_TYPE_EDGE_FALLING: 68 polarity &= ~mask; 69 type |= mask; 70 break; 71 case IRQ_TYPE_LEVEL_HIGH: 72 polarity |= mask; 73 type &= ~mask; 74 break; 75 case IRQ_TYPE_LEVEL_LOW: 76 polarity &= ~mask; 77 type &= ~mask; 78 break; 79 default: 80 return -EINVAL; 81 } 82 83 cp_intc_write(polarity, CP_INTC_SYS_POLARITY(reg)); 84 cp_intc_write(type, CP_INTC_SYS_TYPE(reg)); 85 86 return 0; 87 } 88 89 static struct irq_chip cp_intc_irq_chip = { 90 .name = "cp_intc", 91 .irq_ack = cp_intc_ack_irq, 92 .irq_mask = cp_intc_mask_irq, 93 .irq_unmask = cp_intc_unmask_irq, 94 .irq_set_type = cp_intc_set_irq_type, 95 .flags = IRQCHIP_SKIP_SET_WAKE, 96 }; 97 98 static struct irq_domain *cp_intc_domain; 99 100 static int cp_intc_host_map(struct irq_domain *h, unsigned int virq, 101 irq_hw_number_t hw) 102 { 103 pr_debug("cp_intc_host_map(%d, 0x%lx)\n", virq, hw); 104 105 irq_set_chip(virq, &cp_intc_irq_chip); 106 irq_set_probe(virq); 107 irq_set_handler(virq, handle_edge_irq); 108 return 0; 109 } 110 111 static const struct irq_domain_ops cp_intc_host_ops = { 112 .map = cp_intc_host_map, 113 .xlate = irq_domain_xlate_onetwocell, 114 }; 115 116 int __init cp_intc_of_init(struct device_node *node, struct device_node *parent) 117 { 118 u32 num_irq = davinci_soc_info.intc_irq_num; 119 u8 *irq_prio = davinci_soc_info.intc_irq_prios; 120 u32 *host_map = davinci_soc_info.intc_host_map; 121 unsigned num_reg = BITS_TO_LONGS(num_irq); 122 int i, irq_base; 123 124 davinci_intc_type = DAVINCI_INTC_TYPE_CP_INTC; 125 if (node) { 126 davinci_intc_base = of_iomap(node, 0); 127 if (of_property_read_u32(node, "ti,intc-size", &num_irq)) 128 pr_warn("unable to get intc-size, default to %d\n", 129 num_irq); 130 } else { 131 davinci_intc_base = ioremap(davinci_soc_info.intc_base, SZ_8K); 132 } 133 if (WARN_ON(!davinci_intc_base)) 134 return -EINVAL; 135 136 cp_intc_write(0, CP_INTC_GLOBAL_ENABLE); 137 138 /* Disable all host interrupts */ 139 cp_intc_write(0, CP_INTC_HOST_ENABLE(0)); 140 141 /* Disable system interrupts */ 142 for (i = 0; i < num_reg; i++) 143 cp_intc_write(~0, CP_INTC_SYS_ENABLE_CLR(i)); 144 145 /* Set to normal mode, no nesting, no priority hold */ 146 cp_intc_write(0, CP_INTC_CTRL); 147 cp_intc_write(0, CP_INTC_HOST_CTRL); 148 149 /* Clear system interrupt status */ 150 for (i = 0; i < num_reg; i++) 151 cp_intc_write(~0, CP_INTC_SYS_STAT_CLR(i)); 152 153 /* Enable nIRQ (what about nFIQ?) */ 154 cp_intc_write(1, CP_INTC_HOST_ENABLE_IDX_SET); 155 156 /* 157 * Priority is determined by host channel: lower channel number has 158 * higher priority i.e. channel 0 has highest priority and channel 31 159 * had the lowest priority. 160 */ 161 num_reg = (num_irq + 3) >> 2; /* 4 channels per register */ 162 if (irq_prio) { 163 unsigned j, k; 164 u32 val; 165 166 for (k = i = 0; i < num_reg; i++) { 167 for (val = j = 0; j < 4; j++, k++) { 168 val >>= 8; 169 if (k < num_irq) 170 val |= irq_prio[k] << 24; 171 } 172 173 cp_intc_write(val, CP_INTC_CHAN_MAP(i)); 174 } 175 } else { 176 /* 177 * Default everything to channel 15 if priority not specified. 178 * Note that channel 0-1 are mapped to nFIQ and channels 2-31 179 * are mapped to nIRQ. 180 */ 181 for (i = 0; i < num_reg; i++) 182 cp_intc_write(0x0f0f0f0f, CP_INTC_CHAN_MAP(i)); 183 } 184 185 if (host_map) 186 for (i = 0; host_map[i] != -1; i++) 187 cp_intc_write(host_map[i], CP_INTC_HOST_MAP(i)); 188 189 irq_base = irq_alloc_descs(-1, 0, num_irq, 0); 190 if (irq_base < 0) { 191 pr_warn("Couldn't allocate IRQ numbers\n"); 192 irq_base = 0; 193 } 194 195 /* create a legacy host */ 196 cp_intc_domain = irq_domain_add_legacy(node, num_irq, 197 irq_base, 0, &cp_intc_host_ops, NULL); 198 199 if (!cp_intc_domain) { 200 pr_err("cp_intc: failed to allocate irq host!\n"); 201 return -EINVAL; 202 } 203 204 /* Enable global interrupt */ 205 cp_intc_write(1, CP_INTC_GLOBAL_ENABLE); 206 207 return 0; 208 } 209 210 void __init cp_intc_init(void) 211 { 212 cp_intc_of_init(NULL, NULL); 213 } 214 215 IRQCHIP_DECLARE(cp_intc, "ti,cp-intc", cp_intc_of_init); 216
Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.