From e1732cf6558265d26cc50fae0a204669c719f11a Mon Sep 17 00:00:00 2001 From: Andrei Danaila Date: Mon, 16 Mar 2015 13:46:55 -0700 Subject: [PATCH] mhi: core: Enable MHI reset operation Enable MHI reset for dynamic recovery of MHI transport errors. CRs-Fixed: 797757 Change-Id: I522503ab12d90d5391884772952960201f3585fb Signed-off-by: Andrei Danaila --- drivers/platform/msm/mhi/mhi.h | 8 ++- drivers/platform/msm/mhi/mhi_isr.c | 24 +++++++- drivers/platform/msm/mhi/mhi_main.c | 2 +- drivers/platform/msm/mhi/mhi_mmio_ops.c | 27 +++++++++ drivers/platform/msm/mhi/mhi_pm.c | 19 ++++++- drivers/platform/msm/mhi/mhi_states.c | 75 ++++++++++++++++++++++++- drivers/platform/msm/mhi/mhi_sys.h | 4 +- 7 files changed, 150 insertions(+), 9 deletions(-) diff --git a/drivers/platform/msm/mhi/mhi.h b/drivers/platform/msm/mhi/mhi.h index 3d5e074fc999..2ebcfd9613b9 100644 --- a/drivers/platform/msm/mhi/mhi.h +++ b/drivers/platform/msm/mhi/mhi.h @@ -23,6 +23,7 @@ #include #include #include +#include extern struct mhi_pcie_devices mhi_devices; @@ -115,7 +116,8 @@ enum MHI_STATE { MHI_STATE_M2 = 0x4, MHI_STATE_M3 = 0x5, MHI_STATE_BHI = 0x7, - MHI_STATE_LIMIT = 0x8, + MHI_STATE_SYS_ERR = 0x8, + MHI_STATE_LIMIT = 0x9, MHI_STATE_reserved = 0x80000000 }; @@ -170,6 +172,7 @@ enum MHI_PKT_TYPE { MHI_PKT_TYPE_CMD_COMPLETION_EVENT = 0x21, MHI_PKT_TYPE_TX_EVENT = 0x22, MHI_PKT_TYPE_EE_EVENT = 0x40, + MHI_PKT_TYPE_SYS_ERR_EVENT = 0xFF, }; struct __packed mhi_tx_pkt { @@ -541,6 +544,8 @@ enum MHI_STATUS parse_cmd_event(struct mhi_device_ctxt *ctxt, int parse_event_thread(void *ctxt); enum MHI_STATUS mhi_test_for_device_ready( struct mhi_device_ctxt *mhi_dev_ctxt); +enum MHI_STATUS mhi_test_for_device_reset( + struct mhi_device_ctxt *mhi_dev_ctxt); enum MHI_STATUS validate_ring_el_addr(struct mhi_ring *ring, uintptr_t addr); enum MHI_STATUS validate_ev_el_addr(struct mhi_ring *ring, uintptr_t addr); int mhi_state_change_thread(void *ctxt); @@ -590,5 +595,6 @@ u32 mhi_reg_read_field(void __iomem *io_addr, uintptr_t io_offset, void mhi_exit_m2(struct mhi_device_ctxt *mhi_dev_ctxt); int mhi_runtime_suspend(struct device *dev); int mhi_runtime_resume(struct device *dev); +enum MHI_STATUS mhi_trigger_reset(struct mhi_device_ctxt *mhi_dev_ctxt); #endif diff --git a/drivers/platform/msm/mhi/mhi_isr.c b/drivers/platform/msm/mhi/mhi_isr.c index ec907fa26af6..286d8ad25982 100644 --- a/drivers/platform/msm/mhi/mhi_isr.c +++ b/drivers/platform/msm/mhi/mhi_isr.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2014, The Linux Foundation. All rights reserved. +/* Copyright (c) 2014-2015, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -71,6 +71,7 @@ static enum MHI_STATUS mhi_process_event_ring( struct mhi_event_ctxt *ev_ctxt = NULL; struct mhi_ring *local_ev_ctxt = &mhi_dev_ctxt->mhi_local_event_ctxt[ev_index]; + enum MHI_STATUS ret_val = MHI_STATUS_SUCCESS; ev_ctxt = &mhi_dev_ctxt->mhi_ctrl_seg->mhi_ec_list[ev_index]; @@ -103,7 +104,7 @@ static enum MHI_STATUS mhi_process_event_ring( ev_index); __pm_stay_awake(&mhi_dev_ctxt->w_lock); __pm_relax(&mhi_dev_ctxt->w_lock); - parse_cmd_event(mhi_dev_ctxt, + ret_val = parse_cmd_event(mhi_dev_ctxt, &event_to_process); break; case MHI_PKT_TYPE_TX_EVENT: @@ -143,6 +144,13 @@ static enum MHI_STATUS mhi_process_event_ring( } break; } + case MHI_PKT_TYPE_SYS_ERR_EVENT: + mhi_log(MHI_MSG_INFO, + "MHI System Error Detected. Triggering Reset\n"); + if (!mhi_trigger_reset(mhi_dev_ctxt)) + mhi_log(MHI_MSG_ERROR, + "Failed to reset for SYSERR recovery\n"); + break; default: mhi_log(MHI_MSG_ERROR, "Unsupported packet type code 0x%x\n", @@ -154,6 +162,11 @@ static enum MHI_STATUS mhi_process_event_ring( device_rp = (union mhi_event_pkt *)mhi_p2v_addr( mhi_dev_ctxt->mhi_ctrl_seg_info, (u64)ev_ctxt->mhi_event_read_ptr); + if (mhi_dev_ctxt->mhi_state == MHI_STATE_SYS_ERR) { + mhi_log(MHI_MSG_ERROR, + "Detected system error, stopping.\n"); + return MHI_STATUS_ERROR; + } --event_quota; } return MHI_STATUS_SUCCESS; @@ -200,6 +213,13 @@ int parse_event_thread(void *ctxt) MHI_GET_EVENT_RING_INFO(EVENT_RING_POLLING, mhi_dev_ctxt->ev_ring_props[i], ev_poll_en) + if (mhi_dev_ctxt->mhi_state == MHI_STATE_SYS_ERR) { + mhi_log(MHI_MSG_INFO, + "SYS_ERR detected, not processing events\n"); + atomic_set(&mhi_dev_ctxt->flags.events_pending, + 0); + break; + } if (ev_poll_en) { mhi_process_event_ring(mhi_dev_ctxt, mhi_dev_ctxt->alloced_ev_rings[i], diff --git a/drivers/platform/msm/mhi/mhi_main.c b/drivers/platform/msm/mhi/mhi_main.c index 002629c4ba09..ba38bae8c7fc 100644 --- a/drivers/platform/msm/mhi/mhi_main.c +++ b/drivers/platform/msm/mhi/mhi_main.c @@ -239,7 +239,6 @@ enum MHI_STATUS mhi_open_channel(struct mhi_client_handle *client_handle) chan = client_handle->chan; mhi_log(MHI_MSG_INFO, "Entered: Client opening chan 0x%x\n", chan); - init_completion(&client_handle->chan_open_complete); mhi_dev_ctxt = client_handle->mhi_dev_ctxt; switch (mhi_dev_ctxt->dev_exec_env) { case MHI_EXEC_ENV_PBL: @@ -688,6 +687,7 @@ enum MHI_STATUS mhi_send_cmd(struct mhi_device_ctxt *mhi_dev_ctxt, break; case MHI_COMMAND_START_CHAN: switch (from_state) { + case MHI_CHAN_STATE_DISABLED: case MHI_CHAN_STATE_ENABLED: case MHI_CHAN_STATE_STOP: to_state = MHI_CHAN_STATE_RUNNING; diff --git a/drivers/platform/msm/mhi/mhi_mmio_ops.c b/drivers/platform/msm/mhi/mhi_mmio_ops.c index 8dbe4eedbaba..cb652753abab 100644 --- a/drivers/platform/msm/mhi/mhi_mmio_ops.c +++ b/drivers/platform/msm/mhi/mhi_mmio_ops.c @@ -13,6 +13,33 @@ #include "mhi_hwio.h" #include "mhi.h" +enum MHI_STATUS mhi_test_for_device_reset(struct mhi_device_ctxt *mhi_dev_ctxt) +{ + u32 pcie_word_val = 0; + u32 expiry_counter; + mhi_log(MHI_MSG_INFO, "Waiting for MMIO RESET bit to be cleared.\n"); + pcie_word_val = mhi_reg_read(mhi_dev_ctxt->mmio_addr, MHISTATUS); + MHI_READ_FIELD(pcie_word_val, + MHICTRL_RESET_MASK, + MHICTRL_RESET_SHIFT); + if (pcie_word_val == 0xFFFFFFFF) + return MHI_STATUS_LINK_DOWN; + while (MHI_STATE_RESET != pcie_word_val && expiry_counter < 100) { + expiry_counter++; + mhi_log(MHI_MSG_ERROR, + "Device is not RESET, sleeping and retrying.\n"); + msleep(MHI_READY_STATUS_TIMEOUT_MS); + pcie_word_val = mhi_reg_read(mhi_dev_ctxt->mmio_addr, MHICTRL); + MHI_READ_FIELD(pcie_word_val, + MHICTRL_RESET_MASK, + MHICTRL_RESET_SHIFT); + } + + if (MHI_STATE_READY != pcie_word_val) + return MHI_STATUS_DEVICE_NOT_READY; + return MHI_STATUS_SUCCESS; +} + enum MHI_STATUS mhi_test_for_device_ready(struct mhi_device_ctxt *mhi_dev_ctxt) { u32 pcie_word_val = 0; diff --git a/drivers/platform/msm/mhi/mhi_pm.c b/drivers/platform/msm/mhi/mhi_pm.c index 2838492299c8..24969e1ab36f 100644 --- a/drivers/platform/msm/mhi/mhi_pm.c +++ b/drivers/platform/msm/mhi/mhi_pm.c @@ -24,12 +24,14 @@ /* Write only sysfs attributes */ static DEVICE_ATTR(MHI_M3, S_IWUSR, NULL, sysfs_init_m3); static DEVICE_ATTR(MHI_M0, S_IWUSR, NULL, sysfs_init_m0); +static DEVICE_ATTR(MHI_RESET, S_IWUSR, NULL, sysfs_init_mhi_reset); /* Read only sysfs attributes */ static struct attribute *mhi_attributes[] = { &dev_attr_MHI_M3.attr, &dev_attr_MHI_M0.attr, + &dev_attr_MHI_RESET.attr, NULL, }; @@ -141,7 +143,22 @@ ssize_t sysfs_init_m3(struct device *dev, struct device_attribute *attr, return count; } - +ssize_t sysfs_init_mhi_reset(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct mhi_device_ctxt *mhi_dev_ctxt = + &mhi_devices.device_list[0].mhi_ctxt; + enum MHI_STATUS ret_val = MHI_STATUS_SUCCESS; + mhi_log(MHI_MSG_INFO, "Triggering MHI Reset.\n"); + ret_val = mhi_trigger_reset(mhi_dev_ctxt); + if (ret_val != MHI_STATUS_SUCCESS) + mhi_log(MHI_MSG_CRITICAL, + "Failed to trigger MHI RESET ret %d\n", + ret_val); + else + mhi_log(MHI_MSG_INFO, "Triggered! MHI RESET\n"); + return count; +} ssize_t sysfs_init_m0(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { diff --git a/drivers/platform/msm/mhi/mhi_states.c b/drivers/platform/msm/mhi/mhi_states.c index 208dc45336d0..3d6a1fc9db4e 100644 --- a/drivers/platform/msm/mhi/mhi_states.c +++ b/drivers/platform/msm/mhi/mhi_states.c @@ -254,7 +254,6 @@ static enum MHI_STATUS mhi_process_link_down( mhi_deassert_device_wake(mhi_dev_ctxt); write_unlock_irqrestore(&mhi_dev_ctxt->xfer_lock, flags); - mhi_dev_ctxt->flags.stop_threads = 1; while (!mhi_dev_ctxt->ev_thread_stopped) { @@ -415,6 +414,21 @@ static void mhi_reset_ev_ctxt(struct mhi_device_ctxt *mhi_dev_ctxt, local_ev_ctxt->wp = local_ev_ctxt->base; } +static enum MHI_STATUS mhi_set_state_of_all_channels( + struct mhi_device_ctxt *mhi_dev_ctxt, + enum MHI_CHAN_STATE new_state) +{ + u32 i = 0; + struct mhi_chan_ctxt *chan_ctxt = NULL; + if (new_state >= MHI_CHAN_STATE_LIMIT) + return MHI_STATUS_ERROR; + for (i = 0; i < MHI_MAX_CHANNELS; ++i) { + chan_ctxt = &mhi_dev_ctxt->mhi_ctrl_seg->mhi_cc_list[i]; + chan_ctxt->mhi_chan_state = new_state; + } + return MHI_STATUS_SUCCESS; +} + static enum MHI_STATUS process_reset_transition( struct mhi_device_ctxt *mhi_dev_ctxt, enum STATE_TRANSITION cur_work_item) @@ -422,9 +436,17 @@ static enum MHI_STATUS process_reset_transition( u32 i = 0; u32 ev_ring_index; enum MHI_STATUS ret_val = MHI_STATUS_SUCCESS; + unsigned long flags = 0; + mhi_log(MHI_MSG_INFO, "Processing RESET state transition\n"); + write_lock_irqsave(&mhi_dev_ctxt->xfer_lock, flags); + mhi_dev_ctxt->mhi_state = MHI_STATE_RESET; + write_unlock_irqrestore(&mhi_dev_ctxt->xfer_lock, flags); mhi_dev_ctxt->counters.mhi_reset_cntr++; mhi_dev_ctxt->dev_exec_env = MHI_EXEC_ENV_PBL; + ret_val = mhi_test_for_device_reset(mhi_dev_ctxt); + ret_val = mhi_set_state_of_all_channels(mhi_dev_ctxt, + MHI_CHAN_STATE_DISABLED); ret_val = mhi_test_for_device_ready(mhi_dev_ctxt); switch (ret_val) { case MHI_STATUS_SUCCESS: @@ -494,6 +516,7 @@ enum MHI_STATUS start_chan_sync(struct mhi_client_handle *client_handle) { enum MHI_STATUS ret_val = MHI_STATUS_SUCCESS; int r = 0; + init_completion(&client_handle->chan_open_complete); ret_val = mhi_send_cmd(client_handle->mhi_dev_ctxt, MHI_COMMAND_START_CHAN, client_handle->chan); @@ -587,6 +610,8 @@ static enum MHI_STATUS process_amss_transition( enum STATE_TRANSITION cur_work_item) { enum MHI_STATUS ret_val; + struct mhi_client_handle *client_handle = NULL; + int i = 0; mhi_log(MHI_MSG_INFO, "Processing AMSS state transition\n"); mhi_dev_ctxt->dev_exec_env = MHI_EXEC_ENV_AMSS; @@ -608,8 +633,21 @@ static enum MHI_STATUS process_amss_transition( mhi_log(MHI_MSG_CRITICAL, "Failed to probe MHI CORE clients, ret 0x%x\n", ret_val); + enable_clients(mhi_dev_ctxt, mhi_dev_ctxt->dev_exec_env); + } else { + mhi_log(MHI_MSG_INFO, "MHI is initialized\n"); + for (i = 0; i < MHI_MAX_CHANNELS; ++i) { + client_handle = mhi_dev_ctxt->client_handle_list[i]; + if (client_handle && client_handle->chan_status) + ret_val = start_chan_sync(client_handle); + if (ret_val) + mhi_log(MHI_MSG_ERROR, + "Failed to start chan %d ret %d\n", + i, ret_val); + + } + ring_all_chan_dbs(mhi_dev_ctxt); } - enable_clients(mhi_dev_ctxt, mhi_dev_ctxt->dev_exec_env); atomic_dec(&mhi_dev_ctxt->flags.data_pending); mhi_log(MHI_MSG_INFO, "Exited\n"); return MHI_STATUS_SUCCESS; @@ -618,11 +656,42 @@ static enum MHI_STATUS process_amss_transition( static void mhi_set_m_state(struct mhi_device_ctxt *mhi_dev_ctxt, enum MHI_STATE new_state) { - mhi_reg_write_field(mhi_dev_ctxt, + if (MHI_STATE_RESET == new_state) { + mhi_reg_write_field(mhi_dev_ctxt, + mhi_dev_ctxt->mmio_addr, MHICTRL, + MHICTRL_RESET_MASK, + MHICTRL_RESET_SHIFT, + 1); + } else { + mhi_reg_write_field(mhi_dev_ctxt, mhi_dev_ctxt->mmio_addr, MHICTRL, MHICTRL_MHISTATE_MASK, MHICTRL_MHISTATE_SHIFT, new_state); + } +} + +enum MHI_STATUS mhi_trigger_reset(struct mhi_device_ctxt *mhi_dev_ctxt) +{ + enum MHI_STATUS ret_val; + unsigned long flags = 0; + + mhi_log(MHI_MSG_INFO, "Entered\n"); + write_lock_irqsave(&mhi_dev_ctxt->xfer_lock, flags); + mhi_dev_ctxt->mhi_state = MHI_STATE_SYS_ERR; + write_unlock_irqrestore(&mhi_dev_ctxt->xfer_lock, flags); + + mhi_log(MHI_MSG_INFO, "Setting RESET to MDM.\n"); + mhi_set_m_state(mhi_dev_ctxt, MHI_STATE_RESET); + mhi_log(MHI_MSG_INFO, "Transitioning state to RESET\n"); + ret_val = mhi_init_state_transition(mhi_dev_ctxt, + STATE_TRANSITION_RESET); + if (MHI_STATUS_SUCCESS != ret_val) + mhi_log(MHI_MSG_CRITICAL, + "Failed to initiate 0x%x state trans ret %d\n", + STATE_TRANSITION_RESET, ret_val); + mhi_log(MHI_MSG_INFO, "Exiting\n"); + return ret_val; } static enum MHI_STATUS process_stt_work_item( diff --git a/drivers/platform/msm/mhi/mhi_sys.h b/drivers/platform/msm/mhi/mhi_sys.h index 9a9ad5df9f08..63b8e1e8ace8 100644 --- a/drivers/platform/msm/mhi/mhi_sys.h +++ b/drivers/platform/msm/mhi/mhi_sys.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2014, The Linux Foundation. All rights reserved. +/* Copyright (c) 2014-2015, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -75,5 +75,7 @@ ssize_t sysfs_init_m3(struct device *dev, struct device_attribute *attr, const char *buf, size_t count); ssize_t sysfs_init_m0(struct device *dev, struct device_attribute *attr, const char *buf, size_t count); +ssize_t sysfs_init_mhi_reset(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count); #endif