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>
This commit is contained in:
parent
0af9a4590b
commit
e1732cf655
7 changed files with 150 additions and 9 deletions
|
@ -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
|
||||
|
|
|
@ -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],
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Add table
Reference in a new issue