diff --git a/drivers/pci/controller/pci-msm.c b/drivers/pci/controller/pci-msm.c index efbcf874a0a5..3a4059d69da0 100644 --- a/drivers/pci/controller/pci-msm.c +++ b/drivers/pci/controller/pci-msm.c @@ -559,6 +559,8 @@ enum msm_pcie_drv_cmds { MSM_PCIE_DRV_CMD_DISABLE = 0xc0000001, MSM_PCIE_DRV_CMD_ENABLE_L1SS_SLEEP = 0xc0000005, MSM_PCIE_DRV_CMD_DISABLE_L1SS_SLEEP = 0xc0000006, + MSM_PCIE_DRV_CMD_DISABLE_PC = 0xc0000007, + MSM_PCIE_DRV_CMD_ENABLE_PC = 0xc0000008, }; /* DRV IPC message type */ @@ -596,6 +598,8 @@ struct msm_pcie_drv_info { struct msm_pcie_drv_msg drv_disable; /* payload to request back */ struct msm_pcie_drv_msg drv_enable_l1ss_sleep; /* enable l1ss sleep */ struct msm_pcie_drv_msg drv_disable_l1ss_sleep; /* disable l1ss sleep */ + struct msm_pcie_drv_msg drv_enable_pc; /* enable drv pc */ + struct msm_pcie_drv_msg drv_disable_pc; /* disable drv pc */ int dev_id; u16 seq; u16 reply_seq; @@ -741,6 +745,12 @@ struct msm_pcie_dev_t { struct msm_pcie_device_info pcidev_table[MAX_DEVICE_NUM]; struct msm_pcie_drv_info *drv_info; + struct work_struct drv_enable_pc_work; + struct work_struct drv_disable_pc_work; + + /* cache drv pc req from RC client, by default drv pc is enabled */ + int drv_disable_pc_vote; + struct mutex drv_pc_lock; void (*rumi_init)(struct msm_pcie_dev_t *pcie_dev); }; @@ -752,6 +762,9 @@ struct msm_root_dev_t { static u32 msm_pcie_keep_resources_on; +/* high prio WQ */ +static struct workqueue_struct *mpcie_wq; + /* debugfs values */ static u32 rc_sel = BIT(0); static u32 base_sel; @@ -938,6 +951,8 @@ static const struct msm_pcie_irq_info_t msm_pcie_irq_info[MSM_PCIE_MAX_IRQ] = { {"int_global_int", 0} }; +static int msm_pcie_drv_send_rpmsg(struct msm_pcie_dev_t *pcie_dev, + struct msm_pcie_drv_msg *msg); static void msm_pcie_config_sid(struct msm_pcie_dev_t *dev); static void msm_pcie_config_l0s_disable_all(struct msm_pcie_dev_t *dev, struct pci_bus *bus); @@ -5405,6 +5420,12 @@ static int msm_pcie_setup_drv(struct msm_pcie_dev_t *pcie_dev, drv_info->dev_id, MSM_PCIE_DRV_CMD_DISABLE_L1SS_SLEEP); + msm_pcie_setup_drv_msg(&drv_info->drv_enable_pc, drv_info->dev_id, + MSM_PCIE_DRV_CMD_ENABLE_PC); + + msm_pcie_setup_drv_msg(&drv_info->drv_disable_pc, drv_info->dev_id, + MSM_PCIE_DRV_CMD_DISABLE_PC); + init_completion(&drv_info->completion); drv_info->timeout_ms = IPC_TIMEOUT_MS; pcie_dev->drv_info = drv_info; @@ -6125,8 +6146,13 @@ static void msm_pcie_drv_notify_client(struct pcie_drv_sta *pcie_drv, if (drv_info->ep_connected) { msm_pcie_notify_client(pcie_dev, event); - if (event & MSM_PCIE_EVENT_DRV_DISCONNECT) + if (event & MSM_PCIE_EVENT_DRV_DISCONNECT) { + mutex_lock(&pcie_dev->drv_pc_lock); drv_info->ep_connected = false; + cancel_work_sync(&pcie_dev->drv_disable_pc_work); + cancel_work_sync(&pcie_dev->drv_enable_pc_work); + mutex_unlock(&pcie_dev->drv_pc_lock); + } } } } @@ -6259,6 +6285,22 @@ static void msm_pcie_early_notifier(void *data) msm_pcie_drv_notify_client(pcie_drv, MSM_PCIE_EVENT_WAKEUP); }; +static void msm_pcie_drv_disable_pc(struct work_struct *w) +{ + struct msm_pcie_dev_t *pcie_dev = container_of(w, struct msm_pcie_dev_t, + drv_disable_pc_work); + + msm_pcie_drv_send_rpmsg(pcie_dev, &pcie_dev->drv_info->drv_disable_pc); +} + +static void msm_pcie_drv_enable_pc(struct work_struct *w) +{ + struct msm_pcie_dev_t *pcie_dev = container_of(w, struct msm_pcie_dev_t, + drv_enable_pc_work); + + msm_pcie_drv_send_rpmsg(pcie_dev, &pcie_dev->drv_info->drv_enable_pc); +} + static void msm_pcie_drv_connect_worker(struct work_struct *work) { struct pcie_drv_sta *pcie_drv = container_of(work, struct pcie_drv_sta, @@ -6289,7 +6331,13 @@ static void msm_pcie_drv_connect_worker(struct work_struct *work) msm_pcie_notify_client(pcie_dev, MSM_PCIE_EVENT_DRV_CONNECT); + + mutex_lock(&pcie_dev->drv_pc_lock); drv_info->ep_connected = true; + + if (pcie_dev->drv_disable_pc_vote) + queue_work(mpcie_wq, &pcie_dev->drv_disable_pc_work); + mutex_unlock(&pcie_dev->drv_pc_lock); } subsys_register_early_notifier("adsp", PCIE_DRV_LAYER_NOTIF, @@ -6344,10 +6392,15 @@ static int __init pcie_init(void) mutex_init(&msm_pcie_dev[i].setup_lock); mutex_init(&msm_pcie_dev[i].recovery_lock); mutex_init(&msm_pcie_dev[i].aspm_lock); + mutex_init(&msm_pcie_dev[i].drv_pc_lock); spin_lock_init(&msm_pcie_dev[i].wakeup_lock); spin_lock_init(&msm_pcie_dev[i].irq_lock); msm_pcie_dev[i].drv_ready = false; msm_pcie_dev[i].l23_rdy_poll_timeout = L23_READY_POLL_TIMEOUT; + INIT_WORK(&msm_pcie_dev[i].drv_disable_pc_work, + msm_pcie_drv_disable_pc); + INIT_WORK(&msm_pcie_dev[i].drv_enable_pc_work, + msm_pcie_drv_enable_pc); } for (i = 0; i < MAX_RC_NUM * MAX_DEVICE_NUM; i++) { msm_pcie_dev_tbl[i].bdf = 0; @@ -6370,6 +6423,11 @@ static int __init pcie_init(void) if (ret) return ret; + mpcie_wq = alloc_ordered_workqueue("mpcie_wq", + WQ_MEM_RECLAIM | WQ_HIGHPRI); + if (!mpcie_wq) + return -ENOMEM; + INIT_WORK(&pcie_drv.drv_connect, msm_pcie_drv_connect_worker); pcie_drv.msm_pcie_dev = msm_pcie_dev; @@ -6379,6 +6437,8 @@ static int __init pcie_init(void) ret); ret = platform_driver_register(&msm_pcie_driver); + if (ret) + destroy_workqueue(mpcie_wq); return ret; } @@ -6389,6 +6449,9 @@ static void __exit pcie_exit(void) pr_info("PCIe: %s\n", __func__); + if (mpcie_wq) + destroy_workqueue(mpcie_wq); + platform_driver_unregister(&msm_pcie_driver); msm_pcie_debugfs_exit(); @@ -6881,8 +6944,6 @@ static int msm_pcie_drv_suspend(struct msm_pcie_dev_t *pcie_dev, /* disable global irq - no more linkdown/aer detection */ disable_irq(pcie_dev->irq[MSM_PCIE_INT_GLOBAL_INT].num); - drv_info->drv_enable.pkt.dword[2] = options & MSM_PCIE_CONFIG_NO_DRV_PC; - ret = msm_pcie_drv_send_rpmsg(pcie_dev, &drv_info->drv_enable); if (ret) { ret = -EBUSY; @@ -6986,6 +7047,10 @@ int msm_pcie_pm_control(enum msm_pcie_pm_opt pm_opt, u32 busnr, void *user, PCIE_DBG(pcie_dev, "PCIe: RC%d: DRV: user requests for DRV suspend\n", rc_idx); + + /* make sure disable pc is done before enabling drv */ + flush_work(&pcie_dev->drv_disable_pc_work); + ret = msm_pcie_drv_suspend(pcie_dev, options); break; case MSM_PCIE_SUSPEND: @@ -7099,6 +7164,30 @@ int msm_pcie_pm_control(enum msm_pcie_pm_opt pm_opt, u32 busnr, void *user, msm_pcie_handle_linkdown(pcie_dev); spin_unlock_irqrestore(&msm_pcie_dev[rc_idx].irq_lock, flags); break; + case MSM_PCIE_DRV_PC_CTRL: + PCIE_DBG(&msm_pcie_dev[rc_idx], + "User of RC%d requests handling drv pc options %u.\n", + rc_idx, options); + + mutex_lock(&pcie_dev->drv_pc_lock); + pcie_dev->drv_disable_pc_vote = + options & MSM_PCIE_CONFIG_NO_DRV_PC; + + if (!pcie_dev->drv_info || !pcie_dev->drv_info->ep_connected) { + mutex_unlock(&pcie_dev->drv_pc_lock); + break; + } + + if (pcie_dev->drv_disable_pc_vote) { + queue_work(mpcie_wq, &pcie_dev->drv_disable_pc_work); + } else { + queue_work(mpcie_wq, &pcie_dev->drv_enable_pc_work); + + /* make sure enable pc happens asap */ + flush_work(&pcie_dev->drv_enable_pc_work); + } + mutex_unlock(&pcie_dev->drv_pc_lock); + break; default: PCIE_ERR(&msm_pcie_dev[rc_idx], "PCIe: RC%d: unsupported pm operation:%d.\n", diff --git a/include/linux/msm_pcie.h b/include/linux/msm_pcie.h index a90725e93b0f..9e88e0a7781d 100644 --- a/include/linux/msm_pcie.h +++ b/include/linux/msm_pcie.h @@ -23,6 +23,7 @@ enum msm_pcie_pm_opt { MSM_PCIE_DISABLE_PC, MSM_PCIE_ENABLE_PC, MSM_PCIE_HANDLE_LINKDOWN, + MSM_PCIE_DRV_PC_CTRL, }; enum msm_pcie_event {