[8df0306] | 1 | /*
|
---|
| 2 | * Copyright (c) 2018 Petr Pavlu
|
---|
| 3 | * All rights reserved.
|
---|
| 4 | *
|
---|
| 5 | * Redistribution and use in source and binary forms, with or without
|
---|
| 6 | * modification, are permitted provided that the following conditions
|
---|
| 7 | * are met:
|
---|
| 8 | *
|
---|
| 9 | * - Redistributions of source code must retain the above copyright
|
---|
| 10 | * notice, this list of conditions and the following disclaimer.
|
---|
| 11 | * - Redistributions in binary form must reproduce the above copyright
|
---|
| 12 | * notice, this list of conditions and the following disclaimer in the
|
---|
| 13 | * documentation and/or other materials provided with the distribution.
|
---|
| 14 | * - The name of the author may not be used to endorse or promote products
|
---|
| 15 | * derived from this software without specific prior written permission.
|
---|
| 16 | *
|
---|
| 17 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
---|
| 18 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
---|
| 19 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
---|
| 20 | * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
---|
| 21 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
---|
| 22 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
---|
| 23 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
---|
| 24 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
---|
| 25 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
---|
| 26 | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
---|
| 27 | */
|
---|
| 28 |
|
---|
| 29 | /** @addtogroup uspace_drv_gicv2
|
---|
| 30 | * @{
|
---|
| 31 | */
|
---|
| 32 |
|
---|
| 33 | /** @file
|
---|
| 34 | */
|
---|
| 35 |
|
---|
| 36 | #include <async.h>
|
---|
| 37 | #include <bitops.h>
|
---|
| 38 | #include <ddi.h>
|
---|
| 39 | #include <ddf/log.h>
|
---|
| 40 | #include <errno.h>
|
---|
| 41 | #include <macros.h>
|
---|
| 42 | #include <str_error.h>
|
---|
| 43 | #include <ipc/irc.h>
|
---|
| 44 | #include <stdint.h>
|
---|
| 45 |
|
---|
| 46 | #include "gicv2.h"
|
---|
| 47 |
|
---|
| 48 | /** GICv2 distributor register map. */
|
---|
| 49 | typedef struct {
|
---|
| 50 | /** Distributor control register. */
|
---|
| 51 | ioport32_t ctlr;
|
---|
| 52 | #define GICV2D_CTLR_ENABLE_FLAG 0x1
|
---|
| 53 |
|
---|
| 54 | /** Interrupt controller type register. */
|
---|
| 55 | const ioport32_t typer;
|
---|
| 56 | #define GICV2D_TYPER_IT_LINES_NUMBER_SHIFT 0
|
---|
| 57 | #define GICV2D_TYPER_IT_LINES_NUMBER_MASK \
|
---|
| 58 | (0x1f << GICV2D_TYPER_IT_LINES_NUMBER_SHIFT)
|
---|
| 59 |
|
---|
| 60 | /** Distributor implementer identification register. */
|
---|
| 61 | const ioport32_t iidr;
|
---|
| 62 | /** Reserved. */
|
---|
| 63 | PADD32(5);
|
---|
| 64 | /** Implementation defined registers. */
|
---|
| 65 | ioport32_t impl[8];
|
---|
| 66 | /** Reserved. */
|
---|
| 67 | PADD32(16);
|
---|
| 68 | /** Interrupt group registers. */
|
---|
| 69 | ioport32_t igroupr[32];
|
---|
| 70 | /** Interrupt set-enable registers. */
|
---|
| 71 | ioport32_t isenabler[32];
|
---|
| 72 | /** Interrupt clear-enable registers. */
|
---|
| 73 | ioport32_t icenabler[32];
|
---|
| 74 | /** Interrupt set-pending registers. */
|
---|
| 75 | ioport32_t ispendr[32];
|
---|
| 76 | /** Interrupt clear-pending registers. */
|
---|
| 77 | ioport32_t icpendr[32];
|
---|
| 78 | /** GICv2 interrupt set-active registers. */
|
---|
| 79 | ioport32_t isactiver[32];
|
---|
| 80 | /** Interrupt clear-active registers. */
|
---|
| 81 | ioport32_t icactiver[32];
|
---|
| 82 | /** Interrupt priority registers. */
|
---|
| 83 | ioport32_t ipriorityr[255];
|
---|
| 84 | /** Reserved. */
|
---|
| 85 | PADD32(1);
|
---|
| 86 | /** Interrupt processor target registers. First 8 words are read-only.
|
---|
| 87 | */
|
---|
| 88 | ioport32_t itargetsr[255];
|
---|
| 89 | /** Reserved. */
|
---|
| 90 | PADD32(1);
|
---|
| 91 | /** Interrupt configuration registers. */
|
---|
| 92 | ioport32_t icfgr[64];
|
---|
| 93 | /** Implementation defined registers. */
|
---|
| 94 | ioport32_t impl2[64];
|
---|
| 95 | /** Non-secure access control registers. */
|
---|
| 96 | ioport32_t nsacr[64];
|
---|
| 97 | /** Software generated interrupt register. */
|
---|
| 98 | ioport32_t sgir;
|
---|
| 99 | /** Reserved. */
|
---|
| 100 | PADD32(3);
|
---|
| 101 | /** SGI clear-pending registers. */
|
---|
| 102 | ioport32_t cpendsgir[4];
|
---|
| 103 | /** SGI set-pending registers. */
|
---|
| 104 | ioport32_t spendsgir[4];
|
---|
| 105 | /** Reserved. */
|
---|
| 106 | PADD32(40);
|
---|
| 107 | /** Implementation defined identification registers. */
|
---|
| 108 | const ioport32_t impl3[12];
|
---|
| 109 | } gicv2_distr_regs_t;
|
---|
| 110 |
|
---|
| 111 | /* GICv2 CPU interface register map. */
|
---|
| 112 | typedef struct {
|
---|
| 113 | /** CPU interface control register. */
|
---|
| 114 | ioport32_t ctlr;
|
---|
| 115 | #define GICV2C_CTLR_ENABLE_FLAG 0x1
|
---|
| 116 |
|
---|
| 117 | /** Interrupt priority mask register. */
|
---|
| 118 | ioport32_t pmr;
|
---|
| 119 | /** Binary point register. */
|
---|
| 120 | ioport32_t bpr;
|
---|
| 121 | /** Interrupt acknowledge register. */
|
---|
| 122 | const ioport32_t iar;
|
---|
| 123 | #define GICV2C_IAR_INTERRUPT_ID_SHIFT 0
|
---|
| 124 | #define GICV2C_IAR_INTERRUPT_ID_MASK \
|
---|
| 125 | (0x3ff << GICV2C_IAR_INTERRUPT_ID_SHIFT)
|
---|
| 126 | #define GICV2C_IAR_CPUID_SHIFT 10
|
---|
| 127 | #define GICV2C_IAR_CPUID_MASK \
|
---|
| 128 | (0x7 << GICV2C_IAR_CPUID_SHIFT)
|
---|
| 129 |
|
---|
| 130 | /** End of interrupt register. */
|
---|
| 131 | ioport32_t eoir;
|
---|
| 132 | /** Running priority register. */
|
---|
| 133 | const ioport32_t rpr;
|
---|
| 134 | /** Highest priority pending interrupt register. */
|
---|
| 135 | const ioport32_t hppir;
|
---|
| 136 | /** Aliased binary point register. */
|
---|
| 137 | ioport32_t abpr;
|
---|
| 138 | /** Aliased interrupt acknowledge register. */
|
---|
| 139 | const ioport32_t aiar;
|
---|
| 140 | /** Aliased end of interrupt register. */
|
---|
| 141 | ioport32_t aeoir;
|
---|
| 142 | /** Aliased highest priority pending interrupt register. */
|
---|
| 143 | const ioport32_t ahppir;
|
---|
| 144 | /** Reserved. */
|
---|
| 145 | PADD32(5);
|
---|
| 146 | /** Implementation defined registers. */
|
---|
| 147 | ioport32_t impl[36];
|
---|
| 148 | /** Active priorities registers. */
|
---|
| 149 | ioport32_t apr[4];
|
---|
| 150 | /** Non-secure active priorities registers. */
|
---|
| 151 | ioport32_t nsapr[4];
|
---|
| 152 | /** Reserved. */
|
---|
| 153 | PADD32(3);
|
---|
| 154 | /** CPU interface identification register. */
|
---|
| 155 | const ioport32_t iidr;
|
---|
| 156 | /** Unallocated. */
|
---|
| 157 | PADD32(960);
|
---|
| 158 | /** Deactivate interrupt register. */
|
---|
| 159 | ioport32_t dir;
|
---|
| 160 | } gicv2_cpui_regs_t;
|
---|
| 161 |
|
---|
| 162 | static errno_t gicv2_enable_irq(gicv2_t *gicv2, sysarg_t irq)
|
---|
| 163 | {
|
---|
| 164 | if (irq > gicv2->max_irq)
|
---|
| 165 | return EINVAL;
|
---|
| 166 |
|
---|
| 167 | ddf_msg(LVL_NOTE, "Enable interrupt '%" PRIun "'.", irq);
|
---|
| 168 |
|
---|
| 169 | gicv2_distr_regs_t *distr = (gicv2_distr_regs_t *) gicv2->distr;
|
---|
| 170 | pio_write_32(&distr->isenabler[irq / 32], BIT_V(uint32_t, irq % 32));
|
---|
| 171 | return EOK;
|
---|
| 172 | }
|
---|
| 173 |
|
---|
| 174 | /** Client connection handler.
|
---|
| 175 | *
|
---|
| 176 | * @param icall Call data of the request that opened the connection.
|
---|
| 177 | * @param arg Local argument.
|
---|
| 178 | */
|
---|
| 179 | static void gicv2_connection(ipc_call_t *icall, void *arg)
|
---|
| 180 | {
|
---|
| 181 | ipc_call_t call;
|
---|
| 182 | gicv2_t *gicv2;
|
---|
| 183 |
|
---|
| 184 | /*
|
---|
| 185 | * Answer the first IPC_M_CONNECT_ME_TO call.
|
---|
| 186 | */
|
---|
| 187 | async_answer_0(icall, EOK);
|
---|
| 188 |
|
---|
| 189 | gicv2 = (gicv2_t *)ddf_dev_data_get(ddf_fun_get_dev((ddf_fun_t *)arg));
|
---|
| 190 |
|
---|
| 191 | while (true) {
|
---|
| 192 | async_get_call(&call);
|
---|
| 193 |
|
---|
| 194 | if (!ipc_get_imethod(&call)) {
|
---|
| 195 | /* The other side has hung up. */
|
---|
| 196 | async_answer_0(&call, EOK);
|
---|
| 197 | return;
|
---|
| 198 | }
|
---|
| 199 |
|
---|
| 200 | switch (ipc_get_imethod(&call)) {
|
---|
| 201 | case IRC_ENABLE_INTERRUPT:
|
---|
| 202 | async_answer_0(&call,
|
---|
| 203 | gicv2_enable_irq(gicv2, ipc_get_arg1(&call)));
|
---|
| 204 | break;
|
---|
| 205 | case IRC_DISABLE_INTERRUPT:
|
---|
| 206 | /* XXX TODO */
|
---|
| 207 | async_answer_0(&call, EOK);
|
---|
| 208 | break;
|
---|
| 209 | case IRC_CLEAR_INTERRUPT:
|
---|
| 210 | /* Noop */
|
---|
| 211 | async_answer_0(&call, EOK);
|
---|
| 212 | break;
|
---|
| 213 | default:
|
---|
| 214 | async_answer_0(&call, EINVAL);
|
---|
| 215 | break;
|
---|
| 216 | }
|
---|
| 217 | }
|
---|
| 218 | }
|
---|
| 219 |
|
---|
| 220 | /** Add a GICv2 device. */
|
---|
| 221 | errno_t gicv2_add(gicv2_t *gicv2, gicv2_res_t *res)
|
---|
| 222 | {
|
---|
| 223 | ddf_fun_t *fun_a = NULL;
|
---|
| 224 | errno_t rc;
|
---|
| 225 |
|
---|
| 226 | rc = pio_enable((void *) res->distr_base, sizeof(gicv2_distr_regs_t),
|
---|
| 227 | &gicv2->distr);
|
---|
| 228 | if (rc != EOK) {
|
---|
| 229 | ddf_msg(
|
---|
| 230 | LVL_ERROR, "Error enabling PIO for distributor registers.");
|
---|
| 231 | goto error;
|
---|
| 232 | }
|
---|
| 233 |
|
---|
| 234 | rc = pio_enable(
|
---|
| 235 | (void *) res->cpui_base, sizeof(gicv2_cpui_regs_t), &gicv2->cpui);
|
---|
| 236 | if (rc != EOK) {
|
---|
| 237 | ddf_msg(LVL_ERROR,
|
---|
| 238 | "Error enabling PIO for CPU interface registers.");
|
---|
| 239 | goto error;
|
---|
| 240 | }
|
---|
| 241 |
|
---|
| 242 | fun_a = ddf_fun_create(gicv2->dev, fun_exposed, "a");
|
---|
| 243 | if (fun_a == NULL) {
|
---|
| 244 | ddf_msg(LVL_ERROR, "Failed creating function 'a'.");
|
---|
| 245 | rc = ENOMEM;
|
---|
| 246 | goto error;
|
---|
| 247 | }
|
---|
| 248 |
|
---|
| 249 | ddf_fun_set_conn_handler(fun_a, gicv2_connection);
|
---|
| 250 |
|
---|
| 251 | rc = ddf_fun_bind(fun_a);
|
---|
| 252 | if (rc != EOK) {
|
---|
| 253 | ddf_msg(LVL_ERROR, "Failed binding function 'a': %s",
|
---|
| 254 | str_error(rc));
|
---|
| 255 | goto error;
|
---|
| 256 | }
|
---|
| 257 |
|
---|
| 258 | rc = ddf_fun_add_to_category(fun_a, "irc");
|
---|
| 259 | if (rc != EOK)
|
---|
| 260 | goto error;
|
---|
| 261 |
|
---|
| 262 | /* Get maximum number of interrupts. */
|
---|
| 263 | gicv2_distr_regs_t *distr = (gicv2_distr_regs_t *) gicv2->distr;
|
---|
| 264 | uint32_t typer = pio_read_32(&distr->typer);
|
---|
| 265 | gicv2->max_irq = (((typer & GICV2D_TYPER_IT_LINES_NUMBER_MASK) >>
|
---|
| 266 | GICV2D_TYPER_IT_LINES_NUMBER_SHIFT) + 1) * 32;
|
---|
| 267 |
|
---|
| 268 | return EOK;
|
---|
| 269 | error:
|
---|
| 270 | if (fun_a != NULL)
|
---|
| 271 | ddf_fun_destroy(fun_a);
|
---|
| 272 | return rc;
|
---|
| 273 | }
|
---|
| 274 |
|
---|
| 275 | /** Remove a GICv2 device. */
|
---|
| 276 | errno_t gicv2_remove(gicv2_t *gicv2)
|
---|
| 277 | {
|
---|
| 278 | return ENOTSUP;
|
---|
| 279 | }
|
---|
| 280 |
|
---|
| 281 | /** A GICv2 device gone. */
|
---|
| 282 | errno_t gicv2_gone(gicv2_t *gicv2)
|
---|
| 283 | {
|
---|
| 284 | return ENOTSUP;
|
---|
| 285 | }
|
---|
| 286 |
|
---|
| 287 | /** @}
|
---|
| 288 | */
|
---|