summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndrei Danaila <adanaila@codeaurora.org>2015-03-16 13:46:55 -0700
committerDavid Keitel <dkeitel@codeaurora.org>2016-03-23 21:11:24 -0700
commite1732cf6558265d26cc50fae0a204669c719f11a (patch)
tree566768f70faf082b4bab62766497b25f75e4d6bf
parent0af9a4590b3a25a409607615c1c319c79f85722c (diff)
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 <adanaila@codeaurora.org>
-rw-r--r--drivers/platform/msm/mhi/mhi.h8
-rw-r--r--drivers/platform/msm/mhi/mhi_isr.c24
-rw-r--r--drivers/platform/msm/mhi/mhi_main.c2
-rw-r--r--drivers/platform/msm/mhi/mhi_mmio_ops.c27
-rw-r--r--drivers/platform/msm/mhi/mhi_pm.c19
-rw-r--r--drivers/platform/msm/mhi/mhi_states.c75
-rw-r--r--drivers/platform/msm/mhi/mhi_sys.h4
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 <linux/sched.h>
#include <linux/cdev.h>
#include <mach/msm_pcie.h>
+#include <linux/sched.h>
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