2015-05-29 01:04:01 -07:00
/*
* Copyright 2014 Cisco Systems , Inc . All rights reserved .
*
* This program is free software ; you may redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation ; version 2 of the License .
*
* THE SOFTWARE IS PROVIDED " AS IS " , WITHOUT WARRANTY OF ANY KIND ,
* EXPRESS OR IMPLIED , INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY , FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT . IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM , DAMAGES OR OTHER LIABILITY , WHETHER IN AN
* ACTION OF CONTRACT , TORT OR OTHERWISE , ARISING FROM , OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE .
*/
# include <linux/string.h>
# include <linux/errno.h>
# include <linux/pci.h>
# include <linux/interrupt.h>
# include "vnic_dev.h"
# include "vnic_intr.h"
# include "vnic_stats.h"
# include "snic_io.h"
# include "snic.h"
/*
* snic_isr_msix_wq : MSIx ISR for work queue .
*/
static irqreturn_t
snic_isr_msix_wq ( int irq , void * data )
{
struct snic * snic = data ;
unsigned long wq_work_done = 0 ;
snic - > s_stats . misc . last_isr_time = jiffies ;
2016-03-17 00:51:10 -07:00
atomic64_inc ( & snic - > s_stats . misc . ack_isr_cnt ) ;
2015-05-29 01:04:01 -07:00
wq_work_done = snic_wq_cmpl_handler ( snic , - 1 ) ;
svnic_intr_return_credits ( & snic - > intr [ SNIC_MSIX_WQ ] ,
wq_work_done ,
1 /* unmask intr */ ,
1 /* reset intr timer */ ) ;
return IRQ_HANDLED ;
} /* end of snic_isr_msix_wq */
static irqreturn_t
snic_isr_msix_io_cmpl ( int irq , void * data )
{
struct snic * snic = data ;
unsigned long iocmpl_work_done = 0 ;
snic - > s_stats . misc . last_isr_time = jiffies ;
2016-03-17 00:51:10 -07:00
atomic64_inc ( & snic - > s_stats . misc . cmpl_isr_cnt ) ;
2015-05-29 01:04:01 -07:00
iocmpl_work_done = snic_fwcq_cmpl_handler ( snic , - 1 ) ;
svnic_intr_return_credits ( & snic - > intr [ SNIC_MSIX_IO_CMPL ] ,
iocmpl_work_done ,
1 /* unmask intr */ ,
1 /* reset intr timer */ ) ;
return IRQ_HANDLED ;
} /* end of snic_isr_msix_io_cmpl */
static irqreturn_t
snic_isr_msix_err_notify ( int irq , void * data )
{
struct snic * snic = data ;
snic - > s_stats . misc . last_isr_time = jiffies ;
2016-03-17 00:51:10 -07:00
atomic64_inc ( & snic - > s_stats . misc . errnotify_isr_cnt ) ;
2015-05-29 01:04:01 -07:00
svnic_intr_return_all_credits ( & snic - > intr [ SNIC_MSIX_ERR_NOTIFY ] ) ;
snic_log_q_error ( snic ) ;
/*Handling link events */
snic_handle_link_event ( snic ) ;
return IRQ_HANDLED ;
} /* end of snic_isr_msix_err_notify */
void
snic_free_intr ( struct snic * snic )
{
int i ;
/* ONLY interrupt mode MSIX is supported */
for ( i = 0 ; i < ARRAY_SIZE ( snic - > msix ) ; i + + ) {
if ( snic - > msix [ i ] . requested ) {
2017-02-01 15:21:21 +01:00
free_irq ( pci_irq_vector ( snic - > pdev , i ) ,
2015-05-29 01:04:01 -07:00
snic - > msix [ i ] . devid ) ;
}
}
} /* end of snic_free_intr */
int
snic_request_intr ( struct snic * snic )
{
int ret = 0 , i ;
enum vnic_dev_intr_mode intr_mode ;
intr_mode = svnic_dev_get_intr_mode ( snic - > vdev ) ;
SNIC_BUG_ON ( intr_mode ! = VNIC_DEV_INTR_MODE_MSIX ) ;
/*
* Currently HW supports single WQ and CQ . So passing devid as snic .
* When hardware supports multiple WQs and CQs , one idea is
* to pass devid as corresponding WQ or CQ ptr and retrieve snic
* from queue ptr .
* Except for err_notify , which is always one .
*/
sprintf ( snic - > msix [ SNIC_MSIX_WQ ] . devname ,
" %.11s-scsi-wq " ,
snic - > name ) ;
snic - > msix [ SNIC_MSIX_WQ ] . isr = snic_isr_msix_wq ;
snic - > msix [ SNIC_MSIX_WQ ] . devid = snic ;
sprintf ( snic - > msix [ SNIC_MSIX_IO_CMPL ] . devname ,
" %.11s-io-cmpl " ,
snic - > name ) ;
snic - > msix [ SNIC_MSIX_IO_CMPL ] . isr = snic_isr_msix_io_cmpl ;
snic - > msix [ SNIC_MSIX_IO_CMPL ] . devid = snic ;
sprintf ( snic - > msix [ SNIC_MSIX_ERR_NOTIFY ] . devname ,
" %.11s-err-notify " ,
snic - > name ) ;
snic - > msix [ SNIC_MSIX_ERR_NOTIFY ] . isr = snic_isr_msix_err_notify ;
snic - > msix [ SNIC_MSIX_ERR_NOTIFY ] . devid = snic ;
for ( i = 0 ; i < ARRAY_SIZE ( snic - > msix ) ; i + + ) {
2017-02-01 15:21:21 +01:00
ret = request_irq ( pci_irq_vector ( snic - > pdev , i ) ,
2015-05-29 01:04:01 -07:00
snic - > msix [ i ] . isr ,
0 ,
snic - > msix [ i ] . devname ,
snic - > msix [ i ] . devid ) ;
if ( ret ) {
SNIC_HOST_ERR ( snic - > shost ,
2017-06-30 14:59:50 +01:00
" MSI-X: request_irq(%d) failed %d \n " ,
2015-05-29 01:04:01 -07:00
i ,
ret ) ;
snic_free_intr ( snic ) ;
break ;
}
snic - > msix [ i ] . requested = 1 ;
}
return ret ;
2017-06-30 14:59:50 +01:00
} /* end of snic_request_intr */
2015-05-29 01:04:01 -07:00
int
snic_set_intr_mode ( struct snic * snic )
{
unsigned int n = ARRAY_SIZE ( snic - > wq ) ;
unsigned int m = SNIC_CQ_IO_CMPL_MAX ;
2017-02-01 15:21:21 +01:00
unsigned int vecs = n + m + 1 ;
2015-05-29 01:04:01 -07:00
/*
* We need n WQs , m CQs , and n + m + 1 INTRs
* ( last INTR is used for WQ / CQ errors and notification area
*/
BUILD_BUG_ON ( ( ARRAY_SIZE ( snic - > wq ) + SNIC_CQ_IO_CMPL_MAX ) >
ARRAY_SIZE ( snic - > intr ) ) ;
2017-02-01 15:21:21 +01:00
if ( snic - > wq_count < n | | snic - > cq_count < n + m )
goto fail ;
2015-05-29 01:04:01 -07:00
2017-02-01 15:21:21 +01:00
if ( pci_alloc_irq_vectors ( snic - > pdev , vecs , vecs , PCI_IRQ_MSIX ) < 0 )
goto fail ;
snic - > wq_count = n ;
snic - > cq_count = n + m ;
snic - > intr_count = vecs ;
snic - > err_intr_offset = SNIC_MSIX_ERR_NOTIFY ;
SNIC_ISR_DBG ( snic - > shost , " Using MSI-X Interrupts \n " ) ;
svnic_dev_set_intr_mode ( snic - > vdev , VNIC_DEV_INTR_MODE_MSIX ) ;
return 0 ;
fail :
svnic_dev_set_intr_mode ( snic - > vdev , VNIC_DEV_INTR_MODE_UNKNOWN ) ;
2015-05-29 01:04:01 -07:00
return - EINVAL ;
} /* end of snic_set_intr_mode */
void
snic_clear_intr_mode ( struct snic * snic )
{
2017-02-01 15:21:21 +01:00
pci_free_irq_vectors ( snic - > pdev ) ;
2015-05-29 01:04:01 -07:00
svnic_dev_set_intr_mode ( snic - > vdev , VNIC_DEV_INTR_MODE_INTX ) ;
}