aos/usr/drivers/gic_dist/main.c
Daniel Schwyn 6d444bf552 Main handout
Signed-off-by: Daniel Schwyn <daniel.schwyn@inf.ethz.ch>
2022-03-03 14:57:51 +01:00

221 lines
6.3 KiB
C

/*
* Copyright (c) 2020, 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 <stdio.h>
#include <stdlib.h>
#include <aos/aos.h>
#include <drivers/gic_dist.h>
#include <dev/pl390_gic_dist_dev.h>
struct gic_dist_s {
pl390_gic_dist_t devgic;
uint32_t it_num_lines;
int cpu_count;
};
enum IrqType {
IrqType_SGI,
IrqType_PPI,
IrqType_SPI
};
/**
* \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;
}
}
void gic_dist_raise_softirq(struct gic_dist_s * gds, uint8_t cpumask, uint8_t irq)
{
uint32_t regval = (cpumask << 16) | irq;
pl390_gic_dist_ICDSGIR_wr(&gds->devgic, regval);
};
errval_t gic_dist_enable_interrupt(struct gic_dist_s *gds, int int_id,
uint8_t cpu_targets, uint16_t prio)
{
// For now, we support only edge triggered and one to one interrupts
bool edge_triggered = 1;
bool one_to_n = 0;
//Disable forwarding
pl390_gic_dist_ICDDCR_enable_wrf(&gds->devgic, 0x0);
assert(edge_triggered);
GIC_DIST_DEBUG("enable int=%d forwarding to cpu_mask=%d\n", int_id, cpu_targets);
uint32_t ind = int_id / 32;
uint32_t bit_mask = (1U << (int_id % 32));
enum IrqType irq_type = get_irq_type(int_id);
// We allow PPI on any core, and SPI only on instance 0
if(!(irq_type == IrqType_SPI && int_id <= gds->it_num_lines))
{
GIC_DIST_DEBUG("invalid int_id=%d\n", int_id);
return SYS_ERR_IRQ_INVALID;
}
// Enable
// 1 Bit per interrupt
uint32_t regval = pl390_gic_dist_ICDISER_rd(&gds->devgic, ind);
regval |= bit_mask;
pl390_gic_dist_ICDISER_wr(&gds->devgic, 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(&gds->devgic, ind, prio);
break;
case 1:
pl390_gic_dist_ICDIPR_prio_off1_wrf(&gds->devgic, ind, prio);
break;
case 2:
pl390_gic_dist_ICDIPR_prio_off2_wrf(&gds->devgic, ind, prio);
break;
case 3:
pl390_gic_dist_ICDIPR_prio_off3_wrf(&gds->devgic, ind, prio);
break;
}
// Target processors (only SPIs)
// 8 Bit per interrupt
ind = int_id/4;
if (irq_type == IrqType_SPI) { // rest is ro
switch (int_id % 4) {
case 0:
pl390_gic_dist_ICDIPTR_targets_off0_wrf(&gds->devgic, ind, cpu_targets);
break;
case 1:
pl390_gic_dist_ICDIPTR_targets_off1_wrf(&gds->devgic, ind, cpu_targets);
break;
case 2:
pl390_gic_dist_ICDIPTR_targets_off2_wrf(&gds->devgic, ind, cpu_targets);
break;
case 3:
pl390_gic_dist_ICDIPTR_targets_off3_wrf(&gds->devgic, ind, cpu_targets);
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(&gds->devgic, ind, val);
break;
case 1:
pl390_gic_dist_ICDICR_conf1_wrf(&gds->devgic, ind, val);
break;
case 2:
pl390_gic_dist_ICDICR_conf2_wrf(&gds->devgic, ind, val);
break;
case 3:
pl390_gic_dist_ICDICR_conf3_wrf(&gds->devgic, ind, val);
break;
case 4:
pl390_gic_dist_ICDICR_conf4_wrf(&gds->devgic, ind, val);
break;
case 5:
pl390_gic_dist_ICDICR_conf5_wrf(&gds->devgic, ind, val);
break;
case 6:
pl390_gic_dist_ICDICR_conf6_wrf(&gds->devgic, ind, val);
break;
case 7:
pl390_gic_dist_ICDICR_conf7_wrf(&gds->devgic, ind, val);
break;
case 8:
pl390_gic_dist_ICDICR_conf8_wrf(&gds->devgic, ind, val);
break;
case 9:
pl390_gic_dist_ICDICR_conf9_wrf(&gds->devgic, ind, val);
break;
case 10:
pl390_gic_dist_ICDICR_conf10_wrf(&gds->devgic, ind, val);
break;
case 11:
pl390_gic_dist_ICDICR_conf11_wrf(&gds->devgic, ind, val);
break;
case 12:
pl390_gic_dist_ICDICR_conf12_wrf(&gds->devgic, ind, val);
break;
case 13:
pl390_gic_dist_ICDICR_conf13_wrf(&gds->devgic, ind, val);
break;
case 14:
pl390_gic_dist_ICDICR_conf14_wrf(&gds->devgic, ind, val);
break;
case 15:
pl390_gic_dist_ICDICR_conf15_wrf(&gds->devgic, ind, val);
break;
}
//Re-enable forwarding
pl390_gic_dist_ICDDCR_enable_wrf(&gds->devgic, 0x1);
return SYS_ERR_OK;
}
errval_t gic_dist_init(struct gic_dist_s** gds_ret, void* base) {
GIC_DIST_DEBUG("Driver init\n");
assert(gds_ret != NULL);
assert(base != NULL);
struct gic_dist_s * gds = calloc(sizeof(struct gic_dist_s), 1);
assert(gds);
*gds_ret = gds;
// Hardware init
pl390_gic_dist_initialize(&gds->devgic, base);
// read GIC configuration
pl390_gic_dist_ICDICTR_t gic_config = pl390_gic_dist_ICDICTR_rd(&gds->devgic);
// ARM GIC 2.0 TRM, Table 4-6
// This is the number of ICDISERs, i.e. #SPIs
// Number of SGIs (0-15) and PPIs (16-31) is fixed
uint32_t it_num_lines_tmp =
pl390_gic_dist_ICDICTR_it_lines_num_extract(gic_config);
gds->it_num_lines = 32*(it_num_lines_tmp + 1);
gds->cpu_count = pl390_gic_dist_ICDICTR_cpu_number_extract(gic_config) + 1;
GIC_DIST_DEBUG("interrupt lines = %d, cpu_count = %d\n", gds->it_num_lines,
gds->cpu_count);
// enable interrupt forwarding from distributor to cpu interface
pl390_gic_dist_ICDDCR_enable_wrf(&gds->devgic, 0x1);
return SYS_ERR_OK;
}