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