Changeset View
Changeset View
Standalone View
Standalone View
sys/arm/lpc/lpc_intc.c
Show All 30 Lines | |||||
#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/kernel.h> | #include <sys/kernel.h> | ||||
#include <sys/module.h> | #include <sys/module.h> | ||||
#include <sys/malloc.h> | #include <sys/malloc.h> | ||||
#include <sys/rman.h> | #include <sys/rman.h> | ||||
#include <sys/timetc.h> | #include <sys/timetc.h> | ||||
#include <sys/cpuset.h> | |||||
#include <machine/bus.h> | #include <machine/bus.h> | ||||
#include <machine/intr.h> | #include <machine/intr.h> | ||||
#include <dev/fdt/fdt_common.h> | #include <dev/fdt/fdt_common.h> | ||||
#include <dev/ofw/openfirm.h> | #include <dev/ofw/openfirm.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 <arm/lpc/lpcreg.h> | #include <arm/lpc/lpcreg.h> | ||||
#include "pic_if.h" | |||||
struct lpc_intc_softc { | struct lpc_intc_softc { | ||||
struct resource * li_res; | device_t li_dev; | ||||
struct resource * li_mem_res; | |||||
struct resource * li_irq_res; | |||||
bus_space_tag_t li_bst; | bus_space_tag_t li_bst; | ||||
bus_space_handle_t li_bsh; | bus_space_handle_t li_bsh; | ||||
void * li_intrhand; | |||||
}; | }; | ||||
static int lpc_intc_probe(device_t); | static int lpc_intc_probe(device_t); | ||||
static int lpc_intc_attach(device_t); | static int lpc_intc_attach(device_t); | ||||
static void lpc_intc_eoi(void *); | static int lpc_intc_intr(void *); | ||||
static int lpc_intc_config(device_t, int, enum intr_trigger, enum intr_polarity); | |||||
static void lpc_intc_mask(device_t, int); | |||||
static void lpc_intc_unmask(device_t, int); | |||||
static void lpc_intc_eoi(device_t, int); | |||||
static struct lpc_intc_softc *intc_softc = NULL; | #define intc_read_4(_sc, _reg) \ | ||||
bus_space_read_4((_sc)->li_bst, (_sc)->li_bsh, _reg) | |||||
#define intc_write_4(_sc, _reg, _val) \ | |||||
bus_space_write_4((_sc)->li_bst, (_sc)->li_bsh, _reg, _val) | |||||
#define intc_read_4(reg) \ | |||||
bus_space_read_4(intc_softc->li_bst, intc_softc->li_bsh, reg) | |||||
#define intc_write_4(reg, val) \ | |||||
bus_space_write_4(intc_softc->li_bst, intc_softc->li_bsh, reg, val) | |||||
static int | static int | ||||
lpc_intc_probe(device_t dev) | lpc_intc_probe(device_t dev) | ||||
{ | { | ||||
if (!ofw_bus_status_okay(dev)) | if (!ofw_bus_status_okay(dev)) | ||||
return (ENXIO); | return (ENXIO); | ||||
if (!ofw_bus_is_compatible(dev, "lpc,pic")) | if (!ofw_bus_is_compatible(dev, "lpc,pic")) | ||||
return (ENXIO); | return (ENXIO); | ||||
device_set_desc(dev, "LPC32x0 Interrupt Controller"); | device_set_desc(dev, "LPC32x0 Interrupt Controller"); | ||||
return (BUS_PROBE_DEFAULT); | return (BUS_PROBE_DEFAULT); | ||||
} | } | ||||
static int | static int | ||||
lpc_intc_attach(device_t dev) | lpc_intc_attach(device_t dev) | ||||
{ | { | ||||
struct lpc_intc_softc *sc = device_get_softc(dev); | struct lpc_intc_softc *sc = device_get_softc(dev); | ||||
int rid = 0; | int rid = 0; | ||||
if (intc_softc) | sc->li_dev = dev; | ||||
sc->li_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, | |||||
RF_ACTIVE); | |||||
if (!sc->li_mem_res) { | |||||
device_printf(dev, "could not alloc memory resource\n"); | |||||
return (ENXIO); | return (ENXIO); | ||||
} | |||||
sc->li_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, | rid = 0; | ||||
RF_ACTIVE); | sc->li_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, | ||||
if (!sc->li_res) { | RF_ACTIVE | RF_SHAREABLE); | ||||
device_printf(dev, "could not alloc resources\n"); | if (!sc->li_irq_res) { | ||||
device_printf(dev, "could not alloc interrupt resource\n"); | |||||
bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->li_mem_res); | |||||
return (ENXIO); | return (ENXIO); | ||||
} | } | ||||
sc->li_bst = rman_get_bustag(sc->li_res); | sc->li_bst = rman_get_bustag(sc->li_mem_res); | ||||
sc->li_bsh = rman_get_bushandle(sc->li_res); | sc->li_bsh = rman_get_bushandle(sc->li_mem_res); | ||||
intc_softc = sc; | |||||
arm_post_filter = lpc_intc_eoi; | |||||
if (bus_setup_intr(dev, sc->li_irq_res, INTR_TYPE_MISC | INTR_CONTROLLER, | |||||
lpc_intc_intr, NULL, sc, &sc->li_intrhand)) { | |||||
device_printf(dev, "could not setup interrupt handler\n"); | |||||
bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->li_mem_res); | |||||
bus_release_resource(dev, SYS_RES_IRQ, 0, sc->li_irq_res); | |||||
return (ENXIO); | |||||
} | |||||
arm_register_pic(dev, 0); | |||||
/* Clear interrupt status registers and disable all interrupts */ | /* Clear interrupt status registers and disable all interrupts */ | ||||
intc_write_4(LPC_INTC_MIC_ER, 0); | intc_write_4(sc, LPC_INTC_ER, 0); | ||||
intc_write_4(LPC_INTC_SIC1_ER, 0); | intc_write_4(sc, LPC_INTC_RSR, ~0); | ||||
intc_write_4(LPC_INTC_SIC2_ER, 0); | |||||
intc_write_4(LPC_INTC_MIC_RSR, ~0); | |||||
intc_write_4(LPC_INTC_SIC1_RSR, ~0); | |||||
intc_write_4(LPC_INTC_SIC2_RSR, ~0); | |||||
return (0); | return (0); | ||||
} | } | ||||
static device_method_t lpc_intc_methods[] = { | static int | ||||
DEVMETHOD(device_probe, lpc_intc_probe), | lpc_intc_intr(void *arg) | ||||
DEVMETHOD(device_attach, lpc_intc_attach), | |||||
{ 0, 0 } | |||||
}; | |||||
static driver_t lpc_intc_driver = { | |||||
"pic", | |||||
lpc_intc_methods, | |||||
sizeof(struct lpc_intc_softc), | |||||
}; | |||||
static devclass_t lpc_intc_devclass; | |||||
DRIVER_MODULE(pic, simplebus, lpc_intc_driver, lpc_intc_devclass, 0, 0); | |||||
int | |||||
arm_get_next_irq(int last) | |||||
{ | { | ||||
struct lpc_intc_softc *sc = (struct lpc_intc_softc *)arg; | |||||
uint32_t value; | uint32_t value; | ||||
int i; | int i; | ||||
/* IRQs 0-31 are mapped to LPC_INTC_MIC_SR */ | value = intc_read_4(sc, LPC_INTC_SR); | ||||
value = intc_read_4(LPC_INTC_MIC_SR); | |||||
for (i = 0; i < 32; i++) { | for (i = 0; i < 32; i++) { | ||||
if (value & (1 << i)) | if (value & (1 << i)) | ||||
return (i); | arm_dispatch_irq(sc->li_dev, NULL, i); | ||||
} | } | ||||
/* IRQs 32-63 are mapped to LPC_INTC_SIC1_SR */ | return (FILTER_HANDLED); | ||||
value = intc_read_4(LPC_INTC_SIC1_SR); | |||||
for (i = 0; i < 32; i++) { | |||||
if (value & (1 << i)) | |||||
return (i + 32); | |||||
} | } | ||||
/* IRQs 64-95 are mapped to LPC_INTC_SIC2_SR */ | static int | ||||
value = intc_read_4(LPC_INTC_SIC2_SR); | lpc_intc_config(device_t dev, int irq, enum intr_trigger trig, | ||||
for (i = 0; i < 32; i++) { | enum intr_polarity pol) | ||||
if (value & (1 << i)) | { | ||||
return (i + 64); | /* no-op */ | ||||
return (0); | |||||
} | } | ||||
return (-1); | static void | ||||
} | lpc_intc_mask(device_t dev, int irq) | ||||
void | |||||
arm_mask_irq(uintptr_t nb) | |||||
{ | { | ||||
int reg; | struct lpc_intc_softc *sc = device_get_softc(dev); | ||||
uint32_t value; | uint32_t value; | ||||
/* Make sure that interrupt isn't active already */ | /* Make sure interrupt isn't active already */ | ||||
lpc_intc_eoi((void *)nb); | lpc_intc_eoi(dev, irq); | ||||
if (nb > 63) { | |||||
nb -= 64; | |||||
reg = LPC_INTC_SIC2_ER; | |||||
} else if (nb > 31) { | |||||
nb -= 32; | |||||
reg = LPC_INTC_SIC1_ER; | |||||
} else | |||||
reg = LPC_INTC_MIC_ER; | |||||
/* Clear bit in ER register */ | /* Clear bit in ER register */ | ||||
value = intc_read_4(reg); | value = intc_read_4(sc, LPC_INTC_ER); | ||||
value &= ~(1 << nb); | value &= ~(1 << irq); | ||||
intc_write_4(reg, value); | intc_write_4(sc, LPC_INTC_ER, value); | ||||
} | } | ||||
void | static void | ||||
arm_unmask_irq(uintptr_t nb) | lpc_intc_unmask(device_t dev, int irq) | ||||
{ | { | ||||
int reg; | struct lpc_intc_softc *sc = device_get_softc(dev); | ||||
uint32_t value; | uint32_t value; | ||||
if (nb > 63) { | |||||
nb -= 64; | |||||
reg = LPC_INTC_SIC2_ER; | |||||
} else if (nb > 31) { | |||||
nb -= 32; | |||||
reg = LPC_INTC_SIC1_ER; | |||||
} else | |||||
reg = LPC_INTC_MIC_ER; | |||||
/* Set bit in ER register */ | /* Set bit in ER register */ | ||||
value = intc_read_4(reg); | value = intc_read_4(sc, LPC_INTC_ER); | ||||
value |= (1 << nb); | value |= (1 << irq); | ||||
intc_write_4(reg, value); | intc_write_4(sc, LPC_INTC_ER, value); | ||||
} | } | ||||
static void | static void | ||||
lpc_intc_eoi(void *data) | lpc_intc_eoi(device_t dev, int irq) | ||||
{ | { | ||||
int reg; | struct lpc_intc_softc *sc = device_get_softc(dev); | ||||
int nb = (int)data; | |||||
uint32_t value; | uint32_t value; | ||||
if (nb > 63) { | |||||
nb -= 64; | |||||
reg = LPC_INTC_SIC2_RSR; | |||||
} else if (nb > 31) { | |||||
nb -= 32; | |||||
reg = LPC_INTC_SIC1_RSR; | |||||
} else | |||||
reg = LPC_INTC_MIC_RSR; | |||||
/* Set bit in RSR register */ | /* Set bit in RSR register */ | ||||
value = intc_read_4(reg); | value = intc_read_4(sc, LPC_INTC_RSR); | ||||
value |= (1 << nb); | value |= (1 << irq); | ||||
intc_write_4(reg, value); | intc_write_4(sc, LPC_INTC_RSR, value); | ||||
} | } | ||||
static device_method_t lpc_intc_methods[] = { | |||||
/* Device interface */ | |||||
DEVMETHOD(device_probe, lpc_intc_probe), | |||||
DEVMETHOD(device_attach, lpc_intc_attach), | |||||
/* Interrupt controller interface */ | |||||
DEVMETHOD(pic_config, lpc_intc_config), | |||||
DEVMETHOD(pic_mask, lpc_intc_mask), | |||||
DEVMETHOD(pic_unmask, lpc_intc_unmask), | |||||
DEVMETHOD(pic_eoi, lpc_intc_eoi), | |||||
{ 0, 0 } | |||||
}; | |||||
static driver_t lpc_intc_driver = { | |||||
"pic", | |||||
lpc_intc_methods, | |||||
sizeof(struct lpc_intc_softc), | |||||
}; | |||||
static devclass_t lpc_intc_devclass; | |||||
DRIVER_MODULE(pic, simplebus, lpc_intc_driver, lpc_intc_devclass, 0, 0); | |||||
struct fdt_fixup_entry fdt_fixup_table[] = { | struct fdt_fixup_entry fdt_fixup_table[] = { | ||||
{ NULL, NULL } | { NULL, NULL } | ||||
}; | }; | ||||
static int | static int | ||||
fdt_pic_decode_ic(phandle_t node, pcell_t *intr, int *interrupt, int *trig, | fdt_pic_decode_ic(phandle_t node, pcell_t *intr, int *interrupt, int *trig, | ||||
int *pol) | int *pol) | ||||
Show All 14 Lines |