mirror of
https://github.com/riscv-software-src/opensbi.git
synced 2026-07-27 00:11:30 +01:00
lib: utils/irqchip/aplic: implement direct mode irqchip callbacks
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>
This commit is contained in:
committed by
Anup Patel
parent
c504877ed0
commit
5ee0559445
+283
-25
@@ -70,6 +70,10 @@
|
||||
(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
|
||||
|
||||
@@ -93,6 +97,12 @@
|
||||
#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
|
||||
|
||||
@@ -119,10 +129,104 @@ 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;
|
||||
u32 i, j, tmp, domaincfg;
|
||||
int locked;
|
||||
|
||||
/* Set domain configuration to 0 */
|
||||
@@ -160,23 +264,19 @@ static void aplic_init(struct aplic_data *aplic)
|
||||
deleg->last_irq = tmp;
|
||||
}
|
||||
for (j = deleg->first_irq; j <= deleg->last_irq; j++)
|
||||
writel(APLIC_SOURCECFG_D | deleg->child_index,
|
||||
(void *)(aplic->addr + APLIC_SOURCECFG_BASE +
|
||||
(j - 1) * sizeof(u32)));
|
||||
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++) {
|
||||
writel(0, (void *)(aplic->addr + APLIC_IDC_BASE +
|
||||
i * APLIC_IDC_SIZE + APLIC_IDC_IDELIVERY));
|
||||
writel(0, (void *)(aplic->addr + APLIC_IDC_BASE +
|
||||
i * APLIC_IDC_SIZE + APLIC_IDC_IFORCE));
|
||||
writel(APLIC_DISABLE_ITHRESHOLD, (void *)(aplic->addr +
|
||||
APLIC_IDC_BASE +
|
||||
(i * APLIC_IDC_SIZE) +
|
||||
APLIC_IDC_ITHRESHOLD));
|
||||
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) {
|
||||
@@ -191,6 +291,11 @@ static void aplic_init(struct aplic_data *aplic)
|
||||
}
|
||||
}
|
||||
|
||||
domaincfg = APLIC_DOMAINCFG_IE;
|
||||
|
||||
writel(domaincfg, (void *)(aplic->addr + APLIC_DOMAINCFG));
|
||||
}
|
||||
|
||||
void aplic_reinit_all(void)
|
||||
{
|
||||
struct aplic_data *aplic;
|
||||
@@ -245,6 +350,154 @@ static int aplic_check_msicfg(struct aplic_msicfg_data *msicfg)
|
||||
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;
|
||||
@@ -297,22 +550,27 @@ int aplic_cold_irqchip_init(struct aplic_data *aplic)
|
||||
return rc;
|
||||
}
|
||||
|
||||
if (aplic->num_idc) {
|
||||
for (i = 0; i < aplic->num_idc; i++)
|
||||
sbi_hartmask_set_hartindex(aplic->idc_map[i],
|
||||
&aplic->irqchip.target_harts);
|
||||
} else {
|
||||
sbi_hartmask_set_all(&aplic->irqchip.target_harts);
|
||||
}
|
||||
|
||||
/* Register irqchip device */
|
||||
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;
|
||||
rc = sbi_irqchip_add_device(&aplic->irqchip);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
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);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user