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:
Andrei Danaila 2015-03-16 13:46:55 -07:00 committed by David Keitel
parent 0af9a4590b
commit e1732cf655
7 changed files with 150 additions and 9 deletions

View file

@ -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

View file

@ -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],

View file

@ -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;

View file

@ -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;

View file

@ -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)
{

View file

@ -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(

View file

@ -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