Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at v4.14 343 lines 9.6 kB view raw
1// SPDX-License-Identifier: GPL-2.0 2/* 3 * Meta internal (HWSTATMETA) interrupt code. 4 * 5 * Copyright (C) 2011-2012 Imagination Technologies Ltd. 6 * 7 * This code is based on the code in SoC/common/irq.c and SoC/comet/irq.c 8 * The code base could be generalised/merged as a lot of the functionality is 9 * similar. Until this is done, we try to keep the code simple here. 10 */ 11 12#include <linux/interrupt.h> 13#include <linux/io.h> 14#include <linux/irqdomain.h> 15 16#include <asm/irq.h> 17#include <asm/hwthread.h> 18 19#define PERF0VECINT 0x04820580 20#define PERF1VECINT 0x04820588 21#define PERF0TRIG_OFFSET 16 22#define PERF1TRIG_OFFSET 17 23 24/** 25 * struct metag_internal_irq_priv - private meta internal interrupt data 26 * @domain: IRQ domain for all internal Meta IRQs (HWSTATMETA) 27 * @unmasked: Record of unmasked IRQs 28 */ 29struct metag_internal_irq_priv { 30 struct irq_domain *domain; 31 32 unsigned long unmasked; 33}; 34 35/* Private data for the one and only internal interrupt controller */ 36static struct metag_internal_irq_priv metag_internal_irq_priv; 37 38static unsigned int metag_internal_irq_startup(struct irq_data *data); 39static void metag_internal_irq_shutdown(struct irq_data *data); 40static void metag_internal_irq_ack(struct irq_data *data); 41static void metag_internal_irq_mask(struct irq_data *data); 42static void metag_internal_irq_unmask(struct irq_data *data); 43#ifdef CONFIG_SMP 44static int metag_internal_irq_set_affinity(struct irq_data *data, 45 const struct cpumask *cpumask, bool force); 46#endif 47 48static struct irq_chip internal_irq_edge_chip = { 49 .name = "HWSTATMETA-IRQ", 50 .irq_startup = metag_internal_irq_startup, 51 .irq_shutdown = metag_internal_irq_shutdown, 52 .irq_ack = metag_internal_irq_ack, 53 .irq_mask = metag_internal_irq_mask, 54 .irq_unmask = metag_internal_irq_unmask, 55#ifdef CONFIG_SMP 56 .irq_set_affinity = metag_internal_irq_set_affinity, 57#endif 58}; 59 60/* 61 * metag_hwvec_addr - get the address of *VECINT regs of irq 62 * 63 * This function is a table of supported triggers on HWSTATMETA 64 * Could do with a structure, but better keep it simple. Changes 65 * in this code should be rare. 66 */ 67static inline void __iomem *metag_hwvec_addr(irq_hw_number_t hw) 68{ 69 void __iomem *addr; 70 71 switch (hw) { 72 case PERF0TRIG_OFFSET: 73 addr = (void __iomem *)PERF0VECINT; 74 break; 75 case PERF1TRIG_OFFSET: 76 addr = (void __iomem *)PERF1VECINT; 77 break; 78 default: 79 addr = NULL; 80 break; 81 } 82 return addr; 83} 84 85/* 86 * metag_internal_startup - setup an internal irq 87 * @irq: the irq to startup 88 * 89 * Multiplex interrupts for @irq onto TR1. Clear any pending 90 * interrupts. 91 */ 92static unsigned int metag_internal_irq_startup(struct irq_data *data) 93{ 94 /* Clear (toggle) the bit in HWSTATMETA for our interrupt. */ 95 metag_internal_irq_ack(data); 96 97 /* Enable the interrupt by unmasking it */ 98 metag_internal_irq_unmask(data); 99 100 return 0; 101} 102 103/* 104 * metag_internal_irq_shutdown - turn off the irq 105 * @irq: the irq number to turn off 106 * 107 * Mask @irq and clear any pending interrupts. 108 * Stop muxing @irq onto TR1. 109 */ 110static void metag_internal_irq_shutdown(struct irq_data *data) 111{ 112 /* Disable the IRQ at the core by masking it. */ 113 metag_internal_irq_mask(data); 114 115 /* Clear (toggle) the bit in HWSTATMETA for our interrupt. */ 116 metag_internal_irq_ack(data); 117} 118 119/* 120 * metag_internal_irq_ack - acknowledge irq 121 * @irq: the irq to ack 122 */ 123static void metag_internal_irq_ack(struct irq_data *data) 124{ 125 irq_hw_number_t hw = data->hwirq; 126 unsigned int bit = 1 << hw; 127 128 if (metag_in32(HWSTATMETA) & bit) 129 metag_out32(bit, HWSTATMETA); 130} 131 132/** 133 * metag_internal_irq_mask() - mask an internal irq by unvectoring 134 * @data: data for the internal irq to mask 135 * 136 * HWSTATMETA has no mask register. Instead the IRQ is unvectored from the core 137 * and retriggered if necessary later. 138 */ 139static void metag_internal_irq_mask(struct irq_data *data) 140{ 141 struct metag_internal_irq_priv *priv = &metag_internal_irq_priv; 142 irq_hw_number_t hw = data->hwirq; 143 void __iomem *vec_addr = metag_hwvec_addr(hw); 144 145 clear_bit(hw, &priv->unmasked); 146 147 /* there is no interrupt mask, so unvector the interrupt */ 148 metag_out32(0, vec_addr); 149} 150 151/** 152 * meta_intc_unmask_edge_irq_nomask() - unmask an edge irq by revectoring 153 * @data: data for the internal irq to unmask 154 * 155 * HWSTATMETA has no mask register. Instead the IRQ is revectored back to the 156 * core and retriggered if necessary. 157 */ 158static void metag_internal_irq_unmask(struct irq_data *data) 159{ 160 struct metag_internal_irq_priv *priv = &metag_internal_irq_priv; 161 irq_hw_number_t hw = data->hwirq; 162 unsigned int bit = 1 << hw; 163 void __iomem *vec_addr = metag_hwvec_addr(hw); 164 unsigned int thread = hard_processor_id(); 165 166 set_bit(hw, &priv->unmasked); 167 168 /* there is no interrupt mask, so revector the interrupt */ 169 metag_out32(TBI_TRIG_VEC(TBID_SIGNUM_TR1(thread)), vec_addr); 170 171 /* 172 * Re-trigger interrupt 173 * 174 * Writing a 1 toggles, and a 0->1 transition triggers. We only 175 * retrigger if the status bit is already set, which means we 176 * need to clear it first. Retriggering is fundamentally racy 177 * because if the interrupt fires again after we clear it we 178 * could end up clearing it again and the interrupt handler 179 * thinking it hasn't fired. Therefore we need to keep trying to 180 * retrigger until the bit is set. 181 */ 182 if (metag_in32(HWSTATMETA) & bit) { 183 metag_out32(bit, HWSTATMETA); 184 while (!(metag_in32(HWSTATMETA) & bit)) 185 metag_out32(bit, HWSTATMETA); 186 } 187} 188 189#ifdef CONFIG_SMP 190/* 191 * metag_internal_irq_set_affinity - set the affinity for an interrupt 192 */ 193static int metag_internal_irq_set_affinity(struct irq_data *data, 194 const struct cpumask *cpumask, bool force) 195{ 196 unsigned int cpu, thread; 197 irq_hw_number_t hw = data->hwirq; 198 /* 199 * Wire up this interrupt from *VECINT to the Meta core. 200 * 201 * Note that we can't wire up *VECINT to interrupt more than 202 * one cpu (the interrupt code doesn't support it), so we just 203 * pick the first cpu we find in 'cpumask'. 204 */ 205 cpu = cpumask_any_and(cpumask, cpu_online_mask); 206 thread = cpu_2_hwthread_id[cpu]; 207 208 metag_out32(TBI_TRIG_VEC(TBID_SIGNUM_TR1(thread)), 209 metag_hwvec_addr(hw)); 210 211 return 0; 212} 213#endif 214 215/* 216 * metag_internal_irq_demux - irq de-multiplexer 217 * @irq: the interrupt number 218 * @desc: the interrupt description structure for this irq 219 * 220 * The cpu receives an interrupt on TR1 when an interrupt has 221 * occurred. It is this function's job to demux this irq and 222 * figure out exactly which trigger needs servicing. 223 */ 224static void metag_internal_irq_demux(struct irq_desc *desc) 225{ 226 struct metag_internal_irq_priv *priv = irq_desc_get_handler_data(desc); 227 irq_hw_number_t hw; 228 unsigned int irq_no; 229 u32 status; 230 231recalculate: 232 status = metag_in32(HWSTATMETA) & priv->unmasked; 233 234 for (hw = 0; status != 0; status >>= 1, ++hw) { 235 if (status & 0x1) { 236 /* 237 * Map the hardware IRQ number to a virtual Linux IRQ 238 * number. 239 */ 240 irq_no = irq_linear_revmap(priv->domain, hw); 241 242 /* 243 * Only fire off interrupts that are 244 * registered to be handled by the kernel. 245 * Other interrupts are probably being 246 * handled by other Meta hardware threads. 247 */ 248 generic_handle_irq(irq_no); 249 250 /* 251 * The handler may have re-enabled interrupts 252 * which could have caused a nested invocation 253 * of this code and make the copy of the 254 * status register we are using invalid. 255 */ 256 goto recalculate; 257 } 258 } 259} 260 261/** 262 * internal_irq_map() - Map an internal meta IRQ to a virtual IRQ number. 263 * @hw: Number of the internal IRQ. Must be in range. 264 * 265 * Returns: The virtual IRQ number of the Meta internal IRQ specified by 266 * @hw. 267 */ 268int internal_irq_map(unsigned int hw) 269{ 270 struct metag_internal_irq_priv *priv = &metag_internal_irq_priv; 271 if (!priv->domain) 272 return -ENODEV; 273 return irq_create_mapping(priv->domain, hw); 274} 275 276/** 277 * metag_internal_irq_init_cpu - regsister with the Meta cpu 278 * @cpu: the CPU to register on 279 * 280 * Configure @cpu's TR1 irq so that we can demux irqs. 281 */ 282static void metag_internal_irq_init_cpu(struct metag_internal_irq_priv *priv, 283 int cpu) 284{ 285 unsigned int thread = cpu_2_hwthread_id[cpu]; 286 unsigned int signum = TBID_SIGNUM_TR1(thread); 287 int irq = tbisig_map(signum); 288 289 /* Register the multiplexed IRQ handler */ 290 irq_set_chained_handler_and_data(irq, metag_internal_irq_demux, priv); 291 irq_set_irq_type(irq, IRQ_TYPE_LEVEL_LOW); 292} 293 294/** 295 * metag_internal_intc_map() - map an internal irq 296 * @d: irq domain of internal trigger block 297 * @irq: virtual irq number 298 * @hw: hardware irq number within internal trigger block 299 * 300 * This sets up a virtual irq for a specified hardware interrupt. The irq chip 301 * and handler is configured. 302 */ 303static int metag_internal_intc_map(struct irq_domain *d, unsigned int irq, 304 irq_hw_number_t hw) 305{ 306 /* only register interrupt if it is mapped */ 307 if (!metag_hwvec_addr(hw)) 308 return -EINVAL; 309 310 irq_set_chip_and_handler(irq, &internal_irq_edge_chip, 311 handle_edge_irq); 312 return 0; 313} 314 315static const struct irq_domain_ops metag_internal_intc_domain_ops = { 316 .map = metag_internal_intc_map, 317}; 318 319/** 320 * metag_internal_irq_register - register internal IRQs 321 * 322 * Register the irq chip and handler function for all internal IRQs 323 */ 324int __init init_internal_IRQ(void) 325{ 326 struct metag_internal_irq_priv *priv = &metag_internal_irq_priv; 327 unsigned int cpu; 328 329 /* Set up an IRQ domain */ 330 priv->domain = irq_domain_add_linear(NULL, 32, 331 &metag_internal_intc_domain_ops, 332 priv); 333 if (unlikely(!priv->domain)) { 334 pr_err("meta-internal-intc: cannot add IRQ domain\n"); 335 return -ENOMEM; 336 } 337 338 /* Setup TR1 for all cpus. */ 339 for_each_possible_cpu(cpu) 340 metag_internal_irq_init_cpu(priv, cpu); 341 342 return 0; 343};