summaryrefslogtreecommitdiff
path: root/usr/src/uts/common/io/cpqary3/cpqary3_interrupts.c
diff options
context:
space:
mode:
Diffstat (limited to 'usr/src/uts/common/io/cpqary3/cpqary3_interrupts.c')
-rw-r--r--usr/src/uts/common/io/cpqary3/cpqary3_interrupts.c162
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;
}