Skip to content

Commit

Permalink
xive: Provide a way to override some IPI sources
Browse files Browse the repository at this point in the history
Some devices such as NX or the NPU will use some of the XIVE
provided IPIs for their own interrupts. Thus we need a way for
those to provide a custom irq_source_ops for portions of the IPI
space in order for them to provide their own attributes() and
if needed, interrutps() callbacks.

We achieve that by creating a second list of sources which can
overlap the primary.

The global stock of IPIs is registered by XIVE in the secondary
list which is searched when no match is found in the primary.

A new API xive_register_ipi_source() is provided for those devices
to create an overlapping source structure in the primary list for
a subset of the IPIs. Those IPIs must have been previously allocated
using xive_alloc_ipi_irqs()

Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Signed-off-by: Stewart Smith <stewart@linux.vnet.ibm.com>
  • Loading branch information
ozbenh authored and stewartsmith committed Nov 15, 2016
1 parent bd4eaed commit aecfaf0
Show file tree
Hide file tree
Showing 6 changed files with 77 additions and 60 deletions.
50 changes: 19 additions & 31 deletions core/interrupts.c
Expand Up @@ -32,17 +32,20 @@
#define ICP_MFRR 0xc /* 8-bit access */

static LIST_HEAD(irq_sources);
static LIST_HEAD(irq_sources2);
static struct lock irq_lock = LOCK_UNLOCKED;

