diff --git a/include/sbi/sbi_irqchip.h b/include/sbi/sbi_irqchip.h index d2c47ae8..77b54110 100644 --- a/include/sbi/sbi_irqchip.h +++ b/include/sbi/sbi_irqchip.h @@ -18,12 +18,21 @@ struct sbi_scratch; /** irqchip hardware device */ struct sbi_irqchip_device { - /** Node in the list of irqchip devices */ + /** Node in the list of irqchip devices (private) */ struct sbi_dlist node; + /** Internal data of all hardware interrupts of this irqchip (private) */ + struct sbi_irqchip_hwirq_data *hwirqs; + + /** List of interrupt handlers */ + struct sbi_dlist handler_list; + /** Unique ID of this irqchip */ u32 id; + /** Number of hardware IRQs of this irqchip */ + u32 num_hwirq; + /** Set of harts targetted by this irqchip */ struct sbi_hartmask target_harts; @@ -32,6 +41,21 @@ struct sbi_irqchip_device { /** Process hardware interrupts from this irqchip */ int (*process_hwirqs)(struct sbi_irqchip_device *chip); + + /** Setup a hardware interrupt of this irqchip */ + int (*hwirq_setup)(struct sbi_irqchip_device *chip, u32 hwirq); + + /** Cleanup a hardware interrupt of this irqchip */ + void (*hwirq_cleanup)(struct sbi_irqchip_device *chip, u32 hwirq); + + /** End of hardware interrupt of this irqchip */ + void (*hwirq_eoi)(struct sbi_irqchip_device *chip, u32 hwirq); + + /** Mask a hardware interrupt of this irqchip */ + void (*hwirq_mask)(struct sbi_irqchip_device *chip, u32 hwirq); + + /** Unmask a hardware interrupt of this irqchip */ + void (*hwirq_unmask)(struct sbi_irqchip_device *chip, u32 hwirq); }; /** @@ -44,6 +68,36 @@ struct sbi_irqchip_device { */ int sbi_irqchip_process(void); +/** + * Process a hwirq of an irqchip device + * + * This function is called by irqchip drivers to handle hardware + * interrupts of the irqchip. + */ +int sbi_irqchip_process_hwirq(struct sbi_irqchip_device *chip, u32 hwirq); + +/** Unmask a hardware interrupt */ +int sbi_irqchip_unmask_hwirq(struct sbi_irqchip_device *chip, u32 hwirq); + +/** Mask a hardware interrupt */ +int sbi_irqchip_mask_hwirq(struct sbi_irqchip_device *chip, u32 hwirq); + +/** Default raw hardware interrupt handler */ +int sbi_irqchip_raw_handler_default(struct sbi_irqchip_device *chip, u32 hwirq); + +/** Set raw hardware interrupt handler */ +int sbi_irqchip_set_raw_handler(struct sbi_irqchip_device *chip, u32 hwirq, + int (*raw_hndl)(struct sbi_irqchip_device *, u32)); + +/** Register a hardware interrupt handler */ +int sbi_irqchip_register_handler(struct sbi_irqchip_device *chip, + u32 first_hwirq, u32 num_hwirq, + int (*callback)(u32 hwirq, void *opaque), void *opaque); + +/** Unregister a hardware interrupt handler */ +int sbi_irqchip_unregister_handler(struct sbi_irqchip_device *chip, + u32 first_hwirq, u32 num_hwirq); + /** Find an irqchip device based on unique ID */ struct sbi_irqchip_device *sbi_irqchip_find_device(u32 id); diff --git a/lib/sbi/sbi_irqchip.c b/lib/sbi/sbi_irqchip.c index 5df6189b..f0744830 100644 --- a/lib/sbi/sbi_irqchip.c +++ b/lib/sbi/sbi_irqchip.c @@ -7,11 +7,36 @@ * Anup Patel */ +#include #include #include #include #include +/** Internal irqchip hardware interrupt data */ +struct sbi_irqchip_hwirq_data { + /** raw hardware interrupt handler */ + int (*raw_handler)(struct sbi_irqchip_device *chip, u32 hwirq); +}; + +/** Internal irqchip interrupt handler */ +struct sbi_irqchip_handler { + /** Node in the list of irqchip handlers (private) */ + struct sbi_dlist node; + + /** First hardware IRQ handled by this handler */ + u32 first_hwirq; + + /** Number of consecutive hardware IRQs handled by this handler */ + u32 num_hwirq; + + /** Callback function of this handler */ + int (*callback)(u32 hwirq, void *priv); + + /** Callback private data */ + void *priv; +}; + struct sbi_irqchip_hart_data { struct sbi_irqchip_device *chip; }; @@ -30,6 +55,172 @@ int sbi_irqchip_process(void) return hd->chip->process_hwirqs(hd->chip); } +int sbi_irqchip_process_hwirq(struct sbi_irqchip_device *chip, u32 hwirq) +{ + struct sbi_irqchip_hwirq_data *data; + + if (!chip || chip->num_hwirq <= hwirq) + return SBI_EINVAL; + + data = &chip->hwirqs[hwirq]; + if (!data->raw_handler) + return SBI_ENOENT; + + return data->raw_handler(chip, hwirq); +} + +int sbi_irqchip_unmask_hwirq(struct sbi_irqchip_device *chip, u32 hwirq) +{ + if (!chip || chip->num_hwirq <= hwirq) + return SBI_EINVAL; + + if (chip->hwirq_unmask) + chip->hwirq_unmask(chip, hwirq); + return 0; +} + +int sbi_irqchip_mask_hwirq(struct sbi_irqchip_device *chip, u32 hwirq) +{ + if (!chip || chip->num_hwirq <= hwirq) + return SBI_EINVAL; + + if (chip->hwirq_mask) + chip->hwirq_mask(chip, hwirq); + return 0; +} + +static struct sbi_irqchip_handler *sbi_irqchip_find_handler(struct sbi_irqchip_device *chip, + u32 hwirq) +{ + struct sbi_irqchip_handler *h; + + if (!chip || chip->num_hwirq <= hwirq) + return NULL; + + sbi_list_for_each_entry(h, &chip->handler_list, node) { + if (h->first_hwirq <= hwirq && hwirq < (h->first_hwirq + h->num_hwirq)) + return h; + } + + return NULL; +} + +int sbi_irqchip_raw_handler_default(struct sbi_irqchip_device *chip, u32 hwirq) +{ + struct sbi_irqchip_handler *h; + int rc; + + if (!chip || chip->num_hwirq <= hwirq) + return SBI_EINVAL; + + h = sbi_irqchip_find_handler(chip, hwirq); + rc = h->callback(hwirq, h->priv); + + if (chip->hwirq_eoi) + chip->hwirq_eoi(chip, hwirq); + + return rc; +} + +int sbi_irqchip_set_raw_handler(struct sbi_irqchip_device *chip, u32 hwirq, + int (*raw_hndl)(struct sbi_irqchip_device *, u32)) +{ + struct sbi_irqchip_hwirq_data *data; + + if (!chip || chip->num_hwirq <= hwirq) + return SBI_EINVAL; + + data = &chip->hwirqs[hwirq]; + data->raw_handler = raw_hndl; + return 0; +} + +int sbi_irqchip_register_handler(struct sbi_irqchip_device *chip, + u32 first_hwirq, u32 num_hwirq, + int (*callback)(u32 hwirq, void *opaque), void *priv) +{ + struct sbi_irqchip_handler *h; + u32 i, j; + int rc; + + if (!chip || !num_hwirq || !callback) + return SBI_EINVAL; + if (chip->num_hwirq <= first_hwirq || + chip->num_hwirq <= (first_hwirq + num_hwirq - 1)) + return SBI_EBAD_RANGE; + + h = sbi_irqchip_find_handler(chip, first_hwirq); + if (h) + return SBI_EALREADY; + h = sbi_irqchip_find_handler(chip, first_hwirq + num_hwirq - 1); + if (h) + return SBI_EALREADY; + + h = sbi_zalloc(sizeof(*h)); + if (!h) + return SBI_ENOMEM; + h->first_hwirq = first_hwirq; + h->num_hwirq = num_hwirq; + h->callback = callback; + h->priv = priv; + sbi_list_add_tail(&h->node, &chip->handler_list); + + if (chip->hwirq_setup) { + for (i = 0; i < h->num_hwirq; i++) { + rc = chip->hwirq_setup(chip, h->first_hwirq + i); + if (rc) { + if (chip->hwirq_cleanup) { + for (j = 0; j < i; j++) + chip->hwirq_cleanup(chip, h->first_hwirq + j); + } + sbi_list_del(&h->node); + sbi_free(h); + return rc; + } + } + } + + if (chip->hwirq_unmask) { + for (i = 0; i < h->num_hwirq; i++) + chip->hwirq_unmask(chip, h->first_hwirq + i); + } + + return 0; +} + +int sbi_irqchip_unregister_handler(struct sbi_irqchip_device *chip, + u32 first_hwirq, u32 num_hwirq) +{ + struct sbi_irqchip_handler *fh, *lh; + u32 i; + + if (!chip || !num_hwirq) + return SBI_EINVAL; + if (chip->num_hwirq <= first_hwirq || + chip->num_hwirq <= (first_hwirq + num_hwirq - 1)) + return SBI_EBAD_RANGE; + + fh = sbi_irqchip_find_handler(chip, first_hwirq); + if (!fh || fh->first_hwirq != first_hwirq || fh->num_hwirq != num_hwirq) + return SBI_ENODEV; + lh = sbi_irqchip_find_handler(chip, first_hwirq + num_hwirq - 1); + if (!lh || lh != fh) + return SBI_ENODEV; + + if (chip->hwirq_mask) { + for (i = 0; i < fh->num_hwirq; i++) + chip->hwirq_mask(chip, fh->first_hwirq + i); + } + + if (chip->hwirq_cleanup) { + for (i = 0; i < fh->num_hwirq; i++) + chip->hwirq_cleanup(chip, fh->first_hwirq + i); + } + + sbi_list_del(&fh->node); + return 0; +} + struct sbi_irqchip_device *sbi_irqchip_find_device(u32 id) { struct sbi_irqchip_device *chip; @@ -46,9 +237,9 @@ int sbi_irqchip_add_device(struct sbi_irqchip_device *chip) { struct sbi_irqchip_hart_data *hd; struct sbi_scratch *scratch; - u32 h; + u32 i, h; - if (!chip || !sbi_hartmask_weight(&chip->target_harts)) + if (!chip || !chip->num_hwirq || !sbi_hartmask_weight(&chip->target_harts)) return SBI_EINVAL; if (sbi_irqchip_find_device(chip->id)) @@ -68,6 +259,14 @@ int sbi_irqchip_add_device(struct sbi_irqchip_device *chip) } } + chip->hwirqs = sbi_zalloc(sizeof(*chip->hwirqs) * chip->num_hwirq); + if (!chip->hwirqs) + return SBI_ENOMEM; + for (i = 0; i < chip->num_hwirq; i++) + sbi_irqchip_set_raw_handler(chip, i, sbi_irqchip_raw_handler_default); + + SBI_INIT_LIST_HEAD(&chip->handler_list); + sbi_list_add_tail(&chip->node, &irqchip_list); return 0; } diff --git a/lib/utils/irqchip/aplic.c b/lib/utils/irqchip/aplic.c index d47a810b..ec69c82b 100644 --- a/lib/utils/irqchip/aplic.c +++ b/lib/utils/irqchip/aplic.c @@ -307,6 +307,7 @@ int aplic_cold_irqchip_init(struct aplic_data *aplic) /* Register irqchip device */ aplic->irqchip.id = aplic->unique_id; + aplic->irqchip.num_hwirq = aplic->num_source + 1; rc = sbi_irqchip_add_device(&aplic->irqchip); if (rc) return rc; diff --git a/lib/utils/irqchip/imsic.c b/lib/utils/irqchip/imsic.c index 0f296c89..7559a069 100644 --- a/lib/utils/irqchip/imsic.c +++ b/lib/utils/irqchip/imsic.c @@ -346,9 +346,17 @@ int imsic_data_check(struct imsic_data *imsic) return 0; } +static int imsic_hwirq_setup(struct sbi_irqchip_device *chip, u32 hwirq) +{ + if (!hwirq || hwirq == IMSIC_IPI_ID) + return SBI_ENOTSUPP; + return 0; +} + static struct sbi_irqchip_device imsic_device = { .warm_init = imsic_warm_irqchip_init, .process_hwirqs = imsic_process_hwirqs, + .hwirq_setup = imsic_hwirq_setup, }; int imsic_cold_irqchip_init(struct imsic_data *imsic) @@ -392,6 +400,7 @@ int imsic_cold_irqchip_init(struct imsic_data *imsic) /* Register irqchip device */ imsic_device.id = imsic->unique_id; + imsic_device.num_hwirq = imsic->num_ids + 1; sbi_hartmask_set_all(&imsic_device.target_harts); rc = sbi_irqchip_add_device(&imsic_device); if (rc) diff --git a/lib/utils/irqchip/plic.c b/lib/utils/irqchip/plic.c index 973f7c2a..2d721724 100644 --- a/lib/utils/irqchip/plic.c +++ b/lib/utils/irqchip/plic.c @@ -281,6 +281,7 @@ int plic_cold_irqchip_init(struct plic_data *plic) /* Register irqchip device */ plic->irqchip.id = plic->unique_id; + plic->irqchip.num_hwirq = plic->num_src + 1; plic->irqchip.warm_init = plic_warm_irqchip_init; return sbi_irqchip_add_device(&plic->irqchip); }