274 lines
7.7 KiB
C
274 lines
7.7 KiB
C
/*
|
|
* Copyright (c) 2012, ETH Zurich.
|
|
* All rights reserved.
|
|
*
|
|
* This file is distributed under the terms in the attached LICENSE file.
|
|
* If you do not find this file, copies can be found by writing to:
|
|
* ETH Zurich D-INFK, CAB F.78, Universitaetstr 6, CH-8092 Zurich.
|
|
*/
|
|
|
|
#include <kernel.h>
|
|
|
|
#include <dev/pl390_gic_cpuif_dev.h>
|
|
#include <dev/pl390_gic_dist_dev.h>
|
|
#include <arch/arm/gic.h>
|
|
#include <arch/arm/platform.h>
|
|
#include <paging_kernel_arch.h>
|
|
#include <irq.h>
|
|
#include <getopt/getopt.h>
|
|
|
|
static pl390_gic_cpuif_t gic;
|
|
static pl390_gic_dist_t gic_dist;
|
|
|
|
#define MSG(format, ...) printk( LOG_NOTE, "GIC: "format, ## __VA_ARGS__ )
|
|
|
|
extern lpaddr_t platform_gic_cpu_interface_base;
|
|
extern lpaddr_t platform_gic_distributor_base;
|
|
|
|
// Command line arguments
|
|
static struct cmdarg cmdargs[] = {
|
|
{"gic", ArgType_UInt, { .uinteger = &platform_gic_cpu_interface_base }},
|
|
{"gicdist", ArgType_UInt, { .uinteger = &platform_gic_distributor_base }},
|
|
{NULL, 0, {NULL}}
|
|
};
|
|
|
|
enum IrqType {
|
|
IrqType_SGI,
|
|
IrqType_PPI,
|
|
IrqType_SPI
|
|
};
|
|
|
|
#define CPU_SIZE 0x1000
|
|
#define DIST_SIZE 0x1000
|
|
|
|
/**
|
|
* \brief Returns the IRQ type based on the interrupt ID
|
|
*
|
|
* We have three types of interrupts
|
|
* 1) Software generated Interrupts (SGI): IDs 0-15
|
|
* 2) Private Peripheral Interrupts (PPI): IDs 16-31
|
|
* 3) Shared Peripheral Interrups (SPI): IDs 32-
|
|
*
|
|
* \return The type of the interrupt.
|
|
*/
|
|
static enum IrqType get_irq_type(uint32_t int_id)
|
|
{
|
|
if (int_id < 16) {
|
|
return IrqType_SGI;
|
|
} else if (int_id < 32) {
|
|
return IrqType_PPI;
|
|
} else {
|
|
return IrqType_SPI;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Initialize the global interrupt controller
|
|
*
|
|
* There are three types of interrupts
|
|
* 1) Software generated Interrupts (SGI) - IDs 0-15
|
|
* 2) Private Peripheral Interrupts (PPI) - IDs 16-31
|
|
* 3) Shared Peripheral Interrups (SPI) - IDs 32...
|
|
*/
|
|
void gic_init(void)
|
|
{
|
|
parse_commandline(kernel_command_line, cmdargs);
|
|
printk(LOG_NOTE, "GICv1: Initializing. cpu_if=%p, dist=%p\n",
|
|
platform_gic_cpu_interface_base, platform_gic_distributor_base);
|
|
lvaddr_t gic_cpu_base =
|
|
paging_map_device(platform_gic_cpu_interface_base, CPU_SIZE );
|
|
pl390_gic_cpuif_initialize(&gic, (mackerel_addr_t)gic_cpu_base );
|
|
|
|
lvaddr_t gic_dist_base =
|
|
paging_map_device(platform_gic_distributor_base, DIST_SIZE );
|
|
pl390_gic_dist_initialize(&gic_dist, (mackerel_addr_t)gic_dist_base );
|
|
|
|
// set priority mask of cpu interface, currently set to lowest priority
|
|
// to accept all interrupts
|
|
pl390_gic_cpuif_ICCPMR_wr(&gic, 0xff);
|
|
|
|
// set binary point to define split of group- and subpriority
|
|
// currently we allow for 8 subpriorities
|
|
pl390_gic_cpuif_ICCBPR_wr(&gic, 0x2);
|
|
|
|
// enable interrupt forwarding to processor
|
|
pl390_gic_cpuif_ICCICR_enable_wrf(&gic, 0x1);
|
|
|
|
MSG("gic_init done\n");
|
|
}
|
|
|
|
void __attribute__((noreturn)) gic_disable_all_irqs(void)
|
|
{
|
|
panic("gic_disable_all_irqs NYI for armv7");
|
|
// XXX Rewrite according to pl390 interface changes!
|
|
// ALSO remove noreturn option
|
|
|
|
/* //disable PPI interrupts */
|
|
/* pl390_gic_cpuif_PPI_ICDICER_wr(&gic, (uint16_t)0xffff); */
|
|
|
|
/* //disable SPI interrupts */
|
|
/* for(uint8_t i=0; i < it_num_lines; i++) { */
|
|
/* pl390_gic_cpuif_SPI_ICDICER_wr(&gic, i, (uint32_t)0xffffffff); */
|
|
/* } */
|
|
}
|
|
|
|
uint32_t platform_get_active_irq(void)
|
|
{
|
|
return pl390_gic_cpuif_ICCIAR_rd(&gic);
|
|
}
|
|
|
|
uint32_t gic_cpu_count(void){
|
|
return 1;
|
|
/*
|
|
* LH: The CPU count can be determined using the gic distributor, but
|
|
* it is called before the distributor is initialized. However,
|
|
* with the new coreboot it seems to be unnecessary anyway.
|
|
*
|
|
* return pl390_gic_dist_ICDICTR_cpu_number_rdf(&gic_dist);
|
|
*/
|
|
}
|
|
|
|
void gic_raise_softirq(uint8_t cpumask, uint8_t irq)
|
|
{
|
|
assert(!"NYI"); // This needs distributor access
|
|
}
|
|
|
|
void platform_acknowledge_irq(uint32_t irq)
|
|
{
|
|
pl390_gic_cpuif_ICCEOIR_rawwr(&gic, irq);
|
|
}
|
|
|
|
//enable interrupt forwarding to processor
|
|
void gic_cpu_interface_enable(void)
|
|
{
|
|
pl390_gic_cpuif_ICCICR_wr(&gic, 0x1);
|
|
}
|
|
|
|
errval_t platform_init_ic_bsp(void) {
|
|
gic_init();
|
|
gic_cpu_interface_enable();
|
|
return SYS_ERR_OK;
|
|
}
|
|
|
|
errval_t platform_init_ic_app(void) {
|
|
gic_init();
|
|
gic_cpu_interface_enable();
|
|
return SYS_ERR_OK;
|
|
}
|
|
|
|
/**
|
|
* \brief Enable an PPI interrupt
|
|
*
|
|
* \see ARM Generic Interrupt Controller Architecture Specification v1.0
|
|
*
|
|
* \param int_id
|
|
* \param prio Priority of the interrupt (lower is higher). We allow 0..15.
|
|
* The number of priority bits is implementation specific, but at least 16
|
|
* (using bits [7:4] of the priority field, chapter 3.3)
|
|
* \param 0 is level-sensitive, 1 is edge-triggered
|
|
* \param 0 is N-to-N, 1 is 1-N
|
|
*/
|
|
errval_t platform_enable_interrupt(uint32_t int_id, uint16_t prio,
|
|
bool edge_triggered, bool one_to_n)
|
|
{
|
|
// Set Interrupt Set-Enable Register
|
|
uint32_t ind = int_id / 32;
|
|
uint32_t bit_mask = (1U << (int_id % 32));
|
|
|
|
MSG("platform_enable_interrupt for id=0x%"PRIx32", "
|
|
"bit_mask=0x%"PRIx32", index=0x%"PRIx32"\n",
|
|
int_id, bit_mask, ind);
|
|
|
|
enum IrqType irq_type = get_irq_type(int_id);
|
|
// We only allow enableing PPI interrupts.
|
|
if(irq_type != IrqType_PPI) {
|
|
return SYS_ERR_IRQ_INVALID;
|
|
}
|
|
|
|
// Enable
|
|
// 1 Bit per interrupt
|
|
uint32_t regval = pl390_gic_dist_ICDISER_rd(&gic_dist, ind);
|
|
regval |= bit_mask;
|
|
pl390_gic_dist_ICDISER_wr(&gic_dist, ind, regval);
|
|
|
|
// TODO: cleanup pl390 mackerel file so that we don't need bit magic
|
|
// here. -SG, 2012/12/13
|
|
|
|
// Priority
|
|
// 8 Bit per interrupt
|
|
// chp 4.3.10
|
|
ind = int_id/4;
|
|
// XXX: check that priorities work properly, -SG, 2012/12/13
|
|
prio = (prio & 0xF)<<4;
|
|
switch(int_id % 4) {
|
|
case 0:
|
|
pl390_gic_dist_ICDIPR_prio_off0_wrf(&gic_dist, ind, prio);
|
|
break;
|
|
case 1:
|
|
pl390_gic_dist_ICDIPR_prio_off1_wrf(&gic_dist, ind, prio);
|
|
break;
|
|
case 2:
|
|
pl390_gic_dist_ICDIPR_prio_off2_wrf(&gic_dist, ind, prio);
|
|
break;
|
|
case 3:
|
|
pl390_gic_dist_ICDIPR_prio_off3_wrf(&gic_dist, ind, prio);
|
|
break;
|
|
}
|
|
|
|
// Configuration registers
|
|
// 2 Bit per IRQ
|
|
ind = int_id/16;
|
|
uint8_t val = ((edge_triggered&0x1) << 1) | (one_to_n&0x1);
|
|
switch (int_id % 16) {
|
|
case 0:
|
|
pl390_gic_dist_ICDICR_conf0_wrf(&gic_dist, ind, val);
|
|
break;
|
|
case 1:
|
|
pl390_gic_dist_ICDICR_conf1_wrf(&gic_dist, ind, val);
|
|
break;
|
|
case 2:
|
|
pl390_gic_dist_ICDICR_conf2_wrf(&gic_dist, ind, val);
|
|
break;
|
|
case 3:
|
|
pl390_gic_dist_ICDICR_conf3_wrf(&gic_dist, ind, val);
|
|
break;
|
|
case 4:
|
|
pl390_gic_dist_ICDICR_conf4_wrf(&gic_dist, ind, val);
|
|
break;
|
|
case 5:
|
|
pl390_gic_dist_ICDICR_conf5_wrf(&gic_dist, ind, val);
|
|
break;
|
|
case 6:
|
|
pl390_gic_dist_ICDICR_conf6_wrf(&gic_dist, ind, val);
|
|
break;
|
|
case 7:
|
|
pl390_gic_dist_ICDICR_conf7_wrf(&gic_dist, ind, val);
|
|
break;
|
|
case 8:
|
|
pl390_gic_dist_ICDICR_conf8_wrf(&gic_dist, ind, val);
|
|
break;
|
|
case 9:
|
|
pl390_gic_dist_ICDICR_conf9_wrf(&gic_dist, ind, val);
|
|
break;
|
|
case 10:
|
|
pl390_gic_dist_ICDICR_conf10_wrf(&gic_dist, ind, val);
|
|
break;
|
|
case 11:
|
|
pl390_gic_dist_ICDICR_conf11_wrf(&gic_dist, ind, val);
|
|
break;
|
|
case 12:
|
|
pl390_gic_dist_ICDICR_conf12_wrf(&gic_dist, ind, val);
|
|
break;
|
|
case 13:
|
|
pl390_gic_dist_ICDICR_conf13_wrf(&gic_dist, ind, val);
|
|
break;
|
|
case 14:
|
|
pl390_gic_dist_ICDICR_conf14_wrf(&gic_dist, ind, val);
|
|
break;
|
|
case 15:
|
|
pl390_gic_dist_ICDICR_conf15_wrf(&gic_dist, ind, val);
|
|
break;
|
|
}
|
|
return SYS_ERR_OK;
|
|
}
|