diff options
Diffstat (limited to 'usr/src/uts/common/io/cpqary3/cpqary3_interrupts.c')
-rw-r--r-- | usr/src/uts/common/io/cpqary3/cpqary3_interrupts.c | 162 |
1 files changed, 129 insertions, 33 deletions
diff --git a/usr/src/uts/common/io/cpqary3/cpqary3_interrupts.c b/usr/src/uts/common/io/cpqary3/cpqary3_interrupts.c index 3ce89b1431..a6982ce254 100644 --- a/usr/src/uts/common/io/cpqary3/cpqary3_interrupts.c +++ b/usr/src/uts/common/io/cpqary3/cpqary3_interrupts.c @@ -15,11 +15,102 @@ #include "cpqary3.h" +static char * +cpqary3_interrupt_type_name(int type) +{ + switch (type) { + case DDI_INTR_TYPE_MSI: + return ("MSI"); + case DDI_INTR_TYPE_FIXED: + return ("fixed"); + default: + return ("?"); + } +} + +static int +cpqary3_interrupts_disable(cpqary3_t *cpq) +{ + if (cpq->cpq_interrupt_cap & DDI_INTR_FLAG_BLOCK) { + return (ddi_intr_block_disable(cpq->cpq_interrupts, + cpq->cpq_ninterrupts)); + } else { + VERIFY(cpq->cpq_ninterrupts == 0); + + return (ddi_intr_disable(cpq->cpq_interrupts[0])); + } +} + +static int +cpqary3_interrupts_enable(cpqary3_t *cpq) +{ + if (cpq->cpq_interrupt_cap & DDI_INTR_FLAG_BLOCK) { + return (ddi_intr_block_enable(cpq->cpq_interrupts, + cpq->cpq_ninterrupts)); + } else { + VERIFY(cpq->cpq_ninterrupts == 0); + + return (ddi_intr_enable(cpq->cpq_interrupts[0])); + } +} + +static void +cpqary3_interrupts_free(cpqary3_t *cpq) +{ + for (int i = 0; i < cpq->cpq_ninterrupts; i++) { + (void) ddi_intr_free(cpq->cpq_interrupts[i]); + } + cpq->cpq_ninterrupts = 0; + cpq->cpq_interrupt_type = 0; + cpq->cpq_interrupt_cap = 0; +} + +static int +cpqary3_interrupts_alloc(cpqary3_t *cpq, int type) +{ + int nintrs = 0; + int navail = 0; + + if (ddi_intr_get_nintrs(cpq->dip, type, &nintrs) != DDI_SUCCESS) { + dev_err(cpq->dip, CE_WARN, "could not count %s interrupts", + cpqary3_interrupt_type_name(type)); + return (DDI_FAILURE); + } + if (nintrs < 1) { + dev_err(cpq->dip, CE_WARN, "no %s interrupts supported", + cpqary3_interrupt_type_name(type)); + return (DDI_FAILURE); + } + + if (ddi_intr_get_navail(cpq->dip, type, &navail) != DDI_SUCCESS) { + dev_err(cpq->dip, CE_WARN, "could not count available %s " + "interrupts", cpqary3_interrupt_type_name(type)); + return (DDI_FAILURE); + } + if (navail < 1) { + dev_err(cpq->dip, CE_WARN, "no %s interrupts available", + cpqary3_interrupt_type_name(type)); + return (DDI_FAILURE); + } + + if (ddi_intr_alloc(cpq->dip, cpq->cpq_interrupts, + type, 0, 1, &cpq->cpq_ninterrupts, DDI_INTR_ALLOC_STRICT) != + DDI_SUCCESS) { + dev_err(cpq->dip, CE_WARN, "%s interrupt allocation failed", + cpqary3_interrupt_type_name(type)); + cpqary3_interrupts_free(cpq); + return (DDI_FAILURE); + } + + cpq->cpq_init_level |= CPQARY3_INITLEVEL_INT_ALLOC; + cpq->cpq_interrupt_type = type; + return (DDI_SUCCESS); +} + int cpqary3_interrupts_setup(cpqary3_t *cpq) { int types; - int nintrs = 0, navail = 0; unsigned ipri; uint_t (*hw_isr)(caddr_t, caddr_t); @@ -35,41 +126,39 @@ cpqary3_interrupts_setup(cpqary3_t *cpq) panic("unknown controller mode"); } - /* - * Ensure that at least one fixed interrupt is available to us. - */ if (ddi_intr_get_supported_types(cpq->dip, &types) != DDI_SUCCESS) { dev_err(cpq->dip, CE_WARN, "could not get support interrupts"); goto fail; } - if (!(types & DDI_INTR_TYPE_FIXED)) { - dev_err(cpq->dip, CE_WARN, "DDI_INTR_TYPE_FIXED not supported"); - goto fail; - } - if (ddi_intr_get_nintrs(cpq->dip, DDI_INTR_TYPE_FIXED, &nintrs) != - DDI_SUCCESS) { - dev_err(cpq->dip, CE_WARN, "could not count fixed interrupts"); - goto fail; - } - if (ddi_intr_get_navail(cpq->dip, DDI_INTR_TYPE_FIXED, &navail) != - DDI_SUCCESS || navail < 1) { - dev_err(cpq->dip, CE_WARN, "no fixed interrupts available"); - goto fail; + + /* + * Try for an MSI first. + */ + if (types & DDI_INTR_TYPE_MSI) { + if (cpqary3_interrupts_alloc(cpq, DDI_INTR_TYPE_MSI) == + DDI_SUCCESS) { + goto add_handler; + } } /* - * Set the flag first, as we are still expected to call ddi_intr_free() - * for a partial, but failed, allocation of interrupts. + * Otherwise, fall back to fixed interrupts. */ - cpq->cpq_init_level |= CPQARY3_INITLEVEL_INT_ALLOC; - if (ddi_intr_alloc(cpq->dip, cpq->cpq_interrupts, - DDI_INTR_TYPE_FIXED, 0, 1, &cpq->cpq_ninterrupts, - DDI_INTR_ALLOC_NORMAL) != DDI_SUCCESS) { - dev_err(cpq->dip, CE_WARN, "interrupt allocation failed"); - goto fail; + if (types & DDI_INTR_TYPE_FIXED) { + if (cpqary3_interrupts_alloc(cpq, DDI_INTR_TYPE_FIXED) == + DDI_SUCCESS) { + goto add_handler; + } } /* + * We were unable to allocate any interrupts. + */ + dev_err(cpq->dip, CE_WARN, "interrupt allocation failed"); + goto fail; + +add_handler: + /* * Ensure that we have not been given a high-level interrupt, as our * interrupt handlers do not support them. */ @@ -84,9 +173,17 @@ cpqary3_interrupts_setup(cpqary3_t *cpq) goto fail; } + if (ddi_intr_get_cap(cpq->cpq_interrupts[0], + &cpq->cpq_interrupt_cap) != DDI_SUCCESS) { + dev_err(cpq->dip, CE_WARN, "could not get %s interrupt cap", + cpqary3_interrupt_type_name(cpq->cpq_interrupt_type)); + goto fail; + } + if (ddi_intr_add_handler(cpq->cpq_interrupts[0], hw_isr, (caddr_t)cpq, NULL) != DDI_SUCCESS) { - dev_err(cpq->dip, CE_WARN, "adding interrupt failed"); + dev_err(cpq->dip, CE_WARN, "adding %s interrupt failed", + cpqary3_interrupt_type_name(cpq->cpq_interrupt_type)); goto fail; } cpq->cpq_init_level |= CPQARY3_INITLEVEL_INT_ADDED; @@ -94,8 +191,9 @@ cpqary3_interrupts_setup(cpqary3_t *cpq) /* * Enable the interrupt handler. */ - if (ddi_intr_enable(cpq->cpq_interrupts[0]) != DDI_SUCCESS) { - dev_err(cpq->dip, CE_WARN, "enable interrupt failed"); + if (cpqary3_interrupts_enable(cpq) != DDI_SUCCESS) { + dev_err(cpq->dip, CE_WARN, "enable %s interrupt failed", + cpqary3_interrupt_type_name(cpq->cpq_interrupt_type)); goto fail; } cpq->cpq_init_level |= CPQARY3_INITLEVEL_INT_ENABLED; @@ -111,7 +209,7 @@ void cpqary3_interrupts_teardown(cpqary3_t *cpq) { if (cpq->cpq_init_level & CPQARY3_INITLEVEL_INT_ENABLED) { - (void) ddi_intr_disable(cpq->cpq_interrupts[0]); + (void) cpqary3_interrupts_disable(cpq); cpq->cpq_init_level &= ~CPQARY3_INITLEVEL_INT_ENABLED; } @@ -119,13 +217,11 @@ cpqary3_interrupts_teardown(cpqary3_t *cpq) if (cpq->cpq_init_level & CPQARY3_INITLEVEL_INT_ADDED) { (void) ddi_intr_remove_handler(cpq->cpq_interrupts[0]); - cpq->cpq_init_level &= CPQARY3_INITLEVEL_INT_ADDED; + cpq->cpq_init_level &= ~CPQARY3_INITLEVEL_INT_ADDED; } if (cpq->cpq_init_level & CPQARY3_INITLEVEL_INT_ALLOC) { - for (int i = 0; i < cpq->cpq_ninterrupts; i++) { - (void) ddi_intr_free(cpq->cpq_interrupts[i]); - } + cpqary3_interrupts_free(cpq); cpq->cpq_init_level &= ~CPQARY3_INITLEVEL_INT_ALLOC; } |