Changeset View
Standalone View
sys/arm64/arm64/gic_v3.c
- This file was added.
/*- | |||||
* Copyright (c) 2015 The FreeBSD Foundation | |||||
* All rights reserved. | |||||
* | |||||
* This software was developed by Semihalf under | |||||
* the sponsorship of the FreeBSD Foundation. | |||||
* | |||||
* Redistribution and use in source and binary forms, with or without | |||||
* modification, are permitted provided that the following conditions | |||||
* are met: | |||||
* 1. Redistributions of source code must retain the above copyright | |||||
* notice, this list of conditions and the following disclaimer. | |||||
* 2. Redistributions in binary form must reproduce the above copyright | |||||
* notice, this list of conditions and the following disclaimer in the | |||||
* documentation and/or other materials provided with the distribution. | |||||
* | |||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND | |||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE | |||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | |||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | |||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | |||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | |||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | |||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |||||
* SUCH DAMAGE. | |||||
*/ | |||||
#include <sys/cdefs.h> | |||||
__FBSDID("$FreeBSD$"); | |||||
#include <sys/param.h> | |||||
#include <sys/systm.h> | |||||
#include <sys/bus.h> | |||||
#include <sys/kernel.h> | |||||
#include <sys/ktr.h> | |||||
#include <sys/malloc.h> | |||||
#include <sys/module.h> | |||||
#include <sys/rman.h> | |||||
#include <sys/pcpu.h> | |||||
#include <sys/proc.h> | |||||
#include <sys/cpuset.h> | |||||
#include <sys/lock.h> | |||||
#include <sys/mutex.h> | |||||
#include <vm/vm.h> | |||||
#include <vm/pmap.h> | |||||
#include <machine/bus.h> | |||||
#include <machine/intr.h> | |||||
#include "pic_if.h" | |||||
#include "gic_v3_reg.h" | |||||
#include "gic_v3_var.h" | |||||
/* Device and PIC methods */ | |||||
static void gic_v3_dispatch(device_t, struct trapframe *); | |||||
static void gic_v3_eoi(device_t, u_int); | |||||
static void gic_v3_mask_irq(device_t, u_int); | |||||
static void gic_v3_unmask_irq(device_t, u_int); | |||||
static device_method_t gic_v3_methods[] = { | |||||
/* Device interface */ | |||||
DEVMETHOD(device_detach, gic_v3_detach), | |||||
/* PIC interface */ | |||||
DEVMETHOD(pic_dispatch, gic_v3_dispatch), | |||||
DEVMETHOD(pic_eoi, gic_v3_eoi), | |||||
DEVMETHOD(pic_mask, gic_v3_mask_irq), | |||||
DEVMETHOD(pic_unmask, gic_v3_unmask_irq), | |||||
/* End */ | |||||
DEVMETHOD_END | |||||
}; | |||||
DEFINE_CLASS_0(gic_v3, gic_v3_driver, gic_v3_methods, | |||||
sizeof(struct gic_v3_softc)); | |||||
/* | |||||
* Driver-specific definitions. | |||||
*/ | |||||
MALLOC_DEFINE(M_GIC_V3, "GICv3", GIC_V3_DEVSTR); | |||||
/* | |||||
* Helper functions and definitions. | |||||
*/ | |||||
/* Destination registers, either Distributor or Re-Distributor */ | |||||
enum gic_v3_xdist { | |||||
DIST = 0, | |||||
REDIST, | |||||
andrew: What is this needed for if it's marked as unused? | |||||
}; | |||||
/* Helper routines starting with gic_v3_ */ | |||||
static int gic_v3_dist_init(struct gic_v3_softc *); | |||||
static int gic_v3_redist_find(struct gic_v3_softc *); | |||||
static int gic_v3_redist_init(struct gic_v3_softc *); | |||||
static int gic_v3_cpu_init(struct gic_v3_softc *); | |||||
static void gic_v3_wait_for_rwp(struct gic_v3_softc *, enum gic_v3_xdist); | |||||
/* A sequence of init functions for primary (boot) CPU */ | |||||
typedef int (*gic_v3_initseq_t) (struct gic_v3_softc *); | |||||
/* Primary CPU initialization sequence */ | |||||
static gic_v3_initseq_t gic_v3_primary_init[] = { | |||||
gic_v3_dist_init, | |||||
gic_v3_redist_init, | |||||
gic_v3_cpu_init, | |||||
NULL | |||||
}; | |||||
/* | |||||
* Device interface. | |||||
*/ | |||||
int | |||||
gic_v3_attach(device_t dev) | |||||
{ | |||||
struct gic_v3_softc *sc; | |||||
gic_v3_initseq_t *init_func; | |||||
uint32_t typer; | |||||
int rid; | |||||
int err; | |||||
size_t i; | |||||
sc = device_get_softc(dev); | |||||
sc->gic_registered = FALSE; | |||||
sc->dev = dev; | |||||
err = 0; | |||||
/* Initialize mutex */ | |||||
mtx_init(&sc->gic_mtx, "GICv3 lock", NULL, MTX_SPIN); | |||||
/* | |||||
* Allocate array of struct resource. | |||||
* One entry for Distributor and all remaining for Re-Distributor. | |||||
*/ | |||||
sc->gic_res = malloc( | |||||
sizeof(sc->gic_res) * (sc->gic_redists.nregions + 1), | |||||
M_GIC_V3, M_WAITOK); | |||||
/* Now allocate corresponding resources */ | |||||
for (i = 0, rid = 0; i < (sc->gic_redists.nregions + 1); i++, rid++) { | |||||
sc->gic_res[rid] = bus_alloc_resource_any(dev, SYS_RES_MEMORY, | |||||
&rid, RF_ACTIVE); | |||||
if (sc->gic_res[rid] == NULL) | |||||
return (ENXIO); | |||||
} | |||||
/* | |||||
* Distributor interface | |||||
*/ | |||||
sc->gic_dist = sc->gic_res[0]; | |||||
Done Inline Actions4 lines of magic numbers andrew: 4 lines of magic numbers | |||||
/* | |||||
* Re-Dristributor interface | |||||
*/ | |||||
/* Allocate space under region descriptions */ | |||||
sc->gic_redists.regions = malloc( | |||||
sizeof(*sc->gic_redists.regions) * sc->gic_redists.nregions, | |||||
M_GIC_V3, M_WAITOK); | |||||
Done Inline ActionsExtra braces andrew: Extra braces | |||||
Done Inline ActionsFrom style(9): zbb: From style(9):
No braces (`{' and `}') are used for control statements with zero or only a… | |||||
/* Fill-up bus_space information for each region. */ | |||||
for (i = 0, rid = 1; i < sc->gic_redists.nregions; i++, rid++) | |||||
sc->gic_redists.regions[i] = sc->gic_res[rid]; | |||||
Done Inline ActionsWhy the extra inderection? It's calling a list of 3 functions that don't change. andrew: Why the extra inderection? It's calling a list of 3 functions that don't change. | |||||
Done Inline Actionsand why does everybody write function calls through pointers in that style? It's not required by style(9). IMO, the write way to spell that call would be err = init_func(sc); ian: and why does everybody write function calls through pointers in that style? It's not required… | |||||
Done Inline ActionsHmm? I don't think you can call init_func(sc); here. It would be an error.
err = func1(); if (err != 0) goto error; err = func2(); if (err != 0) goto error; .... zbb: Hmm? I don't think you can call `init_func(sc);` here. It would be an error.
Extra indirection… | |||||
Done Inline ActionsThis comment is now completely detached from the code it was commenting on. My point was that these two lines are equivelent: (*init_func)(); init_func(); When you've got a pointer to a function, the compiler knows that, there's no need to add parens and a star to make it call through the pointer. ian: This comment is now completely detached from the code it was commenting on.
My point was that… | |||||
Done Inline ActionsYou can comment to the previous diff and the comment will be in the right place. error: called object type 'gic_v3_initseq_t *' (aka 'int (**)(struct gic_v3_softc *)') is not a function or function pointer err = init_func(sc); ~~~~~~~~~^ zbb: You can comment to the previous diff and the comment will be in the right place.
But back to… | |||||
Done Inline ActionsOh! I completely missed that the pointer was double-indirect through the typedef. My mistake, sorry about that. ian: Oh! I completely missed that the pointer was double-indirect through the typedef. My mistake… | |||||
/* Get the number of supported SPI interrupts */ | |||||
typer = gic_d_read(sc, 4, GICD_TYPER); | |||||
sc->gic_nirqs = GICD_TYPER_I_NUM(typer); | |||||
if (sc->gic_nirqs > GIC_I_NUM_MAX) | |||||
sc->gic_nirqs = GIC_I_NUM_MAX; | |||||
Done Inline ActionsMagic numbers are still here. In the interest of avoiding further round-trips I'm happy for you to either fix prior to commit or update in a subsequent commit. emaste: Magic numbers are still here. In the interest of avoiding further round-trips I'm happy for you… | |||||
/* Get the number of supported interrupt identifier bits */ | |||||
sc->gic_idbits = GICD_TYPER_IDBITS(typer); | |||||
if (bootverbose) { | |||||
device_printf(dev, "SPIs: %u, IDs: %u\n", | |||||
sc->gic_nirqs, (1 << sc->gic_idbits) - 1); | |||||
} | |||||
/* Train init sequence for boot CPU */ | |||||
for (init_func = gic_v3_primary_init; *init_func != NULL; init_func++) { | |||||
err = (*init_func)(sc); | |||||
if (err != 0) | |||||
return (err); | |||||
} | |||||
/* | |||||
* Full success. | |||||
* Now register PIC to the interrupts handling layer. | |||||
*/ | |||||
arm_register_root_pic(dev, sc->gic_nirqs); | |||||
sc->gic_registered = TRUE; | |||||
Done Inline ActionsWhy do we need i in this loop? And there could be a comment on what resources are being released, i.e. why sc->gic_redists.nregions + 1. andrew: Why do we need `i` in this loop? And there could be a comment on what resources are being… | |||||
Done Inline Actionsi is used as a kosher way to iterate in loop - using size_t type, whereas rid has to be int. zbb: `i` is used as a kosher way to iterate in loop - using size_t type, whereas rid has to be int. | |||||
Done Inline ActionsAlthough this won't work if sc->gic_redists.nregions > INT_MAX anyway - we could probably just document that fact with a KASSERT. emaste: Although this won't work if sc->gic_redists.nregions > INT_MAX anyway - we could probably just… | |||||
Done Inline ActionsOK. I will remove i from this loop. Regarding gic_refists.nregions + 1 then AFAIK we have proper comment when allocating resource: /* * Allocate array of struct resource. * One entry for Distributor and all remaining for Re-Distributor. */ So now we have to comment on allocation and deallocation? zbb: OK. I will remove `i` from this loop. Regarding `gic_refists.nregions + 1` then AFAIK we have… | |||||
Not Done Inline Actions
Actually, you could just put a single kassert above at line 140 since we'll already break if rid > INT_MAX on emaste: > So now we have to comment on allocation and deallocation?
Actually, you could just put a… | |||||
return (0); | |||||
} | |||||
int | |||||
gic_v3_detach(device_t dev) | |||||
{ | |||||
struct gic_v3_softc *sc; | |||||
size_t i; | |||||
int rid; | |||||
sc = device_get_softc(dev); | |||||
if (device_is_attached(dev)) { | |||||
/* | |||||
Not Done Inline ActionsYou should at least printf or panic or something :) imp: You should at least printf or panic or something :)
| |||||
* XXX: We should probably deregister PIC | |||||
*/ | |||||
if (sc->gic_registered) | |||||
panic("Trying to detach registered PIC"); | |||||
} | |||||
for (rid = 0; rid < (sc->gic_redists.nregions + 1); rid++) | |||||
bus_release_resource(dev, SYS_RES_MEMORY, rid, sc->gic_res[rid]); | |||||
for (i = 0; i < MAXCPU; i++) | |||||
free(sc->gic_redists.pcpu[i], M_GIC_V3); | |||||
free(sc->gic_res, M_GIC_V3); | |||||
free(sc->gic_redists.regions, M_GIC_V3); | |||||
return (0); | |||||
} | |||||
/* | |||||
* PIC interface. | |||||
*/ | |||||
static void | |||||
gic_v3_dispatch(device_t dev, struct trapframe *frame) | |||||
{ | |||||
uint64_t active_irq; | |||||
while (1) { | |||||
active_irq = gic_icc_read(IAR1); | |||||
if (__predict_false(active_irq == ICC_IAR1_EL1_SPUR)) | |||||
break; | |||||
if (__predict_true((active_irq >= GIC_FIRST_PPI && | |||||
active_irq <= GIC_LAST_SPI))) { | |||||
arm_dispatch_intr(active_irq, frame); | |||||
continue; | |||||
} | |||||
if (active_irq <= GIC_LAST_SGI || active_irq >= GIC_FIRST_LPI) { | |||||
/* | |||||
* TODO: Implement proper SGI/LPI handling. | |||||
* Mask it if such is received for some reason. | |||||
*/ | |||||
device_printf(dev, | |||||
"Received unsupported interrupt type: %s\n", | |||||
active_irq >= GIC_FIRST_LPI ? "LPI" : "SGI"); | |||||
PIC_MASK(dev, active_irq); | |||||
} | |||||
} | |||||
} | |||||
Done Inline ActionsWhy 32? andrew: Why 32? | |||||
Done Inline ActionsThis is the same "32" as in "uint32_t". sizeof(uint32_t) * NBBY looks better here. zbb: This is the same "32" as in "uint32_t".
I can see a certain amount of exaggeration to define… | |||||
Done Inline ActionsWhy not do this something like: #define GICD_ICENABLER(irq) (0x0180 + (((irq) >> 5) * 4)) #define GICD_INTR_MASK(irq) (1u << ((irq) % 32)) gic_r_write(sc, 4, GICD_ICENABLER(irq), GICD_INTR_MASK(irq)); andrew: Why not do this something like:
#define GICD_ICENABLER(irq) (0x0180 + (((irq) >> 5) * 4))… | |||||
static void | |||||
gic_v3_eoi(device_t dev, u_int irq) | |||||
{ | |||||
Done Inline ActionsThere should be a macro for PAGE_SIZE_64K andrew: There should be a macro for PAGE_SIZE_64K | |||||
gic_icc_write(EOIR1, (uint64_t)irq); | |||||
} | |||||
static void | |||||
gic_v3_mask_irq(device_t dev, u_int irq) | |||||
{ | |||||
Done Inline ActionsShould this be a panic? andrew: Should this be a panic? | |||||
Done Inline ActionsThis may be more reasonable zbb: This may be more reasonable | |||||
struct gic_v3_softc *sc; | |||||
sc = device_get_softc(dev); | |||||
if (irq >= GIC_FIRST_PPI && irq <= GIC_LAST_PPI) { /* PPIs in corresponding Re-Distributor */ | |||||
gic_r_write(sc, 4, | |||||
GICR_SGI_BASE_SIZE + GICD_ICENABLER(irq), GICD_I_MASK(irq)); | |||||
gic_v3_wait_for_rwp(sc, REDIST); | |||||
} else if (irq >= GIC_FIRST_SPI && irq <= GIC_LAST_SPI) { /* SPIs in distributor */ | |||||
gic_r_write(sc, 4, GICD_ICENABLER(irq), GICD_I_MASK(irq)); | |||||
gic_v3_wait_for_rwp(sc, DIST); | |||||
} else | |||||
panic("%s: Unsupported IRQ number %u", __func__, irq); | |||||
} | |||||
static void | |||||
gic_v3_unmask_irq(device_t dev, u_int irq) | |||||
{ | |||||
struct gic_v3_softc *sc; | |||||
sc = device_get_softc(dev); | |||||
if (irq >= GIC_FIRST_PPI && irq <= GIC_LAST_PPI) { /* PPIs in corresponding Re-Distributor */ | |||||
gic_r_write(sc, 4, | |||||
GICR_SGI_BASE_SIZE + GICD_ISENABLER(irq), GICD_I_MASK(irq)); | |||||
gic_v3_wait_for_rwp(sc, REDIST); | |||||
} else if (irq >= GIC_FIRST_SPI && irq <= GIC_LAST_SPI) { /* SPIs in distributor */ | |||||
gic_d_write(sc, 4, GICD_ISENABLER(irq), GICD_I_MASK(irq)); | |||||
gic_v3_wait_for_rwp(sc, DIST); | |||||
} else | |||||
panic("%s: Unsupported IRQ number %u", __func__, irq); | |||||
} | |||||
/* | |||||
* Helper routines | |||||
Done Inline Actions1 second seems a long time to wait for a register to change. andrew: 1 second seems a long time to wait for a register to change. | |||||
Done Inline ActionsYes. This is taken from Linux. zbb: Yes. This is taken from Linux. | |||||
*/ | |||||
static void | |||||
Done Inline ActionsThis is only needed in one case below. andrew: This is only needed in one case below. | |||||
Done Inline ActionsStill looks better than PCPU_GET(cpuid) in line. zbb: Still looks better than PCPU_GET(cpuid) in line. | |||||
gic_v3_wait_for_rwp(struct gic_v3_softc *sc, enum gic_v3_xdist xdist) | |||||
{ | |||||
struct resource *res; | |||||
Done Inline Actions(Why the parentheses?) andrew: (Why the parentheses?) | |||||
u_int cpuid; | |||||
size_t us_left = 1000000; | |||||
cpuid = PCPU_GET(cpuid); | |||||
switch (xdist) { | |||||
case DIST: | |||||
res = sc->gic_dist; | |||||
break; | |||||
case REDIST: | |||||
res = sc->gic_redists.pcpu[cpuid]; | |||||
break; | |||||
default: | |||||
KASSERT(0, ("%s: Attempt to wait for unknown RWP", __func__)); | |||||
Done Inline ActionsThis magic value should come from armreg.h andrew: This magic value should come from armreg.h | |||||
return; | |||||
} | |||||
Not Done Inline ActionsDoes delay 1 really delay 1us every time over 1M iterations? I guess it isn't critical, but if you only want to wait a second for something time sources are more reliable. Then again, why wait a full second here? imp: Does delay 1 really delay 1us every time over 1M iterations? I guess it isn't critical, but if… | |||||
Not Done Inline ActionsDELAY is because we if fact expect to get immediate response from GIC. If we don't get any this most probably means malfunction. I would suggest to wait that second and panic instead of device_printf(). I don't have any good explanation why 1 second and not for example 0.5. There are no timing details in the GIC documentation related to that and the one below so this value was based on what Linux code does. zbb: DELAY is because we if fact expect to get immediate response from GIC. If we don't get any this… | |||||
while ((bus_read_4(res, GICD_CTLR) & GICD_CTLR_RWP) != 0) { | |||||
DELAY(1); | |||||
if (us_left-- == 0) | |||||
panic("GICD Register write pending for too long"); | |||||
} | |||||
} | |||||
/* CPU interface. */ | |||||
static __inline void | |||||
gic_v3_cpu_priority(uint64_t mask) | |||||
{ | |||||
/* Set prority mask */ | |||||
gic_icc_write(PMR, mask & ICC_PMR_EL1_PRIO_MASK); | |||||
} | |||||
static int | |||||
gic_v3_cpu_enable_sre(struct gic_v3_softc *sc) | |||||
{ | |||||
Done Inline Actionsif ((sre & ICC_SRE_EL1_SRE) != 0) { andrew: `if ((sre & ICC_SRE_EL1_SRE) != 0) {` | |||||
uint64_t sre; | |||||
u_int cpuid; | |||||
cpuid = PCPU_GET(cpuid); | |||||
/* | |||||
* Set the SRE bit to enable access to GIC CPU interface | |||||
* via system registers. | |||||
*/ | |||||
sre = READ_SPECIALREG(icc_sre_el1); | |||||
sre |= ICC_SRE_EL1_SRE; | |||||
Done Inline ActionsIsn't there a macro for this? andrew: Isn't there a macro for this? | |||||
Done Inline Actionsyes, yes READ_SPECIALREG zbb: yes, yes READ_SPECIALREG | |||||
WRITE_SPECIALREG(icc_sre_el1, sre); | |||||
isb(); | |||||
/* | |||||
* Now ensure that the bit is set. | |||||
*/ | |||||
Done Inline ActionsREAD_SPECIALREG andrew: READ_SPECIALREG | |||||
sre = READ_SPECIALREG(icc_sre_el1); | |||||
if ((sre & ICC_SRE_EL1_SRE) == 0) { | |||||
/* We are done. This was disabled in EL2 */ | |||||
device_printf(sc->dev, "ERROR: CPU%u cannot enable CPU interface " | |||||
"via system registers\n", cpuid); | |||||
return (ENXIO); | |||||
} else if (bootverbose) { | |||||
device_printf(sc->dev, | |||||
"CPU%u enabled CPU interface via system registers\n", | |||||
cpuid); | |||||
} | |||||
return (0); | |||||
} | |||||
static int | |||||
gic_v3_cpu_init(struct gic_v3_softc *sc) | |||||
{ | |||||
int err; | |||||
/* Enable access to CPU interface via system registers */ | |||||
err = gic_v3_cpu_enable_sre(sc); | |||||
if (err != 0) | |||||
Done Inline ActionsThis could be if (err) return (err); andrew: This could be
if (err)
return (err); | |||||
return (err); | |||||
/* Priority mask to minimum - accept all interrupts */ | |||||
gic_v3_cpu_priority(GIC_PRIORITY_MIN); | |||||
/* Disable EOI mode */ | |||||
gic_icc_clear(CTLR, ICC_CTLR_EL1_EOIMODE); | |||||
/* Enable group 1 (insecure) interrups */ | |||||
gic_icc_set(IGRPEN1, ICC_IGRPEN0_EL1_EN); | |||||
return (0); | |||||
Done Inline ActionsTo make this return (0); andrew: To make this
return (0); | |||||
} | |||||
/* Distributor */ | |||||
Done Inline ActionsMagic number, should be something like: #define GICD_INTS_PER_foo 16 andrew: Magic number, should be something like:
#define GICD_INTS_PER_foo 16 | |||||
static int | |||||
gic_v3_dist_init(struct gic_v3_softc *sc) | |||||
{ | |||||
uint64_t aff; | |||||
Done Inline ActionsAnd here andrew: And here | |||||
u_int i; | |||||
/* | |||||
* 1. Disable the Distributor | |||||
*/ | |||||
gic_d_write(sc, 4, GICD_CTLR, 0); | |||||
gic_v3_wait_for_rwp(sc, DIST); | |||||
/* | |||||
Done Inline ActionsAnd here andrew: And here | |||||
* 2. Configure the Distributor | |||||
*/ | |||||
/* Set all global interrupts to be level triggered, active low. */ | |||||
for (i = GIC_FIRST_SPI; i < sc->gic_nirqs; i += GICD_I_PER_ICFGRn) | |||||
gic_d_write(sc, 4, GICD_ICFGR(i), 0x00000000); | |||||
/* Set priority to all shared interrupts */ | |||||
for (i = GIC_FIRST_SPI; | |||||
i < sc->gic_nirqs; i += GICD_I_PER_IPRIORITYn) { | |||||
/* Set highest priority */ | |||||
gic_d_write(sc, 4, GICD_IPRIORITYR(i), GIC_PRIORITY_MAX); | |||||
} | |||||
/* | |||||
* Disable all interrupts. Leave PPI and SGIs as they are enabled in | |||||
* Re-Distributor registers. | |||||
*/ | |||||
for (i = GIC_FIRST_SPI; i < sc->gic_nirqs; i += GICD_I_PER_ISENABLERn) | |||||
Done Inline ActionsMagic numbers and extra braces andrew: Magic numbers and extra braces | |||||
gic_d_write(sc, 4, GICD_ICENABLER(i), 0xFFFFFFFF); | |||||
gic_v3_wait_for_rwp(sc, DIST); | |||||
/* | |||||
* 3. Enable Distributor | |||||
*/ | |||||
/* Enable Distributor with ARE, Group 1 */ | |||||
gic_d_write(sc, 4, GICD_CTLR, GICD_CTLR_ARE_NS | GICD_CTLR_G1A | | |||||
GICD_CTLR_G1); | |||||
/* | |||||
* 4. Route all interrupts to boot CPU. | |||||
*/ | |||||
aff = CPU_AFFINITY(PCPU_GET(cpuid)); | |||||
for (i = GIC_FIRST_SPI; i < sc->gic_nirqs; i++) | |||||
Done Inline ActionsWhat is special about 32? andrew: What is special about 32? | |||||
Done Inline ActionsThis can be changed to FIRST_SPI indeed. zbb: This can be changed to FIRST_SPI indeed. | |||||
gic_d_write(sc, 4, GICD_IROUTER(i), aff); | |||||
return (0); | |||||
} | |||||
/* Re-Distributor */ | |||||
static int | |||||
gic_v3_redist_find(struct gic_v3_softc *sc) | |||||
{ | |||||
struct resource r_res; | |||||
bus_space_handle_t r_bsh; | |||||
uint64_t aff; | |||||
uint64_t typer; | |||||
uint32_t pidr2; | |||||
u_int cpuid; | |||||
size_t i; | |||||
cpuid = PCPU_GET(cpuid); | |||||
/* Allocate struct resource for this CPU's Re-Distributor registers */ | |||||
sc->gic_redists.pcpu[cpuid] = | |||||
malloc(sizeof(*sc->gic_redists.pcpu[0]), M_GIC_V3, M_WAITOK); | |||||
aff = CPU_AFFINITY(cpuid); | |||||
/* Affinity in format for comparison with typer */ | |||||
aff = (CPU_AFF3(aff) << 24) | (CPU_AFF2(aff) << 16) | | |||||
(CPU_AFF1(aff) << 8) | CPU_AFF0(aff); | |||||
if (bootverbose) { | |||||
device_printf(sc->dev, | |||||
"Start searching for Re-Distributor\n"); | |||||
} | |||||
/* Iterate through Re-Distributor regions */ | |||||
for (i = 0; i < sc->gic_redists.nregions; i++) { | |||||
/* Take a copy of the region's resource */ | |||||
Done Inline ActionsMagic number, should be something like: #define GICR_TYPER_foo_SHIFT 32 andrew: Magic number, should be something like:
#define GICR_TYPER_foo_SHIFT 32 | |||||
r_res = *sc->gic_redists.regions[i]; | |||||
r_bsh = rman_get_bushandle(&r_res); | |||||
pidr2 = bus_read_4(&r_res, GICR_PIDR2); | |||||
switch (pidr2 & GICR_PIDR2_ARCH_MASK) { | |||||
case GICR_PIDR2_ARCH_GICv3: /* fall through */ | |||||
case GICR_PIDR2_ARCH_GICv4: | |||||
break; | |||||
default: | |||||
device_printf(sc->dev, | |||||
"No Re-Distributor found for CPU%u\n", cpuid); | |||||
free(sc->gic_redists.pcpu[cpuid], M_GIC_V3); | |||||
return (ENODEV); | |||||
} | |||||
do { | |||||
typer = bus_read_8(&r_res, GICR_TYPER); | |||||
if ((typer >> GICR_TYPER_AFF_SHIFT) == aff) { | |||||
KASSERT(sc->gic_redists.pcpu[cpuid] != NULL, | |||||
("Invalid pointer to per-CPU redistributor")); | |||||
/* Copy res contents to its final destination */ | |||||
*sc->gic_redists.pcpu[cpuid] = r_res; | |||||
if (bootverbose) { | |||||
device_printf(sc->dev, | |||||
"CPU%u Re-Distributor has been found\n", | |||||
cpuid); | |||||
} | |||||
return (0); | |||||
} | |||||
r_bsh += (GICR_RD_BASE_SIZE + GICR_SGI_BASE_SIZE); | |||||
Done Inline ActionsWhat is special about PAGE_SIZE_64K * 2? andrew: What is special about `PAGE_SIZE_64K * 2`? | |||||
Done Inline ActionsAdded macros zbb: Added macros | |||||
if ((typer & GICR_TYPER_VLPIS) != 0) { | |||||
r_bsh += | |||||
(GICR_VLPI_BASE_SIZE + GICR_RESERVED_SIZE); | |||||
} | |||||
Done Inline ActionsThis would normally be } while ((typer & GICR_TYPER_LAST) == 0); andrew: This would normally be
} while ((typer & GICR_TYPER_LAST) == 0); | |||||
Done Inline Actionsnormally? zbb: normally? | |||||
Done Inline Actionstypically for style(9) I don't see it mentioned in style(9), but I've always been told bitwise comparison of e.g. flags should be == 0 or != 0. This might just be tribal knowledge passed on via mailing lists / code review and should be added to style(9). emaste: typically for style(9)
I don't see it mentioned in style(9), but I've always been told bitwise… | |||||
Done Inline ActionsOK. I see. zbb: OK. I see. | |||||
Done Inline ActionsI think it comes from Do not use ! for tests unless it is a boolean,.... andrew: I think it comes from `Do not use ! for tests unless it is a boolean,...`. | |||||
Done Inline ActionsYes, indeed. I'll see about feeding all of these back into style(9). emaste: Yes, indeed. I'll see about feeding all of these back into style(9).
| |||||
rman_set_bushandle(&r_res, r_bsh); | |||||
} while ((typer & GICR_TYPER_LAST) == 0); | |||||
} | |||||
free(sc->gic_redists.pcpu[cpuid], M_GIC_V3); | |||||
device_printf(sc->dev, "No Re-Distributor found for CPU%u\n", cpuid); | |||||
return (ENXIO); | |||||
} | |||||
static int | |||||
gic_v3_redist_wake(struct gic_v3_softc *sc) | |||||
{ | |||||
uint32_t waker; | |||||
size_t us_left = 1000000; | |||||
waker = gic_r_read(sc, 4, GICR_WAKER); | |||||
/* Wake up Re-Distributor for this CPU */ | |||||
waker &= ~GICR_WAKER_PS; | |||||
gic_r_write(sc, 4, GICR_WAKER, waker); | |||||
/* | |||||
* When clearing ProcessorSleep bit it is required to wait for | |||||
* ChildrenAsleep to become zero following the processor power-on. | |||||
*/ | |||||
while ((gic_r_read(sc, 4, GICR_WAKER) & GICR_WAKER_CA) != 0) { | |||||
DELAY(1); | |||||
if (us_left-- == 0) { | |||||
Not Done Inline ActionsSame comments here. why 1 second? imp: Same comments here. why 1 second?
| |||||
panic("Could not wake Re-Distributor for CPU%u", | |||||
PCPU_GET(cpuid)); | |||||
} | |||||
} | |||||
Done Inline Actionsif (err != 0) andrew: `if (err != 0)` | |||||
if (bootverbose) { | |||||
device_printf(sc->dev, "CPU%u Re-Distributor woke up\n", | |||||
PCPU_GET(cpuid)); | |||||
} | |||||
return (0); | |||||
} | |||||
static int | |||||
gic_v3_redist_init(struct gic_v3_softc *sc) | |||||
{ | |||||
int err; | |||||
size_t i; | |||||
err = gic_v3_redist_find(sc); | |||||
Done Inline ActionsMagic number andrew: Magic number | |||||
if (err != 0) | |||||
return (err); | |||||
err = gic_v3_redist_wake(sc); | |||||
if (err != 0) | |||||
return (err); | |||||
/* Disable SPIs */ | |||||
Done Inline ActionsIf there is no clean up you can just return above. andrew: If there is no clean up you can just return above. | |||||
gic_r_write(sc, 4, GICR_SGI_BASE_SIZE + GICR_ICENABLER0, | |||||
GICR_I_ENABLER_PPI_MASK); | |||||
/* Enable SGIs */ | |||||
Done Inline ActionsMagic numbers andrew: Magic numbers | |||||
gic_r_write(sc, 4, GICR_SGI_BASE_SIZE + GICR_ISENABLER0, | |||||
GICR_I_ENABLER_SGI_MASK); | |||||
/* Set priority for SGIs and PPIs */ | |||||
for (i = 0; i <= GIC_LAST_PPI; i += GICR_I_PER_IPRIORITYn) { | |||||
gic_r_write(sc, 4, GICR_SGI_BASE_SIZE + GICD_IPRIORITYR(i), | |||||
GIC_PRIORITY_MAX); | |||||
} | |||||
gic_v3_wait_for_rwp(sc, REDIST); | |||||
return (0); | |||||
} |
What is this needed for if it's marked as unused?