mirror of
https://github.com/riscv-software-src/opensbi.git
synced 2026-07-26 15:51:38 +01:00
Add inline register accessors (aplic_sourcecfg_write/read, aplic_target_write, aplic_irq_setie/clrie/clrip, aplic_idc_base/ read/write) to consolidate all MMIO access behind typed helpers and eliminate open-coded address arithmetic throughout the driver. Add field-packing macros APLIC_TARGET_HART_IDX, APLIC_TARGET_IPRIO, and APLIC_TARGET_GUEST_IDX for constructing APLIC_TARGET register values. Add aplic_is_direct_mode() mode helper, aplic_hwirq_flags_to_sourcecfg() to map generic hwirq flags to APLIC source modes, aplic_hwirq_is_delegated() to detect S-mode delegated sources, aplic_find_idc_index() to map a hart index to its IDC slot, and aplic_first_target_hart() to select the first available IDC hart for initial interrupt targeting. Implement the full sbi_irqchip callback set for direct (IDC) mode: - warm_init: enables IDC interrupt delivery and threshold per hart - process_hwirqs: reads TOPI and dispatches via sbi_irqchip_process_hwirq, skipping delegated sources - hwirq_setup: programs sourcecfg from hwirq_flags and sets the TARGET register to the first available IDC hart - hwirq_cleanup: clears IE, IP, sourcecfg, and TARGET on teardown - hwirq_eoi: reads CLAIMI to acknowledge the interrupt on the IDC - hwirq_set_affinity: reprograms TARGET to the specified hart index - hwirq_mask/unmask: clears/sets IE via CLRIENUM/SETIENUM Register the irqchip device and populate target_harts from idc_map only when targets_mmode is set in aplic_cold_irqchip_init(). Co-developed-by: Raymond Mao <raymond.mao@riscstar.com> Signed-off-by Raymond Mao <raymond.mao@riscstar.com> Signed-off-by: Oza Pawandeep <pawandeep.oza@oss.qualcomm.com> Reviewed-by: Anup Patel <anup@brainfault.org> Link: https://lore.kernel.org/r/20260721214833.687361-2-pawandeep.oza@oss.qualcomm.com Signed-off-by: Anup Patel <anup@brainfault.org>
579 lines
16 KiB
C
579 lines
16 KiB
C
/*
|
|
* SPDX-License-Identifier: BSD-2-Clause
|
|
*
|
|
* Copyright (c) 2021 Western Digital Corporation or its affiliates.
|
|
* Copyright (c) 2022 Ventana Micro Systems Inc.
|
|
*
|
|
* Authors:
|
|
* Anup Patel <anup.patel@wdc.com>
|
|
*/
|
|
|
|
#include <sbi/riscv_io.h>
|
|
#include <sbi/sbi_console.h>
|
|
#include <sbi/sbi_domain.h>
|
|
#include <sbi/sbi_error.h>
|
|
#include <sbi_utils/irqchip/aplic.h>
|
|
|
|
#define APLIC_MAX_IDC (1UL << 14)
|
|
#define APLIC_MAX_SOURCE 1024
|
|
|
|
#define APLIC_DOMAINCFG 0x0000
|
|
#define APLIC_DOMAINCFG_IE (1 << 8)
|
|
#define APLIC_DOMAINCFG_DM (1 << 2)
|
|
#define APLIC_DOMAINCFG_BE (1 << 0)
|
|
|
|
#define APLIC_SOURCECFG_BASE 0x0004
|
|
#define APLIC_SOURCECFG_D (1 << 10)
|
|
#define APLIC_SOURCECFG_CHILDIDX_MASK 0x000003ff
|
|
#define APLIC_SOURCECFG_SM_MASK 0x00000007
|
|
#define APLIC_SOURCECFG_SM_INACTIVE 0x0
|
|
#define APLIC_SOURCECFG_SM_DETACH 0x1
|
|
#define APLIC_SOURCECFG_SM_EDGE_RISE 0x4
|
|
#define APLIC_SOURCECFG_SM_EDGE_FALL 0x5
|
|
#define APLIC_SOURCECFG_SM_LEVEL_HIGH 0x6
|
|
#define APLIC_SOURCECFG_SM_LEVEL_LOW 0x7
|
|
|
|
#define APLIC_MMSICFGADDR 0x1bc0
|
|
#define APLIC_MMSICFGADDRH 0x1bc4
|
|
#define APLIC_SMSICFGADDR 0x1bc8
|
|
#define APLIC_SMSICFGADDRH 0x1bcc
|
|
|
|
#define APLIC_xMSICFGADDRH_L (1UL << 31)
|
|
#define APLIC_xMSICFGADDRH_HHXS_MASK 0x1f
|
|
#define APLIC_xMSICFGADDRH_HHXS_SHIFT 24
|
|
#define APLIC_xMSICFGADDRH_LHXS_MASK 0x7
|
|
#define APLIC_xMSICFGADDRH_LHXS_SHIFT 20
|
|
#define APLIC_xMSICFGADDRH_HHXW_MASK 0x7
|
|
#define APLIC_xMSICFGADDRH_HHXW_SHIFT 16
|
|
#define APLIC_xMSICFGADDRH_LHXW_MASK 0xf
|
|
#define APLIC_xMSICFGADDRH_LHXW_SHIFT 12
|
|
#define APLIC_xMSICFGADDRH_BAPPN_MASK 0xfff
|
|
|
|
#define APLIC_xMSICFGADDR_PPN_SHIFT 12
|
|
|
|
#define APLIC_xMSICFGADDR_PPN_HART(__lhxs) \
|
|
((1UL << (__lhxs)) - 1)
|
|
|
|
#define APLIC_xMSICFGADDR_PPN_LHX_MASK(__lhxw) \
|
|
((1UL << (__lhxw)) - 1)
|
|
#define APLIC_xMSICFGADDR_PPN_LHX_SHIFT(__lhxs) \
|
|
((__lhxs))
|
|
#define APLIC_xMSICFGADDR_PPN_LHX(__lhxw, __lhxs) \
|
|
(APLIC_xMSICFGADDR_PPN_LHX_MASK(__lhxw) << \
|
|
APLIC_xMSICFGADDR_PPN_LHX_SHIFT(__lhxs))
|
|
|
|
#define APLIC_xMSICFGADDR_PPN_HHX_MASK(__hhxw) \
|
|
((1UL << (__hhxw)) - 1)
|
|
#define APLIC_xMSICFGADDR_PPN_HHX_SHIFT(__hhxs) \
|
|
((__hhxs) + APLIC_xMSICFGADDR_PPN_SHIFT)
|
|
#define APLIC_xMSICFGADDR_PPN_HHX(__hhxw, __hhxs) \
|
|
(APLIC_xMSICFGADDR_PPN_HHX_MASK(__hhxw) << \
|
|
APLIC_xMSICFGADDR_PPN_HHX_SHIFT(__hhxs))
|
|
|
|
#define APLIC_TARGET_GUEST_IDX(__gidx) \
|
|
((((u32)(__gidx)) & APLIC_TARGET_GUEST_IDX_MASK) << \
|
|
APLIC_TARGET_GUEST_IDX_SHIFT)
|
|
|
|
#define APLIC_SETIP_BASE 0x1c00
|
|
#define APLIC_SETIPNUM 0x1cdc
|
|
|
|
#define APLIC_CLRIP_BASE 0x1d00
|
|
#define APLIC_CLRIPNUM 0x1ddc
|
|
|
|
#define APLIC_SETIE_BASE 0x1e00
|
|
#define APLIC_SETIENUM 0x1edc
|
|
|
|
#define APLIC_CLRIE_BASE 0x1f00
|
|
#define APLIC_CLRIENUM 0x1fdc
|
|
|
|
#define APLIC_SETIPNUM_LE 0x2000
|
|
#define APLIC_SETIPNUM_BE 0x2004
|
|
|
|
#define APLIC_TARGET_BASE 0x3004
|
|
#define APLIC_TARGET_HART_IDX_SHIFT 18
|
|
#define APLIC_TARGET_HART_IDX_MASK 0x3fff
|
|
#define APLIC_TARGET_GUEST_IDX_SHIFT 12
|
|
#define APLIC_TARGET_GUEST_IDX_MASK 0x3f
|
|
#define APLIC_TARGET_IPRIO_MASK 0xff
|
|
#define APLIC_TARGET_EIID_MASK 0x7ff
|
|
|
|
#define APLIC_TARGET_HART_IDX(__hidx) \
|
|
((((u32)(__hidx)) & APLIC_TARGET_HART_IDX_MASK) << \
|
|
APLIC_TARGET_HART_IDX_SHIFT)
|
|
#define APLIC_TARGET_IPRIO(__prio) \
|
|
(((u32)(__prio)) & APLIC_TARGET_IPRIO_MASK)
|
|
|
|
#define APLIC_IDC_BASE 0x4000
|
|
#define APLIC_IDC_SIZE 32
|
|
|
|
#define APLIC_IDC_IDELIVERY 0x00
|
|
|
|
#define APLIC_IDC_IFORCE 0x04
|
|
|
|
#define APLIC_IDC_ITHRESHOLD 0x08
|
|
|
|
#define APLIC_IDC_TOPI 0x18
|
|
#define APLIC_IDC_TOPI_ID_SHIFT 16
|
|
#define APLIC_IDC_TOPI_ID_MASK 0x3ff
|
|
#define APLIC_IDC_TOPI_PRIO_MASK 0xff
|
|
|
|
#define APLIC_IDC_CLAIMI 0x1c
|
|
|
|
#define APLIC_DEFAULT_PRIORITY 1
|
|
#define APLIC_DISABLE_IDELIVERY 0
|
|
#define APLIC_ENABLE_IDELIVERY 1
|
|
#define APLIC_DISABLE_ITHRESHOLD 1
|
|
#define APLIC_ENABLE_ITHRESHOLD 0
|
|
|
|
static SBI_LIST_HEAD(aplic_list);
|
|
static void aplic_writel_msicfg(struct aplic_msicfg_data *msicfg,
|
|
void *msicfgaddr, void *msicfgaddrH);
|
|
|
|
static inline bool aplic_is_direct_mode(struct aplic_data *aplic)
|
|
{
|
|
return aplic && aplic->num_idc;
|
|
}
|
|
|
|
static inline void aplic_sourcecfg_write(struct aplic_data *aplic,
|
|
u32 hwirq, u32 val)
|
|
{
|
|
writel(val, (void *)(aplic->addr + APLIC_SOURCECFG_BASE +
|
|
(hwirq - 1) * sizeof(u32)));
|
|
}
|
|
|
|
static inline u32 aplic_sourcecfg_read(struct aplic_data *aplic, u32 hwirq)
|
|
{
|
|
return readl((void *)(aplic->addr + APLIC_SOURCECFG_BASE +
|
|
(hwirq - 1) * sizeof(u32)));
|
|
}
|
|
|
|
static inline void aplic_target_write(struct aplic_data *aplic,
|
|
u32 hwirq, u32 val)
|
|
{
|
|
writel(val, (void *)(aplic->addr + APLIC_TARGET_BASE +
|
|
(hwirq - 1) * sizeof(u32)));
|
|
}
|
|
|
|
static inline void aplic_irq_setie(struct aplic_data *aplic, u32 hwirq)
|
|
{
|
|
writel(hwirq, (void *)(aplic->addr + APLIC_SETIENUM));
|
|
}
|
|
|
|
static inline void aplic_irq_clrie(struct aplic_data *aplic, u32 hwirq)
|
|
{
|
|
writel(hwirq, (void *)(aplic->addr + APLIC_CLRIENUM));
|
|
}
|
|
|
|
static inline void aplic_irq_clrip(struct aplic_data *aplic, u32 hwirq)
|
|
{
|
|
writel(hwirq, (void *)(aplic->addr + APLIC_CLRIPNUM));
|
|
}
|
|
|
|
static inline void *aplic_idc_base(struct aplic_data *aplic, u32 idc_index)
|
|
{
|
|
return (void *)(aplic->addr + APLIC_IDC_BASE +
|
|
idc_index * APLIC_IDC_SIZE);
|
|
}
|
|
|
|
static inline u32 aplic_idc_read(struct aplic_data *aplic, u32 idc_index,
|
|
u32 reg)
|
|
{
|
|
return readl(aplic_idc_base(aplic, idc_index) + reg);
|
|
}
|
|
|
|
static inline void aplic_idc_write(struct aplic_data *aplic, u32 idc_index,
|
|
u32 reg, u32 val)
|
|
{
|
|
writel(val, aplic_idc_base(aplic, idc_index) + reg);
|
|
}
|
|
|
|
static u32 aplic_hwirq_flags_to_sourcecfg(u32 hwirq_flags)
|
|
{
|
|
switch (hwirq_flags & SBI_HWIRQ_FLAGS_LEVEL_SENSE_MASK) {
|
|
case SBI_HWIRQ_FLAGS_EDGE_RISING:
|
|
return APLIC_SOURCECFG_SM_EDGE_RISE;
|
|
case SBI_HWIRQ_FLAGS_EDGE_FALLING:
|
|
return APLIC_SOURCECFG_SM_EDGE_FALL;
|
|
case SBI_HWIRQ_FLAGS_LEVEL_HIGH:
|
|
return APLIC_SOURCECFG_SM_LEVEL_HIGH;
|
|
case SBI_HWIRQ_FLAGS_LEVEL_LOW:
|
|
return APLIC_SOURCECFG_SM_LEVEL_LOW;
|
|
default:
|
|
return APLIC_SOURCECFG_SM_INACTIVE;
|
|
}
|
|
}
|
|
|
|
static bool aplic_hwirq_is_delegated(struct aplic_data *aplic, u32 hwirq)
|
|
{
|
|
u32 sourcecfg;
|
|
|
|
sourcecfg = aplic_sourcecfg_read(aplic, hwirq);
|
|
return !!(sourcecfg & APLIC_SOURCECFG_D);
|
|
}
|
|
|
|
static int aplic_find_idc_index(struct aplic_data *aplic, u32 hart_index)
|
|
{
|
|
u32 i;
|
|
|
|
for (i = 0; i < aplic->num_idc; i++) {
|
|
if (aplic->idc_map[i] == hart_index)
|
|
return i;
|
|
}
|
|
|
|
return SBI_ENOENT;
|
|
}
|
|
|
|
static void aplic_init(struct aplic_data *aplic)
|
|
{
|
|
struct aplic_delegate_data *deleg;
|
|
u32 i, j, tmp, domaincfg;
|
|
int locked;
|
|
|
|
/* Set domain configuration to 0 */
|
|
writel(0, (void *)(aplic->addr + APLIC_DOMAINCFG));
|
|
|
|
/* Disable all interrupts */
|
|
for (i = 0; i <= aplic->num_source; i += 32)
|
|
writel(-1U, (void *)(aplic->addr + APLIC_CLRIE_BASE +
|
|
(i / 32) * sizeof(u32)));
|
|
|
|
/* Set interrupt type and priority for all interrupts */
|
|
for (i = 1; i <= aplic->num_source; i++) {
|
|
/* Set IRQ source configuration to 0 */
|
|
writel(0, (void *)(aplic->addr + APLIC_SOURCECFG_BASE +
|
|
(i - 1) * sizeof(u32)));
|
|
/* Set IRQ target hart index and priority to 1 */
|
|
writel(APLIC_DEFAULT_PRIORITY, (void *)(aplic->addr +
|
|
APLIC_TARGET_BASE +
|
|
(i - 1) * sizeof(u32)));
|
|
}
|
|
|
|
/* Configure IRQ delegation */
|
|
for (i = 0; i < APLIC_MAX_DELEGATE; i++) {
|
|
deleg = &aplic->delegate[i];
|
|
if (!deleg->first_irq || !deleg->last_irq)
|
|
continue;
|
|
if (aplic->num_source < deleg->first_irq ||
|
|
aplic->num_source < deleg->last_irq)
|
|
continue;
|
|
if (deleg->child_index > APLIC_SOURCECFG_CHILDIDX_MASK)
|
|
continue;
|
|
if (deleg->first_irq > deleg->last_irq) {
|
|
tmp = deleg->first_irq;
|
|
deleg->first_irq = deleg->last_irq;
|
|
deleg->last_irq = tmp;
|
|
}
|
|
for (j = deleg->first_irq; j <= deleg->last_irq; j++)
|
|
aplic_sourcecfg_write(aplic, j, APLIC_SOURCECFG_D | deleg->child_index);
|
|
}
|
|
|
|
/* Default initialization of IDC structures */
|
|
if (aplic_is_direct_mode(aplic)) {
|
|
for (i = 0; i < aplic->num_idc; i++) {
|
|
aplic_idc_write(aplic, i, APLIC_IDC_IDELIVERY,
|
|
APLIC_DISABLE_IDELIVERY);
|
|
aplic_idc_write(aplic, i, APLIC_IDC_IFORCE, 0);
|
|
aplic_idc_write(aplic, i, APLIC_IDC_ITHRESHOLD,
|
|
APLIC_DISABLE_ITHRESHOLD);
|
|
}
|
|
} else {
|
|
/* MSI configuration */
|
|
locked = readl((void *)(aplic->addr + APLIC_MMSICFGADDRH)) & APLIC_xMSICFGADDRH_L;
|
|
if (aplic->targets_mmode && aplic->has_msicfg_mmode && !locked) {
|
|
aplic_writel_msicfg(&aplic->msicfg_mmode,
|
|
(void *)(aplic->addr + APLIC_MMSICFGADDR),
|
|
(void *)(aplic->addr + APLIC_MMSICFGADDRH));
|
|
}
|
|
if (aplic->targets_mmode && aplic->has_msicfg_smode && !locked) {
|
|
aplic_writel_msicfg(&aplic->msicfg_smode,
|
|
(void *)(aplic->addr + APLIC_SMSICFGADDR),
|
|
(void *)(aplic->addr + APLIC_SMSICFGADDRH));
|
|
}
|
|
}
|
|
|
|
domaincfg = APLIC_DOMAINCFG_IE;
|
|
|
|
writel(domaincfg, (void *)(aplic->addr + APLIC_DOMAINCFG));
|
|
}
|
|
|
|
void aplic_reinit_all(void)
|
|
{
|
|
struct aplic_data *aplic;
|
|
|
|
sbi_list_for_each_entry(aplic, &aplic_list, node)
|
|
aplic_init(aplic);
|
|
}
|
|
|
|
static void aplic_writel_msicfg(struct aplic_msicfg_data *msicfg,
|
|
void *msicfgaddr, void *msicfgaddrH)
|
|
{
|
|
u32 val;
|
|
unsigned long base_ppn;
|
|
|
|
/* Compute the MSI base PPN */
|
|
base_ppn = msicfg->base_addr >> APLIC_xMSICFGADDR_PPN_SHIFT;
|
|
base_ppn &= ~APLIC_xMSICFGADDR_PPN_HART(msicfg->lhxs);
|
|
base_ppn &= ~APLIC_xMSICFGADDR_PPN_LHX(msicfg->lhxw, msicfg->lhxs);
|
|
base_ppn &= ~APLIC_xMSICFGADDR_PPN_HHX(msicfg->hhxw, msicfg->hhxs);
|
|
|
|
/* Write the lower MSI config register */
|
|
writel((u32)base_ppn, msicfgaddr);
|
|
|
|
/* Write the upper MSI config register */
|
|
val = (((u64)base_ppn) >> 32) &
|
|
APLIC_xMSICFGADDRH_BAPPN_MASK;
|
|
val |= (msicfg->lhxw & APLIC_xMSICFGADDRH_LHXW_MASK)
|
|
<< APLIC_xMSICFGADDRH_LHXW_SHIFT;
|
|
val |= (msicfg->hhxw & APLIC_xMSICFGADDRH_HHXW_MASK)
|
|
<< APLIC_xMSICFGADDRH_HHXW_SHIFT;
|
|
val |= (msicfg->lhxs & APLIC_xMSICFGADDRH_LHXS_MASK)
|
|
<< APLIC_xMSICFGADDRH_LHXS_SHIFT;
|
|
val |= (msicfg->hhxs & APLIC_xMSICFGADDRH_HHXS_MASK)
|
|
<< APLIC_xMSICFGADDRH_HHXS_SHIFT;
|
|
writel(val, msicfgaddrH);
|
|
}
|
|
|
|
static int aplic_check_msicfg(struct aplic_msicfg_data *msicfg)
|
|
{
|
|
if (APLIC_xMSICFGADDRH_LHXS_MASK < msicfg->lhxs)
|
|
return SBI_EINVAL;
|
|
|
|
if (APLIC_xMSICFGADDRH_LHXW_MASK < msicfg->lhxw)
|
|
return SBI_EINVAL;
|
|
|
|
if (APLIC_xMSICFGADDRH_HHXS_MASK < msicfg->hhxs)
|
|
return SBI_EINVAL;
|
|
|
|
if (APLIC_xMSICFGADDRH_HHXW_MASK < msicfg->hhxw)
|
|
return SBI_EINVAL;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int aplic_warm_init(struct sbi_irqchip_device *chip)
|
|
{
|
|
struct aplic_data *aplic;
|
|
u32 hart_index;
|
|
int idc_index;
|
|
|
|
aplic = container_of(chip, struct aplic_data, irqchip);
|
|
|
|
hart_index = current_hartindex();
|
|
idc_index = aplic_find_idc_index(aplic, hart_index);
|
|
if (idc_index < 0)
|
|
return 0;
|
|
|
|
aplic_idc_write(aplic, idc_index, APLIC_IDC_ITHRESHOLD,
|
|
APLIC_ENABLE_ITHRESHOLD);
|
|
aplic_idc_write(aplic, idc_index, APLIC_IDC_IDELIVERY,
|
|
APLIC_ENABLE_IDELIVERY);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int aplic_process_hwirqs(struct sbi_irqchip_device *chip)
|
|
{
|
|
struct aplic_data *aplic;
|
|
u32 hart_index, claimi, hwirq;
|
|
int idc_index, rc = 0, tmp;
|
|
|
|
aplic = container_of(chip, struct aplic_data, irqchip);
|
|
|
|
hart_index = current_hartindex();
|
|
idc_index = aplic_find_idc_index(aplic, hart_index);
|
|
if (idc_index < 0)
|
|
return 0;
|
|
|
|
while ((claimi = aplic_idc_read(aplic, idc_index, APLIC_IDC_CLAIMI))) {
|
|
hwirq = (claimi >> APLIC_IDC_TOPI_ID_SHIFT) &
|
|
APLIC_IDC_TOPI_ID_MASK;
|
|
|
|
if (!hwirq)
|
|
break;
|
|
|
|
if (aplic_hwirq_is_delegated(aplic, hwirq))
|
|
continue;
|
|
|
|
tmp = sbi_irqchip_process_hwirq(chip, hwirq);
|
|
if (tmp)
|
|
rc = tmp;
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
static int aplic_hwirq_setup(struct sbi_irqchip_device *chip,
|
|
u32 hwirq, u32 hwirq_flags)
|
|
{
|
|
struct aplic_data *aplic;
|
|
u32 sourcecfg;
|
|
|
|
aplic = container_of(chip, struct aplic_data, irqchip);
|
|
|
|
sourcecfg = aplic_hwirq_flags_to_sourcecfg(hwirq_flags);
|
|
if (sourcecfg == APLIC_SOURCECFG_SM_INACTIVE) {
|
|
sbi_printf("aplic_hwirq_setup: unsupported flags=0x%lx hwirq=%lu\n",
|
|
(unsigned long)hwirq_flags,
|
|
(unsigned long)hwirq);
|
|
return SBI_EINVAL;
|
|
}
|
|
aplic_sourcecfg_write(aplic, hwirq, sourcecfg);
|
|
|
|
if (aplic_hwirq_is_delegated(aplic, hwirq)) {
|
|
sbi_printf("aplic_hwirq_setup: hwirq=%lu is delegated\n",
|
|
(unsigned long)hwirq);
|
|
return SBI_ENOTSUPP;
|
|
}
|
|
|
|
aplic_irq_clrie(aplic, hwirq);
|
|
aplic_irq_clrip(aplic, hwirq);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void aplic_hwirq_cleanup(struct sbi_irqchip_device *chip, u32 hwirq)
|
|
{
|
|
struct aplic_data *aplic;
|
|
|
|
aplic = container_of(chip, struct aplic_data, irqchip);
|
|
if (aplic_hwirq_is_delegated(aplic, hwirq))
|
|
return;
|
|
|
|
aplic_irq_clrie(aplic, hwirq);
|
|
aplic_irq_clrip(aplic, hwirq);
|
|
aplic_sourcecfg_write(aplic, hwirq, APLIC_SOURCECFG_SM_INACTIVE);
|
|
aplic_target_write(aplic, hwirq, APLIC_DEFAULT_PRIORITY);
|
|
}
|
|
|
|
static int aplic_hwirq_set_affinity(struct sbi_irqchip_device *chip,
|
|
u32 hwirq, u32 hart_index)
|
|
{
|
|
struct aplic_data *aplic;
|
|
int idc_index;
|
|
|
|
aplic = container_of(chip, struct aplic_data, irqchip);
|
|
if (aplic_hwirq_is_delegated(aplic, hwirq))
|
|
return SBI_ENOTSUPP;
|
|
|
|
idc_index = aplic_find_idc_index(aplic, hart_index);
|
|
if (idc_index < 0)
|
|
return SBI_EINVAL;
|
|
|
|
aplic_target_write(aplic, hwirq,
|
|
APLIC_TARGET_HART_IDX(hart_index) |
|
|
APLIC_TARGET_IPRIO(APLIC_DEFAULT_PRIORITY));
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void aplic_hwirq_mask(struct sbi_irqchip_device *chip, u32 hwirq)
|
|
{
|
|
struct aplic_data *aplic;
|
|
|
|
aplic = container_of(chip, struct aplic_data, irqchip);
|
|
if (aplic_hwirq_is_delegated(aplic, hwirq))
|
|
return;
|
|
|
|
aplic_irq_clrie(aplic, hwirq);
|
|
}
|
|
|
|
static void aplic_hwirq_unmask(struct sbi_irqchip_device *chip, u32 hwirq)
|
|
{
|
|
struct aplic_data *aplic;
|
|
|
|
aplic = container_of(chip, struct aplic_data, irqchip);
|
|
if (aplic_hwirq_is_delegated(aplic, hwirq))
|
|
return;
|
|
|
|
aplic_irq_setie(aplic, hwirq);
|
|
}
|
|
|
|
static struct sbi_irqchip_device aplic_irqchip_template = {
|
|
.warm_init = aplic_warm_init,
|
|
.process_hwirqs = aplic_process_hwirqs,
|
|
.hwirq_setup = aplic_hwirq_setup,
|
|
.hwirq_cleanup = aplic_hwirq_cleanup,
|
|
.hwirq_set_affinity = aplic_hwirq_set_affinity,
|
|
.hwirq_mask = aplic_hwirq_mask,
|
|
.hwirq_unmask = aplic_hwirq_unmask,
|
|
};
|
|
|
|
int aplic_cold_irqchip_init(struct aplic_data *aplic)
|
|
{
|
|
int rc;
|
|
struct aplic_delegate_data *deleg;
|
|
u32 first_deleg_irq, last_deleg_irq, i;
|
|
|
|
/* Sanity checks */
|
|
if (!aplic ||
|
|
!aplic->num_source || APLIC_MAX_SOURCE <= aplic->num_source ||
|
|
APLIC_MAX_IDC <= aplic->num_idc)
|
|
return SBI_EINVAL;
|
|
if (aplic->targets_mmode && aplic->has_msicfg_mmode) {
|
|
rc = aplic_check_msicfg(&aplic->msicfg_mmode);
|
|
if (rc)
|
|
return rc;
|
|
}
|
|
if (aplic->targets_mmode && aplic->has_msicfg_smode) {
|
|
rc = aplic_check_msicfg(&aplic->msicfg_smode);
|
|
if (rc)
|
|
return rc;
|
|
}
|
|
|
|
/* Init the APLIC registers */
|
|
aplic_init(aplic);
|
|
|
|
/*
|
|
* Add APLIC region to the root domain if:
|
|
* 1) It targets M-mode of any HART directly or via MSIs
|
|
* 2) All interrupts are delegated to some child APLIC
|
|
*/
|
|
first_deleg_irq = -1U;
|
|
last_deleg_irq = 0;
|
|
for (i = 0; i < APLIC_MAX_DELEGATE; i++) {
|
|
deleg = &aplic->delegate[i];
|
|
if (deleg->first_irq < first_deleg_irq)
|
|
first_deleg_irq = deleg->first_irq;
|
|
if (last_deleg_irq < deleg->last_irq)
|
|
last_deleg_irq = deleg->last_irq;
|
|
}
|
|
|
|
if (aplic->targets_mmode ||
|
|
((first_deleg_irq < last_deleg_irq) &&
|
|
(last_deleg_irq == aplic->num_source) &&
|
|
(first_deleg_irq == 1))) {
|
|
rc = sbi_domain_root_add_memrange(aplic->addr, aplic->size, PAGE_SIZE,
|
|
SBI_DOMAIN_MEMREGION_MMIO |
|
|
SBI_DOMAIN_MEMREGION_M_READABLE |
|
|
SBI_DOMAIN_MEMREGION_M_WRITABLE);
|
|
if (rc)
|
|
return rc;
|
|
}
|
|
|
|
if ((aplic->targets_mmode) && aplic_is_direct_mode(aplic)) {
|
|
aplic->irqchip = aplic_irqchip_template;
|
|
aplic->irqchip.id = aplic->unique_id;
|
|
aplic->irqchip.caps = SBI_IRQCHIP_CAPS_WIRED;
|
|
aplic->irqchip.num_hwirq = aplic->num_source + 1;
|
|
|
|
for (i = 0; i < aplic->num_idc; i++)
|
|
sbi_hartmask_set_hartindex(aplic->idc_map[i],
|
|
&aplic->irqchip.target_harts);
|
|
|
|
rc = sbi_irqchip_add_device(&aplic->irqchip);
|
|
if (rc) {
|
|
sbi_printf("aplic_cold_irqchip_init: sbi_irqchip_add_device failed rc=%d id=%lu mode=%s target_weight=%lu\n",
|
|
rc,
|
|
(unsigned long)aplic->irqchip.id,
|
|
"direct",
|
|
(unsigned long)sbi_hartmask_weight(
|
|
&aplic->irqchip.target_harts));
|
|
return rc;
|
|
}
|
|
}
|
|
/* Attach to the aplic list */
|
|
sbi_list_add_tail(&aplic->node, &aplic_list);
|
|
|
|
return 0;
|
|
}
|