// SPDX-License-Identifier: GPL-2.0 /* * Copyright (C) 2021 Western Digital Corporation or its affiliates. * Copyright (C) 2022 Ventana Micro Systems Inc.
*/
while (id < last_id) {
isel = id / BITS_PER_LONG;
isel *= BITS_PER_LONG / IMSIC_EIPx_BITS;
isel += pend ? IMSIC_EIP0 : IMSIC_EIE0;
/* * Prepare the ID mask to be programmed in the * IMSIC EIEx and EIPx registers. These registers * are XLEN-wide and we must not touch IDs which * are < base_id and >= (base_id + num_id).
*/
ireg = 0; for (i = id & (__riscv_xlen - 1); id < last_id && i < __riscv_xlen; i++) {
ireg |= BIT(i);
id++;
}
/* * The IMSIC EIEx and EIPx registers are indirectly * accessed via using ISELECT and IREG CSRs so we * need to access these CSRs without getting preempted. * * All existing users of this function call this * function with local IRQs disabled so we don't * need to do anything special here.
*/ if (val)
imsic_csr_set(isel, ireg); else
imsic_csr_clear(isel, ireg);
}
}
staticbool __imsic_local_sync(struct imsic_local_priv *lpriv)
{ struct imsic_local_config *tlocal, *mlocal; struct imsic_vector *vec, *tvec, *mvec; bool ret = true; int i;
lockdep_assert_held(&lpriv->lock);
for_each_set_bit(i, lpriv->dirty_bitmap, imsic->global.nr_ids + 1) { if (!i || (!imsic_noipi && i == IMSIC_IPI_ID)) goto skip;
vec = &lpriv->vectors[i];
if (READ_ONCE(vec->enable))
__imsic_id_set_enable(i); else
__imsic_id_clear_enable(i);
/* * Clear the previous vector pointer of the new vector only * after the movement is complete on the old CPU.
*/
mvec = READ_ONCE(vec->move_prev); if (mvec) { /* * If the old vector has not been updated then * try again in the next sync-up call.
*/ if (READ_ONCE(mvec->move_next)) {
ret = false; continue;
}
WRITE_ONCE(vec->move_prev, NULL);
}
/* * If a vector was being moved to a new vector on some other * CPU then we can get a MSI during the movement so check the * ID pending bit and re-trigger the new ID on other CPU using * MMIO write.
*/
mvec = READ_ONCE(vec->move_next); if (mvec) { /* * Devices having non-atomic MSI update might see * an intermediate state so check both old ID and * new ID for pending interrupts. * * For details, see imsic_irq_set_affinity().
*/
tvec = vec->local_id == mvec->local_id ?
NULL : &lpriv->vectors[mvec->local_id];
if (tvec && !irq_can_move_in_process_context(irq_get_irq_data(vec->irq)) &&
__imsic_id_read_clear_pending(tvec->local_id)) { /* Retrigger temporary vector if it was already in-use */ if (READ_ONCE(tvec->enable)) {
tlocal = per_cpu_ptr(imsic->global.local, tvec->cpu);
writel_relaxed(tvec->local_id, tlocal->msi_va);
}
if (force_all)
bitmap_fill(lpriv->dirty_bitmap, imsic->global.nr_ids + 1); if (!__imsic_local_sync(lpriv))
__imsic_local_timer_start(lpriv, smp_processor_id());
/* * The spinlock acquire/release semantics ensure that changes * to vector enable, vector move and dirty bitmap are visible * to the target CPU.
*/
/* * We schedule a timer on the target CPU if the target CPU is not * same as the current CPU. An offline CPU will unconditionally * synchronize IDs through imsic_starting_cpu() when the * CPU is brought up.
*/ if (cpu_online(cpu)) { if (cpu == smp_processor_id()) { if (__imsic_local_sync(lpriv)) return;
}
if (WARN_ON_ONCE(old_vec->cpu == new_vec->cpu)) return;
old_lpriv = per_cpu_ptr(imsic->lpriv, old_vec->cpu); if (WARN_ON_ONCE(&old_lpriv->vectors[old_vec->local_id] != old_vec)) return;
new_lpriv = per_cpu_ptr(imsic->lpriv, new_vec->cpu); if (WARN_ON_ONCE(&new_lpriv->vectors[new_vec->local_id] != new_vec)) return;
/* * Move and re-trigger the new vector based on the pending * state of the old vector because we might get a device * interrupt on the old vector while device was being moved * to the new vector.
*/
enabled = imsic_vector_move_update(old_lpriv, old_vec, true, false, new_vec);
imsic_vector_move_update(new_lpriv, new_vec, false, enabled, old_vec);
}
/* Find number of guest index bits in MSI address */
rc = of_property_read_u32(to_of_node(fwnode), "riscv,guest-index-bits",
&global->guest_index_bits); if (rc)
global->guest_index_bits = 0;
/* Find number of HART index bits */
rc = of_property_read_u32(to_of_node(fwnode), "riscv,hart-index-bits",
&global->hart_index_bits); if (rc) { /* Assume default value */
global->hart_index_bits = __fls(*nr_parent_irqs); if (BIT(global->hart_index_bits) < *nr_parent_irqs)
global->hart_index_bits++;
}
/* Find number of group index bits */
rc = of_property_read_u32(to_of_node(fwnode), "riscv,group-index-bits",
&global->group_index_bits); if (rc)
global->group_index_bits = 0;
/* * Find first bit position of group index. * If not specified assumed the default APLIC-IMSIC configuration.
*/
rc = of_property_read_u32(to_of_node(fwnode), "riscv,group-index-shift",
&global->group_index_shift); if (rc)
global->group_index_shift = IMSIC_MMIO_PAGE_SHIFT * 2;
/* Find number of interrupt identities */
rc = of_property_read_u32(to_of_node(fwnode), "riscv,num-ids",
&global->nr_ids); if (rc) {
pr_err("%pfwP: number of interrupt identities not found\n", fwnode); return rc;
}
/* Find number of guest interrupt identities */
rc = of_property_read_u32(to_of_node(fwnode), "riscv,num-guest-ids",
&global->nr_guest_ids); if (rc)
global->nr_guest_ids = global->nr_ids;
/* Find number of parent interrupts */ while (!imsic_get_parent_hartid(fwnode, *nr_parent_irqs, &hartid))
(*nr_parent_irqs)++; if (!*nr_parent_irqs) {
pr_err("%pfwP: no parent irqs available\n", fwnode); return -EINVAL;
}
/* Sanity check guest index bits */
i = BITS_PER_LONG - IMSIC_MMIO_PAGE_SHIFT; if (i < global->guest_index_bits) {
pr_err("%pfwP: guest index bits too big\n", fwnode); return -EINVAL;
}
/* Sanity check HART index bits */
i = BITS_PER_LONG - IMSIC_MMIO_PAGE_SHIFT - global->guest_index_bits; if (i < global->hart_index_bits) {
pr_err("%pfwP: HART index bits too big\n", fwnode); return -EINVAL;
}
/* Sanity check group index bits */
i = BITS_PER_LONG - IMSIC_MMIO_PAGE_SHIFT -
global->guest_index_bits - global->hart_index_bits; if (i < global->group_index_bits) {
pr_err("%pfwP: group index bits too big\n", fwnode); return -EINVAL;
}
/* Sanity check group index shift */
i = global->group_index_bits + global->group_index_shift - 1; if (i >= BITS_PER_LONG) {
pr_err("%pfwP: group index shift too big\n", fwnode); return -EINVAL;
}
/* Sanity check number of interrupt identities */ if (global->nr_ids < IMSIC_MIN_ID ||
global->nr_ids >= IMSIC_MAX_ID ||
(global->nr_ids & IMSIC_MIN_ID) != IMSIC_MIN_ID) {
pr_err("%pfwP: invalid number of interrupt identities\n", fwnode); return -EINVAL;
}
/* Sanity check number of guest interrupt identities */ if (global->nr_guest_ids < IMSIC_MIN_ID ||
global->nr_guest_ids >= IMSIC_MAX_ID ||
(global->nr_guest_ids & IMSIC_MIN_ID) != IMSIC_MIN_ID) {
pr_err("%pfwP: invalid number of guest interrupt identities\n", fwnode); return -EINVAL;
}
/* * Only one IMSIC instance allowed in a platform for clean * implementation of SMP IRQ affinity and per-CPU IPIs. * * This means on a multi-socket (or multi-die) platform we * will have multiple MMIO regions for one IMSIC instance.
*/ if (imsic) {
pr_err("%pfwP: already initialized hence ignoring\n", fwnode); return -EALREADY;
}
if (!riscv_isa_extension_available(NULL, SxAIA)) {
pr_err("%pfwP: AIA support not available\n", fwnode); return -ENODEV;
}
imsic = kzalloc(sizeof(*imsic), GFP_KERNEL); if (!imsic) return -ENOMEM;
imsic->fwnode = fwnode;
global = &imsic->global;
/* Initialize local (or per-CPU )state */
rc = imsic_local_init(); if (rc) {
pr_err("%pfwP: failed to initialize local state\n",
fwnode); goto out_iounmap;
}
/* Configure handlers for target CPUs */ for (i = 0; i < nr_parent_irqs; i++) {
rc = imsic_get_parent_hartid(fwnode, i, &hartid); if (rc) {
pr_warn("%pfwP: hart ID for parent irq%d not found\n", fwnode, i); continue;
}
cpu = riscv_hartid_to_cpuid(hartid); if (cpu < 0) {
pr_warn("%pfwP: invalid cpuid for parent irq%d\n", fwnode, i); continue;
}
/* Find MMIO location of MSI page */
index = nr_mmios;
reloff = i * BIT(global->guest_index_bits) *
IMSIC_MMIO_PAGE_SZ; for (j = 0; nr_mmios; j++) { if (reloff < resource_size(&mmios[j])) {
index = j; break;
}
/* * MMIO region size may not be aligned to * BIT(global->guest_index_bits) * IMSIC_MMIO_PAGE_SZ * if holes are present.
*/
reloff -= ALIGN(resource_size(&mmios[j]),
BIT(global->guest_index_bits) * IMSIC_MMIO_PAGE_SZ);
} if (index >= nr_mmios) {
pr_warn("%pfwP: MMIO not found for parent irq%d\n", fwnode, i); continue;
}
/* If no CPU handlers found then can't take interrupts */ if (!nr_handlers) {
pr_err("%pfwP: No CPU handlers found\n", fwnode);
rc = -ENODEV; goto out_local_cleanup;
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.