1/* 2 * Copyright (c) 2014, ETH Zurich. 3 * All rights reserved. 4 * 5 * This file is distributed under the terms in the attached LICENSE file. 6 * If you do not find this file, copies can be found by writing to: 7 * ETH Zurich D-INFK, CAB F.78, Universitaetstr 6, CH-8092 Zurich. 8 */ 9 10#include <stdio.h> 11#include <stdlib.h> 12#include <barrelfish/barrelfish.h> 13#include <barrelfish/nameservice_client.h> 14#include <driverkit/driverkit.h> 15 16#include <dev/pl390_gic_dist_dev.h> 17#include <if/int_route_controller_defs.h> 18 19#include "debug.h" 20 21 22struct pl390_dist_driver_state { 23 pl390_gic_dist_t devgic; 24 uint32_t it_num_lines; 25 int cpu_index; 26 int cpu_count; 27}; 28 29enum IrqType { 30 IrqType_SGI, 31 IrqType_PPI, 32 IrqType_SPI 33}; 34 35/** 36 * \brief Returns the IRQ type based on the interrupt ID 37 * 38 * We have three types of interrupts 39 * 1) Software generated Interrupts (SGI): IDs 0-15 40 * 2) Private Peripheral Interrupts (PPI): IDs 16-31 41 * 3) Shared Peripheral Interrups (SPI): IDs 32- 42 * 43 * \return The type of the interrupt. 44 */ 45static enum IrqType get_irq_type(uint32_t int_id) 46{ 47 if (int_id < 16) { 48 return IrqType_SGI; 49 } else if (int_id < 32) { 50 return IrqType_PPI; 51 } else { 52 return IrqType_SPI; 53 } 54} 55 56 57__attribute__((unused)) 58static void gic_raise_softirq(struct pl390_dist_driver_state * ds, uint8_t cpumask, uint8_t irq) 59{ 60 uint32_t regval = (cpumask << 16) | irq; 61 pl390_gic_dist_ICDSGIR_wr(&ds->devgic, regval); 62}; 63 64 65static errval_t pl390_dist_init(struct pl390_dist_driver_state * ds, mackerel_addr_t reg_base){ 66 pl390_gic_dist_initialize(&ds->devgic, reg_base); 67 68 // read GIC configuration 69 pl390_gic_dist_ICDICTR_t gic_config = pl390_gic_dist_ICDICTR_rd(&ds->devgic); 70 71 // ARM GIC 2.0 TRM, Table 4-6 72 // This is the number of ICDISERs, i.e. #SPIs 73 // Number of SGIs (0-15) and PPIs (16-31) is fixed 74 uint32_t it_num_lines_tmp = 75 pl390_gic_dist_ICDICTR_it_lines_num_extract(gic_config); 76 ds->it_num_lines = 32*(it_num_lines_tmp + 1); 77 ds->cpu_count = pl390_gic_dist_ICDICTR_cpu_number_extract(gic_config) + 1; 78 // TODO: determine cpu index 79 ds->cpu_index = 0; 80 81 PL390_DEBUG("interrupt lines = %d, cpu_count = %d\n", ds->it_num_lines, 82 ds->cpu_count); 83 84 // enable interrupt forwarding from distributor to cpu interface 85 pl390_gic_dist_ICDDCR_enable_wrf(&ds->devgic, 0x1); 86 87 return SYS_ERR_OK; 88} 89 90 91/** 92 * \brief Enable an interrupt 93 * 94 * \see ARM Generic Interrupt Controller Architecture Specification v1.0 95 * 96 * \param int_id 97 * \param cpu_targets 8 Bit mask. One bit for each core in the system. 98 * (chapter 4.3.11) 99 * \param prio Priority of the interrupt (lower is higher). We allow 0..15. 100 * The number of priority bits is implementation specific, but at least 16 101 * (using bits [7:4] of the priority field, chapter 3.3) 102 * \param 0 is level-sensitive, 1 is edge-triggered 103 * \param 0 is N-to-N, 1 is 1-N 104 */ 105static errval_t enable_interrupt(struct pl390_dist_driver_state *ds, int int_id, 106 uint8_t cpu_targets, bool edge_triggered, bool one_to_n, uint16_t prio) 107{ 108 //Disable forwarding 109 pl390_gic_dist_ICDDCR_enable_wrf(&ds->devgic, 0x0); 110 111 //LH: We can't really handle level triggered interrupts in the kernel 112 assert(edge_triggered); 113 PL390_DEBUG("enable int=%d forwarding to cpu_mask=%d\n", int_id, cpu_targets); 114 uint32_t ind = int_id / 32; 115 uint32_t bit_mask = (1U << (int_id % 32)); 116 enum IrqType irq_type = get_irq_type(int_id); 117 118 // We allow PPI on any core, and SPI only on instance 0 119 if(!(irq_type == IrqType_SPI && int_id <= ds->it_num_lines)) 120 { 121 PL390_DEBUG("invalid int_id=%d on cpu=%d\n", int_id, ds->cpu_index); 122 return SYS_ERR_IRQ_INVALID; 123 } 124 125 // Enable 126 // 1 Bit per interrupt 127 uint32_t regval = pl390_gic_dist_ICDISER_rd(&ds->devgic, ind); 128 regval |= bit_mask; 129 pl390_gic_dist_ICDISER_wr(&ds->devgic, ind, regval); 130 131 // TODO: cleanup pl390 mackerel file so that we don't need bit magic 132 // here. -SG, 2012/12/13 133 134 // Priority 135 // 8 Bit per interrupt 136 // chp 4.3.10 137 ind = int_id/4; 138 // XXX: check that priorities work properly, -SG, 2012/12/13 139 prio = (prio & 0xF)<<4; 140 switch(int_id % 4) { 141 case 0: 142 pl390_gic_dist_ICDIPR_prio_off0_wrf(&ds->devgic, ind, prio); 143 break; 144 case 1: 145 pl390_gic_dist_ICDIPR_prio_off1_wrf(&ds->devgic, ind, prio); 146 break; 147 case 2: 148 pl390_gic_dist_ICDIPR_prio_off2_wrf(&ds->devgic, ind, prio); 149 break; 150 case 3: 151 pl390_gic_dist_ICDIPR_prio_off3_wrf(&ds->devgic, ind, prio); 152 break; 153 } 154 155 // Target processors (only SPIs) 156 // 8 Bit per interrupt 157 ind = int_id/4; 158 if (irq_type == IrqType_SPI) { // rest is ro 159 switch (int_id % 4) { 160 case 0: 161 pl390_gic_dist_ICDIPTR_targets_off0_wrf(&ds->devgic, ind, cpu_targets); 162 break; 163 case 1: 164 pl390_gic_dist_ICDIPTR_targets_off1_wrf(&ds->devgic, ind, cpu_targets); 165 break; 166 case 2: 167 pl390_gic_dist_ICDIPTR_targets_off2_wrf(&ds->devgic, ind, cpu_targets); 168 break; 169 case 3: 170 pl390_gic_dist_ICDIPTR_targets_off3_wrf(&ds->devgic, ind, cpu_targets); 171 break; 172 } 173 } 174 175 // Configuration registers 176 // 2 Bit per IRQ 177 ind = int_id/16; 178 uint8_t val = ((edge_triggered&0x1) << 1) | (one_to_n&0x1); 179 switch (int_id % 16) { 180 case 0: 181 pl390_gic_dist_ICDICR_conf0_wrf(&ds->devgic, ind, val); 182 break; 183 case 1: 184 pl390_gic_dist_ICDICR_conf1_wrf(&ds->devgic, ind, val); 185 break; 186 case 2: 187 pl390_gic_dist_ICDICR_conf2_wrf(&ds->devgic, ind, val); 188 break; 189 case 3: 190 pl390_gic_dist_ICDICR_conf3_wrf(&ds->devgic, ind, val); 191 break; 192 case 4: 193 pl390_gic_dist_ICDICR_conf4_wrf(&ds->devgic, ind, val); 194 break; 195 case 5: 196 pl390_gic_dist_ICDICR_conf5_wrf(&ds->devgic, ind, val); 197 break; 198 case 6: 199 pl390_gic_dist_ICDICR_conf6_wrf(&ds->devgic, ind, val); 200 break; 201 case 7: 202 pl390_gic_dist_ICDICR_conf7_wrf(&ds->devgic, ind, val); 203 break; 204 case 8: 205 pl390_gic_dist_ICDICR_conf8_wrf(&ds->devgic, ind, val); 206 break; 207 case 9: 208 pl390_gic_dist_ICDICR_conf9_wrf(&ds->devgic, ind, val); 209 break; 210 case 10: 211 pl390_gic_dist_ICDICR_conf10_wrf(&ds->devgic, ind, val); 212 break; 213 case 11: 214 pl390_gic_dist_ICDICR_conf11_wrf(&ds->devgic, ind, val); 215 break; 216 case 12: 217 pl390_gic_dist_ICDICR_conf12_wrf(&ds->devgic, ind, val); 218 break; 219 case 13: 220 pl390_gic_dist_ICDICR_conf13_wrf(&ds->devgic, ind, val); 221 break; 222 case 14: 223 pl390_gic_dist_ICDICR_conf14_wrf(&ds->devgic, ind, val); 224 break; 225 case 15: 226 pl390_gic_dist_ICDICR_conf15_wrf(&ds->devgic, ind, val); 227 break; 228 } 229 230 //Re-enable forwarding 231 pl390_gic_dist_ICDDCR_enable_wrf(&ds->devgic, 0x1); 232 233 return SYS_ERR_OK; 234} 235 236static void add_mapping(struct int_route_controller_binding *b, 237 const char *label, 238 const char *class, 239 int_route_controller_int_message_t from, 240 int_route_controller_int_message_t to) 241{ 242 errval_t err; 243 PL390_DEBUG("add_mapping: label:%s, class:%s\n" 244 " (%"PRIu64",%"PRIu64", %"PRIu64") to \n" 245 " (%"PRIu64",%"PRIu64", %"PRIu64")\n", label, class, 246 from.port, from.addr, from.msg, 247 to.port, to.addr, to.msg); 248 249 assert(to.port < 8); 250 uint8_t cpu_mask = 1<<to.port; 251 bool edge_triggered = 1; 252 bool one_to_n = 0; 253 uint16_t prio = 1; 254 err = enable_interrupt(b->st, from.port, cpu_mask, edge_triggered, one_to_n, prio); 255 assert(err_is_ok(err)); 256} 257 258static void bind_cb(void *st, errval_t err, struct int_route_controller_binding *b) { 259 struct pl390_dist_driver_state * ds = st; 260 261 if(!err_is_ok(err)){ 262 DEBUG_ERR(err, "Bind failure\n"); 263 return; 264 } 265 266 b->st = st; 267 b->rx_vtbl.add_mapping = add_mapping; 268 269 // Register this binding for all controllers with class pcilnk 270 char label[128]; 271 snprintf(label, sizeof(label), "dist_%d", ds->cpu_index); 272 const char * ctrl_class = "gic_dist"; 273 b->tx_vtbl.register_controller(b, NOP_CONT, label, ctrl_class); 274} 275 276static errval_t connect_to_irs(struct bfdriver_instance *bfi) { 277 // Connect to int route service 278 iref_t int_route_service; 279 errval_t err; 280 PL390_DEBUG("int_ctrl_service lookup...\n"); 281 err = nameservice_blocking_lookup("int_ctrl_service", &int_route_service); 282 if (!err_is_ok(err)) { 283 DEBUG_ERR(err, "Could not lookup int_route_service\n"); 284 return err; 285 } 286 287 err = int_route_controller_bind( 288 int_route_service, bind_cb, bfi, 289 get_default_waitset(), IDC_BIND_FLAGS_DEFAULT); 290 291 if (!err_is_ok(err)) { 292 DEBUG_ERR(err, "Could not bind int_route_service\n"); 293 return err; 294 } 295 296 return SYS_ERR_OK; 297} 298 299 300static errval_t init(struct bfdriver_instance* bfi, uint64_t flags, iref_t *dev) { 301 errval_t err; 302 PL390_DEBUG("Entering driver init. name=%s\n", bfi->name); 303 304 bfi->dstate = malloc(sizeof(struct pl390_dist_driver_state)); 305 assert(bfi->dstate != NULL); 306 307 // Map device registers 308 struct capref mem_cap = { 309 .cnode = bfi->argcn, 310 .slot = 0 311 }; 312 313 lvaddr_t dev_base; 314 err = map_device_cap(mem_cap, &dev_base); 315 USER_PANIC_ON_ERR(err, "map_device_cap"); 316 317 err = pl390_dist_init(bfi->dstate, (mackerel_addr_t)dev_base); 318 USER_PANIC_ON_ERR(err, "pl390_dist_init"); 319 320 err = connect_to_irs(bfi->dstate); 321 USER_PANIC_ON_ERR(err, "connect_to_irs"); 322 323 return SYS_ERR_OK; 324} 325 326static errval_t attach(struct bfdriver_instance* bfi) { 327 return SYS_ERR_OK; 328} 329 330static errval_t detach(struct bfdriver_instance* bfi) { 331 return SYS_ERR_OK; 332} 333 334static errval_t set_sleep_level(struct bfdriver_instance* bfi, uint32_t level) { 335 return SYS_ERR_OK; 336} 337 338static errval_t destroy(struct bfdriver_instance* bfi) { 339 struct pl390_dist_driver_state* uds = bfi->dstate; 340 free(uds); 341 bfi->dstate = NULL; 342 // XXX: Tear-down the service 343 bfi->device = 0x0; 344 return SYS_ERR_OK; 345} 346 347static errval_t get_ep(struct bfdriver_instance* bfi, bool lmp, struct capref* ret_cap) 348{ 349 USER_PANIC("NIY \n"); 350 return SYS_ERR_OK; 351} 352 353DEFINE_MODULE(pl390_dist, init, attach, detach, set_sleep_level, destroy, get_ep); 354