2012-12-21 01:13:19 +04:00
/*
2015-12-16 18:51:46 +03:00
* Copyright ( c ) 2012 - 2015 Qualcomm Atheros , Inc .
2012-12-21 01:13:19 +04:00
*
* 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/pci.h>
# include <linux/moduleparam.h>
2014-09-10 17:34:50 +04:00
# include <linux/interrupt.h>
2012-12-21 01:13:19 +04:00
# include "wil6210.h"
2015-07-30 13:52:02 +03:00
static bool use_msi = true ;
module_param ( use_msi , bool , S_IRUGO ) ;
MODULE_PARM_DESC ( use_msi , " Use MSI interrupt, default - true " ) ;
2012-12-21 01:13:19 +04:00
2014-12-23 10:47:17 +03:00
static
void wil_set_capabilities ( struct wil6210_priv * wil )
{
2015-07-30 13:52:03 +03:00
u32 rev_id = wil_r ( wil , RGF_USER_JTAG_DEV_ID ) ;
2014-12-23 10:47:17 +03:00
bitmap_zero ( wil - > hw_capabilities , hw_capability_last ) ;
switch ( rev_id ) {
case JTAG_DEV_ID_SPARROW_B0 :
2014-12-23 10:47:18 +03:00
wil - > hw_name = " Sparrow B0 " ;
2014-12-23 10:47:17 +03:00
wil - > hw_version = HW_VER_SPARROW_B0 ;
break ;
default :
wil_err ( wil , " Unknown board hardware 0x%08x \n " , rev_id ) ;
2014-12-23 10:47:18 +03:00
wil - > hw_name = " Unknown " ;
2014-12-23 10:47:17 +03:00
wil - > hw_version = HW_VER_UNKNOWN ;
}
2014-12-23 10:47:18 +03:00
wil_info ( wil , " Board hardware is %s \n " , wil - > hw_name ) ;
2014-12-23 10:47:17 +03:00
}
2014-09-10 17:34:50 +04:00
void wil_disable_irq ( struct wil6210_priv * wil )
{
2015-07-30 13:52:02 +03:00
disable_irq ( wil - > pdev - > irq ) ;
2014-09-10 17:34:50 +04:00
}
void wil_enable_irq ( struct wil6210_priv * wil )
{
2015-07-30 13:52:02 +03:00
enable_irq ( wil - > pdev - > irq ) ;
2014-09-10 17:34:50 +04:00
}
2012-12-21 01:13:19 +04:00
/* Bus ops */
static int wil_if_pcie_enable ( struct wil6210_priv * wil )
{
struct pci_dev * pdev = wil - > pdev ;
int rc ;
2014-06-16 20:37:20 +04:00
/* on platforms with buggy ACPI, pdev->msi_enabled may be set to
* allow pci_enable_device to work . This indicates INTx was not routed
* and only MSI should be used
*/
int msi_only = pdev - > msi_enabled ;
2015-07-30 13:52:02 +03:00
bool _use_msi = use_msi ;
2014-06-16 20:37:20 +04:00
2014-09-10 17:34:36 +04:00
wil_dbg_misc ( wil , " %s() \n " , __func__ ) ;
2014-06-16 20:37:20 +04:00
pdev - > msi_enabled = 0 ;
2012-12-21 01:13:19 +04:00
pci_set_master ( pdev ) ;
2015-07-30 13:52:02 +03:00
wil_dbg_misc ( wil , " Setup %s interrupt \n " , use_msi ? " MSI " : " INTx " ) ;
2014-02-18 14:12:04 +04:00
2015-07-30 13:52:02 +03:00
if ( use_msi & & pci_enable_msi ( pdev ) ) {
2014-02-18 14:12:04 +04:00
wil_err ( wil , " pci_enable_msi failed, use INTx \n " ) ;
2015-07-30 13:52:02 +03:00
_use_msi = false ;
2012-12-21 01:13:19 +04:00
}
2015-07-30 13:52:02 +03:00
if ( ! _use_msi & & msi_only ) {
2014-06-16 20:37:20 +04:00
wil_err ( wil , " Interrupt pin not routed, unable to use INTx \n " ) ;
rc = - ENODEV ;
goto stop_master ;
}
2015-07-30 13:52:02 +03:00
rc = wil6210_init_irq ( wil , pdev - > irq , _use_msi ) ;
2012-12-21 01:13:19 +04:00
if ( rc )
goto stop_master ;
/* need reset here to obtain MAC */
2014-03-17 17:34:25 +04:00
mutex_lock ( & wil - > mutex ) ;
2015-02-15 15:02:30 +03:00
rc = wil_reset ( wil , false ) ;
2014-03-17 17:34:25 +04:00
mutex_unlock ( & wil - > mutex ) ;
2012-12-21 01:13:19 +04:00
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 ;
2014-09-10 17:34:36 +04:00
wil_dbg_misc ( wil , " %s() \n " , __func__ ) ;
2012-12-21 01:13:19 +04:00
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 ;
}
2015-12-16 18:51:46 +03:00
static int wil_platform_rop_ramdump ( void * wil_handle , void * buf , uint32_t size )
{
struct wil6210_priv * wil = wil_handle ;
if ( ! wil )
return - EINVAL ;
return wil_fw_copy_crash_dump ( wil , buf , size ) ;
}
static int wil_platform_rop_fw_recovery ( void * wil_handle )
{
struct wil6210_priv * wil = wil_handle ;
if ( ! wil )
return - EINVAL ;
wil_fw_error_recovery ( wil ) ;
return 0 ;
}
2012-12-21 01:13:19 +04:00
static int wil_pcie_probe ( struct pci_dev * pdev , const struct pci_device_id * id )
{
struct wil6210_priv * wil ;
struct device * dev = & pdev - > dev ;
int rc ;
2015-12-16 18:51:46 +03:00
const struct wil_platform_rops rops = {
. ramdump = wil_platform_rop_ramdump ,
. fw_recovery = wil_platform_rop_fw_recovery ,
} ;
2012-12-21 01:13:19 +04:00
/* check HW */
2014-07-14 10:49:37 +04:00
dev_info ( & pdev - > dev , WIL_NAME
2014-12-23 10:47:18 +03:00
" device found [%04x:%04x] (rev %x) \n " ,
2012-12-21 01:13:19 +04:00
( 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 ;
}
2015-06-09 14:11:19 +03:00
wil = wil_if_alloc ( dev ) ;
if ( IS_ERR ( wil ) ) {
rc = ( int ) PTR_ERR ( wil ) ;
dev_err ( dev , " wil_if_alloc failed: %d \n " , rc ) ;
return rc ;
}
wil - > pdev = pdev ;
pci_set_drvdata ( pdev , wil ) ;
/* rollback to if_free */
wil - > platform_handle =
2015-12-16 18:51:46 +03:00
wil_platform_init ( & pdev - > dev , & wil - > platform_ops , & rops , wil ) ;
2015-06-09 14:11:19 +03:00
if ( ! wil - > platform_handle ) {
rc = - ENODEV ;
wil_err ( wil , " wil_platform_init failed \n " ) ;
goto if_free ;
}
/* rollback to err_plat */
2012-12-21 01:13:19 +04:00
rc = pci_enable_device ( pdev ) ;
if ( rc ) {
2015-06-09 14:11:19 +03:00
wil_err ( wil ,
2014-06-16 20:37:20 +04:00
" pci_enable_device failed, retry with MSI only \n " ) ;
/* Work around for platforms that can't allocate IRQ:
* retry with MSI only
*/
pdev - > msi_enabled = 1 ;
rc = pci_enable_device ( pdev ) ;
2012-12-21 01:13:19 +04:00
}
2015-06-09 14:11:19 +03:00
if ( rc ) {
wil_err ( wil ,
" pci_enable_device failed, even with MSI only \n " ) ;
goto err_plat ;
}
2012-12-21 01:13:19 +04:00
/* rollback to err_disable_pdev */
rc = pci_request_region ( pdev , 0 , WIL_NAME ) ;
if ( rc ) {
2015-06-09 14:11:19 +03:00
wil_err ( wil , " pci_request_region failed \n " ) ;
2012-12-21 01:13:19 +04:00
goto err_disable_pdev ;
}
/* rollback to err_release_reg */
2015-06-09 14:11:19 +03:00
wil - > csr = pci_ioremap_bar ( pdev , 0 ) ;
if ( ! wil - > csr ) {
wil_err ( wil , " pci_ioremap_bar failed \n " ) ;
2012-12-21 01:13:19 +04:00
rc = - ENODEV ;
goto err_release_reg ;
}
/* rollback to err_iounmap */
2015-06-09 14:11:19 +03:00
wil_info ( wil , " CSR at %pR -> 0x%p \n " , & pdev - > resource [ 0 ] , wil - > csr ) ;
2012-12-21 01:13:19 +04:00
2014-12-23 10:47:17 +03:00
wil_set_capabilities ( wil ) ;
2014-03-17 17:34:13 +04:00
wil6210_clear_irq ( wil ) ;
2014-09-10 17:34:35 +04:00
2012-12-21 01:13:19 +04:00
/* FW should raise IRQ when ready */
rc = wil_if_pcie_enable ( wil ) ;
if ( rc ) {
wil_err ( wil , " Enable device failed \n " ) ;
2015-06-09 14:11:19 +03:00
goto err_iounmap ;
2012-12-21 01:13:19 +04:00
}
/* 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 ) ;
return 0 ;
2015-06-09 14:11:19 +03:00
bus_disable :
2012-12-21 01:13:19 +04:00
wil_if_pcie_disable ( wil ) ;
2015-06-09 14:11:19 +03:00
err_iounmap :
pci_iounmap ( pdev , wil - > csr ) ;
err_release_reg :
pci_release_region ( pdev , 0 ) ;
err_disable_pdev :
pci_disable_device ( pdev ) ;
err_plat :
2014-09-10 17:34:35 +04:00
if ( wil - > platform_ops . uninit )
wil - > platform_ops . uninit ( wil - > platform_handle ) ;
2015-06-09 14:11:19 +03:00
if_free :
2012-12-21 01:13:19 +04:00
wil_if_free ( wil ) ;
return rc ;
}
static void wil_pcie_remove ( struct pci_dev * pdev )
{
struct wil6210_priv * wil = pci_get_drvdata ( pdev ) ;
2014-08-06 11:32:01 +04:00
void __iomem * csr = wil - > csr ;
2012-12-21 01:13:19 +04:00
2014-09-10 17:34:36 +04:00
wil_dbg_misc ( wil , " %s() \n " , __func__ ) ;
2012-12-21 01:13:19 +04:00
wil6210_debugfs_remove ( wil ) ;
wil_if_remove ( wil ) ;
2014-09-10 17:34:38 +04:00
wil_if_pcie_disable ( wil ) ;
2014-08-06 11:32:01 +04:00
pci_iounmap ( pdev , csr ) ;
2012-12-21 01:13:19 +04:00
pci_release_region ( pdev , 0 ) ;
pci_disable_device ( pdev ) ;
2015-06-09 14:11:19 +03:00
if ( wil - > platform_ops . uninit )
wil - > platform_ops . uninit ( wil - > platform_handle ) ;
wil_if_free ( wil ) ;
2012-12-21 01:13:19 +04:00
}
2014-07-14 10:49:37 +04:00
static const struct pci_device_id wil6210_pcie_ids [ ] = {
2014-12-23 10:47:18 +03:00
{ PCI_DEVICE ( 0x1ae9 , 0x0310 ) } ,
{ PCI_DEVICE ( 0x1ae9 , 0x0302 ) } , /* same as above, firmware broken */
2012-12-21 01:13:19 +04:00
{ /* end: all zeroes */ } ,
} ;
MODULE_DEVICE_TABLE ( pci , wil6210_pcie_ids ) ;
2015-07-30 13:52:05 +03:00
# ifdef CONFIG_PM
2015-10-04 10:23:18 +03:00
# ifdef CONFIG_PM_SLEEP
2015-07-30 13:52:05 +03:00
static int wil6210_suspend ( struct device * dev , bool is_runtime )
{
int rc = 0 ;
struct pci_dev * pdev = to_pci_dev ( dev ) ;
struct wil6210_priv * wil = pci_get_drvdata ( pdev ) ;
wil_dbg_pm ( wil , " %s(%s) \n " , __func__ ,
is_runtime ? " runtime " : " system " ) ;
rc = wil_can_suspend ( wil , is_runtime ) ;
if ( rc )
goto out ;
rc = wil_suspend ( wil , is_runtime ) ;
if ( rc )
goto out ;
/* TODO: how do I bring card in low power state? */
/* disable bus mastering */
pci_clear_master ( pdev ) ;
/* PCI will call pci_save_state(pdev) and pci_prepare_to_sleep(pdev) */
out :
return rc ;
}
static int wil6210_resume ( struct device * dev , bool is_runtime )
{
int rc = 0 ;
struct pci_dev * pdev = to_pci_dev ( dev ) ;
struct wil6210_priv * wil = pci_get_drvdata ( pdev ) ;
wil_dbg_pm ( wil , " %s(%s) \n " , __func__ ,
is_runtime ? " runtime " : " system " ) ;
/* allow master */
pci_set_master ( pdev ) ;
rc = wil_resume ( wil , is_runtime ) ;
if ( rc )
pci_clear_master ( pdev ) ;
return rc ;
}
static int wil6210_pm_suspend ( struct device * dev )
{
return wil6210_suspend ( dev , false ) ;
}
static int wil6210_pm_resume ( struct device * dev )
{
return wil6210_resume ( dev , false ) ;
}
# endif /* CONFIG_PM_SLEEP */
# endif /* CONFIG_PM */
static const struct dev_pm_ops wil6210_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS ( wil6210_pm_suspend , wil6210_pm_resume )
} ;
2012-12-21 01:13:19 +04:00
static struct pci_driver wil6210_driver = {
. probe = wil_pcie_probe ,
. remove = wil_pcie_remove ,
. id_table = wil6210_pcie_ids ,
. name = WIL_NAME ,
2015-07-30 13:52:05 +03:00
. driver = {
. pm = & wil6210_pm_ops ,
} ,
2012-12-21 01:13:19 +04:00
} ;
2015-06-09 14:11:19 +03:00
static int __init wil6210_driver_init ( void )
{
int rc ;
rc = wil_platform_modinit ( ) ;
if ( rc )
return rc ;
rc = pci_register_driver ( & wil6210_driver ) ;
if ( rc )
wil_platform_modexit ( ) ;
return rc ;
}
module_init ( wil6210_driver_init ) ;
static void __exit wil6210_driver_exit ( void )
{
pci_unregister_driver ( & wil6210_driver ) ;
wil_platform_modexit ( ) ;
}
module_exit ( wil6210_driver_exit ) ;
2012-12-21 01:13:19 +04:00
MODULE_LICENSE ( " Dual BSD/GPL " ) ;
MODULE_AUTHOR ( " Qualcomm Atheros <wil6210@qca.qualcomm.com> " ) ;
MODULE_DESCRIPTION ( " Driver for 60g WiFi WIL6210 card " ) ;