M7350/kernel/drivers/platform/msm/mhi/mhi_ssr.c
2024-09-09 08:57:42 +00:00

256 lines
7.1 KiB
C

/* 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
* only version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <mhi_sys.h>
#include <mhi.h>
#include <soc/qcom/subsystem_restart.h>
#include <soc/qcom/subsystem_notif.h>
#include <linux/esoc_client.h>
static int mhi_ssr_notify_cb(struct notifier_block *nb,
unsigned long action, void *data)
{
int ret_val = 0;
struct mhi_device_ctxt *mhi_dev_ctxt =
&mhi_devices.device_list[0].mhi_ctxt;
struct mhi_pcie_dev_info *mhi_pcie_dev = NULL;
mhi_pcie_dev = &mhi_devices.device_list[mhi_devices.nr_of_devices];
if (NULL != mhi_dev_ctxt)
mhi_dev_ctxt->esoc_notif = action;
switch (action) {
case SUBSYS_BEFORE_POWERUP:
mhi_log(MHI_MSG_INFO,
"Received Subsystem event BEFORE_POWERUP\n");
atomic_set(&mhi_dev_ctxt->flags.pending_powerup, 1);
ret_val = init_mhi_base_state(mhi_dev_ctxt);
if (MHI_STATUS_SUCCESS != ret_val)
mhi_log(MHI_MSG_CRITICAL,
"Failed to transition to base state %d.\n",
ret_val);
break;
case SUBSYS_AFTER_POWERUP:
mhi_log(MHI_MSG_INFO,
"Received Subsystem event AFTER_POWERUP\n");
break;
case SUBSYS_POWERUP_FAILURE:
mhi_log(MHI_MSG_INFO,
"Received Subsystem event POWERUP_FAILURE\n");
break;
case SUBSYS_BEFORE_SHUTDOWN:
mhi_log(MHI_MSG_INFO,
"Received Subsystem event BEFORE_SHUTDOWN\n");
mhi_log(MHI_MSG_INFO,
"Not notifying clients\n");
break;
case SUBSYS_AFTER_SHUTDOWN:
mhi_log(MHI_MSG_INFO,
"Received Subsystem event AFTER_SHUTDOWN\n");
mhi_log(MHI_MSG_INFO,
"Not notifying clients\n");
break;
case SUBSYS_RAMDUMP_NOTIFICATION:
mhi_log(MHI_MSG_INFO,
"Received Subsystem event RAMDUMP\n");
mhi_log(MHI_MSG_INFO,
"Not notifying clients\n");
break;
default:
mhi_log(MHI_MSG_INFO,
"Received ESOC notifcation %d, NOT handling\n",
(int)action);
break;
}
return NOTIFY_OK;
}
static struct notifier_block mhi_ssr_nb = {
.notifier_call = mhi_ssr_notify_cb,
};
static void esoc_parse_link_type(struct mhi_device_ctxt *mhi_dev_ctxt)
{
int ret_val;
ret_val = strcmp(mhi_dev_ctxt->esoc_handle->link, "HSIC+PCIe");
mhi_log(MHI_MSG_VERBOSE, "Link type is %s as indicated by ESOC\n",
mhi_dev_ctxt->esoc_handle->link);
if (ret_val)
mhi_dev_ctxt->base_state = STATE_TRANSITION_BHI;
else
mhi_dev_ctxt->base_state = STATE_TRANSITION_RESET;
}
int mhi_esoc_register(struct mhi_device_ctxt *mhi_dev_ctxt)
{
int ret_val = 0;
struct device_node *np;
struct pci_driver *mhi_driver;
struct device *dev = &mhi_dev_ctxt->dev_info->pcie_device->dev;
mhi_dev_ctxt->base_state = STATE_TRANSITION_BHI;
mhi_driver = mhi_dev_ctxt->dev_info->mhi_pcie_driver;
np = dev->of_node;
mhi_dev_ctxt->esoc_handle = devm_register_esoc_client(dev, "mdm");
mhi_log(MHI_MSG_VERBOSE,
"Of table of pcie struct device property is dev->of_node %p\n",
np);
if (IS_ERR_OR_NULL(mhi_dev_ctxt->esoc_handle)) {
mhi_log(MHI_MSG_CRITICAL,
"Failed to register for SSR, ret %lx\n",
(uintptr_t)mhi_dev_ctxt->esoc_handle);
return -EIO;
}
esoc_parse_link_type(mhi_dev_ctxt);
mhi_dev_ctxt->esoc_ssr_handle = subsys_notif_register_notifier(
mhi_dev_ctxt->esoc_handle->name,
&mhi_ssr_nb);
if (IS_ERR_OR_NULL(mhi_dev_ctxt->esoc_ssr_handle)) {
ret_val = PTR_RET(mhi_dev_ctxt->esoc_ssr_handle);
mhi_log(MHI_MSG_CRITICAL,
"Can't find esoc desc ret 0x%lx\n",
(uintptr_t)mhi_dev_ctxt->esoc_ssr_handle);
}
return ret_val;
}
void mhi_notify_client(struct mhi_client_handle *client_handle,
enum MHI_CB_REASON reason)
{
struct mhi_cb_info cb_info = {0};
struct mhi_result result = {0};
cb_info.result = NULL;
cb_info.cb_reason = reason;
if (NULL != client_handle &&
NULL != client_handle->client_info.mhi_client_cb) {
result.user_data = client_handle->user_data;
cb_info.chan = client_handle->chan_info.chan_nr;
cb_info.result = &result;
mhi_log(MHI_MSG_INFO, "Calling back for chan %d, reason %d\n",
cb_info.chan, reason);
client_handle->client_info.mhi_client_cb(&cb_info);
}
}
void mhi_notify_clients(struct mhi_device_ctxt *mhi_dev_ctxt,
enum MHI_CB_REASON reason)
{
int i;
struct mhi_client_handle *client_handle = NULL;
for (i = 0; i < MHI_MAX_CHANNELS; ++i) {
if (VALID_CHAN_NR(i)) {
client_handle = mhi_dev_ctxt->client_handle_list[i];
mhi_notify_client(client_handle, reason);
}
}
}
void mhi_link_state_cb(struct msm_pcie_notify *notify)
{
enum MHI_STATUS ret_val = MHI_STATUS_SUCCESS;
struct mhi_pcie_dev_info *mhi_pcie_dev = notify->data;
struct mhi_device_ctxt *mhi_dev_ctxt = NULL;
int r = 0;
if (NULL == notify || NULL == notify->data) {
mhi_log(MHI_MSG_CRITICAL,
"Incomplete handle received\n");
return;
}
mhi_dev_ctxt = &mhi_pcie_dev->mhi_ctxt;
switch (notify->event) {
case MSM_PCIE_EVENT_LINKDOWN:
mhi_log(MHI_MSG_INFO, "Received MSM_PCIE_EVENT_LINKDOWN\n");
break;
case MSM_PCIE_EVENT_LINKUP:
mhi_log(MHI_MSG_INFO,
"Received MSM_PCIE_EVENT_LINKUP\n");
if (0 == mhi_pcie_dev->link_up_cntr) {
mhi_log(MHI_MSG_INFO,
"Initializing MHI for the first time\n");
r = mhi_ctxt_init(mhi_pcie_dev);
if (r) {
mhi_log(MHI_MSG_ERROR,
"MHI initialization failed, ret %d.\n",
r);
r = msm_pcie_register_event(
&mhi_pcie_dev->mhi_pci_link_event);
mhi_log(MHI_MSG_ERROR,
"Deregistered from PCIe notif r %d.\n",
r);
return;
}
mhi_dev_ctxt = &mhi_pcie_dev->mhi_ctxt;
mhi_pcie_dev->mhi_ctxt.flags.link_up = 1;
pci_set_master(mhi_pcie_dev->pcie_device);
init_mhi_base_state(mhi_dev_ctxt);
} else {
mhi_log(MHI_MSG_INFO,
"Received Link Up Callback\n");
}
mhi_pcie_dev->link_up_cntr++;
break;
case MSM_PCIE_EVENT_WAKEUP:
mhi_log(MHI_MSG_INFO,
"Received MSM_PCIE_EVENT_WAKE\n");
__pm_stay_awake(&mhi_dev_ctxt->w_lock);
__pm_relax(&mhi_dev_ctxt->w_lock);
if (atomic_read(&mhi_dev_ctxt->flags.pending_resume)) {
mhi_log(MHI_MSG_INFO,
"There is a pending resume, doing nothing.\n");
return;
}
ret_val = mhi_init_state_transition(mhi_dev_ctxt,
STATE_TRANSITION_WAKE);
if (MHI_STATUS_SUCCESS != ret_val) {
mhi_log(MHI_MSG_CRITICAL,
"Failed to init state transition, to %d\n",
STATE_TRANSITION_WAKE);
}
break;
default:
mhi_log(MHI_MSG_INFO,
"Received bad link event\n");
return;
}
}
enum MHI_STATUS init_mhi_base_state(struct mhi_device_ctxt *mhi_dev_ctxt)
{
int r = 0;
enum MHI_STATUS ret_val = MHI_STATUS_SUCCESS;
mhi_assert_device_wake(mhi_dev_ctxt);
mhi_dev_ctxt->flags.link_up = 1;
r =
mhi_set_bus_request(mhi_dev_ctxt, 1);
if (r)
mhi_log(MHI_MSG_INFO,
"Failed to scale bus request to active set.\n");
ret_val = mhi_init_state_transition(mhi_dev_ctxt,
mhi_dev_ctxt->base_state);
if (MHI_STATUS_SUCCESS != ret_val) {
mhi_log(MHI_MSG_CRITICAL,
"Failed to start state change event, to %d\n",
mhi_dev_ctxt->base_state);
}
return ret_val;
}