X-Git-Url: https://scm.lunaixsky.com/lunaix-os.git/blobdiff_plain/9daf4fcdae88f72af60aeb0c7722841af02233d4..836d44ecb7a2c37427f6baf8b25e872e9e943d5b:/lunaix-os/hal/irq.c diff --git a/lunaix-os/hal/irq.c b/lunaix-os/hal/irq.c new file mode 100644 index 0000000..b4bc0ec --- /dev/null +++ b/lunaix-os/hal/irq.c @@ -0,0 +1,195 @@ +#include +#include +#include + +static struct irq_domain* default_domain = NULL; +static DEFINE_LLIST(irq_domains); + +static void +__default_servant(irq_t irq, const struct hart_state* state) +{ + return; +} + +struct irq_domain* +irq_create_domain(struct device* intc_dev, const struct irq_domain_ops* ops) +{ + struct irq_domain* domain; + + assert(ops->install_irq); + + domain = new_potens(potens(INT_DOMAIN), struct irq_domain); + device_grant_potens(intc_dev, potens_meta(domain)); + + btrie_init(&domain->irq_map, ilog2(8)); + llist_append(&irq_domains, &domain->list); + domain->ops = ops; + + return domain; +} + +struct irq_domain* +irq_owning_domain(struct device* dev) +{ +#ifdef CONFIG_USE_DEVICETREE + struct device* intc; + struct dtn* intr_parent; + struct potens_meta* domain_m; + + intr_parent = dev->devtree_node->intr.parent; + intc = resolve_device_morph(dt_mobj(intr_parent)); + + if (!intc) { + return NULL; + } + + domain_m = device_get_potens(intc, potens(INT_DOMAIN)); + return !domain_m ?: get_potens(domain_m, struct irq_domain); +#else + return default_domain; +#endif +} + +int +irq_attach_domain(struct irq_domain* parent, struct irq_domain* child) +{ +#ifndef CONFIG_USE_DEVICETREE + parent = parent ?: default_domain; + child->parent = parent; +#endif + return 0; +} + +static void +__irq_create_line(irq_t irq, ptr_t local_int) +{ + struct irq_line_wire *line; + + line = valloc(sizeof(*line)); + line->domain_local = local_int; + line->domain_mapped = local_int; + + irq->line = line; +} + +static void +__irq_create_msi(irq_t irq, ptr_t message) +{ + struct irq_msi_wire *msi; + + msi = valloc(sizeof(*msi)); + msi->message = message; + + irq->msi = msi; +} + +irq_t +irq_declare(enum irq_type type, irq_servant callback, + ptr_t data, void* irq_extra) +{ + irq_t irq; + + irq = valloc(sizeof(*irq)); + *irq = (struct irq_object) { + .type = type, + .serve = callback ?: __default_servant, + .irq_extra = irq_extra + }; + + if (type == IRQ_LINE) { + __irq_create_line(irq, data); + } + + else if (type == IRQ_MESSAGE) { + __irq_create_msi(irq, data); + } + + return irq; +} + +void +irq_revoke(irq_t irq) +{ + if (--(irq->ref) > 0) { + return; + } + vfree(irq); +} + +int +irq_assign(struct irq_domain* domain, irq_t irq) +{ + int err = 0; + if (domain->ops->map_irq) { + err = domain->ops->map_irq(domain, irq, irq->irq_extra); + if (err) { + return err; + } + } + + /* + A domain controller may choose to forward the interrupt + (i.e., irq became transparent and belongs to the higher domain) + We allow controller decide whether to record the irq under its wing + */ + err = domain->ops->install_irq(domain, irq); + if (err) { + return err; + } + + irq->domain = domain; + return 0; +} + +irq_t +irq_find(struct irq_domain* domain, int local_irq) +{ + irq_t irq = NULL; + struct irq_domain* current; + + // Find recursively, in case of irq forwarding + + current = domain ?: default_domain; + while (current && !irq) { + irq = (irq_t)btrie_get(¤t->irq_map, local_irq); + current = irq_parent_domain(current); + } + + return irq; +} + +void +irq_record(struct irq_domain* domain, irq_t irq) +{ + irq->ref++; + btrie_set(&domain->irq_map, irq->vector, irq); +} + +void +irq_set_default_domain(struct irq_domain* domain) +{ + assert(!default_domain); + default_domain = domain; +} + +int +irq_forward_install(struct irq_domain* current, irq_t irq) +{ + struct irq_domain* parent; + + parent = irq_parent_domain(current); + + if (irq->type == IRQ_LINE) { + irq->line->domain_local = irq->line->domain_mapped; + } + + irq->domain = parent; + return parent->ops->install_irq(parent, irq); +} + +struct irq_domain* +irq_get_default_domain() +{ + assert(default_domain); + return default_domain; +}