@ -60,6 +60,8 @@
# define EMULATION_HW 0
# endif
static bool cnss_driver_registered ;
static DEFINE_SPINLOCK ( pci_link_down_lock ) ;
static DEFINE_SPINLOCK ( pci_reg_window_lock ) ;
@ -137,6 +139,7 @@ static DEFINE_SPINLOCK(pci_reg_window_lock);
# define FORCE_WAKE_DELAY_TIMEOUT_US 60000
# define QCA6390_WLAON_QFPROM_PWR_CTRL_REG 0x1F8031C
# define QCA6390_PCIE_SCRATCH_0_SOC_PCIE_REG 0x1E04040
# define POWER_ON_RETRY_MAX_TIMES 3
# define POWER_ON_RETRY_DELAY_MS 200
@ -1236,13 +1239,72 @@ int cnss_pci_is_drv_connected(struct device *dev)
}
EXPORT_SYMBOL ( cnss_pci_is_drv_connected ) ;
# ifdef CONFIG_CNSS_SUPPORT_DUAL_DEV
static struct cnss_plat_data * cnss_get_plat_priv_by_driver_ops (
struct cnss_wlan_driver * driver_ops )
{
int plat_env_count = cnss_get_plat_env_count ( ) ;
struct cnss_plat_data * plat_env ;
struct cnss_pci_data * pci_priv ;
int i = 0 ;
if ( ! driver_ops ) {
cnss_pr_err ( " No cnss driver \n " ) ;
return NULL ;
}
for ( i = 0 ; i < plat_env_count ; i + + ) {
plat_env = cnss_get_plat_env ( i ) ;
if ( ! plat_env )
continue ;
pci_priv = plat_env - > bus_priv ;
if ( ! pci_priv ) {
cnss_pr_err ( " pci_priv is NULL \n " ) ;
continue ;
}
if ( driver_ops = = pci_priv - > driver_ops )
return plat_env ;
}
/* Doesn't find the existing instance,
* so return the fist empty instance
*/
for ( i = 0 ; i < plat_env_count ; i + + ) {
plat_env = cnss_get_plat_env ( i ) ;
if ( ! plat_env )
continue ;
pci_priv = plat_env - > bus_priv ;
if ( ! pci_priv ) {
cnss_pr_err ( " pci_priv is NULL \n " ) ;
continue ;
}
if ( ! pci_priv - > driver_ops )
return plat_env ;
}
return NULL ;
}
# else
static struct cnss_plat_data * cnss_get_plat_priv_by_driver_ops (
struct cnss_wlan_driver * driver_ops )
{
return cnss_bus_dev_to_plat_priv ( NULL ) ;
}
# endif
int cnss_wlan_register_driver ( struct cnss_wlan_driver * driver_ops )
{
int ret = 0 ;
struct cnss_plat_data * plat_priv = cnss_bus_dev_to_plat_priv ( NULL ) ;
struct cnss_plat_data * plat_priv ;
struct cnss_pci_data * pci_priv ;
unsigned int timeout ;
plat_priv = cnss_get_plat_priv_by_driver_ops ( driver_ops ) ;
if ( ! plat_priv ) {
cnss_pr_err ( " plat_priv is NULL \n " ) ;
return - ENODEV ;
@ -1286,9 +1348,10 @@ EXPORT_SYMBOL(cnss_wlan_register_driver);
void cnss_wlan_unregister_driver ( struct cnss_wlan_driver * driver_ops )
{
struct cnss_plat_data * plat_priv = cnss_bus_dev_to_plat_priv ( NULL ) ;
struct cnss_plat_data * plat_priv ;
int ret = 0 ;
plat_priv = cnss_get_plat_priv_by_driver_ops ( driver_ops ) ;
if ( ! plat_priv ) {
cnss_pr_err ( " plat_priv is NULL \n " ) ;
return ;
@ -3216,6 +3279,44 @@ int cnss_pci_start_mhi(struct cnss_pci_data *pci_priv)
if ( ret )
goto out ;
/**
* in the single wlan chipset case , plat_priv - > qrtr_node_id always is 0 ,
* wlan fw will use the hardcode 7 as the qrtr node id .
* in the dual Hastings case , we will read qrtr node id
* from device tree and pass to get plat_priv - > qrtr_node_id ,
* which always is not zero . And then store this new value
* to pcie register , wlan fw will read out this qrtr node id
* from this register and overwrite to the hardcode one
* while do initialization for ipc router .
* without this change , two Hastings will use the same
* qrtr node instance id , which will mess up qmi message
* exchange . According to qrtr spec , every node should
* have unique qrtr node id
*/
if ( plat_priv - > device_id = = QCA6390_DEVICE_ID & &
plat_priv - > qrtr_node_id ) {
u32 val ;
cnss_pr_dbg ( " write 0x%x to QCA6390_PCIE_SCRATCH_0_SOC_PCIE_REG \n " ,
plat_priv - > qrtr_node_id ) ;
ret = cnss_pci_reg_write ( pci_priv ,
QCA6390_PCIE_SCRATCH_0_SOC_PCIE_REG ,
plat_priv - > qrtr_node_id ) ;
if ( ret ) {
cnss_pr_err ( " Failed to write register offset 0x%x, err = %d \n " ,
QCA6390_PCIE_SCRATCH_0_SOC_PCIE_REG , ret ) ;
goto out ;
}
if ( cnss_pci_reg_read ( pci_priv ,
QCA6390_PCIE_SCRATCH_0_SOC_PCIE_REG ,
& val ) )
cnss_pr_err ( " Failed to read QCA6390_PCIE_SCRATCH_0_SOC_PCIE_REG " ) ;
if ( val ! = plat_priv - > qrtr_node_id ) {
cnss_pr_err ( " qrtr node id write to register doesn't match with readout value " ) ;
goto out ;
}
}
ret = cnss_pci_set_mhi_state ( pci_priv , CNSS_MHI_POWER_ON ) ;
if ( ret )
goto out ;
@ -3393,7 +3494,8 @@ static int cnss_pci_probe(struct pci_dev *pci_dev,
{
int ret = 0 ;
struct cnss_pci_data * pci_priv ;
struct cnss_plat_data * plat_priv = cnss_bus_dev_to_plat_priv ( NULL ) ;
int rc_num = pci_dev - > bus - > domain_nr ;
struct cnss_plat_data * plat_priv = cnss_get_plat_priv_by_rc_num ( rc_num ) ;
cnss_pr_dbg ( " PCI is probing, vendor ID: 0x%x, device ID: 0x%x \n " ,
id - > vendor , pci_dev - > device ) ;
@ -3458,6 +3560,8 @@ static int cnss_pci_probe(struct pci_dev *pci_dev,
switch ( pci_dev - > device ) {
case QCA6174_DEVICE_ID :
if ( cnss_get_dual_wlan ( ) & & ! plat_priv - > enumerate_done )
break ;
pci_read_config_word ( pci_dev , QCA6174_REV_ID_OFFSET ,
& pci_priv - > revision_id ) ;
ret = cnss_suspend_pci_link ( pci_priv ) ;
@ -3468,7 +3572,9 @@ static int cnss_pci_probe(struct pci_dev *pci_dev,
break ;
case QCA6290_DEVICE_ID :
case QCA6390_DEVICE_ID :
cnss_pci_set_wlaon_pwr_ctrl ( pci_priv , false , false , false ) ;
if ( cnss_get_dual_wlan ( ) & & plat_priv - > enumerate_done )
cnss_pci_set_wlaon_pwr_ctrl ( pci_priv , false ,
false , false ) ;
case QCN7605_DEVICE_ID :
setup_timer ( & pci_priv - > dev_rddm_timer ,
cnss_dev_rddm_timeout_hdlr ,
@ -3493,6 +3599,9 @@ static int cnss_pci_probe(struct pci_dev *pci_dev,
if ( EMULATION_HW )
break ;
if ( cnss_get_dual_wlan ( ) & & ! plat_priv - > enumerate_done )
break ;
if ( pci_dev - > device ! = QCN7605_DEVICE_ID )
cnss_pci_set_wlaon_pwr_ctrl ( pci_priv , false ,
true , false ) ;
@ -3577,7 +3686,7 @@ static const struct dev_pm_ops cnss_pm_ops = {
cnss_pci_runtime_idle )
} ;
struct pci_driver cnss_pci_driver = {
static st ruct pci_driver cnss_pci_driver = {
. name = " cnss_pci " ,
. id_table = cnss_pci_id_table ,
. probe = cnss_pci_probe ,
@ -3613,19 +3722,79 @@ retry:
}
}
ret = pci_register_driver ( & cnss_pci_driver ) ;
if ( ret ) {
cnss_pr_err ( " Failed to register to PCI framework, err = %d \n " ,
ret ) ;
goto out ;
}
/* in the dual wlan card case, if call pci_register_driver after
* finishing the first pcie device enumeration , it will cause
* the cnss_pci_probe called in advance with the second wlan card ,
* and the sequence like this :
* enter msm_pcie_enumerate - > pci_bus_add_devices - > cnss_pci_probe
* - > exit msm_pcie_enumerate .
* But the correct sequence we expected is like this :
* enter msm_pcie_enumerate - > pci_bus_add_devices - >
* exit msm_pcie_enumerate - > cnss_pci_probe .
* And this unexpected sequence will make the second wlan card do
* pcie link suspend while the pcie enumeration not finished .
* So need to add below logical to avoid doing pcie link suspend
* if the enumeration has not finish .
*/
if ( cnss_get_dual_wlan ( ) ) {
plat_priv - > enumerate_done = true ;
/* Now enumeration is finished, try to suspend PCIe link */
if ( plat_priv - > bus_priv ) {
struct cnss_pci_data * pci_priv = plat_priv - > bus_priv ;
struct pci_dev * pci_dev = pci_priv - > pci_dev ;
switch ( pci_dev - > device ) {
case QCA6174_DEVICE_ID :
pci_read_config_word ( pci_dev ,
QCA6174_REV_ID_OFFSET ,
& pci_priv - > revision_id ) ;
ret = cnss_suspend_pci_link ( pci_priv ) ;
if ( ret )
cnss_pr_err ( " Failed to suspend PCI link, err = %d \n " ,
ret ) ;
cnss_power_off_device ( plat_priv ) ;
break ;
case QCA6290_DEVICE_ID :
case QCA6390_DEVICE_ID :
case QCN7605_DEVICE_ID :
if ( pci_dev - > device ! = QCN7605_DEVICE_ID ) {
cnss_pci_set_wlaon_pwr_ctrl ( pci_priv ,
false ,
false ,
false ) ;
cnss_pci_set_wlaon_pwr_ctrl ( pci_priv ,
false ,
true ,
false ) ;
}
ret = cnss_suspend_pci_link ( pci_priv ) ;
if ( ret )
cnss_pr_err ( " Failed to suspend PCI link, err = %d \n " ,
ret ) ;
cnss_power_off_device ( plat_priv ) ;
if ( ! plat_priv - > bus_priv ) {
cnss_pr_err ( " Failed to probe pci driver \n " ) ;
ret = - ENODEV ;
goto deinit ;
break ;
default :
cnss_pr_err ( " Unknown PCI device found: 0x%x \n " ,
pci_dev - > device ) ;
ret = - ENODEV ;
}
}
}
if ( ! cnss_driver_registered ) {
ret = pci_register_driver ( & cnss_pci_driver ) ;
if ( ret ) {
cnss_pr_err ( " Failed to register to PCI framework, err = %d \n " ,
ret ) ;
goto out ;
}
if ( ! plat_priv - > bus_priv ) {
cnss_pr_err ( " Failed to probe pci driver \n " ) ;
ret = - ENODEV ;
goto deinit ;
}
cnss_driver_registered = true ;
}
return 0 ;
deinit :