#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) { irq_t irq; irq = valloc(sizeof(*irq)); *irq = (struct irq_object) { .type = type, .serve = callback ?: __default_servant, .vector = IRQ_VECTOR_UNSET }; 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, void* irq_spec) { int err = 0; if (domain->ops->map_irq) { err = domain->ops->map_irq(domain, irq, irq_spec); 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; }