diff options
-rw-r--r-- | sys/arch/arm64/conf/GENERIC | 3 | ||||
-rw-r--r-- | sys/arch/arm64/conf/RAMDISK | 3 | ||||
-rw-r--r-- | sys/dev/fdt/files.fdt | 6 | ||||
-rw-r--r-- | sys/dev/fdt/mvgicp.c | 176 | ||||
-rw-r--r-- | sys/dev/fdt/mvicu.c | 214 |
5 files changed, 333 insertions, 69 deletions
diff --git a/sys/arch/arm64/conf/GENERIC b/sys/arch/arm64/conf/GENERIC index e11bec9fee6..b85e1099412 100644 --- a/sys/arch/arm64/conf/GENERIC +++ b/sys/arch/arm64/conf/GENERIC @@ -1,4 +1,4 @@ -# $OpenBSD: GENERIC,v 1.95 2019/01/23 09:58:08 phessler Exp $ +# $OpenBSD: GENERIC,v 1.96 2019/02/03 14:03:36 patrick Exp $ # # GENERIC machine description file # @@ -143,6 +143,7 @@ hitemp* at fdt? # Marvell SoCs mvclock* at fdt? early 1 +mvgicp* at fdt? early 1 mvicu* at fdt? early 1 mvpinctrl* at fdt? early 1 mvgpio* at fdt? diff --git a/sys/arch/arm64/conf/RAMDISK b/sys/arch/arm64/conf/RAMDISK index 4a02114faa1..201a5f0fcb2 100644 --- a/sys/arch/arm64/conf/RAMDISK +++ b/sys/arch/arm64/conf/RAMDISK @@ -1,4 +1,4 @@ -# $OpenBSD: RAMDISK,v 1.80 2019/01/29 22:14:32 patrick Exp $ +# $OpenBSD: RAMDISK,v 1.81 2019/02/03 14:03:36 patrick Exp $ # # GENERIC machine description file # @@ -139,6 +139,7 @@ hireset* at fdt? early 1 # Marvell SoCs mvclock* at fdt? early 1 +mvgicp* at fdt? early 1 mvicu* at fdt? early 1 mvpinctrl* at fdt? early 1 mvgpio* at fdt? diff --git a/sys/dev/fdt/files.fdt b/sys/dev/fdt/files.fdt index be0e2686e94..f90efcdf446 100644 --- a/sys/dev/fdt/files.fdt +++ b/sys/dev/fdt/files.fdt @@ -1,4 +1,4 @@ -# $OpenBSD: files.fdt,v 1.76 2019/01/11 08:02:19 patrick Exp $ +# $OpenBSD: files.fdt,v 1.77 2019/02/03 14:03:36 patrick Exp $ # # Config file and device description for machine-independent FDT code. # Included by ports that need it. @@ -190,6 +190,10 @@ device mvclock attach mvclock at fdt file dev/fdt/mvclock.c mvclock +device mvgicp +attach mvgicp at fdt +file dev/fdt/mvgicp.c mvgicp + device mvgpio attach mvgpio at fdt file dev/fdt/mvgpio.c mvgpio diff --git a/sys/dev/fdt/mvgicp.c b/sys/dev/fdt/mvgicp.c new file mode 100644 index 00000000000..42bd7f98e70 --- /dev/null +++ b/sys/dev/fdt/mvgicp.c @@ -0,0 +1,176 @@ +/* $OpenBSD: mvgicp.c,v 1.1 2019/02/03 14:03:36 patrick Exp $ */ +/* + * Copyright (c) 2019 Patrick Wildt <patrick@blueri.se> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/device.h> +#include <sys/malloc.h> + +#include <uvm/uvm_extern.h> + +#include <machine/intr.h> +#include <machine/bus.h> +#include <machine/fdt.h> + +#include <dev/ofw/openfirm.h> +#include <dev/ofw/ofw_misc.h> +#include <dev/ofw/fdt.h> + +struct mvgicp_softc { + struct device sc_dev; + bus_space_tag_t sc_iot; + bus_space_handle_t sc_ioh; + paddr_t sc_addr; + + uint32_t sc_spi_ranges[4]; + void **sc_spi; + uint32_t sc_nspi; + + struct interrupt_controller sc_ic; + struct interrupt_controller *sc_parent_ic; +}; + +int mvgicp_match(struct device *, void *, void *); +void mvgicp_attach(struct device *, struct device *, void *); + +void * mvgicp_intr_establish(void *, uint64_t *, uint64_t *, + int, int (*)(void *), void *, char *); +void mvgicp_intr_disestablish(void *); + +struct cfattach mvgicp_ca = { + sizeof(struct mvgicp_softc), mvgicp_match, mvgicp_attach +}; + +struct cfdriver mvgicp_cd = { + NULL, "mvgicp", DV_DULL +}; + +int +mvgicp_match(struct device *parent, void *match, void *aux) +{ + struct fdt_attach_args *faa = aux; + + return OF_is_compatible(faa->fa_node, "marvell,ap806-gicp"); +} + +void +mvgicp_attach(struct device *parent, struct device *self, void *aux) +{ + struct mvgicp_softc *sc = (struct mvgicp_softc *)self; + struct fdt_attach_args *faa = aux; + struct interrupt_controller *ic; + uint32_t phandle; + + if (faa->fa_nreg < 1) { + printf(": no registers\n"); + return; + } + + OF_getpropintarray(faa->fa_node, "marvell,spi-ranges", + sc->sc_spi_ranges, sizeof(sc->sc_spi_ranges)); + sc->sc_nspi = sc->sc_spi_ranges[1] + sc->sc_spi_ranges[3]; + sc->sc_spi = mallocarray(sc->sc_nspi, sizeof(void *), + M_DEVBUF, M_WAITOK | M_ZERO); + + sc->sc_iot = faa->fa_iot; + if (bus_space_map(sc->sc_iot, faa->fa_reg[0].addr, + faa->fa_reg[0].size, 0, &sc->sc_ioh)) { + printf(": can't map registers\n"); + return; + } + + /* XXX: Hack to retrieve the physical address (from a CPU PoV). */ + if (!pmap_extract(pmap_kernel(), sc->sc_ioh, &sc->sc_addr)) { + printf(": cannot retrieve msi addr\n"); + return; + } + + extern uint32_t fdt_intr_get_parent(int); + phandle = fdt_intr_get_parent(faa->fa_node); + extern LIST_HEAD(, interrupt_controller) interrupt_controllers; + LIST_FOREACH(ic, &interrupt_controllers, ic_list) { + if (ic->ic_phandle == phandle) + break; + } + sc->sc_parent_ic = ic; + + sc->sc_ic.ic_node = faa->fa_node; + sc->sc_ic.ic_cookie = sc; + sc->sc_ic.ic_establish_msi = mvgicp_intr_establish; + sc->sc_ic.ic_disestablish = mvgicp_intr_disestablish; + fdt_intr_register(&sc->sc_ic); + + printf("\n"); +} + +void * +mvgicp_intr_establish(void *self, uint64_t *addr, uint64_t *data, + int level, int (*func)(void *), void *arg, char *name) +{ + struct mvgicp_softc *sc = (struct mvgicp_softc *)self; + struct interrupt_controller *ic = sc->sc_parent_ic; + uint32_t interrupt[3]; + uint32_t flags; + void *cookie; + int i, spi; + + if (ic == NULL) + return NULL; + + for (i = 0; i < sc->sc_nspi; i++) { + if (sc->sc_spi[i] == NULL) { + spi = i; + break; + } + } + if (i == sc->sc_nspi) + return NULL; + + flags = *data; + + *addr = sc->sc_addr; + *data = spi; + + /* Convert to GIC interrupt source. */ + for (i = 0; i < nitems(sc->sc_spi_ranges); i += 2) { + if (spi < sc->sc_spi_ranges[i + 1]) { + spi += sc->sc_spi_ranges[i]; + break; + } + spi -= sc->sc_spi_ranges[i + 1]; + } + if (i == nitems(sc->sc_spi_ranges)) + return NULL; + + interrupt[0] = 0; + interrupt[1] = spi - 32; + interrupt[2] = flags; + cookie = ic->ic_establish(ic->ic_cookie, interrupt, level, + func, arg, name); + if (cookie == NULL) + return NULL; + + sc->sc_spi[*data] = cookie; + return &sc->sc_spi[*data]; +} + +void +mvgicp_intr_disestablish(void *cookie) +{ + fdt_intr_disestablish(*(void **)cookie); + *(void **)cookie = NULL; +} diff --git a/sys/dev/fdt/mvicu.c b/sys/dev/fdt/mvicu.c index 46baffb370a..f4b6b3bfb8f 100644 --- a/sys/dev/fdt/mvicu.c +++ b/sys/dev/fdt/mvicu.c @@ -1,4 +1,4 @@ -/* $OpenBSD: mvicu.c,v 1.3 2018/08/06 10:52:30 patrick Exp $ */ +/* $OpenBSD: mvicu.c,v 1.4 2019/02/03 14:03:36 patrick Exp $ */ /* * Copyright (c) 2018 Mark Kettenis <kettenis@openbsd.org> * @@ -18,6 +18,7 @@ #include <sys/param.h> #include <sys/systm.h> #include <sys/device.h> +#include <sys/malloc.h> #include <machine/intr.h> #include <machine/bus.h> @@ -31,26 +32,53 @@ #define ICU_SETSPI_NSR_AH 0x14 #define ICU_CLRSPI_NSR_AL 0x18 #define ICU_CLRSPI_NSR_AH 0x1c +#define ICU_SET_SEI_AL 0x50 +#define ICU_SET_SEI_AH 0x54 +#define ICU_CLR_SEI_AL 0x58 +#define ICU_CLR_SEI_AH 0x5c #define ICU_INT_CFG(x) (0x100 + (x) * 4) #define ICU_INT_ENABLE (1 << 24) #define ICU_INT_EDGE (1 << 28) #define ICU_INT_GROUP_SHIFT 29 #define ICU_INT_MASK 0x3ff +#define GICP_SETSPI_NSR 0x00 +#define GICP_CLRSPI_NSR 0x08 + +/* Devices */ +#define ICU_DEVICE_SATA0 109 +#define ICU_DEVICE_SATA1 107 +#define ICU_DEVICE_NIRQ 207 + +/* Groups. */ +#define ICU_GRP_NSR 0x0 +#define ICU_GRP_SR 0x1 +#define ICU_GRP_SEI 0x4 +#define ICU_GRP_REI 0x5 + #define HREAD4(sc, reg) \ (bus_space_read_4((sc)->sc_iot, (sc)->sc_ioh, (reg))) #define HWRITE4(sc, reg, val) \ bus_space_write_4((sc)->sc_iot, (sc)->sc_ioh, (reg), (val)) +struct mvicu_softc; +struct mvicu_subnode { + struct mvicu_softc *sn_sc; + int sn_group; + struct interrupt_controller sn_ic; + struct interrupt_controller *sn_parent_ic; +}; + struct mvicu_softc { struct device sc_dev; bus_space_tag_t sc_iot; bus_space_handle_t sc_ioh; - uint32_t sc_spi_ranges[4]; + uint64_t sc_nsr_addr; + uint64_t sc_sei_addr; - struct interrupt_controller sc_ic; - struct interrupt_controller *sc_parent_ic; + int sc_legacy; + struct mvicu_subnode *sc_nodes; }; int mvicu_match(struct device *, void *, void *); @@ -64,6 +92,7 @@ struct cfdriver mvicu_cd = { NULL, "mvicu", DV_DULL }; +void mvicu_register(struct mvicu_softc *, int, int); void *mvicu_intr_establish(void *, int *, int, int (*)(void *), void *, char *); void mvicu_intr_disestablish(void *); @@ -81,25 +110,13 @@ mvicu_attach(struct device *parent, struct device *self, void *aux) { struct mvicu_softc *sc = (struct mvicu_softc *)self; struct fdt_attach_args *faa = aux; - struct interrupt_controller *ic; - bus_addr_t low, high, setspi_addr, clrspi_addr; - uint32_t phandle; - int node; + int i, node, nchildren; if (faa->fa_nreg < 1) { printf(": no registers\n"); return; } - phandle = OF_getpropint(faa->fa_node, "msi-parent", 0); - node = OF_getnodebyphandle(phandle); - if (node == 0) { - printf(": GICP not found\n"); - return; - } - OF_getpropintarray(node, "marvell,spi-ranges", sc->sc_spi_ranges, - sizeof(sc->sc_spi_ranges)); - sc->sc_iot = faa->fa_iot; if (bus_space_map(sc->sc_iot, faa->fa_reg[0].addr, faa->fa_reg[0].size, 0, &sc->sc_ioh)) { @@ -107,76 +124,141 @@ mvicu_attach(struct device *parent, struct device *self, void *aux) return; } - /* - * This driver assumes that the ICU has been configured by the - * firmware. Do some (minimal) checks to verify that has - * indeed been done. - */ - low = HREAD4(sc, ICU_SETSPI_NSR_AL); - high = HREAD4(sc, ICU_SETSPI_NSR_AH); - setspi_addr = (high << 32) | low; - low = HREAD4(sc, ICU_CLRSPI_NSR_AL); - high = HREAD4(sc, ICU_CLRSPI_NSR_AH); - clrspi_addr = (high << 32) | low; - if (setspi_addr == 0 || clrspi_addr == 0) { - printf(": not configured by firmware\n"); - return; + printf("\n"); + + if (OF_child(faa->fa_node) == 0) { + sc->sc_legacy = 1; + sc->sc_nodes = mallocarray(1, sizeof(*sc->sc_nodes), + M_DEVBUF, M_WAITOK | M_ZERO); + mvicu_register(sc, faa->fa_node, 0); + } else { + for (node = OF_child(faa->fa_node), nchildren = 0; + node; node = OF_peer(node)) + nchildren++; + sc->sc_nodes = mallocarray(nchildren, sizeof(*sc->sc_nodes), + M_DEVBUF, M_WAITOK | M_ZERO); + for (node = OF_child(faa->fa_node), i = 0; node; + node = OF_peer(node)) + mvicu_register(sc, node, i++); } +} - printf("\n"); +void +mvicu_register(struct mvicu_softc *sc, int node, int idx) +{ + struct mvicu_subnode *sn = &sc->sc_nodes[idx]; + struct interrupt_controller *ic; + uint32_t phandle = 0; + uint32_t group; + int i; + + sn->sn_group = -1; + if (OF_is_compatible(node, "marvell,cp110-icu") || + OF_is_compatible(node, "marvell,cp110-icu-nsr")) + sn->sn_group = ICU_GRP_NSR; + if (OF_is_compatible(node, "marvell,cp110-icu-sei")) + sn->sn_group = ICU_GRP_SEI; + + for (i = 0; i < ICU_DEVICE_NIRQ; i++) { + group = HREAD4(sc, ICU_INT_CFG(i)) >> ICU_INT_GROUP_SHIFT; + if ((sn->sn_group == ICU_GRP_NSR && group == ICU_GRP_NSR) || + (sn->sn_group == ICU_GRP_SEI && group == ICU_GRP_SEI)) + HWRITE4(sc, ICU_INT_CFG(i), 0); + } + + sn->sn_sc = sc; + sn->sn_ic.ic_node = node; + sn->sn_ic.ic_cookie = sn; + sn->sn_ic.ic_establish = mvicu_intr_establish; + sn->sn_ic.ic_disestablish = mvicu_intr_disestablish; + + while (node && !phandle) { + phandle = OF_getpropint(node, "msi-parent", 0); + node = OF_parent(node); + } + if (phandle == 0) + return; - extern uint32_t fdt_intr_get_parent(int); - phandle = fdt_intr_get_parent(node); extern LIST_HEAD(, interrupt_controller) interrupt_controllers; LIST_FOREACH(ic, &interrupt_controllers, ic_list) { if (ic->ic_phandle == phandle) break; } - sc->sc_parent_ic = ic; + if (ic == NULL) + return; - sc->sc_ic.ic_node = faa->fa_node; - sc->sc_ic.ic_cookie = sc; - sc->sc_ic.ic_establish = mvicu_intr_establish; - sc->sc_ic.ic_disestablish = mvicu_intr_disestablish; - fdt_intr_register(&sc->sc_ic); + sn->sn_parent_ic = ic; + fdt_intr_register(&sn->sn_ic); } void * mvicu_intr_establish(void *cookie, int *cell, int level, int (*func)(void *), void *arg, char *name) { - struct mvicu_softc *sc = cookie; - struct interrupt_controller *ic = sc->sc_parent_ic; - uint32_t group = cell[0]; - uint32_t idx = cell[1]; - uint32_t interrupt[3]; - uint32_t reg; - int i; + struct mvicu_subnode *sn = cookie; + struct mvicu_softc *sc = sn->sn_sc; + struct interrupt_controller *ic = sn->sn_parent_ic; + uint32_t idx, flags; + uint64_t addr, data; + int edge = 0; - if (ic == NULL) + if (sc->sc_legacy) { + if (cell[0] != ICU_GRP_NSR) + return NULL; + idx = cell[1]; + flags = cell[2]; + edge = ((flags & 0xf) == 0x1); + } else if (sn->sn_group == ICU_GRP_NSR) { + idx = cell[0]; + flags = cell[1]; + edge = ((flags & 0xf) == 0x1); + } else if (sn->sn_group == ICU_GRP_SEI) { + idx = cell[0]; + flags = cell[1]; + edge = 1; + } else { return NULL; + } - reg = HREAD4(sc, ICU_INT_CFG(idx)); - if ((reg & ICU_INT_ENABLE) == 0 || - (reg >> ICU_INT_GROUP_SHIFT) != group) + data = flags; + cookie = ic->ic_establish_msi(ic->ic_cookie, &addr, &data, + level, func, arg, name); + if (cookie == NULL) return NULL; - /* Convert to GIC interrupt source. */ - idx = reg & ICU_INT_MASK; - for (i = 0; i < nitems(sc->sc_spi_ranges); i += 2) { - if (idx < sc->sc_spi_ranges[i + 1]) { - idx += sc->sc_spi_ranges[i]; - break; - } - idx -= sc->sc_spi_ranges[i]; + if (sn->sn_group == ICU_GRP_NSR && !sc->sc_nsr_addr) { + sc->sc_nsr_addr = addr; + HWRITE4(sc, ICU_SETSPI_NSR_AL, + (addr + GICP_SETSPI_NSR) & 0xffffffff); + HWRITE4(sc, ICU_SETSPI_NSR_AH, + (addr + GICP_SETSPI_NSR) >> 32); + HWRITE4(sc, ICU_CLRSPI_NSR_AL, + (addr + GICP_CLRSPI_NSR) & 0xffffffff); + HWRITE4(sc, ICU_CLRSPI_NSR_AH, + (addr + GICP_CLRSPI_NSR) >> 32); } - if (i == nitems(sc->sc_spi_ranges)) - return NULL; - interrupt[0] = 0; - interrupt[1] = idx - 32; - interrupt[2] = cell[2]; - return ic->ic_establish(ic->ic_cookie, interrupt, level, - func, arg, name); + + if (sn->sn_group == ICU_GRP_SEI && !sc->sc_sei_addr) { + sc->sc_sei_addr = addr; + HWRITE4(sc, ICU_SET_SEI_AL, addr & 0xffffffff); + HWRITE4(sc, ICU_SET_SEI_AH, addr >> 32); + } + + /* Configure ICU. */ + HWRITE4(sc, ICU_INT_CFG(idx), data | ICU_INT_ENABLE | + (sn->sn_group << ICU_INT_GROUP_SHIFT) | (edge ? ICU_INT_EDGE : 0)); + + /* Need to configure interrupt for both SATA ports. */ + if (idx == ICU_DEVICE_SATA0 || idx == ICU_DEVICE_SATA1) { + HWRITE4(sc, ICU_INT_CFG(ICU_DEVICE_SATA0), data | + ICU_INT_ENABLE | (sn->sn_group << ICU_INT_GROUP_SHIFT) | + (edge ? ICU_INT_EDGE : 0)); + HWRITE4(sc, ICU_INT_CFG(ICU_DEVICE_SATA1), data | + ICU_INT_ENABLE | (sn->sn_group << ICU_INT_GROUP_SHIFT) | + (edge ? ICU_INT_EDGE : 0)); + } + + return cookie; } void |