Add support for MSI in interrupt controlller.

MSI are implemented via software interrupt. PCIe cards will write
into software interrupt register which will cause inbound shared
interrupt which will be interpreted as a MSI.

Obtained from:	Marvell, Semihalf
This commit is contained in:
Grzegorz Bernacki 2012-09-14 10:05:01 +00:00
parent 373bc54ab1
commit aa0ea9d07a
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=240492
4 changed files with 120 additions and 20 deletions

View File

@ -55,7 +55,12 @@
#elif defined(CPU_ARM11)
#define NIRQ 128
#elif defined(SOC_MV_ARMADAXP)
#define NIRQ 148
#define MAIN_IRQ_NUM 116
#define ERR_IRQ_NUM 32
#define ERR_IRQ (MAIN_IRQ_NUM)
#define MSI_IRQ_NUM 32
#define MSI_IRQ (ERR_IRQ + ERR_IRQ_NUM)
#define NIRQ (MAIN_IRQ_NUM + ERR_IRQ_NUM + MSI_IRQ_NUM)
#else
#define NIRQ 32
#endif

View File

@ -1,6 +1,7 @@
/*-
* Copyright (c) 2006 Benno Rice.
* Copyright (C) 2007-2011 MARVELL INTERNATIONAL LTD.
* Copyright (c) 2012 Semihalf.
* All rights reserved.
*
* Developed by Semihalf.
@ -46,23 +47,34 @@ __FBSDID("$FreeBSD$");
#include <machine/cpufunc.h>
#include <machine/smp.h>
#include <arm/mv/mvvar.h>
#include <dev/ofw/ofw_bus.h>
#include <dev/ofw/ofw_bus_subr.h>
#include <dev/fdt/fdt_common.h>
#define IRQ_ERR 4
#define MAIN_IRQS 116
#ifdef DEBUG
#define debugf(fmt, args...) do { printf("%s(): ", __func__); \
printf(fmt,##args); } while (0)
#else
#define debugf(fmt, args...)
#endif
#define MPIC_INT_ERR 4
#define MPIC_INT_MSI 96
#define IRQ_MASK 0x3ff
#define MPIC_CTRL 0x0
#define MPIC_SOFT_INT 0x4
#define MPIC_SOFT_INT_DRBL1 (1 << 5)
#define MPIC_ERR_CAUSE 0x20
#define MPIC_ISE 0x30
#define MPIC_ICE 0x34
#define MPIC_IN_DOORBELL 0x78
#define MPIC_IN_DOORBELL_MASK 0x7c
#define MPIC_IN_DRBL 0x78
#define MPIC_IN_DRBL_MASK 0x7c
#define MPIC_CTP 0xb0
#define MPIC_CTP 0xb0
#define MPIC_IIACK 0xb4
@ -71,16 +83,20 @@ __FBSDID("$FreeBSD$");
#define MPIC_ERR_MASK 0xec0
struct mv_mpic_softc {
struct resource * mpic_res[2];
device_t sc_dev;
struct resource * mpic_res[3];
bus_space_tag_t mpic_bst;
bus_space_handle_t mpic_bsh;
bus_space_tag_t cpu_bst;
bus_space_handle_t cpu_bsh;
bus_space_tag_t drbl_bst;
bus_space_handle_t drbl_bsh;
};
static struct resource_spec mv_mpic_spec[] = {
{ SYS_RES_MEMORY, 0, RF_ACTIVE },
{ SYS_RES_MEMORY, 1, RF_ACTIVE },
{ SYS_RES_MEMORY, 2, RF_ACTIVE },
{ -1, 0 }
};
@ -92,14 +108,21 @@ static int mv_mpic_probe(device_t);
static int mv_mpic_attach(device_t);
uint32_t mv_mpic_get_cause(void);
uint32_t mv_mpic_get_cause_err(void);
uint32_t mv_mpic_get_msi(void);
static void arm_mask_irq_err(uintptr_t);
static void arm_unmask_irq_err(uintptr_t);
static void arm_unmask_msi(void);
#define MPIC_CPU_WRITE(softc, reg, val) \
bus_space_write_4((softc)->cpu_bst, (softc)->cpu_bsh, (reg), (val))
#define MPIC_CPU_READ(softc, reg) \
bus_space_read_4((softc)->cpu_bst, (softc)->cpu_bsh, (reg))
#define MPIC_DRBL_WRITE(softc, reg, val) \
bus_space_write_4((softc)->drbl_bst, (softc)->drbl_bsh, (reg), (val))
#define MPIC_DRBL_READ(softc, reg) \
bus_space_read_4((softc)->drbl_bst, (softc)->drbl_bsh, (reg))
static int
mv_mpic_probe(device_t dev)
{
@ -123,6 +146,8 @@ mv_mpic_attach(device_t dev)
return (ENXIO);
mv_mpic_sc = sc;
sc->sc_dev = dev;
error = bus_alloc_resources(dev, mv_mpic_spec, sc->mpic_res);
if (error) {
device_printf(dev, "could not allocate resources\n");
@ -135,10 +160,15 @@ mv_mpic_attach(device_t dev)
sc->cpu_bst = rman_get_bustag(sc->mpic_res[1]);
sc->cpu_bsh = rman_get_bushandle(sc->mpic_res[1]);
sc->drbl_bst = rman_get_bustag(sc->mpic_res[2]);
sc->drbl_bsh = rman_get_bushandle(sc->mpic_res[2]);
bus_space_write_4(mv_mpic_sc->mpic_bst, mv_mpic_sc->mpic_bsh,
MPIC_CTRL, 1);
MPIC_CPU_WRITE(mv_mpic_sc, MPIC_CTP, 0);
arm_unmask_msi();
return (0);
}
@ -167,8 +197,10 @@ arm_get_next_irq(int last)
CTR2(KTR_INTR, "%s: irq:%#x", __func__, irq);
if (irq != IRQ_MASK) {
if (irq == IRQ_ERR)
if (irq == MPIC_INT_ERR)
irq = mv_mpic_get_cause_err();
if (irq == MPIC_INT_MSI)
irq = mv_mpic_get_msi();
next = irq;
}
@ -186,11 +218,11 @@ arm_mask_irq(uintptr_t nb)
MPIC_CPU_WRITE(mv_mpic_sc, MPIC_CTP, 1);
if (nb < MAIN_IRQS) {
if (nb < ERR_IRQ) {
bus_space_write_4(mv_mpic_sc->mpic_bst, mv_mpic_sc->mpic_bsh,
MPIC_ICE, nb);
MPIC_CPU_WRITE(mv_mpic_sc, MPIC_ISM, nb);
} else
} else if (nb < MSI_IRQ)
arm_mask_irq_err(nb);
}
@ -201,7 +233,7 @@ arm_mask_irq_err(uintptr_t nb)
uint32_t mask;
uint8_t bit_off;
bit_off = nb - MAIN_IRQS;
bit_off = nb - ERR_IRQ;
mask = MPIC_CPU_READ(mv_mpic_sc, MPIC_ERR_MASK);
mask &= ~(1 << bit_off);
MPIC_CPU_WRITE(mv_mpic_sc, MPIC_ERR_MASK, mask);
@ -213,15 +245,15 @@ arm_unmask_irq(uintptr_t nb)
MPIC_CPU_WRITE(mv_mpic_sc, MPIC_CTP, 0);
if (nb < MAIN_IRQS) {
if (nb < ERR_IRQ) {
bus_space_write_4(mv_mpic_sc->mpic_bst, mv_mpic_sc->mpic_bsh,
MPIC_ISE, nb);
MPIC_CPU_WRITE(mv_mpic_sc, MPIC_ICM, nb);
} else
} else if (nb < MSI_IRQ)
arm_unmask_irq_err(nb);
if (nb == 0)
MPIC_CPU_WRITE(mv_mpic_sc, MPIC_IN_DOORBELL_MASK, 0xffffffff);
MPIC_CPU_WRITE(mv_mpic_sc, MPIC_IN_DRBL_MASK, 0xffffffff);
}
void
@ -231,15 +263,22 @@ arm_unmask_irq_err(uintptr_t nb)
uint8_t bit_off;
bus_space_write_4(mv_mpic_sc->mpic_bst, mv_mpic_sc->mpic_bsh,
MPIC_ISE, IRQ_ERR);
MPIC_CPU_WRITE(mv_mpic_sc, MPIC_ICM, IRQ_ERR);
MPIC_ISE, MPIC_INT_ERR);
MPIC_CPU_WRITE(mv_mpic_sc, MPIC_ICM, MPIC_INT_ERR);
bit_off = nb - MAIN_IRQS;
bit_off = nb - ERR_IRQ;
mask = MPIC_CPU_READ(mv_mpic_sc, MPIC_ERR_MASK);
mask |= (1 << bit_off);
MPIC_CPU_WRITE(mv_mpic_sc, MPIC_ERR_MASK, mask);
}
static void
arm_unmask_msi(void)
{
arm_unmask_irq(MPIC_INT_MSI);
}
uint32_t
mv_mpic_get_cause(void)
{
@ -260,7 +299,61 @@ mv_mpic_get_cause_err(void)
bit_off = ffs(err_cause) - 1;
else
return (-1);
return (MAIN_IRQS + bit_off);
debugf("%s: irq:%x cause:%x\n", __func__, bit_off, err_cause);
return (ERR_IRQ + bit_off);
}
uint32_t
mv_mpic_get_msi(void)
{
uint32_t cause;
uint8_t bit_off;
cause = MPIC_DRBL_READ(mv_mpic_sc, 0);
if (cause)
bit_off = ffs(cause) - 1;
else
return (-1);
debugf("%s: irq:%x cause:%x\n", __func__, bit_off, cause);
cause &= ~(1 << bit_off);
MPIC_DRBL_WRITE(mv_mpic_sc, 0, cause);
return (MSI_IRQ + bit_off);
}
int
mv_msi_data(int irq, uint64_t *addr, uint32_t *data)
{
u_long phys, base, size;
phandle_t node;
int error;
node = ofw_bus_get_node(mv_mpic_sc->sc_dev);
/* Get physical addres of register space */
error = fdt_get_range(OF_parent(node), 0, &phys, &size);
if (error) {
printf("%s: Cannot get register physical address, err:%d",
__func__, error);
return (error);
}
/* Get offset of MPIC register space */
error = fdt_regsize(node, &base, &size);
if (error) {
printf("%s: Cannot get MPIC register offset, err:%d",
__func__, error);
return (error);
}
*addr = phys + base + MPIC_SOFT_INT;
*data = MPIC_SOFT_INT_DRBL1 | irq;
return (0);
}
#if defined(SMP)
@ -283,7 +376,7 @@ pic_ipi_get(int i __unused)
{
uint32_t val;
val = MPIC_CPU_READ(mv_mpic_sc, MPIC_IN_DOORBELL);
val = MPIC_CPU_READ(mv_mpic_sc, MPIC_IN_DRBL);
if (val)
return (ffs(val) - 1);
@ -296,7 +389,7 @@ pic_ipi_clear(int ipi)
uint32_t val;
val = ~(1 << ipi);
MPIC_CPU_WRITE(mv_mpic_sc, MPIC_IN_DOORBELL, val);
MPIC_CPU_WRITE(mv_mpic_sc, MPIC_IN_DRBL, val);
}
#endif

View File

@ -134,4 +134,6 @@ uint32_t mv_drbl_get_cause(int dir, int unit);
void mv_drbl_set_msg(uint32_t val, int mnr, int dir, int unit);
uint32_t mv_drbl_get_msg(int mnr, int dir, int unit);
int mv_msi_data(int irq, uint64_t *addr, uint32_t *data);
#endif /* _MVVAR_H_ */

View File

@ -75,7 +75,7 @@
interrupt-controller;
#address-cells = <0>;
#interrupt-cells = <1>;
reg = <0x20a00 0x500 0x21000 0x800>;
reg = <0x20a00 0x500 0x21000 0x800 0x20400 0x100>;
compatible = "mrvl,mpic";
};