diff --git a/sys/arm/mv/mv_ap806_clock.c b/sys/arm/mv/mv_ap806_clock.c index 1df07c0d4396..2516ce72d35c 100644 --- a/sys/arm/mv/mv_ap806_clock.c +++ b/sys/arm/mv/mv_ap806_clock.c @@ -44,12 +44,14 @@ __FBSDID("$FreeBSD$"); #include #include -#include +#include +#include #include #include -#include + +#include "syscon_if.h" static struct clk_fixed_def ap806_clk_cluster_0 = { .clkdef.id = 0, @@ -92,23 +94,19 @@ static struct clk_fixed_def ap806_clk_sdio = { }; struct mv_ap806_clock_softc { - struct simplebus_softc simplebus_sc; device_t dev; - struct resource *res; + struct syscon *syscon; }; -static struct resource_spec mv_ap806_clock_res_spec[] = { - { SYS_RES_MEMORY, 0, RF_ACTIVE | RF_SHAREABLE }, - { -1, 0 } -}; + static struct ofw_compat_data compat_data[] = { - {"marvell,ap806-clock", 1}, - {NULL, 0} + {"marvell,ap806-clock", 1}, + {NULL, 0} }; -#define RD4(sc, reg) bus_read_4((sc)->res, (reg)) -#define WR4(sc, reg, val) bus_write_4((sc)->res, (reg), (val)) +#define RD4(sc, reg) SYSCON_READ_4((sc)->syscon, (reg)) +#define WR4(sc, reg, val) SYSCON_WRITE_4((sc)->syscon, (reg), (val)) static int mv_ap806_clock_probe(device_t dev) @@ -135,8 +133,9 @@ mv_ap806_clock_attach(device_t dev) sc = device_get_softc(dev); sc->dev = dev; - if (bus_alloc_resources(dev, mv_ap806_clock_res_spec, &sc->res) != 0) { - device_printf(dev, "cannot allocate resources for device\n"); + if (SYSCON_GET_HANDLE(sc->dev, &sc->syscon) != 0 || + sc->syscon == NULL) { + device_printf(dev, "cannot get syscon for device\n"); return (ENXIO); } @@ -160,7 +159,8 @@ mv_ap806_clock_attach(device_t dev) clock_freq = 1333000000; break; default: - device_printf(dev, "Cannot guess clock freq with reg %x\n", reg & 0x1f); + device_printf(dev, "Cannot guess clock freq with reg %x\n", + reg & 0x1f); return (ENXIO); break; }; diff --git a/sys/arm/mv/mv_ap806_gicp.c b/sys/arm/mv/mv_ap806_gicp.c index 98494e88e65b..c876dda224a5 100644 --- a/sys/arm/mv/mv_ap806_gicp.c +++ b/sys/arm/mv/mv_ap806_gicp.c @@ -60,6 +60,7 @@ struct mv_ap806_gicp_softc { ssize_t spi_ranges_cnt; uint32_t *spi_ranges; + struct intr_map_data_fdt *parent_map_data; }; static struct ofw_compat_data compat_data[] = { @@ -96,11 +97,13 @@ mv_ap806_gicp_attach(device_t dev) /* Look for our parent */ if ((intr_parent = ofw_bus_find_iparent(node)) == 0) { - device_printf(dev, "Cannot find our parent interrupt controller\n"); + device_printf(dev, + "Cannot find our parent interrupt controller\n"); return (ENXIO); } if ((sc->parent = OF_device_from_xref(intr_parent)) == NULL) { - device_printf(dev, "cannot find parent interrupt controller device\n"); + device_printf(dev, + "cannot find parent interrupt controller device\n"); return (ENXIO); } @@ -112,7 +115,10 @@ mv_ap806_gicp_attach(device_t dev) device_printf(dev, "Cannot register GICP\n"); return (ENXIO); } - + /* Allocate GIC compatible mapping entry (3 cells) */ + sc->parent_map_data = (struct intr_map_data_fdt *)intr_alloc_map_data( + INTR_MAP_DATA_FDT, sizeof(struct intr_map_data_fdt) + + + 3 * sizeof(phandle_t), M_WAITOK | M_ZERO); OF_device_register_xref(xref, dev); return (0); @@ -125,6 +131,45 @@ mv_ap806_gicp_detach(device_t dev) return (EBUSY); } +static struct intr_map_data * +mv_ap806_gicp_convert_map_data(struct mv_ap806_gicp_softc *sc, + struct intr_map_data *data) +{ + struct intr_map_data_fdt *daf; + uint32_t i, irq_num, irq_type; + + daf = (struct intr_map_data_fdt *)data; + if (daf->ncells != 2) + return (NULL); + + irq_num = daf->cells[0]; + irq_type = daf->cells[1]; + if (irq_num >= MV_AP806_GICP_MAX_NIRQS) + return (NULL); + + /* Construct GIC compatible mapping. */ + sc->parent_map_data->ncells = 3; + sc->parent_map_data->cells[0] = 0; /* SPI */ + sc->parent_map_data->cells[2] = irq_type; + + /* Map the interrupt number to SPI number */ + for (i = 0; i < sc->spi_ranges_cnt / 2; i += 2) { + if (irq_num < sc->spi_ranges[i + 1]) { + irq_num += sc->spi_ranges[i]; + break; + } + + irq_num -= sc->spi_ranges[i]; + } + + sc->parent_map_data->cells[1] = irq_num - 32; + + return ((struct intr_map_data *)sc->parent_map_data); +} + + + + static int mv_ap806_gicp_activate_intr(device_t dev, struct intr_irqsrc *isrc, struct resource *res, struct intr_map_data *data) @@ -132,6 +177,9 @@ mv_ap806_gicp_activate_intr(device_t dev, struct intr_irqsrc *isrc, struct mv_ap806_gicp_softc *sc; sc = device_get_softc(dev); + data = mv_ap806_gicp_convert_map_data(sc, data); + if (data == NULL) + return (EINVAL); return (PIC_ACTIVATE_INTR(sc->parent, isrc, res, data)); } @@ -161,36 +209,20 @@ mv_ap806_gicp_map_intr(device_t dev, struct intr_map_data *data, struct intr_irqsrc **isrcp) { struct mv_ap806_gicp_softc *sc; - struct intr_map_data_fdt *daf; - uint32_t group, irq_num, irq_type; - int i; + int ret; sc = device_get_softc(dev); if (data->type != INTR_MAP_DATA_FDT) return (ENOTSUP); - daf = (struct intr_map_data_fdt *)data; - if (daf->ncells != 3 || daf->cells[0] >= MV_AP806_GICP_MAX_NIRQS) + data = mv_ap806_gicp_convert_map_data(sc, data); + if (data == NULL) return (EINVAL); - group = daf->cells[0]; - irq_num = daf->cells[1]; - irq_type = daf->cells[2]; - - /* Map the interrupt number to spi number */ - for (i = 0; i < sc->spi_ranges_cnt / 2; i += 2) { - if (irq_num < sc->spi_ranges[i + 1]) { - irq_num += sc->spi_ranges[i]; - break; - } - - irq_num -= sc->spi_ranges[i]; - } - - daf->cells[1] = irq_num - 32; - - return (PIC_MAP_INTR(sc->parent, data, isrcp)); + ret = PIC_MAP_INTR(sc->parent, data, isrcp); + (*isrcp)->isrc_dev = sc->dev; + return(ret); } static int @@ -201,6 +233,10 @@ mv_ap806_gicp_deactivate_intr(device_t dev, struct intr_irqsrc *isrc, sc = device_get_softc(dev); + data = mv_ap806_gicp_convert_map_data(sc, data); + if (data == NULL) + return (EINVAL); + return (PIC_DEACTIVATE_INTR(sc->parent, isrc, res, data)); } @@ -211,6 +247,9 @@ mv_ap806_gicp_setup_intr(device_t dev, struct intr_irqsrc *isrc, struct mv_ap806_gicp_softc *sc; sc = device_get_softc(dev); + data = mv_ap806_gicp_convert_map_data(sc, data); + if (data == NULL) + return (EINVAL); return (PIC_SETUP_INTR(sc->parent, isrc, res, data)); } @@ -222,6 +261,9 @@ mv_ap806_gicp_teardown_intr(device_t dev, struct intr_irqsrc *isrc, struct mv_ap806_gicp_softc *sc; sc = device_get_softc(dev); + data = mv_ap806_gicp_convert_map_data(sc, data); + if (data == NULL) + return (EINVAL); return (PIC_TEARDOWN_INTR(sc->parent, isrc, res, data)); } diff --git a/sys/arm/mv/mv_ap806_sei.c b/sys/arm/mv/mv_ap806_sei.c new file mode 100644 index 000000000000..8d3136c2203c --- /dev/null +++ b/sys/arm/mv/mv_ap806_sei.c @@ -0,0 +1,419 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * + * Copyright (c) 2019 Michal Meloun + * + * 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 +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include +#include + +#include "pic_if.h" + +#define MV_AP806_SEI_LOCK(_sc) mtx_lock(&(_sc)->mtx) +#define MV_AP806_SEI_UNLOCK(_sc) mtx_unlock(&(_sc)->mtx) +#define MV_AP806_SEI_LOCK_INIT(_sc) mtx_init(&_sc->mtx, \ + device_get_nameunit(_sc->dev), "mv_ap806_sei", MTX_DEF) +#define MV_AP806_SEI_LOCK_DESTROY(_sc) mtx_destroy(&_sc->mtx); +#define MV_AP806_SEI_ASSERT_LOCKED(_sc) mtx_assert(&_sc->mtx, MA_OWNED); +#define MV_AP806_SEI_ASSERT_UNLOCKED(_sc) mtx_assert(&_sc->mtx, MA_NOTOWNED); + +#define MV_AP806_SEI_MAX_NIRQS 64 +#define GICP_SECR0 0x00 +#define GICP_SECR1 0x04 +#define GICP_SECR(i) (0x00 + (((i)/32) * 0x4)) +#define GICP_SECR_BIT(i) ((i) % 32) +#define GICP_SEMR0 0x20 +#define GICP_SEMR1 0x24 +#define GICP_SEMR(i) (0x20 + (((i)/32) * 0x4)) +#define GICP_SEMR_BIT(i) ((i) % 32) + + + +struct mv_ap806_sei_irqsrc { + struct intr_irqsrc isrc; + u_int irq; +}; + +struct mv_ap806_sei_softc { + device_t dev; + struct resource *mem_res; + struct resource *irq_res; + void *irq_ih; + struct mtx mtx; + + struct mv_ap806_sei_irqsrc *isrcs; +}; + +static struct ofw_compat_data compat_data[] = { + {"marvell,ap806-sei", 1}, + {NULL, 0} +}; + +#define RD4(sc, reg) bus_read_4((sc)->mem_res, (reg)) +#define WR4(sc, reg, val) bus_write_4((sc)->mem_res, (reg), (val)) + +static inline void +mv_ap806_sei_isrc_mask(struct mv_ap806_sei_softc *sc, + struct mv_ap806_sei_irqsrc *sisrc, uint32_t val) +{ + uint32_t tmp; + int bit; + + bit = GICP_SEMR_BIT(sisrc->irq); + MV_AP806_SEI_LOCK(sc); + tmp = RD4(sc, GICP_SEMR(sisrc->irq)); + if (val != 0) + tmp |= 1 << bit; + else + tmp &= ~(1 << bit); + WR4(sc, GICP_SEMR(sisrc->irq), tmp); + MV_AP806_SEI_UNLOCK(sc); +} + +static inline void +mv_ap806_sei_isrc_eoi(struct mv_ap806_sei_softc *sc, + struct mv_ap806_sei_irqsrc *sisrc) +{ + + WR4(sc, GICP_SECR(sisrc->irq), GICP_SECR_BIT(sisrc->irq)); +} + +static void +mv_ap806_sei_enable_intr(device_t dev, struct intr_irqsrc *isrc) +{ + struct mv_ap806_sei_softc *sc; + struct mv_ap806_sei_irqsrc *sisrc; + + sc = device_get_softc(dev); + sisrc = (struct mv_ap806_sei_irqsrc *)isrc; + mv_ap806_sei_isrc_mask(sc, sisrc, 0); +} + +static void +mv_ap806_sei_disable_intr(device_t dev, struct intr_irqsrc *isrc) +{ + struct mv_ap806_sei_softc *sc; + struct mv_ap806_sei_irqsrc *sisrc; + + sc = device_get_softc(dev); + sisrc = (struct mv_ap806_sei_irqsrc *)isrc; + mv_ap806_sei_isrc_mask(sc, sisrc, 1); +} + +static int +mv_ap806_sei_map(device_t dev, struct intr_map_data *data, u_int *irqp) +{ + struct mv_ap806_sei_softc *sc; + struct intr_map_data_fdt *daf; + u_int irq; + + sc = device_get_softc(dev); + + if (data->type != INTR_MAP_DATA_FDT) + return (ENOTSUP); + + daf = (struct intr_map_data_fdt *)data; + if (daf->ncells != 1 || daf->cells[0] >= MV_AP806_SEI_MAX_NIRQS) + return (EINVAL); + irq = daf->cells[0]; + if (irqp != NULL) + *irqp = irq; + + return(0); +} + +static int +mv_ap806_sei_map_intr(device_t dev, struct intr_map_data *data, + struct intr_irqsrc **isrcp) +{ + struct mv_ap806_sei_softc *sc; + u_int irq; + int rv; + + sc = device_get_softc(dev); + rv = mv_ap806_sei_map(dev, data, &irq); + if (rv == 0) + *isrcp = &sc->isrcs[irq].isrc; + + return (rv); +} + + + +static int +mv_ap806_sei_setup_intr(device_t dev, struct intr_irqsrc *isrc, + struct resource *res, struct intr_map_data *data) +{ + struct mv_ap806_sei_softc *sc; + struct mv_ap806_sei_irqsrc *sisrc; + u_int irq; + int rv; + + sc = device_get_softc(dev); + sisrc = (struct mv_ap806_sei_irqsrc *)isrc; + if (data == NULL) + return (ENOTSUP); + rv = mv_ap806_sei_map(dev, data, &irq); + if (rv != 0) + return (rv); + if (irq != sisrc->irq) + return (EINVAL); + mv_ap806_sei_isrc_mask(sc, sisrc, 0); + return (0); +} + +static int +mv_ap806_sei_teardown_intr(device_t dev, struct intr_irqsrc *isrc, + struct resource *res, struct intr_map_data *data) +{ + struct mv_ap806_sei_softc *sc; + struct mv_ap806_sei_irqsrc *sisrc; + + sc = device_get_softc(dev); + sisrc = (struct mv_ap806_sei_irqsrc *)isrc; + + mv_ap806_sei_isrc_mask(sc, sisrc, 1); + return (0); +} + +static void +mv_ap806_sei_pre_ithread(device_t dev, struct intr_irqsrc *isrc) +{ + struct mv_ap806_sei_softc *sc; + struct mv_ap806_sei_irqsrc *sisrc; + + sc = device_get_softc(dev); + sisrc = (struct mv_ap806_sei_irqsrc *)isrc; + + mv_ap806_sei_isrc_mask(sc, sisrc, 1); + mv_ap806_sei_isrc_eoi(sc, sisrc); +} + +static void +mv_ap806_sei_post_ithread(device_t dev, struct intr_irqsrc *isrc) +{ + struct mv_ap806_sei_softc *sc; + struct mv_ap806_sei_irqsrc *sisrc; + + sc = device_get_softc(dev); + sisrc = (struct mv_ap806_sei_irqsrc *)isrc; + + mv_ap806_sei_isrc_mask(sc, sisrc, 1); +} + +static void +mv_ap806_sei_post_filter(device_t dev, struct intr_irqsrc *isrc) +{ + struct mv_ap806_sei_softc *sc; + struct mv_ap806_sei_irqsrc *sisrc; + + sc = device_get_softc(dev); + sisrc = (struct mv_ap806_sei_irqsrc *)isrc; + + mv_ap806_sei_isrc_mask(sc, sisrc, 1); + mv_ap806_sei_isrc_eoi(sc, sisrc); +} + +/* ---------------------------------------------------------------------------- + * + * B u s i n t e r f a c e + */ +static int +mv_ap806_sei_intr(void *arg) +{ + struct mv_ap806_sei_softc *sc; + struct mv_ap806_sei_irqsrc *sirq; + struct trapframe *tf; + uint64_t cause; + u_int irq; + + sc = (struct mv_ap806_sei_softc *)arg; + tf = curthread->td_intr_frame; + while (1) { + cause = RD4(sc, GICP_SECR1); + cause <<= 32; + cause |= RD4(sc, GICP_SECR0); + + irq = ffsll(cause); + if (irq == 0) break; + irq--; + sirq = &sc->isrcs[irq]; + if (intr_isrc_dispatch(&sirq->isrc, tf) != 0) { + mv_ap806_sei_isrc_mask(sc, sirq, 0); + mv_ap806_sei_isrc_eoi(sc, sirq); + device_printf(sc->dev, + "Stray irq %u disabled\n", irq); + } + } + + return (FILTER_HANDLED); +} + + +static int +mv_ap806_sei_probe(device_t dev) +{ + + if (!ofw_bus_status_okay(dev)) + return (ENXIO); + + if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0) + return (ENXIO); + + device_set_desc(dev, "Marvell SEI"); + return (BUS_PROBE_DEFAULT); +} + +static int +mv_ap806_sei_attach(device_t dev) +{ + struct mv_ap806_sei_softc *sc; + phandle_t xref, node; + uint32_t irq; + const char *name; + int rv, rid; + + sc = device_get_softc(dev); + sc->dev = dev; + node = ofw_bus_get_node(dev); + MV_AP806_SEI_LOCK_INIT(sc); + + /* Allocate resources. */ + rid = 0; + sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, + RF_ACTIVE); + if (sc->mem_res == NULL) { + device_printf(dev, "Cannot allocate memory resources\n"); + rv = ENXIO; + goto fail; + } + + rid = 0; + sc->irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_ACTIVE); + if (sc->irq_res == NULL) { + device_printf(dev, "Cannot allocate IRQ resources\n"); + rv = ENXIO; + goto fail; + } + + /* Mask all interrupts) */ + WR4(sc, GICP_SEMR0, 0xFFFFFFFF); + WR4(sc, GICP_SEMR1, 0xFFFFFFFF); + + /* Create all interrupt sources */ + sc->isrcs = malloc(sizeof(*sc->isrcs) * MV_AP806_SEI_MAX_NIRQS, + M_DEVBUF, M_WAITOK | M_ZERO); + name = device_get_nameunit(sc->dev); + for (irq = 0; irq < MV_AP806_SEI_MAX_NIRQS; irq++) { + sc->isrcs[irq].irq = irq; + rv = intr_isrc_register(&sc->isrcs[irq].isrc, + sc->dev, 0, "%s,%u", name, irq); + if (rv != 0) + goto fail; /* XXX deregister ISRCs */ + } + xref = OF_xref_from_node(node);; + if (intr_pic_register(dev, xref) == NULL) { + device_printf(dev, "Cannot register SEI\n"); + rv = ENXIO; + goto fail; + } + if (bus_setup_intr(dev, sc->irq_res,INTR_TYPE_MISC | INTR_MPSAFE, + mv_ap806_sei_intr, NULL, sc, &sc->irq_ih)) { + device_printf(dev, + "Unable to register interrupt handler\n"); + rv = ENXIO; + goto fail; + } + + OF_device_register_xref(xref, dev); + return (0); + +fail: + if (sc->irq_ih != NULL) + bus_teardown_intr(dev, sc->irq_res, sc->irq_ih); + if (sc->irq_res != NULL) + bus_release_resource(dev, SYS_RES_IRQ, 0, sc->irq_res); + if (sc->mem_res != NULL) + bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->mem_res); + MV_AP806_SEI_LOCK_DESTROY(sc); + return (ENXIO); +} + +static int +mv_ap806_sei_detach(device_t dev) +{ + + return (EBUSY); +} + + +static device_method_t mv_ap806_sei_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, mv_ap806_sei_probe), + DEVMETHOD(device_attach, mv_ap806_sei_attach), + DEVMETHOD(device_detach, mv_ap806_sei_detach), + + /* Interrupt controller interface */ + DEVMETHOD(pic_disable_intr, mv_ap806_sei_disable_intr), + DEVMETHOD(pic_enable_intr, mv_ap806_sei_enable_intr), + DEVMETHOD(pic_map_intr, mv_ap806_sei_map_intr), + DEVMETHOD(pic_setup_intr, mv_ap806_sei_setup_intr), + DEVMETHOD(pic_teardown_intr, mv_ap806_sei_teardown_intr), + DEVMETHOD(pic_post_filter, mv_ap806_sei_post_filter), + DEVMETHOD(pic_post_ithread, mv_ap806_sei_post_ithread), + DEVMETHOD(pic_pre_ithread, mv_ap806_sei_pre_ithread), + + DEVMETHOD_END +}; + +static devclass_t mv_ap806_sei_devclass; + +static driver_t mv_ap806_sei_driver = { + "mv_ap806_sei", + mv_ap806_sei_methods, + sizeof(struct mv_ap806_sei_softc), +}; + +EARLY_DRIVER_MODULE(mv_ap806_sei, simplebus, mv_ap806_sei_driver, + mv_ap806_sei_devclass, 0, 0, BUS_PASS_INTERRUPT + BUS_PASS_ORDER_MIDDLE); diff --git a/sys/arm/mv/mv_cp110_clock.c b/sys/arm/mv/mv_cp110_clock.c index 06165f3e8441..753dfaa045f6 100644 --- a/sys/arm/mv/mv_cp110_clock.c +++ b/sys/arm/mv/mv_cp110_clock.c @@ -44,17 +44,17 @@ __FBSDID("$FreeBSD$"); #include #include -#include +#include +#include +#include #include #include -#include -#include - #include #include "clkdev_if.h" +#include "syscon_if.h" /* Clocks */ static struct clk_fixed_def cp110_clk_pll_0 = { @@ -126,24 +126,19 @@ static struct cp110_gate cp110_gates[] = { }; struct mv_cp110_clock_softc { - struct simplebus_softc simplebus_sc; device_t dev; - struct resource *res; + struct syscon *syscon; struct mtx mtx; }; -static struct resource_spec mv_cp110_clock_res_spec[] = { - { SYS_RES_MEMORY, 0, RF_ACTIVE | RF_SHAREABLE }, - { -1, 0 } -}; static struct ofw_compat_data compat_data[] = { {"marvell,cp110-clock", 1}, {NULL, 0} }; -#define RD4(sc, reg) bus_read_4((sc)->res, (reg)) -#define WR4(sc, reg, val) bus_write_4((sc)->res, (reg), (val)) +#define RD4(sc, reg) SYSCON_READ_4((sc)->syscon, (reg)) +#define WR4(sc, reg, val) SYSCON_WRITE_4((sc)->syscon, (reg), (val)) static char * mv_cp110_clock_name(device_t dev, const char *name) @@ -201,8 +196,9 @@ mv_cp110_clock_attach(device_t dev) sc = device_get_softc(dev); sc->dev = dev; - if (bus_alloc_resources(dev, mv_cp110_clock_res_spec, &sc->res) != 0) { - device_printf(dev, "cannot allocate resources for device\n"); + if (SYSCON_GET_HANDLE(sc->dev, &sc->syscon) != 0 || + sc->syscon == NULL) { + device_printf(dev, "cannot get syscon for device\n"); return (ENXIO); } diff --git a/sys/arm/mv/mv_cp110_icu.c b/sys/arm/mv/mv_cp110_icu.c index 4d62670e3a68..e168b141d9e0 100644 --- a/sys/arm/mv/mv_cp110_icu.c +++ b/sys/arm/mv/mv_cp110_icu.c @@ -49,6 +49,7 @@ __FBSDID("$FreeBSD$"); #include #include +#include #include "pic_if.h" #define ICU_GRP_NSR 0x0 @@ -72,6 +73,7 @@ struct mv_cp110_icu_softc { device_t dev; device_t parent; struct resource *res; + struct intr_map_data_fdt *parent_map_data; }; static struct resource_spec mv_cp110_icu_res_spec[] = { @@ -80,8 +82,9 @@ static struct resource_spec mv_cp110_icu_res_spec[] = { }; static struct ofw_compat_data compat_data[] = { - {"marvell,cp110-icu", 1}, - {NULL, 0} + {"marvell,cp110-icu-nsr", 1}, + {"marvell,cp110-icu-sei", 2}, + {NULL, 0} }; #define RD4(sc, reg) bus_read_4((sc)->res, (reg)) @@ -130,6 +133,11 @@ mv_cp110_icu_attach(device_t dev) device_printf(dev, "Cannot register ICU\n"); goto fail; } + + /* Allocate GICP compatible mapping entry (2 cells) */ + sc->parent_map_data = (struct intr_map_data_fdt *)intr_alloc_map_data( + INTR_MAP_DATA_FDT, sizeof(struct intr_map_data_fdt) + + + 3 * sizeof(phandle_t), M_WAITOK | M_ZERO); return (0); fail: @@ -137,6 +145,35 @@ mv_cp110_icu_attach(device_t dev) return (ENXIO); } +static struct intr_map_data * +mv_cp110_icu_convert_map_data(struct mv_cp110_icu_softc *sc, struct intr_map_data *data) +{ + struct intr_map_data_fdt *daf; + uint32_t reg, irq_no, irq_type; + + daf = (struct intr_map_data_fdt *)data; + if (daf->ncells != 2) + return (NULL); + irq_no = daf->cells[0]; + irq_type = daf->cells[1]; + if (irq_no >= MV_CP110_ICU_MAX_NIRQS) + return (NULL); + if (irq_type != IRQ_TYPE_LEVEL_HIGH && + irq_type != IRQ_TYPE_EDGE_RISING) + return (NULL); + + /* We rely on fact that ICU->GIC mapping is preset by bootstrap. */ + reg = RD4(sc, ICU_INT_CFG(irq_no)); + + /* Construct GICP compatible mapping. */ + sc->parent_map_data->ncells = 2; + sc->parent_map_data->cells[0] = reg & ICU_INT_MASK; + sc->parent_map_data->cells[1] = irq_type; + + return ((struct intr_map_data *)sc->parent_map_data); +} + + static int mv_cp110_icu_detach(device_t dev) { @@ -151,7 +188,9 @@ mv_cp110_icu_activate_intr(device_t dev, struct intr_irqsrc *isrc, struct mv_cp110_icu_softc *sc; sc = device_get_softc(dev); - + data = mv_cp110_icu_convert_map_data(sc, data); + if (data == NULL) + return (EINVAL); return (PIC_ACTIVATE_INTR(sc->parent, isrc, res, data)); } @@ -159,7 +198,6 @@ static void mv_cp110_icu_enable_intr(device_t dev, struct intr_irqsrc *isrc) { struct mv_cp110_icu_softc *sc; - sc = device_get_softc(dev); PIC_ENABLE_INTR(sc->parent, isrc); @@ -175,32 +213,43 @@ mv_cp110_icu_disable_intr(device_t dev, struct intr_irqsrc *isrc) PIC_DISABLE_INTR(sc->parent, isrc); } + + static int mv_cp110_icu_map_intr(device_t dev, struct intr_map_data *data, struct intr_irqsrc **isrcp) { struct mv_cp110_icu_softc *sc; struct intr_map_data_fdt *daf; - uint32_t reg; + uint32_t reg, irq_no, irq_type; + int ret; sc = device_get_softc(dev); if (data->type != INTR_MAP_DATA_FDT) return (ENOTSUP); + /* Parse original */ daf = (struct intr_map_data_fdt *)data; - if (daf->ncells != 3 || daf->cells[0] >= MV_CP110_ICU_MAX_NIRQS) + if (daf->ncells != 2) + return (EINVAL); + irq_no = daf->cells[0]; + irq_type = daf->cells[1]; + data = mv_cp110_icu_convert_map_data(sc, data); + if (data == NULL) return (EINVAL); - reg = RD4(sc, ICU_INT_CFG(daf->cells[1])); + reg = RD4(sc, ICU_INT_CFG(irq_no)); + reg |= ICU_INT_ENABLE; + if (irq_type == IRQ_TYPE_LEVEL_HIGH) + reg &= ~ICU_INT_EDGE; + else + reg |= ICU_INT_EDGE; + WR4(sc, ICU_INT_CFG(irq_no), reg); - if ((reg & ICU_INT_ENABLE) == 0) { - reg |= ICU_INT_ENABLE; - WR4(sc, ICU_INT_CFG(daf->cells[1]), reg); - } - - daf->cells[1] = reg & ICU_INT_MASK; - return (PIC_MAP_INTR(sc->parent, data, isrcp)); + ret = PIC_MAP_INTR(sc->parent, data, isrcp); + (*isrcp)->isrc_dev = sc->dev; + return (ret); } static int @@ -210,6 +259,9 @@ mv_cp110_icu_deactivate_intr(device_t dev, struct intr_irqsrc *isrc, struct mv_cp110_icu_softc *sc; sc = device_get_softc(dev); + data = mv_cp110_icu_convert_map_data(sc, data); + if (data == NULL) + return (EINVAL); return (PIC_DEACTIVATE_INTR(sc->parent, isrc, res, data)); } @@ -221,6 +273,9 @@ mv_cp110_icu_setup_intr(device_t dev, struct intr_irqsrc *isrc, struct mv_cp110_icu_softc *sc; sc = device_get_softc(dev); + data = mv_cp110_icu_convert_map_data(sc, data); + if (data == NULL) + return (EINVAL); return (PIC_SETUP_INTR(sc->parent, isrc, res, data)); } @@ -232,6 +287,9 @@ mv_cp110_icu_teardown_intr(device_t dev, struct intr_irqsrc *isrc, struct mv_cp110_icu_softc *sc; sc = device_get_softc(dev); + data = mv_cp110_icu_convert_map_data(sc, data); + if (data == NULL) + return (EINVAL); return (PIC_TEARDOWN_INTR(sc->parent, isrc, res, data)); } @@ -295,5 +353,5 @@ static driver_t mv_cp110_icu_driver = { sizeof(struct mv_cp110_icu_softc), }; -EARLY_DRIVER_MODULE(mv_cp110_icu, simplebus, mv_cp110_icu_driver, +EARLY_DRIVER_MODULE(mv_cp110_icu, mv_cp110_icu_bus, mv_cp110_icu_driver, mv_cp110_icu_devclass, 0, 0, BUS_PASS_INTERRUPT + BUS_PASS_ORDER_LAST); diff --git a/sys/arm/mv/mv_cp110_icu_bus.c b/sys/arm/mv/mv_cp110_icu_bus.c new file mode 100644 index 000000000000..c1c5f4ae7891 --- /dev/null +++ b/sys/arm/mv/mv_cp110_icu_bus.c @@ -0,0 +1,78 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * + * Copyright (c) 2019 Michal Meloun + * All rights reserved. + * + * 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 +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +static struct ofw_compat_data compat_data[] = { + {"marvell,cp110-icu", 1}, + {NULL, 0} +}; + +static int +mv_cp110_icu_bus_probe(device_t dev) +{ + + if (!ofw_bus_status_okay(dev)) + return (ENXIO); + if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0) + return (ENXIO); + + device_set_desc(dev, "Marvell Interrupt Consolidation Unit Bus"); + return (BUS_PROBE_DEFAULT); +} + +static device_method_t mv_cp110_icu_bus_methods[] = { + DEVMETHOD(device_probe, mv_cp110_icu_bus_probe), + + DEVMETHOD_END +}; + +DEFINE_CLASS_1(mv_cp110_icu_bus, mv_cp110_icu_bus_driver, + mv_cp110_icu_bus_methods, sizeof(struct simple_mfd_softc), + simple_mfd_driver); + +static devclass_t mv_cp110_icu_bus_devclass; +EARLY_DRIVER_MODULE(mv_cp110_icu_bus, simplebus, mv_cp110_icu_bus_driver, + mv_cp110_icu_bus_devclass, 0, 0, BUS_PASS_INTERRUPT); +MODULE_VERSION(mv_cp110_icu_bus, 1); diff --git a/sys/arm/mv/mv_thermal.c b/sys/arm/mv/mv_thermal.c index 60eb854ac046..e8ddd045ffa6 100644 --- a/sys/arm/mv/mv_thermal.c +++ b/sys/arm/mv/mv_thermal.c @@ -44,13 +44,14 @@ __FBSDID("$FreeBSD$"); #include #include #include - -#include +#include #include #include -#define CONTROL0 0x00 +#include "syscon_if.h" + +#define CONTROL0 0 /* Offset in config->regs[] array */ #define CONTROL0_TSEN_START (1 << 0) #define CONTROL0_TSEN_RESET (1 << 1) #define CONTROL0_TSEN_EN (1 << 2) @@ -62,12 +63,12 @@ __FBSDID("$FreeBSD$"); #define CONTROL0_MODE_EXTERNAL 0x2 #define CONTROL0_MODE_MASK 0x3 -#define CONTROL1 0x04 +#define CONTROL1 1 /* Offset in config->regs[] array */ /* This doesn't seems to work */ #define CONTROL1_TSEN_SENS_SHIFT 21 #define CONTROL1_TSEN_SENS_MASK 0x7 -#define STATUS 0x00 +#define STATUS 2 /* Offset in config->regs[] array */ #define STATUS_TEMP_MASK 0x3FF enum mv_thermal_type { @@ -77,6 +78,7 @@ enum mv_thermal_type { struct mv_thermal_config { enum mv_thermal_type type; + int regs[3]; int ncpus; int64_t calib_mul; int64_t calib_add; @@ -87,15 +89,16 @@ struct mv_thermal_config { struct mv_thermal_softc { device_t dev; - struct resource *res[2]; + struct syscon *syscon; struct mtx mtx; - struct mv_thermal_config *config; - int cur_sensor; + struct mv_thermal_config *config; + int cur_sensor; }; static struct mv_thermal_config mv_ap806_config = { .type = MV_AP806, + .regs = {0x84, 0x88, 0x8C}, .ncpus = 4, .calib_mul = 423, .calib_add = -150000, @@ -106,6 +109,7 @@ static struct mv_thermal_config mv_ap806_config = { static struct mv_thermal_config mv_cp110_config = { .type = MV_CP110, + .regs = {0x70, 0x74, 0x78}, .calib_mul = 2000096, .calib_add = 1172499100, .calib_div = 420100, @@ -113,22 +117,16 @@ static struct mv_thermal_config mv_cp110_config = { .signed_value = false, }; -static struct resource_spec mv_thermal_res_spec[] = { - { SYS_RES_MEMORY, 0, RF_ACTIVE }, - { SYS_RES_MEMORY, 1, RF_ACTIVE }, - { -1, 0 } -}; - static struct ofw_compat_data compat_data[] = { {"marvell,armada-ap806-thermal", (uintptr_t) &mv_ap806_config}, {"marvell,armada-cp110-thermal", (uintptr_t) &mv_cp110_config}, {NULL, 0} }; -#define RD_STA(sc, reg) bus_read_4((sc)->res[0], (reg)) -#define WR_STA(sc, reg, val) bus_write_4((sc)->res[0], (reg), (val)) -#define RD_CON(sc, reg) bus_read_4((sc)->res[1], (reg)) -#define WR_CON(sc, reg, val) bus_write_4((sc)->res[1], (reg), (val)) +#define RD4(sc, reg) \ + SYSCON_READ_4((sc)->syscon, sc->config->regs[reg]) +#define WR4(sc, reg, val) \ + SYSCON_WRITE_4((sc)->syscon, sc->config->regs[reg], (val)) static inline int32_t sign_extend(uint32_t value, int index) { @@ -146,7 +144,7 @@ mv_thermal_wait_sensor(struct mv_thermal_softc *sc) timeout = 100000; while (--timeout > 0) { - reg = RD_STA(sc, STATUS); + reg = RD4(sc, STATUS); if ((reg & sc->config->valid_mask) == sc->config->valid_mask) break; DELAY(100); @@ -167,12 +165,12 @@ mv_thermal_select_sensor(struct mv_thermal_softc *sc, int sensor) return (0); /* Stop the current reading and reset the module */ - reg = RD_CON(sc, CONTROL0); + reg = RD4(sc, CONTROL0); reg &= ~(CONTROL0_TSEN_START | CONTROL0_TSEN_EN); - WR_CON(sc, CONTROL0, reg); + WR4(sc, CONTROL0, reg); /* Switch to the selected sensor */ - /* + /* * NOTE : Datasheet says to use CONTROL1 for selecting * but when doing so the sensors >0 are never ready * Do what Linux does using undocumented bits in CONTROL0 @@ -185,13 +183,13 @@ mv_thermal_select_sensor(struct mv_thermal_softc *sc, int sensor) reg &= ~(CONTROL0_CHANNEL_MASK << CONTROL0_CHANNEL_SHIFT); reg |= (sensor - 1) << CONTROL0_CHANNEL_SHIFT; } - WR_CON(sc, CONTROL0, reg); + WR4(sc, CONTROL0, reg); sc->cur_sensor = sensor; /* Start the reading */ - reg = RD_CON(sc, CONTROL0); + reg = RD4(sc, CONTROL0); reg |= CONTROL0_TSEN_START | CONTROL0_TSEN_EN; - WR_CON(sc, CONTROL0, reg); + WR4(sc, CONTROL0, reg); return (mv_thermal_wait_sensor(sc)); } @@ -206,7 +204,7 @@ mv_thermal_read_sensor(struct mv_thermal_softc *sc, int sensor, int *temp) if (rv != 0) return (rv); - reg = RD_STA(sc, STATUS) & STATUS_TEMP_MASK; + reg = RD4(sc, STATUS) & STATUS_TEMP_MASK; if (sc->config->signed_value) sample = sign_extend(reg, fls(STATUS_TEMP_MASK) - 1); @@ -225,14 +223,14 @@ ap806_init(struct mv_thermal_softc *sc) uint32_t reg; /* Start the temp capture/conversion */ - reg = RD_CON(sc, CONTROL0); + reg = RD4(sc, CONTROL0); reg &= ~CONTROL0_TSEN_RESET; reg |= CONTROL0_TSEN_START | CONTROL0_TSEN_EN; /* Sample every ~2ms */ reg |= CONTROL0_OSR_MAX << CONTROL0_OSR_SHIFT; - WR_CON(sc, CONTROL0, reg); + WR4(sc, CONTROL0, reg); /* Since we just started the module wait for the sensor to be ready */ mv_thermal_wait_sensor(sc); @@ -245,15 +243,15 @@ cp110_init(struct mv_thermal_softc *sc) { uint32_t reg; - reg = RD_CON(sc, CONTROL1); + reg = RD4(sc, CONTROL1); reg &= (1 << 7); reg |= (1 << 8); - WR_CON(sc, CONTROL1, reg); + WR4(sc, CONTROL1, reg); /* Sample every ~2ms */ - reg = RD_CON(sc, CONTROL0); + reg = RD4(sc, CONTROL0); reg |= CONTROL0_OSR_MAX << CONTROL0_OSR_SHIFT; - WR_CON(sc, CONTROL0, reg); + WR4(sc, CONTROL0, reg); return (0); } @@ -300,19 +298,24 @@ mv_thermal_attach(device_t dev) struct mv_thermal_softc *sc; struct sysctl_ctx_list *ctx; struct sysctl_oid_list *oid; + phandle_t node; char name[255]; char desc[255]; int i; sc = device_get_softc(dev); sc->dev = dev; + node = ofw_bus_get_node(dev); - sc->config = (struct mv_thermal_config *)ofw_bus_search_compatible(dev, compat_data)->ocd_data; + sc->config = (struct mv_thermal_config *) + ofw_bus_search_compatible(dev, compat_data)->ocd_data; mtx_init(&sc->mtx, device_get_nameunit(dev), NULL, MTX_DEF); - if (bus_alloc_resources(dev, mv_thermal_res_spec, sc->res) != 0) { - device_printf(dev, "cannot allocate resources for device\n"); + + if (SYSCON_GET_HANDLE(sc->dev, &sc->syscon) != 0 || + sc->syscon == NULL) { + device_printf(dev, "cannot get syscon for device\n"); return (ENXIO); } @@ -355,8 +358,6 @@ mv_thermal_detach(device_t dev) sc = device_get_softc(dev); - bus_release_resources(dev, mv_thermal_res_spec, sc->res); - return (0); } diff --git a/sys/arm/mv/mvebu_pinctrl.c b/sys/arm/mv/mvebu_pinctrl.c index 3b183dc87674..1f9df8fbf3b6 100644 --- a/sys/arm/mv/mvebu_pinctrl.c +++ b/sys/arm/mv/mvebu_pinctrl.c @@ -44,14 +44,14 @@ __FBSDID("$FreeBSD$"); #include #include -#include +#include + +#include #include #include -#include - -#include "opt_soc.h" +#include "syscon_if.h" #define PINS_PER_REG 8 #define BITS_PER_PIN 4 @@ -68,7 +68,6 @@ struct mv_padconf { size_t npins; }; -#ifdef SOC_MARVELL_8K const static struct mv_pins ap806_pins[] = { {"mpp0", {"gpio", "sdio", NULL, "spi0"}}, {"mpp1", {"gpio", "sdio", NULL, "spi0"}}, @@ -96,29 +95,22 @@ const struct mv_padconf ap806_padconf = { .npins = nitems(ap806_pins), .pins = ap806_pins, }; -#endif struct mv_pinctrl_softc { device_t dev; - struct resource *res; + struct syscon *syscon; struct mv_padconf *padconf; }; -static struct resource_spec mv_pinctrl_res_spec[] = { - { SYS_RES_MEMORY, 0, RF_ACTIVE | RF_SHAREABLE }, - { -1, 0 } -}; static struct ofw_compat_data compat_data[] = { -#ifdef SOC_MARVELL_8K {"marvell,ap806-pinctrl", (uintptr_t)&ap806_padconf}, -#endif {NULL, 0} }; -#define RD4(sc, reg) bus_read_4((sc)->res, (reg)) -#define WR4(sc, reg, val) bus_write_4((sc)->res, (reg), (val)) +#define RD4(sc, reg) SYSCON_READ_4((sc)->syscon, (reg)) +#define WR4(sc, reg, val) SYSCON_WRITE_4((sc)->syscon, (reg), (val)) static void mv_pinctrl_configure_pin(struct mv_pinctrl_softc *sc, uint32_t pin, @@ -200,10 +192,12 @@ mv_pinctrl_attach(device_t dev) sc = device_get_softc(dev); sc->dev = dev; - sc->padconf = (struct mv_padconf *)ofw_bus_search_compatible(dev, compat_data)->ocd_data; + sc->padconf = (struct mv_padconf *) + ofw_bus_search_compatible(dev,compat_data)->ocd_data; - if (bus_alloc_resources(dev, mv_pinctrl_res_spec, &sc->res) != 0) { - device_printf(dev, "cannot allocate resources for device\n"); + if (SYSCON_GET_HANDLE(sc->dev, &sc->syscon) != 0 || + sc->syscon == NULL) { + device_printf(dev, "cannot get syscon for device\n"); return (ENXIO); } diff --git a/sys/arm64/conf/GENERIC b/sys/arm64/conf/GENERIC index 33e2bbf1ecc1..96db964b68c7 100644 --- a/sys/arm64/conf/GENERIC +++ b/sys/arm64/conf/GENERIC @@ -260,6 +260,7 @@ device aw_ccu # Allwinner clock controller device aw_nmi # Allwinner NMI support device mv_cp110_icu # Marvell CP110 ICU device mv_ap806_gicp # Marvell AP806 GICP +device mv_ap806_sei # Marvell AP806 SEI # Real-time clock support device aw_rtc # Allwinner Real-time Clock diff --git a/sys/conf/files.arm64 b/sys/conf/files.arm64 index e8b724708e9b..ac05e28b77e6 100644 --- a/sys/conf/files.arm64 +++ b/sys/conf/files.arm64 @@ -105,12 +105,15 @@ arm/broadcom/bcm2835/bcm2835_wdog.c optional soc_brcm_bcm2837 fdt arm/broadcom/bcm2835/bcm2836.c optional soc_brcm_bcm2837 fdt arm/broadcom/bcm2835/bcm283x_dwc_fdt.c optional dwcotg fdt soc_brcm_bcm2837 arm/mv/a37x0_gpio.c optional a37x0_gpio gpio fdt +arm/mv/armada38x/armada38x_rtc.c optional mv_rtc fdt arm/mv/gpio.c optional mv_gpio fdt arm/mv/mvebu_pinctrl.c optional mvebu_pinctrl fdt -arm/mv/mv_cp110_icu.c optional mv_cp110_icu fdt -arm/mv/mv_ap806_gicp.c optional mv_ap806_gicp fdt arm/mv/mv_ap806_clock.c optional SOC_MARVELL_8K fdt +arm/mv/mv_ap806_gicp.c optional mv_ap806_gicp fdt +arm/mv/mv_ap806_sei.c optional mv_ap806_sei fdt arm/mv/mv_cp110_clock.c optional SOC_MARVELL_8K fdt +arm/mv/mv_cp110_icu.c optional mv_cp110_icu fdt +arm/mv/mv_cp110_icu_bus.c optional mv_cp110_icu fdt arm/mv/mv_thermal.c optional SOC_MARVELL_8K mv_thermal fdt arm/mv/armada38x/armada38x_rtc.c optional mv_rtc fdt arm/xilinx/uart_dev_cdnc.c optional uart soc_xilinx_zynq