Loading drivers/bus/mhi/controllers/mhi_arch_qcom.c +330 −70 Original line number Diff line number Diff line // SPDX-License-Identifier: GPL-2.0-only /* Copyright (c) 2018, The Linux Foundation. All rights reserved.*/ /* Copyright (c) 2018-2019, The Linux Foundation. All rights reserved.*/ #include <asm/dma-iommu.h> #include <linux/async.h> Loading Loading @@ -28,12 +28,12 @@ struct arch_info { struct pci_saved_state *pcie_state; struct pci_saved_state *ref_pcie_state; struct dma_iommu_mapping *mapping; }; struct mhi_bl_info { struct mhi_device *mhi_device; async_cookie_t cookie; void *ipc_log; void *boot_ipc_log; struct mhi_device *boot_dev; struct mhi_link_info current_link_info; struct work_struct bw_scale_work; bool drv_connected; }; /* ipc log markings */ Loading Loading @@ -73,8 +73,10 @@ static void mhi_arch_pci_link_state_cb(struct msm_pcie_notify *notify) struct mhi_controller *mhi_cntrl = notify->data; struct mhi_dev *mhi_dev = mhi_controller_get_devdata(mhi_cntrl); struct pci_dev *pci_dev = mhi_dev->pci_dev; struct arch_info *arch_info = mhi_dev->arch_info; if (notify->event == MSM_PCIE_EVENT_WAKEUP) { switch (notify->event) { case MSM_PCIE_EVENT_WAKEUP: MHI_LOG("Received MSM_PCIE_EVENT_WAKE signal\n"); /* bring link out of d3cold */ Loading @@ -82,6 +84,42 @@ static void mhi_arch_pci_link_state_cb(struct msm_pcie_notify *notify) pm_runtime_get(&pci_dev->dev); pm_runtime_put_noidle(&pci_dev->dev); } break; case MSM_PCIE_EVENT_L1SS_TIMEOUT: MHI_VERB("Received MSM_PCIE_EVENT_L1SS_TIMEOUT signal\n"); pm_runtime_mark_last_busy(&pci_dev->dev); pm_request_autosuspend(&pci_dev->dev); break; case MSM_PCIE_EVENT_DRV_CONNECT: /* drv is connected we can suspend now */ MHI_LOG("Received MSM_PCIE_EVENT_DRV_CONNECT signal\n"); arch_info->drv_connected = true; pm_runtime_allow(&pci_dev->dev); mutex_lock(&mhi_cntrl->pm_mutex); /* if we're in amss attempt a suspend */ if (mhi_dev->powered_on && mhi_cntrl->ee == MHI_EE_AMSS) { pm_runtime_mark_last_busy(&pci_dev->dev); pm_request_autosuspend(&pci_dev->dev); } mutex_unlock(&mhi_cntrl->pm_mutex); break; case MSM_PCIE_EVENT_DRV_DISCONNECT: MHI_LOG("Received MSM_PCIE_EVENT_DRV_DISCONNECT signal\n"); /* * if link suspended bring it out of suspend and disable runtime * suspend */ arch_info->drv_connected = false; pm_runtime_forbid(&pci_dev->dev); break; default: MHI_ERR("Unhandled event 0x%x\n", notify->event); } } Loading Loading @@ -125,10 +163,27 @@ static int mhi_arch_esoc_ops_power_on(void *priv, bool mdm_state) return mhi_pci_probe(pci_dev, NULL); } static void mhi_arch_link_off(struct mhi_controller *mhi_cntrl) { struct mhi_dev *mhi_dev = mhi_controller_get_devdata(mhi_cntrl); struct pci_dev *pci_dev = mhi_dev->pci_dev; MHI_LOG("Entered\n"); pci_set_power_state(pci_dev, PCI_D3hot); /* release the resources */ msm_pcie_pm_control(MSM_PCIE_SUSPEND, mhi_cntrl->bus, pci_dev, NULL, 0); mhi_arch_set_bus_request(mhi_cntrl, 0); MHI_LOG("Exited\n"); } void mhi_arch_esoc_ops_power_off(void *priv, bool mdm_state) { struct mhi_controller *mhi_cntrl = priv; struct mhi_dev *mhi_dev = mhi_controller_get_devdata(mhi_cntrl); struct arch_info *arch_info = mhi_dev->arch_info; MHI_LOG("Enter: mdm_crashed:%d\n", mdm_state); if (!mhi_dev->powered_on) { Loading @@ -141,22 +196,28 @@ void mhi_arch_esoc_ops_power_off(void *priv, bool mdm_state) /* turn the link off */ mhi_deinit_pci_dev(mhi_cntrl); mhi_arch_link_off(mhi_cntrl, false); mhi_arch_link_off(mhi_cntrl); /* wait for boot monitor to exit */ async_synchronize_cookie(arch_info->cookie + 1); mhi_arch_iommu_deinit(mhi_cntrl); mhi_arch_pcie_deinit(mhi_cntrl); mhi_dev->powered_on = false; } static void mhi_bl_dl_cb(struct mhi_device *mhi_dev, static void mhi_bl_dl_cb(struct mhi_device *mhi_device, struct mhi_result *mhi_result) { struct mhi_bl_info *mhi_bl_info = mhi_device_get_devdata(mhi_dev); struct mhi_controller *mhi_cntrl = mhi_device->mhi_cntrl; struct mhi_dev *mhi_dev = mhi_controller_get_devdata(mhi_cntrl); struct arch_info *arch_info = mhi_dev->arch_info; char *buf = mhi_result->buf_addr; /* force a null at last character */ buf[mhi_result->bytes_xferd - 1] = 0; ipc_log_string(mhi_bl_info->ipc_log, "%s %s", DLOG, buf); ipc_log_string(arch_info->boot_ipc_log, "%s %s", DLOG, buf); } static void mhi_bl_dummy_cb(struct mhi_device *mhi_dev, Loading @@ -164,21 +225,23 @@ static void mhi_bl_dummy_cb(struct mhi_device *mhi_dev, { } static void mhi_bl_remove(struct mhi_device *mhi_dev) static void mhi_bl_remove(struct mhi_device *mhi_device) { struct mhi_bl_info *mhi_bl_info = mhi_device_get_devdata(mhi_dev); ipc_log_string(mhi_bl_info->ipc_log, HLOG "Received Remove notif.\n"); struct mhi_controller *mhi_cntrl = mhi_device->mhi_cntrl; struct mhi_dev *mhi_dev = mhi_controller_get_devdata(mhi_cntrl); struct arch_info *arch_info = mhi_dev->arch_info; /* wait for boot monitor to exit */ async_synchronize_cookie(mhi_bl_info->cookie + 1); arch_info->boot_dev = NULL; ipc_log_string(arch_info->boot_ipc_log, HLOG "Received Remove notif.\n"); } static void mhi_bl_boot_monitor(void *data, async_cookie_t cookie) static void mhi_boot_monitor(void *data, async_cookie_t cookie) { struct mhi_bl_info *mhi_bl_info = data; struct mhi_device *mhi_device = mhi_bl_info->mhi_device; struct mhi_controller *mhi_cntrl = mhi_device->mhi_cntrl; struct mhi_controller *mhi_cntrl = data; struct mhi_dev *mhi_dev = mhi_controller_get_devdata(mhi_cntrl); struct arch_info *arch_info = mhi_dev->arch_info; struct mhi_device *boot_dev; /* 15 sec timeout for booting device */ const u32 timeout = msecs_to_jiffies(15000); Loading @@ -187,46 +250,118 @@ static void mhi_bl_boot_monitor(void *data, async_cookie_t cookie) || mhi_cntrl->ee == MHI_EE_DISABLE_TRANSITION, timeout); ipc_log_string(arch_info->boot_ipc_log, HLOG "Device current ee = %s\n", TO_MHI_EXEC_STR(mhi_cntrl->ee)); /* if we successfully booted to amss disable boot log channel */ if (mhi_cntrl->ee == MHI_EE_AMSS) { ipc_log_string(mhi_bl_info->ipc_log, HLOG "Device successfully booted to mission mode\n"); boot_dev = arch_info->boot_dev; if (boot_dev) mhi_unprepare_from_transfer(boot_dev); mhi_unprepare_from_transfer(mhi_device); } else { ipc_log_string(mhi_bl_info->ipc_log, HLOG "Device failed to boot to mission mode, ee = %s\n", TO_MHI_EXEC_STR(mhi_cntrl->ee)); /* enable link inactivity timer to start auto suspend */ msm_pcie_l1ss_timeout_enable(mhi_dev->pci_dev); if (!mhi_dev->drv_supported || arch_info->drv_connected) pm_runtime_allow(&mhi_dev->pci_dev->dev); } } static int mhi_bl_probe(struct mhi_device *mhi_dev, const struct mhi_device_id *id) int mhi_arch_power_up(struct mhi_controller *mhi_cntrl) { char node_name[32]; struct mhi_bl_info *mhi_bl_info; struct mhi_dev *mhi_dev = mhi_controller_get_devdata(mhi_cntrl); struct arch_info *arch_info = mhi_dev->arch_info; mhi_bl_info = devm_kzalloc(&mhi_dev->dev, sizeof(*mhi_bl_info), GFP_KERNEL); if (!mhi_bl_info) return -ENOMEM; /* start a boot monitor */ arch_info->cookie = async_schedule(mhi_boot_monitor, mhi_cntrl); snprintf(node_name, sizeof(node_name), "mhi_bl_%04x_%02u.%02u.%02u", mhi_dev->dev_id, mhi_dev->domain, mhi_dev->bus, mhi_dev->slot); return 0; } mhi_bl_info->ipc_log = ipc_log_context_create(MHI_IPC_LOG_PAGES, node_name, 0); if (!mhi_bl_info->ipc_log) return -EINVAL; static int mhi_arch_pcie_scale_bw(struct mhi_controller *mhi_cntrl, struct pci_dev *pci_dev, struct mhi_link_info *link_info) { int ret, scale; mhi_bl_info->mhi_device = mhi_dev; mhi_device_set_devdata(mhi_dev, mhi_bl_info); ret = msm_pcie_set_link_bandwidth(pci_dev, link_info->target_link_speed, link_info->target_link_width); if (ret) return ret; ipc_log_string(mhi_bl_info->ipc_log, HLOG "Entered SBL, Session ID:0x%x\n", mhi_dev->mhi_cntrl->session_id); /* if we switch to low bw release bus scale voting */ scale = !(link_info->target_link_speed == PCI_EXP_LNKSTA_CLS_2_5GB); mhi_arch_set_bus_request(mhi_cntrl, scale); /* start a thread to monitor entering mission mode */ mhi_bl_info->cookie = async_schedule(mhi_bl_boot_monitor, mhi_bl_info); MHI_VERB("bw changed to speed:0x%x width:0x%x bus_scale:%d\n", link_info->target_link_speed, link_info->target_link_width, scale); return 0; } static void mhi_arch_pcie_bw_scale_work(struct work_struct *work) { struct arch_info *arch_info = container_of(work, struct arch_info, bw_scale_work); struct mhi_dev *mhi_dev = arch_info->mhi_dev; struct pci_dev *pci_dev = mhi_dev->pci_dev; struct device *dev = &pci_dev->dev; struct mhi_controller *mhi_cntrl = dev_get_drvdata(dev); struct mhi_link_info mhi_link_info; struct mhi_link_info *cur_info = &arch_info->current_link_info; int ret; mutex_lock(&mhi_cntrl->pm_mutex); if (!mhi_dev->powered_on || MHI_IS_SUSPENDED(mhi_dev->suspend_mode)) goto exit_work; /* copy the latest speed change */ write_lock_irq(&mhi_cntrl->pm_lock); mhi_link_info = mhi_cntrl->mhi_link_info; write_unlock_irq(&mhi_cntrl->pm_lock); /* link is already set to current settings */ if (cur_info->target_link_speed == mhi_link_info.target_link_speed && cur_info->target_link_width == mhi_link_info.target_link_width) goto exit_work; ret = mhi_arch_pcie_scale_bw(mhi_cntrl, pci_dev, &mhi_link_info); if (ret) goto exit_work; *cur_info = mhi_link_info; exit_work: mutex_unlock(&mhi_cntrl->pm_mutex); } static void mhi_arch_pcie_bw_scale_cb(struct mhi_controller *mhi_cntrl, struct mhi_dev *mhi_dev) { struct arch_info *arch_info = mhi_dev->arch_info; schedule_work(&arch_info->bw_scale_work); } static int mhi_bl_probe(struct mhi_device *mhi_device, const struct mhi_device_id *id) { char node_name[32]; struct mhi_controller *mhi_cntrl = mhi_device->mhi_cntrl; struct mhi_dev *mhi_dev = mhi_controller_get_devdata(mhi_cntrl); struct arch_info *arch_info = mhi_dev->arch_info; snprintf(node_name, sizeof(node_name), "mhi_bl_%04x_%02u.%02u.%02u", mhi_device->dev_id, mhi_device->domain, mhi_device->bus, mhi_device->slot); arch_info->boot_dev = mhi_device; arch_info->boot_ipc_log = ipc_log_context_create(MHI_IPC_LOG_PAGES, node_name, 0); ipc_log_string(arch_info->boot_ipc_log, HLOG "Entered SBL, Session ID:0x%x\n", mhi_cntrl->session_id); return 0; } Loading Loading @@ -257,6 +392,8 @@ int mhi_arch_pcie_init(struct mhi_controller *mhi_cntrl) if (!arch_info) { struct msm_pcie_register_event *reg_event; struct pci_dev *root_port; struct device_node *root_ofnode; arch_info = devm_kzalloc(&mhi_dev->pci_dev->dev, sizeof(*arch_info), GFP_KERNEL); Loading @@ -264,6 +401,7 @@ int mhi_arch_pcie_init(struct mhi_controller *mhi_cntrl) return -ENOMEM; mhi_dev->arch_info = arch_info; arch_info->mhi_dev = mhi_dev; snprintf(node, sizeof(node), "mhi_%04x_%02u.%02u.%02u", mhi_cntrl->dev_id, mhi_cntrl->domain, mhi_cntrl->bus, Loading @@ -283,9 +421,23 @@ int mhi_arch_pcie_init(struct mhi_controller *mhi_cntrl) return -EINVAL; } /* check if root-complex support DRV */ root_port = pci_find_pcie_root_port(mhi_dev->pci_dev); root_ofnode = root_port->dev.of_node; if (root_ofnode->parent) mhi_dev->drv_supported = of_property_read_bool(root_ofnode->parent, "qcom,drv-supported"); /* register with pcie rc for WAKE# events */ reg_event = &arch_info->pcie_reg_event; reg_event->events = MSM_PCIE_EVENT_WAKEUP; reg_event->events = MSM_PCIE_EVENT_WAKEUP | MSM_PCIE_EVENT_L1SS_TIMEOUT; /* if drv supported register for drv connection events */ if (mhi_dev->drv_supported) reg_event->events |= MSM_PCIE_EVENT_DRV_CONNECT | MSM_PCIE_EVENT_DRV_DISCONNECT; reg_event->user = mhi_dev->pci_dev; reg_event->callback = mhi_arch_pci_link_state_cb; reg_event->notify.data = mhi_cntrl; Loading Loading @@ -318,6 +470,17 @@ int mhi_arch_pcie_init(struct mhi_controller *mhi_cntrl) /* save reference state for pcie config space */ arch_info->ref_pcie_state = pci_store_saved_state( mhi_dev->pci_dev); /* * MHI host driver has full autonomy to manage power state. * Disable all automatic power collapse features */ msm_pcie_pm_control(MSM_PCIE_DISABLE_PC, mhi_cntrl->bus, mhi_dev->pci_dev, NULL, 0); mhi_dev->pci_dev->no_d3hot = true; INIT_WORK(&arch_info->bw_scale_work, mhi_arch_pcie_bw_scale_work); mhi_dev->bw_scale = mhi_arch_pcie_bw_scale_cb; mhi_driver_register(&mhi_bl_driver); } Loading Loading @@ -459,56 +622,111 @@ void mhi_arch_iommu_deinit(struct mhi_controller *mhi_cntrl) mhi_cntrl->dev = NULL; } int mhi_arch_link_off(struct mhi_controller *mhi_cntrl, bool graceful) static int mhi_arch_drv_suspend(struct mhi_controller *mhi_cntrl) { struct mhi_dev *mhi_dev = mhi_controller_get_devdata(mhi_cntrl); struct arch_info *arch_info = mhi_dev->arch_info; struct pci_dev *pci_dev = mhi_dev->pci_dev; struct mhi_link_info link_info, *cur_link_info; bool bw_switched = false; int ret; cur_link_info = &arch_info->current_link_info; /* if link is not in gen 1 we need to switch to gen 1 */ if (cur_link_info->target_link_speed != PCI_EXP_LNKSTA_CLS_2_5GB) { link_info.target_link_speed = PCI_EXP_LNKSTA_CLS_2_5GB; link_info.target_link_width = cur_link_info->target_link_width; ret = mhi_arch_pcie_scale_bw(mhi_cntrl, pci_dev, &link_info); if (ret) { MHI_ERR("Failed to switch Gen1 speed\n"); return -EBUSY; } bw_switched = true; } /* do a drv hand off */ ret = msm_pcie_pm_control(MSM_PCIE_DRV_SUSPEND, mhi_cntrl->bus, pci_dev, NULL, 0); /* * we failed to suspend and scaled down pcie bw.. need to scale up again */ if (ret && bw_switched) { mhi_arch_pcie_scale_bw(mhi_cntrl, pci_dev, cur_link_info); return ret; } if (bw_switched) *cur_link_info = link_info; return ret; } int mhi_arch_link_suspend(struct mhi_controller *mhi_cntrl) { struct mhi_dev *mhi_dev = mhi_controller_get_devdata(mhi_cntrl); struct arch_info *arch_info = mhi_dev->arch_info; struct pci_dev *pci_dev = mhi_dev->pci_dev; int ret = 0; MHI_LOG("Entered\n"); if (graceful) { /* disable inactivity timer */ msm_pcie_l1ss_timeout_disable(pci_dev); switch (mhi_dev->suspend_mode) { case MHI_DEFAULT_SUSPEND: pci_clear_master(pci_dev); ret = pci_save_state(mhi_dev->pci_dev); if (ret) { MHI_ERR("Failed with pci_save_state, ret:%d\n", ret); return ret; goto exit_suspend; } arch_info->pcie_state = pci_store_saved_state(pci_dev); pci_disable_device(pci_dev); } /* * We will always attempt to put link into D3hot, however * link down may have happened due to error fatal, so * ignoring the return code */ pci_set_power_state(pci_dev, PCI_D3hot); /* release the resources */ msm_pcie_pm_control(MSM_PCIE_SUSPEND, mhi_cntrl->bus, pci_dev, NULL, 0); msm_pcie_pm_control(MSM_PCIE_SUSPEND, mhi_cntrl->bus, pci_dev, NULL, 0); mhi_arch_set_bus_request(mhi_cntrl, 0); break; case MHI_FAST_LINK_OFF: ret = mhi_arch_drv_suspend(mhi_cntrl); break; case MHI_ACTIVE_STATE: case MHI_FAST_LINK_ON:/* keeping link on do nothing */ break; } MHI_LOG("Exited\n"); exit_suspend: if (ret) msm_pcie_l1ss_timeout_enable(pci_dev); return 0; MHI_LOG("Exited with ret:%d\n", ret); return ret; } int mhi_arch_link_on(struct mhi_controller *mhi_cntrl) static int __mhi_arch_link_resume(struct mhi_controller *mhi_cntrl) { struct mhi_dev *mhi_dev = mhi_controller_get_devdata(mhi_cntrl); struct arch_info *arch_info = mhi_dev->arch_info; struct pci_dev *pci_dev = mhi_dev->pci_dev; struct mhi_link_info *cur_info = &arch_info->current_link_info; int ret; MHI_LOG("Entered\n"); /* request resources and establish link trainning */ /* request bus scale voting if we're on Gen 2 or higher speed */ if (cur_info->target_link_speed != PCI_EXP_LNKSTA_CLS_2_5GB) { ret = mhi_arch_set_bus_request(mhi_cntrl, 1); if (ret) MHI_LOG("Could not set bus frequency, ret:%d\n", ret); } ret = msm_pcie_pm_control(MSM_PCIE_RESUME, mhi_cntrl->bus, pci_dev, NULL, 0); Loading Loading @@ -536,6 +754,48 @@ int mhi_arch_link_on(struct mhi_controller *mhi_cntrl) pci_restore_state(pci_dev); pci_set_master(pci_dev); return 0; } int mhi_arch_link_resume(struct mhi_controller *mhi_cntrl) { struct mhi_dev *mhi_dev = mhi_controller_get_devdata(mhi_cntrl); struct arch_info *arch_info = mhi_dev->arch_info; struct pci_dev *pci_dev = mhi_dev->pci_dev; struct mhi_link_info *cur_info = &arch_info->current_link_info; struct mhi_link_info *updated_info = &mhi_cntrl->mhi_link_info; int ret = 0; MHI_LOG("Entered\n"); switch (mhi_dev->suspend_mode) { case MHI_DEFAULT_SUSPEND: ret = __mhi_arch_link_resume(mhi_cntrl); break; case MHI_FAST_LINK_OFF: ret = msm_pcie_pm_control(MSM_PCIE_RESUME, mhi_cntrl->bus, pci_dev, NULL, 0); break; case MHI_ACTIVE_STATE: case MHI_FAST_LINK_ON: break; } if (ret) { MHI_ERR("Link training failed, ret:%d\n", ret); return ret; } /* BW request from device doesn't match current link speed */ if (cur_info->target_link_speed != updated_info->target_link_speed || cur_info->target_link_width != updated_info->target_link_width) { ret = mhi_arch_pcie_scale_bw(mhi_cntrl, pci_dev, updated_info); if (!ret) *cur_info = *updated_info; } msm_pcie_l1ss_timeout_enable(pci_dev); MHI_LOG("Exited\n"); return 0; Loading drivers/bus/mhi/controllers/mhi_qcom.c +123 −32 Original line number Diff line number Diff line Loading @@ -200,15 +200,39 @@ static int mhi_runtime_suspend(struct device *dev) return 0; } /* if drv is supported we will always go into drv */ if (mhi_dev->drv_supported) { ret = mhi_pm_fast_suspend(mhi_cntrl, true); mhi_dev->suspend_mode = MHI_FAST_LINK_OFF; } else { ret = mhi_pm_suspend(mhi_cntrl); mhi_dev->suspend_mode = MHI_DEFAULT_SUSPEND; /* regular suspend failed, probably a client has a vote */ if (ret == -EBUSY) { ret = mhi_pm_fast_suspend(mhi_cntrl, false); mhi_dev->suspend_mode = MHI_FAST_LINK_ON; } } if (ret) { MHI_LOG("Abort due to ret:%d\n", ret); goto exit_runtime_suspend; } ret = mhi_arch_link_off(mhi_cntrl, true); if (ret) MHI_ERR("Failed to Turn off link ret:%d\n", ret); ret = mhi_arch_link_suspend(mhi_cntrl); /* failed suspending link abort mhi suspend */ if (ret) { MHI_LOG("Failed to suspend link, abort suspend\n"); if (mhi_dev->suspend_mode == MHI_DEFAULT_SUSPEND) mhi_pm_resume(mhi_cntrl); else mhi_pm_fast_resume(mhi_cntrl, mhi_dev->suspend_mode == MHI_FAST_LINK_OFF); mhi_dev->suspend_mode = MHI_ACTIVE_STATE; } exit_runtime_suspend: mutex_unlock(&mhi_cntrl->pm_mutex); Loading Loading @@ -255,12 +279,19 @@ static int mhi_runtime_resume(struct device *dev) } /* turn on link */ ret = mhi_arch_link_on(mhi_cntrl); ret = mhi_arch_link_resume(mhi_cntrl); if (ret) goto rpm_resume_exit; /* enter M0 state */ /* transition to M0 state */ if (mhi_dev->suspend_mode == MHI_DEFAULT_SUSPEND) ret = mhi_pm_resume(mhi_cntrl); else ret = mhi_pm_fast_resume(mhi_cntrl, mhi_dev->suspend_mode == MHI_FAST_LINK_OFF); mhi_dev->suspend_mode = MHI_ACTIVE_STATE; rpm_resume_exit: mutex_unlock(&mhi_cntrl->pm_mutex); Loading @@ -271,41 +302,87 @@ static int mhi_runtime_resume(struct device *dev) static int mhi_system_resume(struct device *dev) { int ret = 0; struct mhi_controller *mhi_cntrl = dev_get_drvdata(dev); ret = mhi_runtime_resume(dev); if (ret) { MHI_ERR("Failed to resume link\n"); } else { pm_runtime_set_active(dev); pm_runtime_enable(dev); } return ret; return mhi_runtime_resume(dev); } int mhi_system_suspend(struct device *dev) { struct mhi_controller *mhi_cntrl = dev_get_drvdata(dev); struct mhi_dev *mhi_dev = mhi_controller_get_devdata(mhi_cntrl); int ret; MHI_LOG("Entered\n"); /* if rpm status still active then force suspend */ if (!pm_runtime_status_suspended(dev)) { ret = mhi_runtime_suspend(dev); mutex_lock(&mhi_cntrl->pm_mutex); if (!mhi_dev->powered_on) { MHI_LOG("Not fully powered, return success\n"); mutex_unlock(&mhi_cntrl->pm_mutex); return 0; } /* * pci framework always makes a dummy vote to rpm * framework to resume before calling system suspend * hence usage count is minimum one */ if (atomic_read(&dev->power.usage_count) > 1) { /* * clients have requested to keep link on, try * fast suspend. No need to notify clients since * we will not be turning off the pcie link */ ret = mhi_pm_fast_suspend(mhi_cntrl, false); mhi_dev->suspend_mode = MHI_FAST_LINK_ON; } else { /* if drv enable always do fast suspend */ if (mhi_dev->drv_supported) { ret = mhi_pm_fast_suspend(mhi_cntrl, true); mhi_dev->suspend_mode = MHI_FAST_LINK_OFF; } else { /* try normal suspend */ mhi_dev->suspend_mode = MHI_DEFAULT_SUSPEND; ret = mhi_pm_suspend(mhi_cntrl); /* * normal suspend failed because we're busy, try * fast suspend before aborting system suspend. * this could happens if client has disabled * device lpm but no active vote for PCIe from * apps processor */ if (ret == -EBUSY) { ret = mhi_pm_fast_suspend(mhi_cntrl, true); mhi_dev->suspend_mode = MHI_FAST_LINK_ON; } } } if (ret) { MHI_LOG("suspend failed ret:%d\n", ret); return ret; MHI_LOG("Abort due to ret:%d\n", ret); goto exit_system_suspend; } ret = mhi_arch_link_suspend(mhi_cntrl); /* failed suspending link abort mhi suspend */ if (ret) { MHI_LOG("Failed to suspend link, abort suspend\n"); if (mhi_dev->suspend_mode == MHI_DEFAULT_SUSPEND) mhi_pm_resume(mhi_cntrl); else mhi_pm_fast_resume(mhi_cntrl, mhi_dev->suspend_mode == MHI_FAST_LINK_OFF); mhi_dev->suspend_mode = MHI_ACTIVE_STATE; } pm_runtime_set_suspended(dev); pm_runtime_disable(dev); exit_system_suspend: mutex_unlock(&mhi_cntrl->pm_mutex); MHI_LOG("Exit with ret:%d\n", ret); MHI_LOG("Exit\n"); return 0; return ret; } /* checks if link is down */ Loading Loading @@ -409,6 +486,13 @@ static int mhi_qcom_power_up(struct mhi_controller *mhi_cntrl) return -EIO; } /* when coming out of SSR, initial ee state is not valid */ mhi_cntrl->ee = 0; ret = mhi_arch_power_up(mhi_cntrl); if (ret) return ret; ret = mhi_async_power_up(mhi_cntrl); /* power up create the dentry */ Loading Loading @@ -445,10 +529,18 @@ static void mhi_status_cb(struct mhi_controller *mhi_cntrl, struct mhi_dev *mhi_dev = priv; struct device *dev = &mhi_dev->pci_dev->dev; if (reason == MHI_CB_IDLE) { MHI_LOG("Schedule runtime suspend 1\n"); switch (reason) { case MHI_CB_IDLE: MHI_LOG("Schedule runtime suspend\n"); pm_runtime_mark_last_busy(dev); pm_request_autosuspend(dev); break; case MHI_CB_BW_REQ: if (mhi_dev->bw_scale) mhi_dev->bw_scale(mhi_cntrl, mhi_dev); break; default: MHI_ERR("Unhandled cb:0x%x\n", reason); } } Loading Loading @@ -660,7 +752,6 @@ int mhi_pci_probe(struct pci_dev *pci_dev, } pm_runtime_mark_last_busy(&pci_dev->dev); pm_runtime_allow(&pci_dev->dev); MHI_LOG("Return successful\n"); Loading drivers/bus/mhi/controllers/mhi_qcom.h +26 −6 Original line number Diff line number Diff line /* SPDX-License-Identifier: GPL-2.0-only */ /* Copyright (c) 2018, The Linux Foundation. All rights reserved.*/ /* Copyright (c) 2018-2019, The Linux Foundation. All rights reserved.*/ #ifndef _MHI_QCOM_ #define _MHI_QCOM_ Loading @@ -21,8 +21,18 @@ extern const char * const mhi_ee_str[MHI_EE_MAX]; #define TO_MHI_EXEC_STR(ee) (ee >= MHI_EE_MAX ? "INVALID_EE" : mhi_ee_str[ee]) enum mhi_suspend_mode { MHI_ACTIVE_STATE, MHI_DEFAULT_SUSPEND, MHI_FAST_LINK_OFF, MHI_FAST_LINK_ON, }; #define MHI_IS_SUSPENDED(mode) (mode) struct mhi_dev { struct pci_dev *pci_dev; bool drv_supported; u32 smmu_cfg; int resn; void *arch_info; Loading @@ -30,6 +40,11 @@ struct mhi_dev { dma_addr_t iova_start; dma_addr_t iova_stop; bool lpm_disabled; enum mhi_suspend_mode suspend_mode; /* if set, soc support dynamic bw scaling */ void (*bw_scale)(struct mhi_controller *mhi_cntrl, struct mhi_dev *mhi_dev); }; void mhi_deinit_pci_dev(struct mhi_controller *mhi_cntrl); Loading @@ -38,12 +53,13 @@ int mhi_pci_probe(struct pci_dev *pci_dev, #ifdef CONFIG_ARCH_QCOM int mhi_arch_power_up(struct mhi_controller *mhi_cntrl); int mhi_arch_pcie_init(struct mhi_controller *mhi_cntrl); void mhi_arch_pcie_deinit(struct mhi_controller *mhi_cntrl); int mhi_arch_iommu_init(struct mhi_controller *mhi_cntrl); void mhi_arch_iommu_deinit(struct mhi_controller *mhi_cntrl); int mhi_arch_link_off(struct mhi_controller *mhi_cntrl, bool graceful); int mhi_arch_link_on(struct mhi_controller *mhi_cntrl); int mhi_arch_link_suspend(struct mhi_controller *mhi_cntrl); int mhi_arch_link_resume(struct mhi_controller *mhi_cntrl); #else Loading @@ -69,13 +85,17 @@ static inline void mhi_arch_pcie_deinit(struct mhi_controller *mhi_cntrl) { } static inline int mhi_arch_link_off(struct mhi_controller *mhi_cntrl, bool graceful) static inline int mhi_arch_link_suspend(struct mhi_controller *mhi_cntrl) { return 0; } static inline int mhi_arch_link_resume(struct mhi_controller *mhi_cntrl) { return 0; } static inline int mhi_arch_link_on(struct mhi_controller *mhi_cntrl) static inline int mhi_arch_power_up(struct mhi_controller *mhi_cntrl) { return 0; } Loading drivers/bus/mhi/core/mhi_init.c +107 −8 File changed.Preview size limit exceeded, changes collapsed. Show changes drivers/bus/mhi/core/mhi_internal.h +7 −1 Original line number Diff line number Diff line Loading @@ -320,6 +320,8 @@ enum mhi_cmd_type { #define MHI_TRE_GET_EV_TIME(tre) ((tre)->ptr) #define MHI_TRE_GET_EV_COOKIE(tre) lower_32_bits((tre)->ptr) #define MHI_TRE_GET_EV_VEID(tre) (((tre)->dword[0] >> 16) & 0xFF) #define MHI_TRE_GET_EV_LINKSPEED(tre) (((tre)->dword[1] >> 24) & 0xFF) #define MHI_TRE_GET_EV_LINKWIDTH(tre) ((tre)->dword[0] & 0xFF) /* transfer descriptor macros */ #define MHI_TRE_DATA_PTR(ptr) (ptr) Loading Loading @@ -352,6 +354,7 @@ enum MHI_PKT_TYPE { MHI_PKT_TYPE_RSC_TX_EVENT = 0x28, MHI_PKT_TYPE_EE_EVENT = 0x40, MHI_PKT_TYPE_TSYNC_EVENT = 0x48, MHI_PKT_TYPE_BW_REQ_EVENT = 0x50, MHI_PKT_TYPE_STALE_EVENT, /* internal event */ }; Loading Loading @@ -451,7 +454,8 @@ enum MHI_PM_STATE { MHI_PM_SHUTDOWN_PROCESS | MHI_PM_FW_DL_ERR))) #define MHI_PM_IN_ERROR_STATE(pm_state) (pm_state >= MHI_PM_FW_DL_ERR) #define MHI_PM_IN_FATAL_STATE(pm_state) (pm_state == MHI_PM_LD_ERR_FATAL_DETECT) #define MHI_DB_ACCESS_VALID(pm_state) (pm_state & MHI_PM_M0) #define MHI_DB_ACCESS_VALID(mhi_cntrl) (mhi_cntrl->pm_state & \ mhi_cntrl->db_access) #define MHI_WAKE_DB_CLEAR_VALID(pm_state) (pm_state & (MHI_PM_M0 | \ MHI_PM_M2 | MHI_PM_M3_EXIT)) #define MHI_WAKE_DB_SET_VALID(pm_state) (pm_state & MHI_PM_M2) Loading Loading @@ -738,6 +742,8 @@ int mhi_get_capability_offset(struct mhi_controller *mhi_cntrl, u32 capability, int mhi_init_timesync(struct mhi_controller *mhi_cntrl); int mhi_create_timesync_sysfs(struct mhi_controller *mhi_cntrl); void mhi_destroy_timesync(struct mhi_controller *mhi_cntrl); int mhi_create_vote_sysfs(struct mhi_controller *mhi_cntrl); void mhi_destroy_vote_sysfs(struct mhi_controller *mhi_cntrl); /* memory allocation methods */ static inline void *mhi_alloc_coherent(struct mhi_controller *mhi_cntrl, Loading Loading
drivers/bus/mhi/controllers/mhi_arch_qcom.c +330 −70 Original line number Diff line number Diff line // SPDX-License-Identifier: GPL-2.0-only /* Copyright (c) 2018, The Linux Foundation. All rights reserved.*/ /* Copyright (c) 2018-2019, The Linux Foundation. All rights reserved.*/ #include <asm/dma-iommu.h> #include <linux/async.h> Loading Loading @@ -28,12 +28,12 @@ struct arch_info { struct pci_saved_state *pcie_state; struct pci_saved_state *ref_pcie_state; struct dma_iommu_mapping *mapping; }; struct mhi_bl_info { struct mhi_device *mhi_device; async_cookie_t cookie; void *ipc_log; void *boot_ipc_log; struct mhi_device *boot_dev; struct mhi_link_info current_link_info; struct work_struct bw_scale_work; bool drv_connected; }; /* ipc log markings */ Loading Loading @@ -73,8 +73,10 @@ static void mhi_arch_pci_link_state_cb(struct msm_pcie_notify *notify) struct mhi_controller *mhi_cntrl = notify->data; struct mhi_dev *mhi_dev = mhi_controller_get_devdata(mhi_cntrl); struct pci_dev *pci_dev = mhi_dev->pci_dev; struct arch_info *arch_info = mhi_dev->arch_info; if (notify->event == MSM_PCIE_EVENT_WAKEUP) { switch (notify->event) { case MSM_PCIE_EVENT_WAKEUP: MHI_LOG("Received MSM_PCIE_EVENT_WAKE signal\n"); /* bring link out of d3cold */ Loading @@ -82,6 +84,42 @@ static void mhi_arch_pci_link_state_cb(struct msm_pcie_notify *notify) pm_runtime_get(&pci_dev->dev); pm_runtime_put_noidle(&pci_dev->dev); } break; case MSM_PCIE_EVENT_L1SS_TIMEOUT: MHI_VERB("Received MSM_PCIE_EVENT_L1SS_TIMEOUT signal\n"); pm_runtime_mark_last_busy(&pci_dev->dev); pm_request_autosuspend(&pci_dev->dev); break; case MSM_PCIE_EVENT_DRV_CONNECT: /* drv is connected we can suspend now */ MHI_LOG("Received MSM_PCIE_EVENT_DRV_CONNECT signal\n"); arch_info->drv_connected = true; pm_runtime_allow(&pci_dev->dev); mutex_lock(&mhi_cntrl->pm_mutex); /* if we're in amss attempt a suspend */ if (mhi_dev->powered_on && mhi_cntrl->ee == MHI_EE_AMSS) { pm_runtime_mark_last_busy(&pci_dev->dev); pm_request_autosuspend(&pci_dev->dev); } mutex_unlock(&mhi_cntrl->pm_mutex); break; case MSM_PCIE_EVENT_DRV_DISCONNECT: MHI_LOG("Received MSM_PCIE_EVENT_DRV_DISCONNECT signal\n"); /* * if link suspended bring it out of suspend and disable runtime * suspend */ arch_info->drv_connected = false; pm_runtime_forbid(&pci_dev->dev); break; default: MHI_ERR("Unhandled event 0x%x\n", notify->event); } } Loading Loading @@ -125,10 +163,27 @@ static int mhi_arch_esoc_ops_power_on(void *priv, bool mdm_state) return mhi_pci_probe(pci_dev, NULL); } static void mhi_arch_link_off(struct mhi_controller *mhi_cntrl) { struct mhi_dev *mhi_dev = mhi_controller_get_devdata(mhi_cntrl); struct pci_dev *pci_dev = mhi_dev->pci_dev; MHI_LOG("Entered\n"); pci_set_power_state(pci_dev, PCI_D3hot); /* release the resources */ msm_pcie_pm_control(MSM_PCIE_SUSPEND, mhi_cntrl->bus, pci_dev, NULL, 0); mhi_arch_set_bus_request(mhi_cntrl, 0); MHI_LOG("Exited\n"); } void mhi_arch_esoc_ops_power_off(void *priv, bool mdm_state) { struct mhi_controller *mhi_cntrl = priv; struct mhi_dev *mhi_dev = mhi_controller_get_devdata(mhi_cntrl); struct arch_info *arch_info = mhi_dev->arch_info; MHI_LOG("Enter: mdm_crashed:%d\n", mdm_state); if (!mhi_dev->powered_on) { Loading @@ -141,22 +196,28 @@ void mhi_arch_esoc_ops_power_off(void *priv, bool mdm_state) /* turn the link off */ mhi_deinit_pci_dev(mhi_cntrl); mhi_arch_link_off(mhi_cntrl, false); mhi_arch_link_off(mhi_cntrl); /* wait for boot monitor to exit */ async_synchronize_cookie(arch_info->cookie + 1); mhi_arch_iommu_deinit(mhi_cntrl); mhi_arch_pcie_deinit(mhi_cntrl); mhi_dev->powered_on = false; } static void mhi_bl_dl_cb(struct mhi_device *mhi_dev, static void mhi_bl_dl_cb(struct mhi_device *mhi_device, struct mhi_result *mhi_result) { struct mhi_bl_info *mhi_bl_info = mhi_device_get_devdata(mhi_dev); struct mhi_controller *mhi_cntrl = mhi_device->mhi_cntrl; struct mhi_dev *mhi_dev = mhi_controller_get_devdata(mhi_cntrl); struct arch_info *arch_info = mhi_dev->arch_info; char *buf = mhi_result->buf_addr; /* force a null at last character */ buf[mhi_result->bytes_xferd - 1] = 0; ipc_log_string(mhi_bl_info->ipc_log, "%s %s", DLOG, buf); ipc_log_string(arch_info->boot_ipc_log, "%s %s", DLOG, buf); } static void mhi_bl_dummy_cb(struct mhi_device *mhi_dev, Loading @@ -164,21 +225,23 @@ static void mhi_bl_dummy_cb(struct mhi_device *mhi_dev, { } static void mhi_bl_remove(struct mhi_device *mhi_dev) static void mhi_bl_remove(struct mhi_device *mhi_device) { struct mhi_bl_info *mhi_bl_info = mhi_device_get_devdata(mhi_dev); ipc_log_string(mhi_bl_info->ipc_log, HLOG "Received Remove notif.\n"); struct mhi_controller *mhi_cntrl = mhi_device->mhi_cntrl; struct mhi_dev *mhi_dev = mhi_controller_get_devdata(mhi_cntrl); struct arch_info *arch_info = mhi_dev->arch_info; /* wait for boot monitor to exit */ async_synchronize_cookie(mhi_bl_info->cookie + 1); arch_info->boot_dev = NULL; ipc_log_string(arch_info->boot_ipc_log, HLOG "Received Remove notif.\n"); } static void mhi_bl_boot_monitor(void *data, async_cookie_t cookie) static void mhi_boot_monitor(void *data, async_cookie_t cookie) { struct mhi_bl_info *mhi_bl_info = data; struct mhi_device *mhi_device = mhi_bl_info->mhi_device; struct mhi_controller *mhi_cntrl = mhi_device->mhi_cntrl; struct mhi_controller *mhi_cntrl = data; struct mhi_dev *mhi_dev = mhi_controller_get_devdata(mhi_cntrl); struct arch_info *arch_info = mhi_dev->arch_info; struct mhi_device *boot_dev; /* 15 sec timeout for booting device */ const u32 timeout = msecs_to_jiffies(15000); Loading @@ -187,46 +250,118 @@ static void mhi_bl_boot_monitor(void *data, async_cookie_t cookie) || mhi_cntrl->ee == MHI_EE_DISABLE_TRANSITION, timeout); ipc_log_string(arch_info->boot_ipc_log, HLOG "Device current ee = %s\n", TO_MHI_EXEC_STR(mhi_cntrl->ee)); /* if we successfully booted to amss disable boot log channel */ if (mhi_cntrl->ee == MHI_EE_AMSS) { ipc_log_string(mhi_bl_info->ipc_log, HLOG "Device successfully booted to mission mode\n"); boot_dev = arch_info->boot_dev; if (boot_dev) mhi_unprepare_from_transfer(boot_dev); mhi_unprepare_from_transfer(mhi_device); } else { ipc_log_string(mhi_bl_info->ipc_log, HLOG "Device failed to boot to mission mode, ee = %s\n", TO_MHI_EXEC_STR(mhi_cntrl->ee)); /* enable link inactivity timer to start auto suspend */ msm_pcie_l1ss_timeout_enable(mhi_dev->pci_dev); if (!mhi_dev->drv_supported || arch_info->drv_connected) pm_runtime_allow(&mhi_dev->pci_dev->dev); } } static int mhi_bl_probe(struct mhi_device *mhi_dev, const struct mhi_device_id *id) int mhi_arch_power_up(struct mhi_controller *mhi_cntrl) { char node_name[32]; struct mhi_bl_info *mhi_bl_info; struct mhi_dev *mhi_dev = mhi_controller_get_devdata(mhi_cntrl); struct arch_info *arch_info = mhi_dev->arch_info; mhi_bl_info = devm_kzalloc(&mhi_dev->dev, sizeof(*mhi_bl_info), GFP_KERNEL); if (!mhi_bl_info) return -ENOMEM; /* start a boot monitor */ arch_info->cookie = async_schedule(mhi_boot_monitor, mhi_cntrl); snprintf(node_name, sizeof(node_name), "mhi_bl_%04x_%02u.%02u.%02u", mhi_dev->dev_id, mhi_dev->domain, mhi_dev->bus, mhi_dev->slot); return 0; } mhi_bl_info->ipc_log = ipc_log_context_create(MHI_IPC_LOG_PAGES, node_name, 0); if (!mhi_bl_info->ipc_log) return -EINVAL; static int mhi_arch_pcie_scale_bw(struct mhi_controller *mhi_cntrl, struct pci_dev *pci_dev, struct mhi_link_info *link_info) { int ret, scale; mhi_bl_info->mhi_device = mhi_dev; mhi_device_set_devdata(mhi_dev, mhi_bl_info); ret = msm_pcie_set_link_bandwidth(pci_dev, link_info->target_link_speed, link_info->target_link_width); if (ret) return ret; ipc_log_string(mhi_bl_info->ipc_log, HLOG "Entered SBL, Session ID:0x%x\n", mhi_dev->mhi_cntrl->session_id); /* if we switch to low bw release bus scale voting */ scale = !(link_info->target_link_speed == PCI_EXP_LNKSTA_CLS_2_5GB); mhi_arch_set_bus_request(mhi_cntrl, scale); /* start a thread to monitor entering mission mode */ mhi_bl_info->cookie = async_schedule(mhi_bl_boot_monitor, mhi_bl_info); MHI_VERB("bw changed to speed:0x%x width:0x%x bus_scale:%d\n", link_info->target_link_speed, link_info->target_link_width, scale); return 0; } static void mhi_arch_pcie_bw_scale_work(struct work_struct *work) { struct arch_info *arch_info = container_of(work, struct arch_info, bw_scale_work); struct mhi_dev *mhi_dev = arch_info->mhi_dev; struct pci_dev *pci_dev = mhi_dev->pci_dev; struct device *dev = &pci_dev->dev; struct mhi_controller *mhi_cntrl = dev_get_drvdata(dev); struct mhi_link_info mhi_link_info; struct mhi_link_info *cur_info = &arch_info->current_link_info; int ret; mutex_lock(&mhi_cntrl->pm_mutex); if (!mhi_dev->powered_on || MHI_IS_SUSPENDED(mhi_dev->suspend_mode)) goto exit_work; /* copy the latest speed change */ write_lock_irq(&mhi_cntrl->pm_lock); mhi_link_info = mhi_cntrl->mhi_link_info; write_unlock_irq(&mhi_cntrl->pm_lock); /* link is already set to current settings */ if (cur_info->target_link_speed == mhi_link_info.target_link_speed && cur_info->target_link_width == mhi_link_info.target_link_width) goto exit_work; ret = mhi_arch_pcie_scale_bw(mhi_cntrl, pci_dev, &mhi_link_info); if (ret) goto exit_work; *cur_info = mhi_link_info; exit_work: mutex_unlock(&mhi_cntrl->pm_mutex); } static void mhi_arch_pcie_bw_scale_cb(struct mhi_controller *mhi_cntrl, struct mhi_dev *mhi_dev) { struct arch_info *arch_info = mhi_dev->arch_info; schedule_work(&arch_info->bw_scale_work); } static int mhi_bl_probe(struct mhi_device *mhi_device, const struct mhi_device_id *id) { char node_name[32]; struct mhi_controller *mhi_cntrl = mhi_device->mhi_cntrl; struct mhi_dev *mhi_dev = mhi_controller_get_devdata(mhi_cntrl); struct arch_info *arch_info = mhi_dev->arch_info; snprintf(node_name, sizeof(node_name), "mhi_bl_%04x_%02u.%02u.%02u", mhi_device->dev_id, mhi_device->domain, mhi_device->bus, mhi_device->slot); arch_info->boot_dev = mhi_device; arch_info->boot_ipc_log = ipc_log_context_create(MHI_IPC_LOG_PAGES, node_name, 0); ipc_log_string(arch_info->boot_ipc_log, HLOG "Entered SBL, Session ID:0x%x\n", mhi_cntrl->session_id); return 0; } Loading Loading @@ -257,6 +392,8 @@ int mhi_arch_pcie_init(struct mhi_controller *mhi_cntrl) if (!arch_info) { struct msm_pcie_register_event *reg_event; struct pci_dev *root_port; struct device_node *root_ofnode; arch_info = devm_kzalloc(&mhi_dev->pci_dev->dev, sizeof(*arch_info), GFP_KERNEL); Loading @@ -264,6 +401,7 @@ int mhi_arch_pcie_init(struct mhi_controller *mhi_cntrl) return -ENOMEM; mhi_dev->arch_info = arch_info; arch_info->mhi_dev = mhi_dev; snprintf(node, sizeof(node), "mhi_%04x_%02u.%02u.%02u", mhi_cntrl->dev_id, mhi_cntrl->domain, mhi_cntrl->bus, Loading @@ -283,9 +421,23 @@ int mhi_arch_pcie_init(struct mhi_controller *mhi_cntrl) return -EINVAL; } /* check if root-complex support DRV */ root_port = pci_find_pcie_root_port(mhi_dev->pci_dev); root_ofnode = root_port->dev.of_node; if (root_ofnode->parent) mhi_dev->drv_supported = of_property_read_bool(root_ofnode->parent, "qcom,drv-supported"); /* register with pcie rc for WAKE# events */ reg_event = &arch_info->pcie_reg_event; reg_event->events = MSM_PCIE_EVENT_WAKEUP; reg_event->events = MSM_PCIE_EVENT_WAKEUP | MSM_PCIE_EVENT_L1SS_TIMEOUT; /* if drv supported register for drv connection events */ if (mhi_dev->drv_supported) reg_event->events |= MSM_PCIE_EVENT_DRV_CONNECT | MSM_PCIE_EVENT_DRV_DISCONNECT; reg_event->user = mhi_dev->pci_dev; reg_event->callback = mhi_arch_pci_link_state_cb; reg_event->notify.data = mhi_cntrl; Loading Loading @@ -318,6 +470,17 @@ int mhi_arch_pcie_init(struct mhi_controller *mhi_cntrl) /* save reference state for pcie config space */ arch_info->ref_pcie_state = pci_store_saved_state( mhi_dev->pci_dev); /* * MHI host driver has full autonomy to manage power state. * Disable all automatic power collapse features */ msm_pcie_pm_control(MSM_PCIE_DISABLE_PC, mhi_cntrl->bus, mhi_dev->pci_dev, NULL, 0); mhi_dev->pci_dev->no_d3hot = true; INIT_WORK(&arch_info->bw_scale_work, mhi_arch_pcie_bw_scale_work); mhi_dev->bw_scale = mhi_arch_pcie_bw_scale_cb; mhi_driver_register(&mhi_bl_driver); } Loading Loading @@ -459,56 +622,111 @@ void mhi_arch_iommu_deinit(struct mhi_controller *mhi_cntrl) mhi_cntrl->dev = NULL; } int mhi_arch_link_off(struct mhi_controller *mhi_cntrl, bool graceful) static int mhi_arch_drv_suspend(struct mhi_controller *mhi_cntrl) { struct mhi_dev *mhi_dev = mhi_controller_get_devdata(mhi_cntrl); struct arch_info *arch_info = mhi_dev->arch_info; struct pci_dev *pci_dev = mhi_dev->pci_dev; struct mhi_link_info link_info, *cur_link_info; bool bw_switched = false; int ret; cur_link_info = &arch_info->current_link_info; /* if link is not in gen 1 we need to switch to gen 1 */ if (cur_link_info->target_link_speed != PCI_EXP_LNKSTA_CLS_2_5GB) { link_info.target_link_speed = PCI_EXP_LNKSTA_CLS_2_5GB; link_info.target_link_width = cur_link_info->target_link_width; ret = mhi_arch_pcie_scale_bw(mhi_cntrl, pci_dev, &link_info); if (ret) { MHI_ERR("Failed to switch Gen1 speed\n"); return -EBUSY; } bw_switched = true; } /* do a drv hand off */ ret = msm_pcie_pm_control(MSM_PCIE_DRV_SUSPEND, mhi_cntrl->bus, pci_dev, NULL, 0); /* * we failed to suspend and scaled down pcie bw.. need to scale up again */ if (ret && bw_switched) { mhi_arch_pcie_scale_bw(mhi_cntrl, pci_dev, cur_link_info); return ret; } if (bw_switched) *cur_link_info = link_info; return ret; } int mhi_arch_link_suspend(struct mhi_controller *mhi_cntrl) { struct mhi_dev *mhi_dev = mhi_controller_get_devdata(mhi_cntrl); struct arch_info *arch_info = mhi_dev->arch_info; struct pci_dev *pci_dev = mhi_dev->pci_dev; int ret = 0; MHI_LOG("Entered\n"); if (graceful) { /* disable inactivity timer */ msm_pcie_l1ss_timeout_disable(pci_dev); switch (mhi_dev->suspend_mode) { case MHI_DEFAULT_SUSPEND: pci_clear_master(pci_dev); ret = pci_save_state(mhi_dev->pci_dev); if (ret) { MHI_ERR("Failed with pci_save_state, ret:%d\n", ret); return ret; goto exit_suspend; } arch_info->pcie_state = pci_store_saved_state(pci_dev); pci_disable_device(pci_dev); } /* * We will always attempt to put link into D3hot, however * link down may have happened due to error fatal, so * ignoring the return code */ pci_set_power_state(pci_dev, PCI_D3hot); /* release the resources */ msm_pcie_pm_control(MSM_PCIE_SUSPEND, mhi_cntrl->bus, pci_dev, NULL, 0); msm_pcie_pm_control(MSM_PCIE_SUSPEND, mhi_cntrl->bus, pci_dev, NULL, 0); mhi_arch_set_bus_request(mhi_cntrl, 0); break; case MHI_FAST_LINK_OFF: ret = mhi_arch_drv_suspend(mhi_cntrl); break; case MHI_ACTIVE_STATE: case MHI_FAST_LINK_ON:/* keeping link on do nothing */ break; } MHI_LOG("Exited\n"); exit_suspend: if (ret) msm_pcie_l1ss_timeout_enable(pci_dev); return 0; MHI_LOG("Exited with ret:%d\n", ret); return ret; } int mhi_arch_link_on(struct mhi_controller *mhi_cntrl) static int __mhi_arch_link_resume(struct mhi_controller *mhi_cntrl) { struct mhi_dev *mhi_dev = mhi_controller_get_devdata(mhi_cntrl); struct arch_info *arch_info = mhi_dev->arch_info; struct pci_dev *pci_dev = mhi_dev->pci_dev; struct mhi_link_info *cur_info = &arch_info->current_link_info; int ret; MHI_LOG("Entered\n"); /* request resources and establish link trainning */ /* request bus scale voting if we're on Gen 2 or higher speed */ if (cur_info->target_link_speed != PCI_EXP_LNKSTA_CLS_2_5GB) { ret = mhi_arch_set_bus_request(mhi_cntrl, 1); if (ret) MHI_LOG("Could not set bus frequency, ret:%d\n", ret); } ret = msm_pcie_pm_control(MSM_PCIE_RESUME, mhi_cntrl->bus, pci_dev, NULL, 0); Loading Loading @@ -536,6 +754,48 @@ int mhi_arch_link_on(struct mhi_controller *mhi_cntrl) pci_restore_state(pci_dev); pci_set_master(pci_dev); return 0; } int mhi_arch_link_resume(struct mhi_controller *mhi_cntrl) { struct mhi_dev *mhi_dev = mhi_controller_get_devdata(mhi_cntrl); struct arch_info *arch_info = mhi_dev->arch_info; struct pci_dev *pci_dev = mhi_dev->pci_dev; struct mhi_link_info *cur_info = &arch_info->current_link_info; struct mhi_link_info *updated_info = &mhi_cntrl->mhi_link_info; int ret = 0; MHI_LOG("Entered\n"); switch (mhi_dev->suspend_mode) { case MHI_DEFAULT_SUSPEND: ret = __mhi_arch_link_resume(mhi_cntrl); break; case MHI_FAST_LINK_OFF: ret = msm_pcie_pm_control(MSM_PCIE_RESUME, mhi_cntrl->bus, pci_dev, NULL, 0); break; case MHI_ACTIVE_STATE: case MHI_FAST_LINK_ON: break; } if (ret) { MHI_ERR("Link training failed, ret:%d\n", ret); return ret; } /* BW request from device doesn't match current link speed */ if (cur_info->target_link_speed != updated_info->target_link_speed || cur_info->target_link_width != updated_info->target_link_width) { ret = mhi_arch_pcie_scale_bw(mhi_cntrl, pci_dev, updated_info); if (!ret) *cur_info = *updated_info; } msm_pcie_l1ss_timeout_enable(pci_dev); MHI_LOG("Exited\n"); return 0; Loading
drivers/bus/mhi/controllers/mhi_qcom.c +123 −32 Original line number Diff line number Diff line Loading @@ -200,15 +200,39 @@ static int mhi_runtime_suspend(struct device *dev) return 0; } /* if drv is supported we will always go into drv */ if (mhi_dev->drv_supported) { ret = mhi_pm_fast_suspend(mhi_cntrl, true); mhi_dev->suspend_mode = MHI_FAST_LINK_OFF; } else { ret = mhi_pm_suspend(mhi_cntrl); mhi_dev->suspend_mode = MHI_DEFAULT_SUSPEND; /* regular suspend failed, probably a client has a vote */ if (ret == -EBUSY) { ret = mhi_pm_fast_suspend(mhi_cntrl, false); mhi_dev->suspend_mode = MHI_FAST_LINK_ON; } } if (ret) { MHI_LOG("Abort due to ret:%d\n", ret); goto exit_runtime_suspend; } ret = mhi_arch_link_off(mhi_cntrl, true); if (ret) MHI_ERR("Failed to Turn off link ret:%d\n", ret); ret = mhi_arch_link_suspend(mhi_cntrl); /* failed suspending link abort mhi suspend */ if (ret) { MHI_LOG("Failed to suspend link, abort suspend\n"); if (mhi_dev->suspend_mode == MHI_DEFAULT_SUSPEND) mhi_pm_resume(mhi_cntrl); else mhi_pm_fast_resume(mhi_cntrl, mhi_dev->suspend_mode == MHI_FAST_LINK_OFF); mhi_dev->suspend_mode = MHI_ACTIVE_STATE; } exit_runtime_suspend: mutex_unlock(&mhi_cntrl->pm_mutex); Loading Loading @@ -255,12 +279,19 @@ static int mhi_runtime_resume(struct device *dev) } /* turn on link */ ret = mhi_arch_link_on(mhi_cntrl); ret = mhi_arch_link_resume(mhi_cntrl); if (ret) goto rpm_resume_exit; /* enter M0 state */ /* transition to M0 state */ if (mhi_dev->suspend_mode == MHI_DEFAULT_SUSPEND) ret = mhi_pm_resume(mhi_cntrl); else ret = mhi_pm_fast_resume(mhi_cntrl, mhi_dev->suspend_mode == MHI_FAST_LINK_OFF); mhi_dev->suspend_mode = MHI_ACTIVE_STATE; rpm_resume_exit: mutex_unlock(&mhi_cntrl->pm_mutex); Loading @@ -271,41 +302,87 @@ static int mhi_runtime_resume(struct device *dev) static int mhi_system_resume(struct device *dev) { int ret = 0; struct mhi_controller *mhi_cntrl = dev_get_drvdata(dev); ret = mhi_runtime_resume(dev); if (ret) { MHI_ERR("Failed to resume link\n"); } else { pm_runtime_set_active(dev); pm_runtime_enable(dev); } return ret; return mhi_runtime_resume(dev); } int mhi_system_suspend(struct device *dev) { struct mhi_controller *mhi_cntrl = dev_get_drvdata(dev); struct mhi_dev *mhi_dev = mhi_controller_get_devdata(mhi_cntrl); int ret; MHI_LOG("Entered\n"); /* if rpm status still active then force suspend */ if (!pm_runtime_status_suspended(dev)) { ret = mhi_runtime_suspend(dev); mutex_lock(&mhi_cntrl->pm_mutex); if (!mhi_dev->powered_on) { MHI_LOG("Not fully powered, return success\n"); mutex_unlock(&mhi_cntrl->pm_mutex); return 0; } /* * pci framework always makes a dummy vote to rpm * framework to resume before calling system suspend * hence usage count is minimum one */ if (atomic_read(&dev->power.usage_count) > 1) { /* * clients have requested to keep link on, try * fast suspend. No need to notify clients since * we will not be turning off the pcie link */ ret = mhi_pm_fast_suspend(mhi_cntrl, false); mhi_dev->suspend_mode = MHI_FAST_LINK_ON; } else { /* if drv enable always do fast suspend */ if (mhi_dev->drv_supported) { ret = mhi_pm_fast_suspend(mhi_cntrl, true); mhi_dev->suspend_mode = MHI_FAST_LINK_OFF; } else { /* try normal suspend */ mhi_dev->suspend_mode = MHI_DEFAULT_SUSPEND; ret = mhi_pm_suspend(mhi_cntrl); /* * normal suspend failed because we're busy, try * fast suspend before aborting system suspend. * this could happens if client has disabled * device lpm but no active vote for PCIe from * apps processor */ if (ret == -EBUSY) { ret = mhi_pm_fast_suspend(mhi_cntrl, true); mhi_dev->suspend_mode = MHI_FAST_LINK_ON; } } } if (ret) { MHI_LOG("suspend failed ret:%d\n", ret); return ret; MHI_LOG("Abort due to ret:%d\n", ret); goto exit_system_suspend; } ret = mhi_arch_link_suspend(mhi_cntrl); /* failed suspending link abort mhi suspend */ if (ret) { MHI_LOG("Failed to suspend link, abort suspend\n"); if (mhi_dev->suspend_mode == MHI_DEFAULT_SUSPEND) mhi_pm_resume(mhi_cntrl); else mhi_pm_fast_resume(mhi_cntrl, mhi_dev->suspend_mode == MHI_FAST_LINK_OFF); mhi_dev->suspend_mode = MHI_ACTIVE_STATE; } pm_runtime_set_suspended(dev); pm_runtime_disable(dev); exit_system_suspend: mutex_unlock(&mhi_cntrl->pm_mutex); MHI_LOG("Exit with ret:%d\n", ret); MHI_LOG("Exit\n"); return 0; return ret; } /* checks if link is down */ Loading Loading @@ -409,6 +486,13 @@ static int mhi_qcom_power_up(struct mhi_controller *mhi_cntrl) return -EIO; } /* when coming out of SSR, initial ee state is not valid */ mhi_cntrl->ee = 0; ret = mhi_arch_power_up(mhi_cntrl); if (ret) return ret; ret = mhi_async_power_up(mhi_cntrl); /* power up create the dentry */ Loading Loading @@ -445,10 +529,18 @@ static void mhi_status_cb(struct mhi_controller *mhi_cntrl, struct mhi_dev *mhi_dev = priv; struct device *dev = &mhi_dev->pci_dev->dev; if (reason == MHI_CB_IDLE) { MHI_LOG("Schedule runtime suspend 1\n"); switch (reason) { case MHI_CB_IDLE: MHI_LOG("Schedule runtime suspend\n"); pm_runtime_mark_last_busy(dev); pm_request_autosuspend(dev); break; case MHI_CB_BW_REQ: if (mhi_dev->bw_scale) mhi_dev->bw_scale(mhi_cntrl, mhi_dev); break; default: MHI_ERR("Unhandled cb:0x%x\n", reason); } } Loading Loading @@ -660,7 +752,6 @@ int mhi_pci_probe(struct pci_dev *pci_dev, } pm_runtime_mark_last_busy(&pci_dev->dev); pm_runtime_allow(&pci_dev->dev); MHI_LOG("Return successful\n"); Loading
drivers/bus/mhi/controllers/mhi_qcom.h +26 −6 Original line number Diff line number Diff line /* SPDX-License-Identifier: GPL-2.0-only */ /* Copyright (c) 2018, The Linux Foundation. All rights reserved.*/ /* Copyright (c) 2018-2019, The Linux Foundation. All rights reserved.*/ #ifndef _MHI_QCOM_ #define _MHI_QCOM_ Loading @@ -21,8 +21,18 @@ extern const char * const mhi_ee_str[MHI_EE_MAX]; #define TO_MHI_EXEC_STR(ee) (ee >= MHI_EE_MAX ? "INVALID_EE" : mhi_ee_str[ee]) enum mhi_suspend_mode { MHI_ACTIVE_STATE, MHI_DEFAULT_SUSPEND, MHI_FAST_LINK_OFF, MHI_FAST_LINK_ON, }; #define MHI_IS_SUSPENDED(mode) (mode) struct mhi_dev { struct pci_dev *pci_dev; bool drv_supported; u32 smmu_cfg; int resn; void *arch_info; Loading @@ -30,6 +40,11 @@ struct mhi_dev { dma_addr_t iova_start; dma_addr_t iova_stop; bool lpm_disabled; enum mhi_suspend_mode suspend_mode; /* if set, soc support dynamic bw scaling */ void (*bw_scale)(struct mhi_controller *mhi_cntrl, struct mhi_dev *mhi_dev); }; void mhi_deinit_pci_dev(struct mhi_controller *mhi_cntrl); Loading @@ -38,12 +53,13 @@ int mhi_pci_probe(struct pci_dev *pci_dev, #ifdef CONFIG_ARCH_QCOM int mhi_arch_power_up(struct mhi_controller *mhi_cntrl); int mhi_arch_pcie_init(struct mhi_controller *mhi_cntrl); void mhi_arch_pcie_deinit(struct mhi_controller *mhi_cntrl); int mhi_arch_iommu_init(struct mhi_controller *mhi_cntrl); void mhi_arch_iommu_deinit(struct mhi_controller *mhi_cntrl); int mhi_arch_link_off(struct mhi_controller *mhi_cntrl, bool graceful); int mhi_arch_link_on(struct mhi_controller *mhi_cntrl); int mhi_arch_link_suspend(struct mhi_controller *mhi_cntrl); int mhi_arch_link_resume(struct mhi_controller *mhi_cntrl); #else Loading @@ -69,13 +85,17 @@ static inline void mhi_arch_pcie_deinit(struct mhi_controller *mhi_cntrl) { } static inline int mhi_arch_link_off(struct mhi_controller *mhi_cntrl, bool graceful) static inline int mhi_arch_link_suspend(struct mhi_controller *mhi_cntrl) { return 0; } static inline int mhi_arch_link_resume(struct mhi_controller *mhi_cntrl) { return 0; } static inline int mhi_arch_link_on(struct mhi_controller *mhi_cntrl) static inline int mhi_arch_power_up(struct mhi_controller *mhi_cntrl) { return 0; } Loading
drivers/bus/mhi/core/mhi_init.c +107 −8 File changed.Preview size limit exceeded, changes collapsed. Show changes
drivers/bus/mhi/core/mhi_internal.h +7 −1 Original line number Diff line number Diff line Loading @@ -320,6 +320,8 @@ enum mhi_cmd_type { #define MHI_TRE_GET_EV_TIME(tre) ((tre)->ptr) #define MHI_TRE_GET_EV_COOKIE(tre) lower_32_bits((tre)->ptr) #define MHI_TRE_GET_EV_VEID(tre) (((tre)->dword[0] >> 16) & 0xFF) #define MHI_TRE_GET_EV_LINKSPEED(tre) (((tre)->dword[1] >> 24) & 0xFF) #define MHI_TRE_GET_EV_LINKWIDTH(tre) ((tre)->dword[0] & 0xFF) /* transfer descriptor macros */ #define MHI_TRE_DATA_PTR(ptr) (ptr) Loading Loading @@ -352,6 +354,7 @@ enum MHI_PKT_TYPE { MHI_PKT_TYPE_RSC_TX_EVENT = 0x28, MHI_PKT_TYPE_EE_EVENT = 0x40, MHI_PKT_TYPE_TSYNC_EVENT = 0x48, MHI_PKT_TYPE_BW_REQ_EVENT = 0x50, MHI_PKT_TYPE_STALE_EVENT, /* internal event */ }; Loading Loading @@ -451,7 +454,8 @@ enum MHI_PM_STATE { MHI_PM_SHUTDOWN_PROCESS | MHI_PM_FW_DL_ERR))) #define MHI_PM_IN_ERROR_STATE(pm_state) (pm_state >= MHI_PM_FW_DL_ERR) #define MHI_PM_IN_FATAL_STATE(pm_state) (pm_state == MHI_PM_LD_ERR_FATAL_DETECT) #define MHI_DB_ACCESS_VALID(pm_state) (pm_state & MHI_PM_M0) #define MHI_DB_ACCESS_VALID(mhi_cntrl) (mhi_cntrl->pm_state & \ mhi_cntrl->db_access) #define MHI_WAKE_DB_CLEAR_VALID(pm_state) (pm_state & (MHI_PM_M0 | \ MHI_PM_M2 | MHI_PM_M3_EXIT)) #define MHI_WAKE_DB_SET_VALID(pm_state) (pm_state & MHI_PM_M2) Loading Loading @@ -738,6 +742,8 @@ int mhi_get_capability_offset(struct mhi_controller *mhi_cntrl, u32 capability, int mhi_init_timesync(struct mhi_controller *mhi_cntrl); int mhi_create_timesync_sysfs(struct mhi_controller *mhi_cntrl); void mhi_destroy_timesync(struct mhi_controller *mhi_cntrl); int mhi_create_vote_sysfs(struct mhi_controller *mhi_cntrl); void mhi_destroy_vote_sysfs(struct mhi_controller *mhi_cntrl); /* memory allocation methods */ static inline void *mhi_alloc_coherent(struct mhi_controller *mhi_cntrl, Loading