/* * Copyright 2014, General Dynamics C4 Systems * * This software may be distributed and modified according to the terms of * the GNU General Public License version 2. Note that NO WARRANTY is provided. * See "LICENSE_GPLv2.txt" for details. * * @TAG(GD_GPL) */ #include #include #define TARGET_CPU_ALLINT(CPU) ( \ ( ((CPU)&0xff)<<0u ) |\ ( ((CPU)&0xff)<<8u ) |\ ( ((CPU)&0xff)<<16u ) |\ ( ((CPU)&0xff)<<24u ) \ ) #define TARGET_CPU0_ALLINT TARGET_CPU_ALLINT(BIT(0)) /* Use this to forward interrupts to all CPUs when debugging */ #define TARGET_CPU_ALL_ALLINT TARGET_CPU_ALLINT(0xff) #define IRQ_SET_ALL 0xffffffff; /* Shift positions for GICD_SGIR register */ #define GICD_SGIR_SGIINTID_SHIFT 0 #define GICD_SGIR_CPUTARGETLIST_SHIFT 16 #define GICD_SGIR_TARGETLISTFILTER_SHIFT 24 #ifndef GIC_PL390_DISTRIBUTOR_PPTR #error GIC_PL390_DISTRIBUTOR_PPTR must be defined for virtual memory access to the gic distributer #else /* GIC_DISTRIBUTOR_PPTR */ volatile struct gic_dist_map * const gic_dist = (volatile struct gic_dist_map*)(GIC_PL390_DISTRIBUTOR_PPTR); #endif /* GIC_DISTRIBUTOR_PPTR */ #ifndef GIC_PL390_CONTROLLER_PPTR #error GIC_PL390_CONTROLLER_PPTR must be defined for virtual memory access to the gic cpu interface #else /* GIC_CONTROLLER_PPTR */ volatile struct gic_cpu_iface_map * const gic_cpuiface = (volatile struct gic_cpu_iface_map*)(GIC_PL390_CONTROLLER_PPTR); #endif /* GIC_CONTROLLER_PPTR */ uint32_t active_irq[CONFIG_MAX_NUM_NODES] = {IRQ_NONE}; /* Get the target id for this processor. We rely on the constraint that the registers * for PPI are read only and return only the current processor as the target. * If this doesn't lead to a valid ID, we emit a warning and default to core 0. */ BOOT_CODE static uint8_t infer_cpu_gic_id(int nirqs) { word_t i; uint32_t target = 0; for (i = 0; i < nirqs; i += 4) { target = gic_dist->targets[i >> 2]; target |= target >> 16; target |= target >> 8; if (target) { break; } } if (!target) { printf("Warning: Could not infer GIC interrupt target ID, assuming 0.\n"); target = BIT(0); } return target & 0xff; } BOOT_CODE static void dist_init(void) { word_t i; int nirqs = 32 * ((gic_dist->ic_type & 0x1f) + 1); gic_dist->enable = 0; for (i = 0; i < nirqs; i += 32) { /* disable */ gic_dist->enable_clr[i >> 5] = IRQ_SET_ALL; /* clear pending */ gic_dist->pending_clr[i >> 5] = IRQ_SET_ALL; } /* reset interrupts priority */ for (i = 32; i < nirqs; i += 4) { if (config_set(CONFIG_ARM_HYPERVISOR_SUPPORT)) { gic_dist->priority[i >> 2] = 0x80808080; } else { gic_dist->priority[i >> 2] = 0; } } /* * reset int target to current cpu * We query which id that the GIC uses for us and use that. */ uint8_t target = infer_cpu_gic_id(nirqs); for (i = 0; i < nirqs; i += 4) { gic_dist->targets[i >> 2] = TARGET_CPU_ALLINT(target); } /* level-triggered, 1-N */ for (i = 64; i < nirqs; i += 32) { gic_dist->config[i >> 5] = 0x55555555; } /* group 0 for secure; group 1 for non-secure */ for (i = 0; i < nirqs; i += 32) { if (config_set(CONFIG_ARM_HYPERVISOR_SUPPORT)) { gic_dist->security[i >> 5] = 0xffffffff; } else { gic_dist->security[i >> 5] = 0; } } /* enable the int controller */ gic_dist->enable = 1; } BOOT_CODE static void cpu_iface_init(void) { uint32_t i; /* For non-Exynos4, the registers are banked per CPU, need to clear them */ gic_dist->enable_clr[0] = IRQ_SET_ALL; gic_dist->pending_clr[0] = IRQ_SET_ALL; /* put everything in group 0; group 1 if in hyp mode */ if (config_set(CONFIG_ARM_HYPERVISOR_SUPPORT)) { gic_dist->security[0] = 0xffffffff; gic_dist->priority[0] = 0x80808080; } else { gic_dist->security[0] = 0; gic_dist->priority[0] = 0x0; } /* clear any software generated interrupts */ for (i = 0; i < 16; i += 4) { gic_dist->sgi_pending_clr[i >> 2] = IRQ_SET_ALL; } gic_cpuiface->icontrol = 0; /* the write to priority mask is ignored if the kernel is * in non-secure mode and the priority mask is already configured * by secure mode software. the elfloader should config the * interrupt routing properly to ensure that the hyp-mode kernel * can get interrupts */ gic_cpuiface->pri_msk_c = 0x000000f0; gic_cpuiface->pb_c = 0x00000003; i = gic_cpuiface->int_ack; while ((i & IRQ_MASK) != IRQ_NONE) { gic_cpuiface->eoi = i; i = gic_cpuiface->int_ack; } gic_cpuiface->icontrol = 1; } void setIRQTrigger(irq_t irq, bool_t trigger) { /* in the gic_config, there is a 2 bit field for each irq, * setting the most significant bit of this field makes the irq edge-triggered, * while 0 indicates that it is level-triggered */ word_t index = irq / 16u; word_t offset = (irq % 16u) * 2; if (trigger) { /* set the bit */ gic_dist->config[index] |= BIT(offset + 1); } else { gic_dist->config[index] &= ~BIT(offset + 1); } } BOOT_CODE void initIRQController(void) { dist_init(); } BOOT_CODE void cpu_initLocalIRQController(void) { cpu_iface_init(); } #ifdef ENABLE_SMP_SUPPORT /* * 25-24: target lister filter * 0b00 - send the ipi to the CPU interfaces specified in the CPU target list * 0b01 - send the ipi to all CPU interfaces except the cpu interface. * that requrested teh ipi * 0b10 - send the ipi only to the CPU interface that requested the IPI. * 0b11 - reserved *. * 23-16: CPU targets list * each bit of CPU target list [7:0] refers to the corresponding CPU interface. * 3-0: SGIINTID * software generated interrupt id, from 0 to 15... */ void ipiBroadcast(irq_t irq, bool_t includeSelfCPU) { gic_dist->sgi_control = (!includeSelfCPU << GICD_SGIR_TARGETLISTFILTER_SHIFT) | (irq << GICD_SGIR_SGIINTID_SHIFT); } void ipi_send_target(irq_t irq, word_t cpuTargetList) { gic_dist->sgi_control = (cpuTargetList << GICD_SGIR_CPUTARGETLIST_SHIFT) | (irq << GICD_SGIR_SGIINTID_SHIFT); } #endif /* ENABLE_SMP_SUPPORT */ #ifdef CONFIG_ARM_HYPERVISOR_SUPPORT #ifndef GIC_PL400_VCPUCTRL_PPTR #error GIC_PL400_VCPUCTRL_PPTR must be defined for virtual memory access to the gic virtual cpu interface control #else /* GIC_PL400_GICVCPUCTRL_PPTR */ volatile struct gich_vcpu_ctrl_map *gic_vcpu_ctrl = (volatile struct gich_vcpu_ctrl_map*)(GIC_PL400_VCPUCTRL_PPTR); #endif /* GIC_PL400_GICVCPUCTRL_PPTR */ unsigned int gic_vcpu_num_list_regs; #endif /* End of CONFIG_ARM_HYPERVISOR_SUPPORT */