437 lines
11 KiB
C
437 lines
11 KiB
C
/*-
|
|
* Copyright (c) 2018 Microsemi Corporation.
|
|
* All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
* SUCH DAMAGE.
|
|
*/
|
|
|
|
/* $FreeBSD$ */
|
|
|
|
#include "smartpqi_includes.h"
|
|
|
|
/*
|
|
* Function to get processor count
|
|
*/
|
|
int os_get_processor_config(pqisrc_softstate_t *softs)
|
|
{
|
|
DBG_FUNC("IN\n");
|
|
softs->num_cpus_online = mp_ncpus;
|
|
DBG_FUNC("OUT\n");
|
|
|
|
return PQI_STATUS_SUCCESS;
|
|
}
|
|
|
|
/*
|
|
* Function to get interrupt count and type supported
|
|
*/
|
|
int os_get_intr_config(pqisrc_softstate_t *softs)
|
|
{
|
|
device_t dev;
|
|
int msi_count = 0;
|
|
int error = 0;
|
|
int ret = PQI_STATUS_SUCCESS;
|
|
dev = softs->os_specific.pqi_dev;
|
|
|
|
DBG_FUNC("IN\n");
|
|
|
|
msi_count = pci_msix_count(dev);
|
|
|
|
if (msi_count > softs->num_cpus_online)
|
|
msi_count = softs->num_cpus_online;
|
|
if (msi_count > PQI_MAX_MSIX)
|
|
msi_count = PQI_MAX_MSIX;
|
|
if (msi_count == 0 || (error = pci_alloc_msix(dev, &msi_count)) != 0) {
|
|
device_printf(dev, "alloc msix failed - msi_count=%d, err=%d; "
|
|
"will try MSI\n", msi_count, error);
|
|
pci_release_msi(dev);
|
|
} else {
|
|
softs->intr_count = msi_count;
|
|
softs->intr_type = INTR_TYPE_MSIX;
|
|
softs->os_specific.msi_enabled = TRUE;
|
|
device_printf(dev, "using MSI-X interrupts (%d vectors)\n",
|
|
msi_count);
|
|
}
|
|
if (!softs->intr_type) {
|
|
msi_count = 1;
|
|
if ((error = pci_alloc_msi(dev, &msi_count)) != 0) {
|
|
device_printf(dev, "alloc msi failed - err=%d; "
|
|
"will use INTx\n", error);
|
|
pci_release_msi(dev);
|
|
} else {
|
|
softs->os_specific.msi_enabled = TRUE;
|
|
softs->intr_count = msi_count;
|
|
softs->intr_type = INTR_TYPE_MSI;
|
|
device_printf(dev, "using MSI interrupts\n");
|
|
}
|
|
}
|
|
|
|
if (!softs->intr_type) {
|
|
device_printf(dev, "using legacy interrupts\n");
|
|
softs->intr_type = INTR_TYPE_FIXED;
|
|
softs->intr_count = 1;
|
|
}
|
|
|
|
if(!softs->intr_type) {
|
|
DBG_FUNC("OUT failed\n");
|
|
ret = PQI_STATUS_FAILURE;
|
|
return ret;
|
|
}
|
|
DBG_FUNC("OUT\n");
|
|
return ret;
|
|
}
|
|
|
|
void os_eventtaskqueue_enqueue(pqisrc_softstate_t *sc)
|
|
{
|
|
taskqueue_enqueue(taskqueue_swi, &sc->os_specific.event_task);
|
|
}
|
|
|
|
void pqisrc_event_worker(void *arg1, int arg2)
|
|
{
|
|
pqisrc_ack_all_events(arg1);
|
|
}
|
|
|
|
/*
|
|
* ithread routine to handle uniprocessor systems
|
|
*/
|
|
static void shared_ithread_routine(void *arg)
|
|
{
|
|
pqi_intr_ctx_t *intr_ctx = (pqi_intr_ctx_t *)arg;
|
|
pqisrc_softstate_t *softs = device_get_softc(intr_ctx->pqi_dev);
|
|
int oq_id = intr_ctx->oq_id;
|
|
|
|
DBG_FUNC("IN\n");
|
|
|
|
pqisrc_process_response_queue(softs, oq_id);
|
|
pqisrc_process_event_intr_src(softs, oq_id - 1);
|
|
|
|
DBG_FUNC("OUT\n");
|
|
}
|
|
|
|
/*
|
|
* ithread routine to process non event response
|
|
*/
|
|
static void common_ithread_routine(void *arg)
|
|
{
|
|
pqi_intr_ctx_t *intr_ctx = (pqi_intr_ctx_t *)arg;
|
|
pqisrc_softstate_t *softs = device_get_softc(intr_ctx->pqi_dev);
|
|
int oq_id = intr_ctx->oq_id;
|
|
|
|
DBG_FUNC("IN\n");
|
|
|
|
pqisrc_process_response_queue(softs, oq_id);
|
|
|
|
DBG_FUNC("OUT\n");
|
|
}
|
|
|
|
static void event_ithread_routine(void *arg)
|
|
{
|
|
pqi_intr_ctx_t *intr_ctx = (pqi_intr_ctx_t *)arg;
|
|
pqisrc_softstate_t *softs = device_get_softc(intr_ctx->pqi_dev);
|
|
int oq_id = intr_ctx->oq_id;
|
|
|
|
DBG_FUNC("IN\n");
|
|
|
|
pqisrc_process_event_intr_src(softs, oq_id);
|
|
|
|
DBG_FUNC("OUT\n");
|
|
}
|
|
|
|
/*
|
|
* Registration of legacy interrupt in case MSI is unsupported
|
|
*/
|
|
int register_legacy_intr(pqisrc_softstate_t *softs)
|
|
{
|
|
int error = 0;
|
|
device_t dev;
|
|
|
|
DBG_FUNC("IN\n");
|
|
|
|
dev = softs->os_specific.pqi_dev;
|
|
|
|
softs->os_specific.pqi_irq_rid[0] = 0;
|
|
softs->os_specific.pqi_irq[0] = bus_alloc_resource_any(dev, \
|
|
SYS_RES_IRQ, &softs->os_specific.pqi_irq_rid[0],
|
|
RF_ACTIVE | RF_SHAREABLE);
|
|
if (NULL == softs->os_specific.pqi_irq[0]) {
|
|
DBG_ERR("Failed to allocate resource for interrupt\n");
|
|
return PQI_STATUS_FAILURE;
|
|
}
|
|
if ((softs->os_specific.msi_ctx = os_mem_alloc(softs,sizeof(pqi_intr_ctx_t))) == NULL) {
|
|
DBG_ERR("Failed to allocate memory for msi_ctx\n");
|
|
return PQI_STATUS_FAILURE;
|
|
}
|
|
softs->os_specific.msi_ctx[0].pqi_dev = dev;
|
|
softs->os_specific.msi_ctx[0].oq_id = 1;
|
|
|
|
error = bus_setup_intr(dev, softs->os_specific.pqi_irq[0],
|
|
INTR_TYPE_CAM | INTR_MPSAFE, \
|
|
NULL, shared_ithread_routine,
|
|
&softs->os_specific.msi_ctx[0],
|
|
&softs->os_specific.intrcookie[0]);
|
|
if (error) {
|
|
DBG_ERR("Failed to setup legacy interrupt err = %d\n", error);
|
|
return error;
|
|
}
|
|
softs->os_specific.intr_registered[0] = TRUE;
|
|
|
|
DBG_FUNC("OUT error = %d\n", error);
|
|
|
|
return error;
|
|
}
|
|
|
|
/*
|
|
* Registration of MSIx
|
|
*/
|
|
int register_msix_intr(pqisrc_softstate_t *softs)
|
|
{
|
|
int error = 0;
|
|
int i = 0;
|
|
device_t dev;
|
|
dev = softs->os_specific.pqi_dev;
|
|
int msix_count = softs->intr_count;
|
|
|
|
DBG_FUNC("IN\n");
|
|
|
|
softs->os_specific.msi_ctx = os_mem_alloc(softs, sizeof(pqi_intr_ctx_t) * msix_count);
|
|
/*Add shared handler */
|
|
if (softs->share_opq_and_eventq) {
|
|
softs->os_specific.pqi_irq_rid[i] = i+1;
|
|
softs->os_specific.pqi_irq[i] = bus_alloc_resource_any(dev, \
|
|
SYS_RES_IRQ,
|
|
&softs->os_specific.pqi_irq_rid[i],
|
|
RF_SHAREABLE | RF_ACTIVE);
|
|
if (NULL == softs->os_specific.pqi_irq[i]) {
|
|
DBG_ERR("Failed to allocate \
|
|
event interrupt resource\n");
|
|
return PQI_STATUS_FAILURE;
|
|
}
|
|
|
|
softs->os_specific.msi_ctx[i].pqi_dev = dev;
|
|
softs->os_specific.msi_ctx[i].oq_id = i+1;
|
|
|
|
error = bus_setup_intr(dev,softs->os_specific.pqi_irq[i],
|
|
INTR_TYPE_CAM | INTR_MPSAFE,\
|
|
NULL,
|
|
shared_ithread_routine,
|
|
&softs->os_specific.msi_ctx[i],
|
|
&softs->os_specific.intrcookie[i]);
|
|
|
|
if (error) {
|
|
DBG_ERR("Failed to setup interrupt for events r=%d\n",
|
|
error);
|
|
return error;
|
|
}
|
|
softs->os_specific.intr_registered[i] = TRUE;
|
|
}
|
|
else {
|
|
/* Add event handler */
|
|
softs->os_specific.pqi_irq_rid[i] = i+1;
|
|
softs->os_specific.pqi_irq[i] = bus_alloc_resource_any(dev, \
|
|
SYS_RES_IRQ,
|
|
&softs->os_specific.pqi_irq_rid[i],
|
|
RF_SHAREABLE | RF_ACTIVE);
|
|
if (NULL == softs->os_specific.pqi_irq[i]) {
|
|
DBG_ERR("ERR : Failed to allocate \
|
|
event interrupt resource\n");
|
|
return PQI_STATUS_FAILURE;
|
|
}
|
|
|
|
|
|
softs->os_specific.msi_ctx[i].pqi_dev = dev;
|
|
softs->os_specific.msi_ctx[i].oq_id = i;
|
|
|
|
|
|
error = bus_setup_intr(dev,softs->os_specific.pqi_irq[i],
|
|
INTR_TYPE_CAM | INTR_MPSAFE,\
|
|
NULL,
|
|
event_ithread_routine,
|
|
&softs->os_specific.msi_ctx[i],
|
|
&softs->os_specific.intrcookie[i]);
|
|
if (error) {
|
|
DBG_ERR("Failed to setup interrupt for events err=%d\n",
|
|
error);
|
|
return error;
|
|
}
|
|
softs->os_specific.intr_registered[i] = TRUE;
|
|
/* Add interrupt handlers*/
|
|
for (i = 1; i < msix_count; ++i) {
|
|
softs->os_specific.pqi_irq_rid[i] = i+1;
|
|
softs->os_specific.pqi_irq[i] = \
|
|
bus_alloc_resource_any(dev,
|
|
SYS_RES_IRQ,
|
|
&softs->os_specific.pqi_irq_rid[i],
|
|
RF_SHAREABLE | RF_ACTIVE);
|
|
if (NULL == softs->os_specific.pqi_irq[i]) {
|
|
DBG_ERR("Failed to allocate \
|
|
msi/x interrupt resource\n");
|
|
return PQI_STATUS_FAILURE;
|
|
}
|
|
softs->os_specific.msi_ctx[i].pqi_dev = dev;
|
|
softs->os_specific.msi_ctx[i].oq_id = i;
|
|
error = bus_setup_intr(dev,
|
|
softs->os_specific.pqi_irq[i],
|
|
INTR_TYPE_CAM | INTR_MPSAFE,\
|
|
NULL,
|
|
common_ithread_routine,
|
|
&softs->os_specific.msi_ctx[i],
|
|
&softs->os_specific.intrcookie[i]);
|
|
if (error) {
|
|
DBG_ERR("Failed to setup \
|
|
msi/x interrupt error = %d\n", error);
|
|
return error;
|
|
}
|
|
softs->os_specific.intr_registered[i] = TRUE;
|
|
}
|
|
}
|
|
|
|
DBG_FUNC("OUT error = %d\n", error);
|
|
|
|
return error;
|
|
}
|
|
|
|
/*
|
|
* Setup interrupt depending on the configuration
|
|
*/
|
|
int os_setup_intr(pqisrc_softstate_t *softs)
|
|
{
|
|
int error = 0;
|
|
|
|
DBG_FUNC("IN\n");
|
|
|
|
if (softs->intr_type == INTR_TYPE_FIXED) {
|
|
error = register_legacy_intr(softs);
|
|
}
|
|
else {
|
|
error = register_msix_intr(softs);
|
|
}
|
|
if (error) {
|
|
DBG_FUNC("OUT failed error = %d\n", error);
|
|
return error;
|
|
}
|
|
|
|
DBG_FUNC("OUT error = %d\n", error);
|
|
|
|
return error;
|
|
}
|
|
|
|
/*
|
|
* Deregistration of legacy interrupt
|
|
*/
|
|
void deregister_pqi_intx(pqisrc_softstate_t *softs)
|
|
{
|
|
device_t dev;
|
|
|
|
DBG_FUNC("IN\n");
|
|
|
|
dev = softs->os_specific.pqi_dev;
|
|
if (softs->os_specific.pqi_irq[0] != NULL) {
|
|
if (softs->os_specific.intr_registered[0]) {
|
|
bus_teardown_intr(dev, softs->os_specific.pqi_irq[0],
|
|
softs->os_specific.intrcookie[0]);
|
|
softs->os_specific.intr_registered[0] = FALSE;
|
|
}
|
|
bus_release_resource(dev, SYS_RES_IRQ,
|
|
softs->os_specific.pqi_irq_rid[0],
|
|
softs->os_specific.pqi_irq[0]);
|
|
softs->os_specific.pqi_irq[0] = NULL;
|
|
os_mem_free(softs, (char*)softs->os_specific.msi_ctx, sizeof(pqi_intr_ctx_t));
|
|
}
|
|
|
|
DBG_FUNC("OUT\n");
|
|
}
|
|
|
|
/*
|
|
* Deregistration of MSIx interrupt
|
|
*/
|
|
void deregister_pqi_msix(pqisrc_softstate_t *softs)
|
|
{
|
|
device_t dev;
|
|
dev = softs->os_specific.pqi_dev;
|
|
int msix_count = softs->intr_count;
|
|
int i = 0;
|
|
|
|
DBG_FUNC("IN\n");
|
|
|
|
os_mem_free(softs, (char*)softs->os_specific.msi_ctx, sizeof(pqi_intr_ctx_t) * msix_count);
|
|
softs->os_specific.msi_ctx = NULL;
|
|
|
|
for (; i < msix_count; ++i) {
|
|
if (softs->os_specific.pqi_irq[i] != NULL) {
|
|
if (softs->os_specific.intr_registered[i]) {
|
|
bus_teardown_intr(dev,
|
|
softs->os_specific.pqi_irq[i],
|
|
softs->os_specific.intrcookie[i]);
|
|
softs->os_specific.intr_registered[i] = FALSE;
|
|
}
|
|
bus_release_resource(dev, SYS_RES_IRQ,
|
|
softs->os_specific.pqi_irq_rid[i],
|
|
softs->os_specific.pqi_irq[i]);
|
|
softs->os_specific.pqi_irq[i] = NULL;
|
|
}
|
|
}
|
|
|
|
DBG_FUNC("OUT\n");
|
|
}
|
|
|
|
/*
|
|
* Function to destroy interrupts registered
|
|
*/
|
|
int os_destroy_intr(pqisrc_softstate_t *softs)
|
|
{
|
|
device_t dev;
|
|
dev = softs->os_specific.pqi_dev;
|
|
|
|
DBG_FUNC("IN\n");
|
|
|
|
if (softs->intr_type == INTR_TYPE_FIXED) {
|
|
deregister_pqi_intx(softs);
|
|
} else if (softs->intr_type == INTR_TYPE_MSIX) {
|
|
deregister_pqi_msix(softs);
|
|
}
|
|
if (softs->os_specific.msi_enabled) {
|
|
pci_release_msi(dev);
|
|
softs->os_specific.msi_enabled = FALSE;
|
|
}
|
|
|
|
DBG_FUNC("OUT\n");
|
|
|
|
return PQI_STATUS_SUCCESS;
|
|
}
|
|
|
|
/*
|
|
* Free interrupt related resources for the adapter
|
|
*/
|
|
void os_free_intr_config(pqisrc_softstate_t *softs)
|
|
{
|
|
device_t dev;
|
|
dev = softs->os_specific.pqi_dev;
|
|
|
|
DBG_FUNC("IN\n");
|
|
|
|
if (softs->os_specific.msi_enabled) {
|
|
pci_release_msi(dev);
|
|
softs->os_specific.msi_enabled = FALSE;
|
|
}
|
|
|
|
DBG_FUNC("OUT\n");
|
|
}
|