void __register_irq_source(struct irq_source *is)
void __register_irq_source(struct irq_source *is, bool secondary)
{
struct irq_source *is1;
struct list_head *list = secondary ? &irq_sources2 : &irq_sources;

prlog(PR_DEBUG, "IRQ: Registering %04x..%04x ops @%p (data %p)\n",
is->start, is->end - 1, is->ops, is->data);
prlog(PR_DEBUG, "IRQ: Registering %04x..%04x ops @%p (data %p)%s\n",
is->start, is->end - 1, is->ops, is->data,
secondary ? " [secondary]" : "");

lock(&irq_lock);
list_for_each(&irq_sources, is1, link) {
list_for_each(list, is1, link) {
if (is->end > is1->start && is->start < is1->end) {
prerror("register IRQ source overlap !\n");
prerror(" new: %x..%x old: %x..%x\n",
Expand All @@ -51,7 +54,7 @@ void __register_irq_source(struct irq_source *is)
assert(0);
}
}
list_add_tail(&irq_sources, &is->link);
list_add_tail(list, &is->link);
unlock(&irq_lock);
}

Expand All @@ -67,13 +70,14 @@ void register_irq_source(const struct irq_source_ops *ops, void *data,
is->ops = ops;
is->data = data;

__register_irq_source(is);
__register_irq_source(is, false);
}

void unregister_irq_source(uint32_t start, uint32_t count)
{
struct irq_source *is;

/* Note: We currently only unregister from the primary sources */
lock(&irq_lock);
list_for_each(&irq_sources, is, link) {
if (start >= is->start && start < is->end) {
Expand Down Expand Up @@ -102,39 +106,24 @@ static struct irq_source *irq_find_source(uint32_t isn)
struct irq_source *is;

lock(&irq_lock);
/*
* XXX This really needs some kind of caching !
*/
list_for_each(&irq_sources, is, link) {
if (isn >= is->start && isn < is->end) {
unlock(&irq_lock);
return is;
}
}
unlock(&irq_lock);

return NULL;
}

void adjust_irq_source(struct irq_source *is, uint32_t new_count)
{
struct irq_source *is1;
uint32_t new_end = is->start + new_count;

prlog(PR_DEBUG, "IRQ: Adjusting %04x..%04x to %04x..%04x\n",
is->start, is->end - 1, is->start, new_end - 1);

lock(&irq_lock);
list_for_each(&irq_sources, is1, link) {
if (is1 == is)
continue;
if (new_end > is1->start && is->start < is1->end) {
prerror("adjust IRQ source overlap !\n");
prerror(" new: %x..%x old: %x..%x\n",
is->start, new_end - 1,
is1->start, is1->end - 1);
assert(0);
list_for_each(&irq_sources2, is, link) {
if (isn >= is->start && isn < is->end) {
unlock(&irq_lock);
return is;
}
}
is->end = new_end;
unlock(&irq_lock);

return NULL;
}

/*
Expand Down Expand Up @@ -474,4 +463,3 @@ void init_interrupts(void)
}
}


10 changes: 5 additions & 5 deletions hw/phb4.c
Expand Up @@ -3174,11 +3174,11 @@ static void phb4_create(struct dt_node *np)
phb4_init_hw(p, true);

/* Register all interrupt sources with XIVE */
xive_register_source(p->base_msi, p->num_irqs - 8, 16, p->int_mmio, 0,
NULL, NULL);
xive_register_source(p->base_lsi, 8, 16,
p->int_mmio + ((p->num_irqs - 8) << 16),
XIVE_SRC_LSI, p, &phb4_lsi_ops);
xive_register_hw_source(p->base_msi, p->num_irqs - 8, 16, p->int_mmio, 0,
NULL, NULL);
xive_register_hw_source(p->base_lsi, 8, 16,
p->int_mmio + ((p->num_irqs - 8) << 16),
XIVE_SRC_LSI, p, &phb4_lsi_ops);

/* Platform additional setup */
if (platform.pci_setup_phb)
Expand Down
6 changes: 3 additions & 3 deletions hw/psi.c
Expand Up @@ -785,9 +785,9 @@ static void psi_init_p9_interrupts(struct psi *psi)
out_be64(psi->regs + PSIHB_IVT_OFFSET, val);

/* Register sources */
xive_register_source(psi->interrupt, P9_PSI_NUM_IRQS,
12, psi->esb_mmio, XIVE_SRC_LSI,
psi, &psi_p9_irq_ops);
xive_register_hw_source(psi->interrupt, P9_PSI_NUM_IRQS,
12, psi->esb_mmio, XIVE_SRC_LSI,
psi, &psi_p9_irq_ops);
}

static void psi_init_interrupts(struct psi *psi)
Expand Down
60 changes: 44 additions & 16 deletions hw/xive.c
Expand Up @@ -103,6 +103,7 @@
/* Use 64K for everything by default */
#define IC_PAGE_SIZE 0x10000
#define TM_PAGE_SIZE 0x10000
#define IPI_ESB_SHIFT (16 + 1)

#define IC_BAR_DEFAULT 0x30203100000ull
#define IC_BAR_SIZE (8 * IC_PAGE_SIZE)
Expand Down Expand Up @@ -1243,9 +1244,6 @@ uint32_t xive_alloc_hw_irqs(uint32_t chip_id, uint32_t count, uint32_t align)
}
x->int_hw_bot = base;

/* Adjust the irq source to avoid overlaps */
adjust_irq_source(&x->ipis.is, base - x->int_base);

/* Initialize the corresponding IVT entries to sane defaults,
* IE entry is valid, not routed and masked, EQ data is set
* to the GIRQ number.
Expand Down Expand Up @@ -1590,7 +1588,7 @@ static void xive_source_eoi(struct irq_source *is, uint32_t isn)
ive = s->xive->ivt_base;
if (!ive)
return;
ive += GIRQ_TO_IDX(isn);
ive += GIRQ_TO_IDX(isn);

/* If it's invalid or masked, don't do anything */
if ((ive->w & IVE_MASKED) || !(ive->w & IVE_VALID))
Expand Down Expand Up @@ -1649,34 +1647,61 @@ static const struct irq_source_ops xive_irq_source_ops = {
.attributes = xive_source_attributes,
};

static void __xive_register_source(struct xive_src *s, uint32_t base,
uint32_t count, uint32_t shift,
void *mmio, uint32_t flags, void *data,
static void __xive_register_source(struct xive *x, struct xive_src *s,
uint32_t base, uint32_t count,
uint32_t shift, void *mmio, uint32_t flags,
bool secondary, void *data,
const struct irq_source_ops *orig_ops)
{
s->esb_base = base;
s->esb_shift = shift;
s->esb_mmio = mmio;
s->flags = flags;
s->orig_ops = orig_ops;

s->xive = x;
s->is.start = base;
s->is.end = base + count;
s->is.ops = &xive_irq_source_ops;
s->is.data = data;

__register_irq_source(&s->is);
__register_irq_source(&s->is, secondary);
}

void xive_register_source(uint32_t base, uint32_t count, uint32_t shift,
void *mmio, uint32_t flags, void *data,
const struct irq_source_ops *ops)
void xive_register_hw_source(uint32_t base, uint32_t count, uint32_t shift,
void *mmio, uint32_t flags, void *data,
const struct irq_source_ops *ops)
{
struct xive_src *s;
struct xive *x = xive_from_isn(base);

assert(x);

s = malloc(sizeof(struct xive_src));
assert(s);
__xive_register_source(s, base, count, shift, mmio, flags, data, ops);
__xive_register_source(x, s, base, count, shift, mmio, flags,
false, data, ops);
}

void xive_register_ipi_source(uint32_t base, uint32_t count, void *data,
const struct irq_source_ops *ops)
{
struct xive_src *s;
struct xive *x = xive_from_isn(base);
uint32_t base_idx = GIRQ_TO_IDX(base);
void *mmio_base;

assert(x);
assert(base >= x->int_base && (base + count) <= x->int_ipi_top);

s = malloc(sizeof(struct xive_src));
assert(s);

/* Callbacks assume the MMIO base corresponds to the first
* interrupt of that source structure so adjust it
*/
mmio_base = x->esb_mmio + (1ul << IPI_ESB_SHIFT) * base_idx;
__xive_register_source(x, s, base, count, IPI_ESB_SHIFT, mmio_base,
XIVE_SRC_EOI_PAGE1, false, data, ops);
}

static void init_one_xive(struct dt_node *np)
Expand Down Expand Up @@ -1744,9 +1769,12 @@ static void init_one_xive(struct dt_node *np)

/* Register built-in source controllers (aka IPIs) */
/* XXX Add new EOI mode for DD2 */
__xive_register_source(&x->ipis, x->int_base,
x->int_hw_bot - x->int_base, 16 + 1,
x->esb_mmio, XIVE_SRC_EOI_PAGE1, NULL, NULL);
__xive_register_source(x, &x->ipis, x->int_base,
x->int_hw_bot - x->int_base, IPI_ESB_SHIFT,
x->esb_mmio, XIVE_SRC_EOI_PAGE1,
true, NULL, NULL);

/* XXX Add registration of escalation sources too */

/* Create a device-tree node for Linux use */
xive_create_mmio_dt_node(x);
Expand Down
3 changes: 1 addition & 2 deletions include/interrupts.h
Expand Up @@ -291,11 +291,10 @@ struct irq_source {
struct list_node link;
};

extern void __register_irq_source(struct irq_source *is);
extern void __register_irq_source(struct irq_source *is, bool secondary);
extern void register_irq_source(const struct irq_source_ops *ops, void *data,
uint32_t start, uint32_t count);
extern void unregister_irq_source(uint32_t start, uint32_t count);
extern void adjust_irq_source(struct irq_source *is, uint32_t new_count);

extern uint32_t get_psi_interrupt(uint32_t chip_id);

Expand Down
8 changes: 5 additions & 3 deletions include/xive.h
Expand Up @@ -376,9 +376,11 @@ uint32_t xive_get_notify_base(uint32_t girq);
#define XIVE_SRC_LSI 0x00000004 /* No Q bit, no retrigger */

struct irq_source_ops;
void xive_register_source(uint32_t base, uint32_t count, uint32_t shift,
void *mmio, uint32_t flags, void *data,
const struct irq_source_ops *ops);
void xive_register_hw_source(uint32_t base, uint32_t count, uint32_t shift,
void *mmio, uint32_t flags, void *data,
const struct irq_source_ops *ops);
void xive_register_ipi_source(uint32_t base, uint32_t count, void *data,
const struct irq_source_ops *ops);

void xive_cpu_callin(struct cpu_thread *cpu);

Expand Down

0 comments on commit aecfaf0

Please sign in to comment.