71d90c04a8
to check the status property in their probe routines. Simplebus used to only instantiate its children whose status="okay" but that was improper behavior, fixed in r261352. Now that it doesn't check anymore and probes all its children; the children all have to do the check because really only the children know how to properly interpret their status property strings. Right now all existing drivers only understand "okay" versus something- that's-not-okay, so they all use the new ofw_bus_status_okay() helper.
704 lines
18 KiB
C
704 lines
18 KiB
C
/*-
|
|
* Copyright (c) 2013 SRI International
|
|
* All rights reserved.
|
|
*
|
|
* This software was developed by SRI International and the University of
|
|
* Cambridge Computer Laboratory under DARPA/AFRL contract (FA8750-10-C-0237)
|
|
* ("CTSRD"), as part of the DARPA CRASH research programme.
|
|
*
|
|
* 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/kernel.h>
|
|
#include <sys/lock.h>
|
|
#include <sys/malloc.h>
|
|
#include <sys/module.h>
|
|
#include <sys/mutex.h>
|
|
#include <sys/systm.h>
|
|
#include <sys/bus.h>
|
|
|
|
#include <machine/bus.h>
|
|
#include <machine/intr_machdep.h>
|
|
|
|
#include <dev/ofw/ofw_bus.h>
|
|
#include <dev/ofw/ofw_bus_subr.h>
|
|
|
|
#include <dev/fdt/fdt_common.h>
|
|
|
|
#include "fdt_ic_if.h"
|
|
|
|
struct beripic_softc;
|
|
|
|
static uint64_t bp_read_cfg(struct beripic_softc *, int);
|
|
static void bp_write_cfg(struct beripic_softc *, int, uint64_t);
|
|
static void bp_detach_resources(device_t);
|
|
static char *bp_strconfig(uint64_t, char *, size_t);
|
|
static void bp_config_source(device_t, int, int, u_long, u_long);
|
|
#ifdef __mips__
|
|
static void bp_set_counter_name(device_t, device_t, int);
|
|
#endif
|
|
|
|
static int beripic_fdt_probe(device_t);
|
|
static int beripic_fdt_attach(device_t);
|
|
|
|
static int beripic_activate_intr(device_t, struct resource *);
|
|
static struct resource *
|
|
beripic_alloc_intr(device_t, device_t, int *, u_long, u_int);
|
|
static int beripic_config_intr(device_t, int, enum intr_trigger,
|
|
enum intr_polarity);
|
|
static int beripic_release_intr(device_t, struct resource *);
|
|
static int beripic_setup_intr(device_t, device_t, struct resource *,
|
|
int, driver_filter_t *, driver_intr_t *, void *, void **);
|
|
static int beripic_teardown_intr(device_t, device_t, struct resource *,
|
|
void *);
|
|
|
|
static int beripic_filter(void *);
|
|
static void beripic_intr(void *);
|
|
|
|
#define BP_MAX_HARD_IRQS 6
|
|
#define BP_FIRST_SOFT 64
|
|
|
|
struct beripic_softc {
|
|
device_t bp_dev;
|
|
struct resource *bp_cfg_res;
|
|
struct resource *bp_read_res;
|
|
struct resource *bp_set_res;
|
|
struct resource *bp_clear_res;
|
|
int bp_cfg_rid;
|
|
int bp_read_rid;
|
|
int bp_set_rid;
|
|
int bp_clear_rid;
|
|
bus_space_tag_t bp_cfg_bst;
|
|
bus_space_tag_t bp_read_bst;
|
|
bus_space_tag_t bp_set_bst;
|
|
bus_space_tag_t bp_clear_bst;
|
|
bus_space_handle_t bp_cfg_bsh;
|
|
bus_space_handle_t bp_read_bsh;
|
|
bus_space_handle_t bp_set_bsh;
|
|
bus_space_handle_t bp_clear_bsh;
|
|
|
|
struct resource *bp_irqs[BP_MAX_HARD_IRQS];
|
|
int bp_irq_rids[BP_MAX_HARD_IRQS];
|
|
int bp_nirqs;
|
|
int bp_next_irq;
|
|
int bp_next_tid;
|
|
|
|
int bp_nthreads;
|
|
|
|
int bp_nhard;
|
|
int bp_nsoft;
|
|
int bp_nsrcs;
|
|
struct rman bp_src_rman;
|
|
|
|
#ifdef __mips__
|
|
mips_intrcnt_t *bp_counters;
|
|
#endif
|
|
|
|
struct mtx bp_cfgmtx;
|
|
};
|
|
|
|
struct beripic_intr_arg {
|
|
driver_filter_t *filter;
|
|
driver_intr_t *intr;
|
|
void *arg;
|
|
struct resource *irq;
|
|
#ifdef __mips__
|
|
mips_intrcnt_t counter;
|
|
#endif
|
|
};
|
|
|
|
struct beripic_cookie {
|
|
struct beripic_intr_arg *bpia;
|
|
struct resource *hirq;
|
|
void *cookie;
|
|
};
|
|
|
|
#define BP_CFG_MASK_E 0x80000000ull
|
|
#define BP_CFG_SHIFT_E 31
|
|
#define BP_CFG_MASK_TID 0x7FFFFF00ull /* Depends on CPU */
|
|
#define BP_CFG_SHIFT_TID 8
|
|
#define BP_CFG_MASK_IRQ 0x0000000Full
|
|
#define BP_CFG_SHIFT_IRQ 0
|
|
#define BP_CFG_VALID (BP_CFG_MASK_E|BP_CFG_MASK_TID|BP_CFG_MASK_IRQ)
|
|
#define BP_CFG_RESERVED ~BP_CFG_VALID
|
|
|
|
#define BP_CFG_ENABLED(cfg) (((cfg) & BP_CFG_MASK_E) >> BP_CFG_SHIFT_E)
|
|
#define BP_CFG_TID(cfg) (((cfg) & BP_CFG_MASK_TID) >> BP_CFG_SHIFT_TID)
|
|
#define BP_CFG_IRQ(cfg) (((cfg) & BP_CFG_MASK_IRQ) >> BP_CFG_SHIFT_IRQ)
|
|
|
|
MALLOC_DEFINE(M_BERIPIC, "beripic", "beripic memory");
|
|
|
|
static uint64_t
|
|
bp_read_cfg(struct beripic_softc *sc, int irq)
|
|
{
|
|
|
|
KASSERT((irq >= 0 && irq < sc->bp_nsrcs),
|
|
("IRQ of of range %d (0-%d)", irq, sc->bp_nsrcs - 1));
|
|
return (bus_space_read_8(sc->bp_cfg_bst, sc->bp_cfg_bsh, irq * 8));
|
|
}
|
|
|
|
static void
|
|
bp_write_cfg(struct beripic_softc *sc, int irq, uint64_t config)
|
|
{
|
|
|
|
KASSERT((irq >= 0 && irq < sc->bp_nsrcs),
|
|
("IRQ of of range %d (0-%d)", irq, sc->bp_nsrcs - 1));
|
|
bus_space_write_8(sc->bp_cfg_bst, sc->bp_cfg_bsh, irq * 8, config);
|
|
}
|
|
|
|
static void
|
|
bp_detach_resources(device_t dev)
|
|
{
|
|
struct beripic_softc *sc;
|
|
int i;
|
|
|
|
sc = device_get_softc(dev);
|
|
|
|
if (sc->bp_cfg_res != NULL) {
|
|
bus_release_resource(dev, SYS_RES_MEMORY, sc->bp_cfg_rid,
|
|
sc->bp_cfg_res);
|
|
sc->bp_cfg_res = NULL;
|
|
}
|
|
if (sc->bp_read_res != NULL) {
|
|
bus_release_resource(dev, SYS_RES_MEMORY, sc->bp_read_rid,
|
|
sc->bp_read_res);
|
|
sc->bp_read_res = NULL;
|
|
}
|
|
if (sc->bp_set_res != NULL) {
|
|
bus_release_resource(dev, SYS_RES_MEMORY, sc->bp_set_rid,
|
|
sc->bp_set_res);
|
|
sc->bp_set_res = NULL;
|
|
}
|
|
if (sc->bp_clear_res != NULL) {
|
|
bus_release_resource(dev, SYS_RES_MEMORY, sc->bp_clear_rid,
|
|
sc->bp_clear_res);
|
|
sc->bp_clear_res = NULL;
|
|
}
|
|
for (i = sc->bp_nirqs - 1; i >= 0; i--) {
|
|
bus_release_resource(dev, SYS_RES_IRQ, sc->bp_irq_rids[i],
|
|
sc->bp_irqs[i]);
|
|
}
|
|
sc->bp_nirqs = 0;
|
|
}
|
|
|
|
static char *
|
|
bp_strconfig(uint64_t config, char *configstr, size_t len)
|
|
{
|
|
|
|
if (snprintf(configstr, len, "%s tid: %llu hardintr %llu",
|
|
BP_CFG_ENABLED(config) ? "enabled" : "disabled",
|
|
BP_CFG_TID(config), BP_CFG_IRQ(config)) > len - 1)
|
|
return (NULL);
|
|
return (configstr);
|
|
}
|
|
|
|
static void
|
|
bp_config_source(device_t ic, int src, int enable, u_long tid, u_long irq)
|
|
{
|
|
struct beripic_softc *sc;
|
|
uint64_t config;
|
|
|
|
sc = device_get_softc(ic);
|
|
|
|
config = 0;
|
|
config |= enable << BP_CFG_SHIFT_E;
|
|
config |= tid << BP_CFG_SHIFT_TID;
|
|
config |= irq << BP_CFG_SHIFT_IRQ;
|
|
|
|
bp_write_cfg(sc, src, config);
|
|
}
|
|
|
|
#ifdef __mips__
|
|
static void
|
|
bp_set_counter_name(device_t ic, device_t child, int src)
|
|
{
|
|
struct beripic_softc *sc;
|
|
char name[MAXCOMLEN + 1];
|
|
|
|
sc = device_get_softc(ic);
|
|
|
|
if (snprintf(name, sizeof(name), "bp%dsrc%d%s%s%s",
|
|
device_get_unit(ic), src, src < sc->bp_nhard ? "" : "s",
|
|
child == NULL ? "" : " ",
|
|
child == NULL ? " " : device_get_nameunit(child)) >= sizeof(name))
|
|
name[sizeof(name) - 2] = '+';
|
|
|
|
mips_intrcnt_setname(sc->bp_counters[src], name);
|
|
}
|
|
#endif
|
|
|
|
static int
|
|
beripic_fdt_probe(device_t dev)
|
|
{
|
|
|
|
if (!ofw_bus_status_okay(dev))
|
|
return (ENXIO);
|
|
|
|
if (!ofw_bus_is_compatible(dev, "sri-cambridge,beri-pic"))
|
|
return (ENXIO);
|
|
|
|
device_set_desc(dev, "BERI Programmable Interrupt Controller");
|
|
return (BUS_PROBE_DEFAULT);
|
|
}
|
|
|
|
static int
|
|
beripic_fdt_attach(device_t dev)
|
|
{
|
|
char configstr[64];
|
|
struct beripic_softc *sc;
|
|
struct fdt_ic *fic;
|
|
pcell_t nhard, nsoft;
|
|
phandle_t ph;
|
|
int error, i, src;
|
|
uint64_t config;
|
|
|
|
sc = device_get_softc(dev);
|
|
sc->bp_dev = dev;
|
|
|
|
mtx_init(&sc->bp_cfgmtx, "beripic config lock", NULL, MTX_DEF);
|
|
|
|
/*
|
|
* FDT lists CONFIG, IP_READ, IP_SET, and IP_CLEAR registers as
|
|
* seperate memory regions in that order.
|
|
*/
|
|
sc->bp_cfg_rid = 0;
|
|
sc->bp_cfg_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY,
|
|
&sc->bp_cfg_rid, RF_ACTIVE);
|
|
if (sc->bp_cfg_res == NULL) {
|
|
device_printf(dev, "failed to map config memory");
|
|
error = ENXIO;
|
|
goto err;
|
|
}
|
|
if (bootverbose)
|
|
device_printf(sc->bp_dev, "config region at mem %p-%p\n",
|
|
(void *)rman_get_start(sc->bp_cfg_res),
|
|
(void *)(rman_get_start(sc->bp_cfg_res) +
|
|
rman_get_size(sc->bp_cfg_res)));
|
|
|
|
sc->bp_read_rid = 1;
|
|
sc->bp_read_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY,
|
|
&sc->bp_read_rid, RF_ACTIVE);
|
|
if (sc->bp_read_res == NULL) {
|
|
device_printf(dev, "failed to map IP read memory");
|
|
error = ENXIO;
|
|
goto err;
|
|
}
|
|
if (bootverbose)
|
|
device_printf(sc->bp_dev, "IP read region at mem %p-%p\n",
|
|
(void *)rman_get_start(sc->bp_read_res),
|
|
(void *)(rman_get_start(sc->bp_read_res) +
|
|
rman_get_size(sc->bp_read_res)));
|
|
|
|
sc->bp_set_rid = 2;
|
|
sc->bp_set_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY,
|
|
&sc->bp_set_rid, RF_ACTIVE);
|
|
if (sc->bp_set_res == NULL) {
|
|
device_printf(dev, "failed to map IP read memory");
|
|
error = ENXIO;
|
|
goto err;
|
|
}
|
|
if (bootverbose)
|
|
device_printf(sc->bp_dev, "IP set region at mem %p-%p\n",
|
|
(void *)rman_get_start(sc->bp_set_res),
|
|
(void *)(rman_get_start(sc->bp_set_res) +
|
|
rman_get_size(sc->bp_set_res)));
|
|
|
|
sc->bp_clear_rid = 3;
|
|
sc->bp_clear_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY,
|
|
&sc->bp_clear_rid, RF_ACTIVE);
|
|
if (sc->bp_clear_res == NULL) {
|
|
device_printf(dev, "failed to map IP read memory");
|
|
error = ENXIO;
|
|
goto err;
|
|
}
|
|
if (bootverbose)
|
|
device_printf(sc->bp_dev, "IP clear region at mem %p-%p\n",
|
|
(void *)rman_get_start(sc->bp_clear_res),
|
|
(void *)(rman_get_start(sc->bp_clear_res) +
|
|
rman_get_size(sc->bp_clear_res)));
|
|
|
|
i = 0;
|
|
for (i = 0; i < BP_MAX_HARD_IRQS; i++) {
|
|
sc->bp_irq_rids[i] = i;
|
|
sc->bp_irqs[i] = bus_alloc_resource_any(dev, SYS_RES_IRQ,
|
|
&sc->bp_irq_rids[i], RF_ACTIVE | RF_SHAREABLE);
|
|
if (sc->bp_irqs[i] == NULL)
|
|
break;
|
|
}
|
|
if (i == 0) {
|
|
device_printf(dev, "failed to allocate any parent IRQs!");
|
|
error = ENXIO;
|
|
goto err;
|
|
}
|
|
sc->bp_nirqs = i;
|
|
|
|
ph = ofw_bus_gen_get_node(device_get_parent(dev), dev);
|
|
|
|
#ifndef SMP
|
|
sc->bp_nthreads = 1;
|
|
#else
|
|
sc->bp_nthreads = 1;
|
|
/* XXX: get nthreads from cpu(s) somehow */
|
|
#endif
|
|
|
|
if (OF_getprop(ph, "hard-interrupt-sources", &nhard, sizeof(nhard))
|
|
<= 0) {
|
|
device_printf(dev, "failed to get number of hard sources");
|
|
error = ENXIO;
|
|
goto err;
|
|
}
|
|
if (OF_getprop(ph, "soft-interrupt-sources", &nsoft, sizeof(nsoft))
|
|
<= 0) {
|
|
device_printf(dev, "failed to get number of soft sources");
|
|
error = ENXIO;
|
|
goto err;
|
|
}
|
|
|
|
sc->bp_nhard = nhard;
|
|
sc->bp_nsoft = nsoft;
|
|
sc->bp_nsrcs = sc->bp_nhard + sc->bp_nsoft;
|
|
/* XXX: should deal with gap between hard and soft */
|
|
KASSERT(sc->bp_nhard <= BP_FIRST_SOFT,
|
|
("too many hard sources"));
|
|
KASSERT(rman_get_size(sc->bp_cfg_res) / 8 == sc->bp_nsrcs,
|
|
("config space size does not match sources"));
|
|
KASSERT(sc->bp_nhard % 64 == 0,
|
|
("Non-multiple of 64 intr counts not supported"));
|
|
KASSERT(sc->bp_nsoft % 64 == 0,
|
|
("Non-multiple of 64 intr counts not supported"));
|
|
if (bootverbose)
|
|
device_printf(dev, "%d hard and %d soft sources\n",
|
|
sc->bp_nhard, sc->bp_nsoft);
|
|
|
|
#ifdef __mips__
|
|
sc->bp_counters = malloc(sizeof(*sc->bp_counters) * sc->bp_nsrcs,
|
|
M_BERIPIC, M_WAITOK|M_ZERO);
|
|
for (i = 0; i < sc->bp_nsrcs; i++) {
|
|
sc->bp_counters[i] = mips_intrcnt_create("");
|
|
bp_set_counter_name(dev, NULL, i);
|
|
}
|
|
#endif
|
|
|
|
sc->bp_src_rman.rm_start = 0;
|
|
sc->bp_src_rman.rm_end = sc->bp_nsrcs - 1;
|
|
sc->bp_src_rman.rm_type = RMAN_ARRAY;
|
|
sc->bp_src_rman.rm_descr = "Interrupt source";
|
|
if (rman_init(&(sc->bp_src_rman)) != 0 ||
|
|
rman_manage_region(&(sc->bp_src_rman), 0, sc->bp_nsrcs - 1) != 0) {
|
|
device_printf(dev, "Failed to set up sources rman");
|
|
error = ENXIO;
|
|
goto err;
|
|
}
|
|
|
|
sc->bp_cfg_bst = rman_get_bustag(sc->bp_cfg_res);
|
|
sc->bp_cfg_bsh = rman_get_bushandle(sc->bp_cfg_res);
|
|
sc->bp_read_bst = rman_get_bustag(sc->bp_read_res);
|
|
sc->bp_read_bsh = rman_get_bushandle(sc->bp_read_res);
|
|
sc->bp_set_bst = rman_get_bustag(sc->bp_set_res);
|
|
sc->bp_set_bsh = rman_get_bushandle(sc->bp_set_res);
|
|
sc->bp_clear_bst = rman_get_bustag(sc->bp_clear_res);
|
|
sc->bp_clear_bsh = rman_get_bushandle(sc->bp_clear_res);
|
|
|
|
for (src = 0; src < sc->bp_nsrcs; src++) {
|
|
config = bp_read_cfg(sc, src);
|
|
if (config == 0)
|
|
continue;
|
|
|
|
if (bootverbose) {
|
|
device_printf(dev, "initial config: src %d: %s\n", src,
|
|
bp_strconfig(config, configstr, sizeof(configstr)));
|
|
if (config & BP_CFG_RESERVED)
|
|
device_printf(dev,
|
|
"reserved bits not 0: 0x%016jx\n",
|
|
(uintmax_t) config);
|
|
}
|
|
|
|
bp_config_source(dev, src, 0, 0, 0);
|
|
}
|
|
|
|
fic = malloc(sizeof(*fic), M_BERIPIC, M_WAITOK|M_ZERO);
|
|
fic->iph = ph;
|
|
fic->dev = dev;
|
|
SLIST_INSERT_HEAD(&fdt_ic_list_head, fic, fdt_ics);
|
|
|
|
return (0);
|
|
err:
|
|
bp_detach_resources(dev);
|
|
|
|
return (error);
|
|
}
|
|
|
|
static struct resource *
|
|
beripic_alloc_intr(device_t ic, device_t child, int *rid, u_long irq,
|
|
u_int flags)
|
|
{
|
|
struct beripic_softc *sc;
|
|
struct resource *rv;
|
|
|
|
sc = device_get_softc(ic);
|
|
|
|
rv = rman_reserve_resource(&(sc->bp_src_rman), irq, irq, 1, flags,
|
|
child);
|
|
if (rv == NULL)
|
|
printf("%s: could not reserve source interrupt for %s\n",
|
|
__func__, device_get_nameunit(child));
|
|
rman_set_rid(rv, *rid);
|
|
|
|
if ((flags & RF_ACTIVE) &&
|
|
beripic_activate_intr(ic, rv) != 0) {
|
|
printf("%s: could not activate interrupt\n", __func__);
|
|
rman_release_resource(rv);
|
|
return (NULL);
|
|
}
|
|
|
|
return (rv);
|
|
}
|
|
|
|
static int
|
|
beripic_release_intr(device_t ic, struct resource *r)
|
|
{
|
|
|
|
return (rman_release_resource(r));
|
|
}
|
|
|
|
static int
|
|
beripic_activate_intr(device_t ic, struct resource *r)
|
|
{
|
|
|
|
return (rman_activate_resource(r));
|
|
}
|
|
|
|
static int
|
|
beripic_deactivate_intr(device_t ic, struct resource *r)
|
|
{
|
|
|
|
return (rman_deactivate_resource(r));
|
|
}
|
|
|
|
static int
|
|
beripic_config_intr(device_t dev, int irq, enum intr_trigger trig,
|
|
enum intr_polarity pol)
|
|
{
|
|
|
|
if (trig != INTR_TRIGGER_CONFORM || pol != INTR_POLARITY_CONFORM)
|
|
return (EINVAL);
|
|
|
|
return (0);
|
|
}
|
|
|
|
static int
|
|
beripic_setup_intr(device_t ic, device_t child, struct resource *irq,
|
|
int flags, driver_filter_t *filter, driver_intr_t *intr, void *arg,
|
|
void **cookiep)
|
|
{
|
|
struct beripic_softc *sc;
|
|
struct beripic_intr_arg *bpia;
|
|
struct beripic_cookie *bpc;
|
|
int error;
|
|
u_long hirq, src, tid;
|
|
|
|
sc = device_get_softc(ic);
|
|
|
|
src = rman_get_start(irq);
|
|
|
|
KASSERT(src < sc->bp_nsrcs, ("source (%lu) out of range 0-%d",
|
|
src, sc->bp_nsrcs - 1));
|
|
|
|
bpia = malloc(sizeof(*bpia), M_BERIPIC, M_WAITOK|M_ZERO);
|
|
bpia->filter = filter;
|
|
bpia->intr = intr;
|
|
bpia->arg = arg;
|
|
bpia->irq = irq;
|
|
#ifdef __mips__
|
|
bpia->counter = sc->bp_counters[src];
|
|
bp_set_counter_name(ic, child, src);
|
|
#endif
|
|
|
|
bpc = malloc(sizeof(*bpc), M_BERIPIC, M_WAITOK|M_ZERO);
|
|
bpc->bpia = bpia;
|
|
|
|
mtx_lock(&(sc->bp_cfgmtx));
|
|
bpc->hirq = sc->bp_irqs[sc->bp_next_irq];
|
|
hirq = rman_get_start(bpc->hirq);
|
|
tid = sc->bp_next_tid;
|
|
|
|
error = BUS_SETUP_INTR(device_get_parent(ic), ic, bpc->hirq, flags,
|
|
beripic_filter, intr == NULL ? NULL : beripic_intr, bpia,
|
|
&(bpc->cookie));
|
|
if (error != 0)
|
|
goto err;
|
|
|
|
#ifdef NOTYET
|
|
#ifdef SMP
|
|
/* XXX: bind ithread to cpu */
|
|
sc->bp_next_tid++;
|
|
if (sc->bp_next_tid >= sc->bp_nthreads)
|
|
sc->bp_next_tid = 0;
|
|
#endif
|
|
#endif
|
|
if (sc->bp_next_tid == 0) {
|
|
sc->bp_next_irq++;
|
|
if (sc->bp_next_irq >= sc->bp_nirqs)
|
|
sc->bp_next_irq = 0;
|
|
}
|
|
mtx_unlock(&(sc->bp_cfgmtx));
|
|
|
|
*cookiep = bpc;
|
|
|
|
bp_config_source(ic, rman_get_start(irq), 1, tid, hirq);
|
|
|
|
return (0);
|
|
err:
|
|
free(bpc, M_BERIPIC);
|
|
free(bpia, M_BERIPIC);
|
|
|
|
return (error);
|
|
}
|
|
|
|
static int
|
|
beripic_teardown_intr(device_t dev, device_t child, struct resource *irq,
|
|
void *cookie)
|
|
{
|
|
struct beripic_cookie *bpc;
|
|
int error;
|
|
|
|
bpc = cookie;
|
|
|
|
bp_config_source(dev, rman_get_start(irq), 0, 0, 0);
|
|
|
|
free(bpc->bpia, M_BERIPIC);
|
|
|
|
error = BUS_TEARDOWN_INTR(device_get_parent(dev), dev, bpc->hirq,
|
|
bpc->cookie);
|
|
|
|
free(bpc, M_BERIPIC);
|
|
|
|
return (error);
|
|
}
|
|
|
|
static int
|
|
beripic_filter(void *arg)
|
|
{
|
|
struct beripic_intr_arg *bpic;
|
|
|
|
bpic = arg;
|
|
|
|
#ifdef __mips__
|
|
mips_intrcnt_inc(bpic->counter);
|
|
#endif
|
|
|
|
/* XXX: Add a check that our source is high */
|
|
|
|
if (bpic->filter == NULL)
|
|
return (FILTER_SCHEDULE_THREAD);
|
|
|
|
return (bpic->filter(bpic->arg));
|
|
}
|
|
|
|
static void
|
|
beripic_intr(void *arg)
|
|
{
|
|
struct beripic_intr_arg *bpic;
|
|
|
|
bpic = arg;
|
|
|
|
KASSERT(bpic->intr != NULL,
|
|
("%s installed, but no child intr", __func__));
|
|
|
|
bpic->intr(bpic->arg);
|
|
}
|
|
|
|
#ifdef SMP
|
|
static void
|
|
beripic_setup_ipi(device_t ic, u_int tid, u_int ipi_irq)
|
|
{
|
|
|
|
bp_config_source(ic, BP_FIRST_SOFT + tid, 1, tid, ipi_irq);
|
|
}
|
|
|
|
static void
|
|
beripic_send_ipi(device_t ic, u_int tid)
|
|
{
|
|
struct beripic_softc *sc;
|
|
uint64_t bit;
|
|
|
|
sc = device_get_softc(ic);
|
|
|
|
KASSERT(tid < sc->bp_nsoft, ("tid (%d) too large\n", tid));
|
|
|
|
bit = 1ULL << (tid % 64);
|
|
bus_space_write_8(sc->bp_set_bst, sc->bp_set_bsh,
|
|
(BP_FIRST_SOFT / 8) + (tid / 64), bit);
|
|
}
|
|
|
|
static void
|
|
beripic_clear_ipi(device_t ic, u_int tid)
|
|
{
|
|
struct beripic_softc *sc;
|
|
uint64_t bit;
|
|
|
|
sc = device_get_softc(ic);
|
|
|
|
KASSERT(tid < sc->bp_nsoft, ("tid (%d) to large\n", tid));
|
|
|
|
bit = 1ULL << (tid % 64);
|
|
bus_space_write_8(sc->bp_clear_bst, sc->bp_clear_bsh,
|
|
(BP_FIRST_SOFT / 8) + (tid / 64), bit);
|
|
}
|
|
#endif
|
|
|
|
devclass_t beripic_devclass;
|
|
|
|
static device_method_t beripic_fdt_methods[] = {
|
|
/* Device interface */
|
|
DEVMETHOD(device_probe, beripic_fdt_probe),
|
|
DEVMETHOD(device_attach, beripic_fdt_attach),
|
|
|
|
DEVMETHOD(fdt_ic_activate_intr, beripic_activate_intr),
|
|
DEVMETHOD(fdt_ic_alloc_intr, beripic_alloc_intr),
|
|
DEVMETHOD(fdt_ic_config_intr, beripic_config_intr),
|
|
DEVMETHOD(fdt_ic_deactivate_intr, beripic_deactivate_intr),
|
|
DEVMETHOD(fdt_ic_release_intr, beripic_release_intr),
|
|
DEVMETHOD(fdt_ic_setup_intr, beripic_setup_intr),
|
|
DEVMETHOD(fdt_ic_teardown_intr, beripic_teardown_intr),
|
|
|
|
#ifdef SMP
|
|
DEVMETHOD(fdt_ic_setup_ipi, beripic_setup_ipi),
|
|
DEVMETHOD(fdt_ic_clear_ipi, beripic_clear_ipi),
|
|
DEVMETHOD(fdt_ic_send_ipi, beripic_send_ipi),
|
|
#endif
|
|
|
|
{ 0, 0 },
|
|
};
|
|
|
|
static driver_t beripic_fdt_driver = {
|
|
"beripic",
|
|
beripic_fdt_methods,
|
|
sizeof(struct beripic_softc)
|
|
};
|
|
|
|
DRIVER_MODULE(beripic, simplebus, beripic_fdt_driver, beripic_devclass, 0, 0);
|