2012-12-20 13:13:19 -08:00
/*
* Copyright ( c ) 2012 Qualcomm Atheros , Inc .
*
* Permission to use , copy , modify , and / or distribute this software for any
* purpose with or without fee is hereby granted , provided that the above
* copyright notice and this permission notice appear in all copies .
*
* THE SOFTWARE IS PROVIDED " AS IS " AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS . IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL , DIRECT , INDIRECT , OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE , DATA OR PROFITS , WHETHER IN AN
* ACTION OF CONTRACT , NEGLIGENCE OR OTHER TORTIOUS ACTION , ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE .
*/
# include <linux/module.h>
# include <linux/debugfs.h>
# include <linux/pci.h>
# include <linux/moduleparam.h>
# include "wil6210.h"
static int use_msi = 1 ;
module_param ( use_msi , int , S_IRUGO ) ;
MODULE_PARM_DESC ( use_msi ,
" Use MSI interrupt: "
" 0 - don't, 1 - (default) - single, or 3 " ) ;
/* Bus ops */
static int wil_if_pcie_enable ( struct wil6210_priv * wil )
{
struct pci_dev * pdev = wil - > pdev ;
int rc ;
pci_set_master ( pdev ) ;
/*
* how many MSI interrupts to request ?
*/
switch ( use_msi ) {
case 3 :
case 1 :
case 0 :
break ;
default :
wil_err ( wil , " Invalid use_msi=%d, default to 1 \n " ,
use_msi ) ;
use_msi = 1 ;
}
wil - > n_msi = use_msi ;
if ( wil - > n_msi ) {
2013-01-28 18:31:06 +02:00
wil_dbg_misc ( wil , " Setup %d MSI interrupts \n " , use_msi ) ;
2012-12-20 13:13:19 -08:00
rc = pci_enable_msi_block ( pdev , wil - > n_msi ) ;
if ( rc & & ( wil - > n_msi = = 3 ) ) {
wil_err ( wil , " 3 MSI mode failed, try 1 MSI \n " ) ;
wil - > n_msi = 1 ;
rc = pci_enable_msi_block ( pdev , wil - > n_msi ) ;
}
if ( rc ) {
wil_err ( wil , " pci_enable_msi failed, use INTx \n " ) ;
wil - > n_msi = 0 ;
}
} else {
2013-01-28 18:31:06 +02:00
wil_dbg_misc ( wil , " MSI interrupts disabled, use INTx \n " ) ;
2012-12-20 13:13:19 -08:00
}
rc = wil6210_init_irq ( wil , pdev - > irq ) ;
if ( rc )
goto stop_master ;
/* need reset here to obtain MAC */
rc = wil_reset ( wil ) ;
if ( rc )
goto release_irq ;
return 0 ;
release_irq :
wil6210_fini_irq ( wil , pdev - > irq ) ;
/* safe to call if no MSI */
pci_disable_msi ( pdev ) ;
stop_master :
pci_clear_master ( pdev ) ;
return rc ;
}
static int wil_if_pcie_disable ( struct wil6210_priv * wil )
{
struct pci_dev * pdev = wil - > pdev ;
pci_clear_master ( pdev ) ;
/* disable and release IRQ */
wil6210_fini_irq ( wil , pdev - > irq ) ;
/* safe to call if no MSI */
pci_disable_msi ( pdev ) ;
/* TODO: disable HW */
return 0 ;
}
static int wil_pcie_probe ( struct pci_dev * pdev , const struct pci_device_id * id )
{
struct wil6210_priv * wil ;
struct device * dev = & pdev - > dev ;
void __iomem * csr ;
int rc ;
/* check HW */
dev_info ( & pdev - > dev , WIL_NAME " device found [%04x:%04x] (rev %x) \n " ,
( int ) pdev - > vendor , ( int ) pdev - > device , ( int ) pdev - > revision ) ;
if ( pci_resource_len ( pdev , 0 ) ! = WIL6210_MEM_SIZE ) {
dev_err ( & pdev - > dev , " Not " WIL_NAME " ? "
" BAR0 size is %lu while expecting %lu \n " ,
( ulong ) pci_resource_len ( pdev , 0 ) , WIL6210_MEM_SIZE ) ;
return - ENODEV ;
}
rc = pci_enable_device ( pdev ) ;
if ( rc ) {
dev_err ( & pdev - > dev , " pci_enable_device failed \n " ) ;
return - ENODEV ;
}
/* rollback to err_disable_pdev */
rc = pci_request_region ( pdev , 0 , WIL_NAME ) ;
if ( rc ) {
dev_err ( & pdev - > dev , " pci_request_region failed \n " ) ;
goto err_disable_pdev ;
}
/* rollback to err_release_reg */
csr = pci_ioremap_bar ( pdev , 0 ) ;
if ( ! csr ) {
dev_err ( & pdev - > dev , " pci_ioremap_bar failed \n " ) ;
rc = - ENODEV ;
goto err_release_reg ;
}
/* rollback to err_iounmap */
dev_info ( & pdev - > dev , " CSR at %pR -> %p \n " , & pdev - > resource [ 0 ] , csr ) ;
wil = wil_if_alloc ( dev , csr ) ;
if ( IS_ERR ( wil ) ) {
rc = ( int ) PTR_ERR ( wil ) ;
dev_err ( dev , " wil_if_alloc failed: %d \n " , rc ) ;
goto err_iounmap ;
}
/* rollback to if_free */
pci_set_drvdata ( pdev , wil ) ;
wil - > pdev = pdev ;
/* FW should raise IRQ when ready */
rc = wil_if_pcie_enable ( wil ) ;
if ( rc ) {
wil_err ( wil , " Enable device failed \n " ) ;
goto if_free ;
}
/* rollback to bus_disable */
rc = wil_if_add ( wil ) ;
if ( rc ) {
wil_err ( wil , " wil_if_add failed: %d \n " , rc ) ;
goto bus_disable ;
}
wil6210_debugfs_init ( wil ) ;
/* check FW is alive */
wmi_echo ( wil ) ;
return 0 ;
bus_disable :
wil_if_pcie_disable ( wil ) ;
if_free :
wil_if_free ( wil ) ;
err_iounmap :
pci_iounmap ( pdev , csr ) ;
err_release_reg :
pci_release_region ( pdev , 0 ) ;
err_disable_pdev :
pci_disable_device ( pdev ) ;
return rc ;
}
static void wil_pcie_remove ( struct pci_dev * pdev )
{
struct wil6210_priv * wil = pci_get_drvdata ( pdev ) ;
wil6210_debugfs_remove ( wil ) ;
wil_if_pcie_disable ( wil ) ;
wil_if_remove ( wil ) ;
wil_if_free ( wil ) ;
pci_iounmap ( pdev , wil - > csr ) ;
pci_release_region ( pdev , 0 ) ;
pci_disable_device ( pdev ) ;
}
static DEFINE_PCI_DEVICE_TABLE ( wil6210_pcie_ids ) = {
{ PCI_DEVICE ( 0x1ae9 , 0x0301 ) } ,
{ /* end: all zeroes */ } ,
} ;
MODULE_DEVICE_TABLE ( pci , wil6210_pcie_ids ) ;
static struct pci_driver wil6210_driver = {
. probe = wil_pcie_probe ,
. remove = wil_pcie_remove ,
. id_table = wil6210_pcie_ids ,
. name = WIL_NAME ,
} ;
module_pci_driver ( wil6210_driver ) ;
MODULE_LICENSE ( " Dual BSD/GPL " ) ;
MODULE_AUTHOR ( " Qualcomm Atheros <wil6210@qca.qualcomm.com> " ) ;
MODULE_DESCRIPTION ( " Driver for 60g WiFi WIL6210 card " ) ;