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/sched.h>
#include <linux/cdev.h> #include <linux/cdev.h>
#include <mach/msm_pcie.h> #include <mach/msm_pcie.h>
#include <linux/sched.h>
extern struct mhi_pcie_devices mhi_devices; extern struct mhi_pcie_devices mhi_devices;
@ -115,7 +116,8 @@ enum MHI_STATE {
MHI_STATE_M2 = 0x4, MHI_STATE_M2 = 0x4,
MHI_STATE_M3 = 0x5, MHI_STATE_M3 = 0x5,
MHI_STATE_BHI = 0x7, MHI_STATE_BHI = 0x7,
MHI_STATE_LIMIT = 0x8, MHI_STATE_SYS_ERR = 0x8,
MHI_STATE_LIMIT = 0x9,
MHI_STATE_reserved = 0x80000000 MHI_STATE_reserved = 0x80000000
}; };
@ -170,6 +172,7 @@ enum MHI_PKT_TYPE {
MHI_PKT_TYPE_CMD_COMPLETION_EVENT = 0x21, MHI_PKT_TYPE_CMD_COMPLETION_EVENT = 0x21,
MHI_PKT_TYPE_TX_EVENT = 0x22, MHI_PKT_TYPE_TX_EVENT = 0x22,
MHI_PKT_TYPE_EE_EVENT = 0x40, MHI_PKT_TYPE_EE_EVENT = 0x40,
MHI_PKT_TYPE_SYS_ERR_EVENT = 0xFF,
}; };
struct __packed mhi_tx_pkt { 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); int parse_event_thread(void *ctxt);
enum MHI_STATUS mhi_test_for_device_ready( enum MHI_STATUS mhi_test_for_device_ready(
struct mhi_device_ctxt *mhi_dev_ctxt); 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_ring_el_addr(struct mhi_ring *ring, uintptr_t addr);
enum MHI_STATUS validate_ev_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); 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); void mhi_exit_m2(struct mhi_device_ctxt *mhi_dev_ctxt);
int mhi_runtime_suspend(struct device *dev); int mhi_runtime_suspend(struct device *dev);
int mhi_runtime_resume(struct device *dev); int mhi_runtime_resume(struct device *dev);
enum MHI_STATUS mhi_trigger_reset(struct mhi_device_ctxt *mhi_dev_ctxt);
#endif #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 * 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 * 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_event_ctxt *ev_ctxt = NULL;
struct mhi_ring *local_ev_ctxt = struct mhi_ring *local_ev_ctxt =
&mhi_dev_ctxt->mhi_local_event_ctxt[ev_index]; &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]; 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); ev_index);
__pm_stay_awake(&mhi_dev_ctxt->w_lock); __pm_stay_awake(&mhi_dev_ctxt->w_lock);
__pm_relax(&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); &event_to_process);
break; break;
case MHI_PKT_TYPE_TX_EVENT: case MHI_PKT_TYPE_TX_EVENT:
@ -143,6 +144,13 @@ static enum MHI_STATUS mhi_process_event_ring(
} }
break; 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: default:
mhi_log(MHI_MSG_ERROR, mhi_log(MHI_MSG_ERROR,
"Unsupported packet type code 0x%x\n", "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( device_rp = (union mhi_event_pkt *)mhi_p2v_addr(
mhi_dev_ctxt->mhi_ctrl_seg_info, mhi_dev_ctxt->mhi_ctrl_seg_info,
(u64)ev_ctxt->mhi_event_read_ptr); (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; --event_quota;
} }
return MHI_STATUS_SUCCESS; return MHI_STATUS_SUCCESS;
@ -200,6 +213,13 @@ int parse_event_thread(void *ctxt)
MHI_GET_EVENT_RING_INFO(EVENT_RING_POLLING, MHI_GET_EVENT_RING_INFO(EVENT_RING_POLLING,
mhi_dev_ctxt->ev_ring_props[i], mhi_dev_ctxt->ev_ring_props[i],
ev_poll_en) 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) { if (ev_poll_en) {
mhi_process_event_ring(mhi_dev_ctxt, mhi_process_event_ring(mhi_dev_ctxt,
mhi_dev_ctxt->alloced_ev_rings[i], 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; chan = client_handle->chan;
mhi_log(MHI_MSG_INFO, mhi_log(MHI_MSG_INFO,
"Entered: Client opening chan 0x%x\n", chan); "Entered: Client opening chan 0x%x\n", chan);
init_completion(&client_handle->chan_open_complete);
mhi_dev_ctxt = client_handle->mhi_dev_ctxt; mhi_dev_ctxt = client_handle->mhi_dev_ctxt;
switch (mhi_dev_ctxt->dev_exec_env) { switch (mhi_dev_ctxt->dev_exec_env) {
case MHI_EXEC_ENV_PBL: case MHI_EXEC_ENV_PBL:
@ -688,6 +687,7 @@ enum MHI_STATUS mhi_send_cmd(struct mhi_device_ctxt *mhi_dev_ctxt,
break; break;
case MHI_COMMAND_START_CHAN: case MHI_COMMAND_START_CHAN:
switch (from_state) { switch (from_state) {
case MHI_CHAN_STATE_DISABLED:
case MHI_CHAN_STATE_ENABLED: case MHI_CHAN_STATE_ENABLED:
case MHI_CHAN_STATE_STOP: case MHI_CHAN_STATE_STOP:
to_state = MHI_CHAN_STATE_RUNNING; to_state = MHI_CHAN_STATE_RUNNING;

View file

@ -13,6 +13,33 @@
#include "mhi_hwio.h" #include "mhi_hwio.h"
#include "mhi.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) enum MHI_STATUS mhi_test_for_device_ready(struct mhi_device_ctxt *mhi_dev_ctxt)
{ {
u32 pcie_word_val = 0; u32 pcie_word_val = 0;

View file

@ -24,12 +24,14 @@
/* Write only sysfs attributes */ /* Write only sysfs attributes */
static DEVICE_ATTR(MHI_M3, S_IWUSR, NULL, sysfs_init_m3); 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_M0, S_IWUSR, NULL, sysfs_init_m0);
static DEVICE_ATTR(MHI_RESET, S_IWUSR, NULL, sysfs_init_mhi_reset);
/* Read only sysfs attributes */ /* Read only sysfs attributes */
static struct attribute *mhi_attributes[] = { static struct attribute *mhi_attributes[] = {
&dev_attr_MHI_M3.attr, &dev_attr_MHI_M3.attr,
&dev_attr_MHI_M0.attr, &dev_attr_MHI_M0.attr,
&dev_attr_MHI_RESET.attr,
NULL, NULL,
}; };
@ -141,7 +143,22 @@ ssize_t sysfs_init_m3(struct device *dev, struct device_attribute *attr,
return count; 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, ssize_t sysfs_init_m0(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count) 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); mhi_deassert_device_wake(mhi_dev_ctxt);
write_unlock_irqrestore(&mhi_dev_ctxt->xfer_lock, flags); write_unlock_irqrestore(&mhi_dev_ctxt->xfer_lock, flags);
mhi_dev_ctxt->flags.stop_threads = 1; mhi_dev_ctxt->flags.stop_threads = 1;
while (!mhi_dev_ctxt->ev_thread_stopped) { 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; 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( static enum MHI_STATUS process_reset_transition(
struct mhi_device_ctxt *mhi_dev_ctxt, struct mhi_device_ctxt *mhi_dev_ctxt,
enum STATE_TRANSITION cur_work_item) enum STATE_TRANSITION cur_work_item)
@ -422,9 +436,17 @@ static enum MHI_STATUS process_reset_transition(
u32 i = 0; u32 i = 0;
u32 ev_ring_index; u32 ev_ring_index;
enum MHI_STATUS ret_val = MHI_STATUS_SUCCESS; enum MHI_STATUS ret_val = MHI_STATUS_SUCCESS;
unsigned long flags = 0;
mhi_log(MHI_MSG_INFO, "Processing RESET state transition\n"); 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->counters.mhi_reset_cntr++;
mhi_dev_ctxt->dev_exec_env = MHI_EXEC_ENV_PBL; 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); ret_val = mhi_test_for_device_ready(mhi_dev_ctxt);
switch (ret_val) { switch (ret_val) {
case MHI_STATUS_SUCCESS: 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; enum MHI_STATUS ret_val = MHI_STATUS_SUCCESS;
int r = 0; int r = 0;
init_completion(&client_handle->chan_open_complete);
ret_val = mhi_send_cmd(client_handle->mhi_dev_ctxt, ret_val = mhi_send_cmd(client_handle->mhi_dev_ctxt,
MHI_COMMAND_START_CHAN, MHI_COMMAND_START_CHAN,
client_handle->chan); client_handle->chan);
@ -587,6 +610,8 @@ static enum MHI_STATUS process_amss_transition(
enum STATE_TRANSITION cur_work_item) enum STATE_TRANSITION cur_work_item)
{ {
enum MHI_STATUS ret_val; 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_log(MHI_MSG_INFO, "Processing AMSS state transition\n");
mhi_dev_ctxt->dev_exec_env = MHI_EXEC_ENV_AMSS; 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, mhi_log(MHI_MSG_CRITICAL,
"Failed to probe MHI CORE clients, ret 0x%x\n", "Failed to probe MHI CORE clients, ret 0x%x\n",
ret_val); 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); atomic_dec(&mhi_dev_ctxt->flags.data_pending);
mhi_log(MHI_MSG_INFO, "Exited\n"); mhi_log(MHI_MSG_INFO, "Exited\n");
return MHI_STATUS_SUCCESS; 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, static void mhi_set_m_state(struct mhi_device_ctxt *mhi_dev_ctxt,
enum MHI_STATE new_state) 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, mhi_dev_ctxt->mmio_addr, MHICTRL,
MHICTRL_MHISTATE_MASK, MHICTRL_MHISTATE_MASK,
MHICTRL_MHISTATE_SHIFT, MHICTRL_MHISTATE_SHIFT,
new_state); 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( 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 * 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 * 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); const char *buf, size_t count);
ssize_t sysfs_init_m0(struct device *dev, struct device_attribute *attr, ssize_t sysfs_init_m0(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count); 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 #endif