Changeset View
Changeset View
Standalone View
Standalone View
sys/arm/mv/mv_ap806_sei.c
Show All 26 Lines | |||||
*/ | */ | ||||
#include <sys/cdefs.h> | #include <sys/cdefs.h> | ||||
__FBSDID("$FreeBSD$"); | __FBSDID("$FreeBSD$"); | ||||
#include <sys/param.h> | #include <sys/param.h> | ||||
#include <sys/systm.h> | #include <sys/systm.h> | ||||
#include <sys/bus.h> | #include <sys/bus.h> | ||||
#include <sys/bitset.h> | |||||
#include <sys/kernel.h> | #include <sys/kernel.h> | ||||
#include <sys/proc.h> | #include <sys/proc.h> | ||||
#include <sys/rman.h> | #include <sys/rman.h> | ||||
#include <sys/lock.h> | #include <sys/lock.h> | ||||
#include <sys/module.h> | #include <sys/module.h> | ||||
#include <sys/mutex.h> | #include <sys/mutex.h> | ||||
#include <machine/bus.h> | #include <machine/bus.h> | ||||
#include <machine/intr.h> | #include <machine/intr.h> | ||||
#include <machine/resource.h> | #include <machine/resource.h> | ||||
#include <dev/fdt/simplebus.h> | #include <dev/fdt/simplebus.h> | ||||
#include <dev/ofw/ofw_bus.h> | #include <dev/ofw/ofw_bus.h> | ||||
#include <dev/ofw/ofw_bus_subr.h> | #include <dev/ofw/ofw_bus_subr.h> | ||||
#include "msi_if.h" | |||||
#include "pic_if.h" | #include "pic_if.h" | ||||
#define MV_AP806_SEI_LOCK(_sc) mtx_lock(&(_sc)->mtx) | #define MV_AP806_SEI_LOCK(_sc) mtx_lock(&(_sc)->mtx) | ||||
#define MV_AP806_SEI_UNLOCK(_sc) mtx_unlock(&(_sc)->mtx) | #define MV_AP806_SEI_UNLOCK(_sc) mtx_unlock(&(_sc)->mtx) | ||||
#define MV_AP806_SEI_LOCK_INIT(_sc) mtx_init(&_sc->mtx, \ | #define MV_AP806_SEI_LOCK_INIT(_sc) mtx_init(&_sc->mtx, \ | ||||
device_get_nameunit(_sc->dev), "mv_ap806_sei", MTX_DEF) | 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_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_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_ASSERT_UNLOCKED(_sc) mtx_assert(&_sc->mtx, MA_NOTOWNED); | ||||
#define MV_AP806_SEI_MAX_NIRQS 64 | |||||
#define GICP_SECR0 0x00 | #define GICP_SECR0 0x00 | ||||
#define GICP_SECR1 0x04 | #define GICP_SECR1 0x04 | ||||
#define GICP_SECR(i) (0x00 + (((i)/32) * 0x4)) | #define GICP_SECR(i) (0x00 + (((i)/32) * 0x4)) | ||||
#define GICP_SECR_BIT(i) ((i) % 32) | #define GICP_SECR_BIT(i) ((i) % 32) | ||||
#define GICP_SEMR0 0x20 | #define GICP_SEMR0 0x20 | ||||
#define GICP_SEMR1 0x24 | #define GICP_SEMR1 0x24 | ||||
#define GICP_SEMR(i) (0x20 + (((i)/32) * 0x4)) | #define GICP_SEMR(i) (0x20 + (((i)/32) * 0x4)) | ||||
#define GICP_SEMR_BIT(i) ((i) % 32) | #define GICP_SEMR_BIT(i) ((i) % 32) | ||||
#define MV_AP806_SEI_AP_FIRST 0 | |||||
#define MV_AP806_SEI_AP_SIZE 21 | |||||
#define MV_AP806_SEI_CP_FIRST 21 | |||||
#define MV_AP806_SEI_CP_SIZE 43 | |||||
#define MV_AP806_SEI_MAX_NIRQS (MV_AP806_SEI_AP_SIZE + MV_AP806_SEI_CP_SIZE) | |||||
#define MV_AP806_SEI_SETSPI_OFFSET 0x30 | |||||
BITSET_DEFINE(sei_msi_bitmap, MV_AP806_SEI_CP_SIZE); | |||||
struct mv_ap806_sei_irqsrc { | struct mv_ap806_sei_irqsrc { | ||||
struct intr_irqsrc isrc; | struct intr_irqsrc isrc; | ||||
u_int irq; | u_int irq; | ||||
}; | }; | ||||
struct mv_ap806_sei_softc { | struct mv_ap806_sei_softc { | ||||
device_t dev; | device_t dev; | ||||
struct resource *mem_res; | struct resource *mem_res; | ||||
struct resource *irq_res; | struct resource *irq_res; | ||||
void *irq_ih; | void *irq_ih; | ||||
struct mtx mtx; | struct mtx mtx; | ||||
struct mv_ap806_sei_irqsrc *isrcs; | struct mv_ap806_sei_irqsrc *isrcs; | ||||
struct sei_msi_bitmap msi_bitmap; | |||||
}; | }; | ||||
static struct ofw_compat_data compat_data[] = { | static struct ofw_compat_data compat_data[] = { | ||||
{"marvell,ap806-sei", 1}, | {"marvell,ap806-sei", 1}, | ||||
{NULL, 0} | {NULL, 0} | ||||
}; | }; | ||||
#define RD4(sc, reg) bus_read_4((sc)->mem_res, (reg)) | #define RD4(sc, reg) bus_read_4((sc)->mem_res, (reg)) | ||||
#define WR4(sc, reg, val) bus_write_4((sc)->mem_res, (reg), (val)) | #define WR4(sc, reg, val) bus_write_4((sc)->mem_res, (reg), (val)) | ||||
static msi_alloc_msi_t mv_ap806_sei_alloc_msi; | |||||
static msi_release_msi_t mv_ap806_sei_release_msi; | |||||
static msi_map_msi_t mv_ap806_sei_map_msi; | |||||
static inline void | static inline void | ||||
mv_ap806_sei_isrc_mask(struct mv_ap806_sei_softc *sc, | mv_ap806_sei_isrc_mask(struct mv_ap806_sei_softc *sc, | ||||
struct mv_ap806_sei_irqsrc *sisrc, uint32_t val) | struct mv_ap806_sei_irqsrc *sisrc, uint32_t val) | ||||
{ | { | ||||
uint32_t tmp; | uint32_t tmp; | ||||
int bit; | int bit; | ||||
bit = GICP_SEMR_BIT(sisrc->irq); | bit = GICP_SEMR_BIT(sisrc->irq); | ||||
▲ Show 20 Lines • Show All 45 Lines • ▼ Show 20 Lines | mv_ap806_sei_map(device_t dev, struct intr_map_data *data, u_int *irqp) | ||||
u_int irq; | u_int irq; | ||||
sc = device_get_softc(dev); | sc = device_get_softc(dev); | ||||
if (data->type != INTR_MAP_DATA_FDT) | if (data->type != INTR_MAP_DATA_FDT) | ||||
return (ENOTSUP); | return (ENOTSUP); | ||||
daf = (struct intr_map_data_fdt *)data; | daf = (struct intr_map_data_fdt *)data; | ||||
if (daf->ncells != 1 || daf->cells[0] >= MV_AP806_SEI_MAX_NIRQS) | if (daf->ncells != 1) | ||||
return (EINVAL); | return (EINVAL); | ||||
if (daf->cells[0] < MV_AP806_SEI_AP_FIRST || | |||||
daf->cells[0] >= MV_AP806_SEI_AP_FIRST + MV_AP806_SEI_AP_SIZE) | |||||
return (EINVAL); | |||||
irq = daf->cells[0]; | irq = daf->cells[0]; | ||||
if (irqp != NULL) | if (irqp != NULL) | ||||
*irqp = irq; | *irqp = irq; | ||||
return(0); | return(0); | ||||
} | } | ||||
static int | static int | ||||
▲ Show 20 Lines • Show All 191 Lines • ▼ Show 20 Lines | mv_ap806_sei_attach(device_t dev) | ||||
if (bus_setup_intr(dev, sc->irq_res,INTR_TYPE_MISC | INTR_MPSAFE, | if (bus_setup_intr(dev, sc->irq_res,INTR_TYPE_MISC | INTR_MPSAFE, | ||||
mv_ap806_sei_intr, NULL, sc, &sc->irq_ih)) { | mv_ap806_sei_intr, NULL, sc, &sc->irq_ih)) { | ||||
device_printf(dev, | device_printf(dev, | ||||
"Unable to register interrupt handler\n"); | "Unable to register interrupt handler\n"); | ||||
rv = ENXIO; | rv = ENXIO; | ||||
goto fail; | goto fail; | ||||
} | } | ||||
/* | |||||
* Bitmap of all IRQs. | |||||
* 1 - available, 0 - used. | |||||
*/ | |||||
BIT_FILL(MV_AP806_SEI_CP_SIZE, &sc->msi_bitmap); | |||||
OF_device_register_xref(xref, dev); | OF_device_register_xref(xref, dev); | ||||
return (0); | return (0); | ||||
fail: | fail: | ||||
if (sc->irq_ih != NULL) | if (sc->irq_ih != NULL) | ||||
bus_teardown_intr(dev, sc->irq_res, sc->irq_ih); | bus_teardown_intr(dev, sc->irq_res, sc->irq_ih); | ||||
if (sc->irq_res != NULL) | if (sc->irq_res != NULL) | ||||
bus_release_resource(dev, SYS_RES_IRQ, 0, sc->irq_res); | bus_release_resource(dev, SYS_RES_IRQ, 0, sc->irq_res); | ||||
if (sc->mem_res != NULL) | if (sc->mem_res != NULL) | ||||
bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->mem_res); | bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->mem_res); | ||||
MV_AP806_SEI_LOCK_DESTROY(sc); | MV_AP806_SEI_LOCK_DESTROY(sc); | ||||
return (ENXIO); | return (ENXIO); | ||||
} | } | ||||
static int | static int | ||||
mv_ap806_sei_detach(device_t dev) | mv_ap806_sei_detach(device_t dev) | ||||
{ | { | ||||
return (EBUSY); | return (EBUSY); | ||||
} | } | ||||
static int | |||||
mv_ap806_sei_alloc_msi(device_t dev, device_t child, int count, int maxcount, | |||||
device_t *pic, struct intr_irqsrc **srcs) | |||||
{ | |||||
struct mv_ap806_sei_softc *sc; | |||||
int i, ret = 0, vector; | |||||
sc = device_get_softc(dev); | |||||
for (i = 0; i < count; i++) { | |||||
/* | |||||
* Find first available MSI vector represented by first set bit | |||||
* in the bitmap. BIT_FFS starts the count from 1, | |||||
* 0 means that nothing was found. | |||||
*/ | |||||
vector = BIT_FFS_AT(MV_AP806_SEI_CP_SIZE, &sc->msi_bitmap, 0); | |||||
if (vector == 0) { | |||||
ret = ENOMEM; | |||||
i--; | |||||
goto fail; | |||||
} | |||||
vector--; | |||||
BIT_CLR(MV_AP806_SEI_CP_SIZE, vector, &sc->msi_bitmap); | |||||
vector += MV_AP806_SEI_CP_FIRST; | |||||
srcs[i] = &sc->isrcs[vector].isrc; | |||||
} | |||||
return (ret); | |||||
fail: | |||||
mv_ap806_sei_release_msi(dev, child, i + 1, srcs); | |||||
return (ret); | |||||
} | |||||
static int | |||||
mv_ap806_sei_release_msi(device_t dev, device_t child, int count, struct intr_irqsrc **srcs) | |||||
{ | |||||
struct mv_ap806_sei_softc *sc; | |||||
int i; | |||||
sc = device_get_softc(dev); | |||||
for (i = 0; i < count; i++) { | |||||
BIT_SET(MV_AP806_SEI_CP_SIZE, | |||||
srcs[i]->isrc_irq - MV_AP806_SEI_CP_FIRST, | |||||
&sc->msi_bitmap); | |||||
} | |||||
return (0); | |||||
} | |||||
static int | |||||
mv_ap806_sei_map_msi(device_t dev, device_t child, struct intr_irqsrc *isrc, | |||||
uint64_t *addr, uint32_t *data) | |||||
{ | |||||
struct mv_ap806_sei_softc *sc; | |||||
sc = device_get_softc(dev); | |||||
*addr = rman_get_start(sc->mem_res) + MV_AP806_SEI_SETSPI_OFFSET; | |||||
*data = isrc->isrc_irq; | |||||
return (0); | |||||
} | |||||
static device_method_t mv_ap806_sei_methods[] = { | static device_method_t mv_ap806_sei_methods[] = { | ||||
/* Device interface */ | /* Device interface */ | ||||
DEVMETHOD(device_probe, mv_ap806_sei_probe), | DEVMETHOD(device_probe, mv_ap806_sei_probe), | ||||
DEVMETHOD(device_attach, mv_ap806_sei_attach), | DEVMETHOD(device_attach, mv_ap806_sei_attach), | ||||
DEVMETHOD(device_detach, mv_ap806_sei_detach), | DEVMETHOD(device_detach, mv_ap806_sei_detach), | ||||
/* Interrupt controller interface */ | /* Interrupt controller interface */ | ||||
DEVMETHOD(pic_disable_intr, mv_ap806_sei_disable_intr), | DEVMETHOD(pic_disable_intr, mv_ap806_sei_disable_intr), | ||||
DEVMETHOD(pic_enable_intr, mv_ap806_sei_enable_intr), | DEVMETHOD(pic_enable_intr, mv_ap806_sei_enable_intr), | ||||
DEVMETHOD(pic_map_intr, mv_ap806_sei_map_intr), | DEVMETHOD(pic_map_intr, mv_ap806_sei_map_intr), | ||||
DEVMETHOD(pic_setup_intr, mv_ap806_sei_setup_intr), | DEVMETHOD(pic_setup_intr, mv_ap806_sei_setup_intr), | ||||
DEVMETHOD(pic_teardown_intr, mv_ap806_sei_teardown_intr), | DEVMETHOD(pic_teardown_intr, mv_ap806_sei_teardown_intr), | ||||
DEVMETHOD(pic_post_filter, mv_ap806_sei_post_filter), | DEVMETHOD(pic_post_filter, mv_ap806_sei_post_filter), | ||||
DEVMETHOD(pic_post_ithread, mv_ap806_sei_post_ithread), | DEVMETHOD(pic_post_ithread, mv_ap806_sei_post_ithread), | ||||
DEVMETHOD(pic_pre_ithread, mv_ap806_sei_pre_ithread), | DEVMETHOD(pic_pre_ithread, mv_ap806_sei_pre_ithread), | ||||
/* MSI interface */ | |||||
DEVMETHOD(msi_alloc_msi, mv_ap806_sei_alloc_msi), | |||||
DEVMETHOD(msi_release_msi, mv_ap806_sei_release_msi), | |||||
DEVMETHOD(msi_map_msi, mv_ap806_sei_map_msi), | |||||
DEVMETHOD_END | DEVMETHOD_END | ||||
}; | }; | ||||
static devclass_t mv_ap806_sei_devclass; | static devclass_t mv_ap806_sei_devclass; | ||||
static driver_t mv_ap806_sei_driver = { | static driver_t mv_ap806_sei_driver = { | ||||
"mv_ap806_sei", | "mv_ap806_sei", | ||||
mv_ap806_sei_methods, | mv_ap806_sei_methods, | ||||
sizeof(struct mv_ap806_sei_softc), | sizeof(struct mv_ap806_sei_softc), | ||||
}; | }; | ||||
EARLY_DRIVER_MODULE(mv_ap806_sei, simplebus, mv_ap806_sei_driver, | EARLY_DRIVER_MODULE(mv_ap806_sei, simplebus, mv_ap806_sei_driver, | ||||
mv_ap806_sei_devclass, 0, 0, BUS_PASS_INTERRUPT + BUS_PASS_ORDER_MIDDLE); | mv_ap806_sei_devclass, 0, 0, BUS_PASS_INTERRUPT + BUS_PASS_ORDER_MIDDLE); |