Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 64120b17 authored by Linux Build Service Account's avatar Linux Build Service Account Committed by Gerrit - the friendly Code Review server
Browse files

Merge "msm: mhi_dev: Support device reset"

parents 7c366cc7 c2fd7892
Loading
Loading
Loading
Loading
+259 −20
Original line number Diff line number Diff line
@@ -37,8 +37,8 @@
/* Wait time on the device for Host to set M0 state */
#define MHI_DEV_M0_MAX_CNT		30
/* Wait time before suspend/resume is complete */
#define MHI_SUSPEND_MIN			10000
#define MHI_SUSPEND_TIMEOUT		3000
#define MHI_SUSPEND_MIN			100
#define MHI_SUSPEND_TIMEOUT		600
#define MHI_MASK_CH_EV_LEN		32
#define MHI_RING_CMD_ID			0
#define MHI_RING_PRIMARY_EVT_ID		1
@@ -58,6 +58,8 @@

#define MHI_IPC_LOG_PAGES		(100)
#define MHI_REGLEN			0x100
#define MHI_INIT			0
#define MHI_REINIT			1

enum mhi_msg_level mhi_msg_lvl = MHI_MSG_ERROR;
enum mhi_msg_level mhi_ipc_msg_lvl = MHI_MSG_VERBOSE;
@@ -68,6 +70,8 @@ static void mhi_hwc_cb(void *priv, enum ipa_mhi_event_type event,
	unsigned long data);
static void mhi_ring_init_cb(void *user_data);
static void mhi_update_state_info(uint32_t info);
static int mhi_deinit(struct mhi_dev *mhi);
static void mhi_dev_resume_init_with_link_up(struct ep_pcie_notify *notify);

void mhi_dev_read_from_host(struct mhi_dev *mhi, struct mhi_addr *transfer)
{
@@ -386,6 +390,9 @@ static void mhi_hwc_cb(void *priv, enum ipa_mhi_event_type event,
		}

		mhi_update_state_info(MHI_STATE_CONNECTED);

		ep_pcie_mask_irq_event(mhi_ctx->phandle,
				EP_PCIE_INT_EVT_MHI_A7, true);
		break;
	case IPA_MHI_EVENT_DATA_AVAILABLE:
		rc = mhi_dev_notify_sm_event(MHI_DEV_EVENT_HW_ACC_WAKEUP);
@@ -1087,6 +1094,82 @@ static void mhi_dev_check_channel_interrupt(struct mhi_dev *mhi)
	}
}

static int mhi_dev_abort(struct mhi_dev *mhi)
{
	struct mhi_dev_channel *ch;
	struct mhi_dev_ring *ring;
	int ch_id = 0, rc = 0;

	/* Hard stop all the channels */
	for (ch_id = 0; ch_id < mhi->cfg.channels; ch_id++) {
		ring = &mhi->ring[ch_id + mhi->ch_ring_start];
		if (ring->state == RING_STATE_UINT)
			continue;

		ch = &mhi->ch[ch_id];
		mutex_lock(&ch->ch_lock);
		mhi->ch[ch_id].state = MHI_DEV_CH_STOPPED;
		mutex_unlock(&ch->ch_lock);
	}

	/* Update ctrl node */
	mhi_update_state_info(MHI_STATE_DISCONNECTED);

	flush_workqueue(mhi->ring_init_wq);
	flush_workqueue(mhi->pending_ring_wq);

	/* Initiate MHI IPA reset */
	ipa_mhi_destroy();

	/* Clean up initialized channels */
	rc = mhi_deinit(mhi);
	if (rc) {
		pr_err("Error during mhi_deinit with %d\n", rc);
		return rc;
	}

	rc = mhi_dev_mmio_mask_chdb_interrupts(mhi_ctx);
	if (rc) {
		pr_err("Failed to enable channel db\n");
		return rc;
	}

	rc = mhi_dev_mmio_disable_ctrl_interrupt(mhi_ctx);
	if (rc) {
		pr_err("Failed to enable control interrupt\n");
		return rc;
	}

	rc = mhi_dev_mmio_disable_cmdb_interrupt(mhi_ctx);
	if (rc) {
		pr_err("Failed to enable command db\n");
		return rc;
	}


	atomic_set(&mhi_ctx->re_init_done, 0);

	mhi_log(MHI_MSG_INFO,
			"Register a PCIe callback during re-init\n");
	mhi_ctx->event_reg.events = EP_PCIE_EVENT_LINKUP;
	mhi_ctx->event_reg.user = mhi_ctx;
	mhi_ctx->event_reg.mode = EP_PCIE_TRIGGER_CALLBACK;
	mhi_ctx->event_reg.callback = mhi_dev_resume_init_with_link_up;
	mhi_ctx->event_reg.options = MHI_REINIT;

	rc = ep_pcie_register_event(mhi_ctx->phandle,
					&mhi_ctx->event_reg);
	if (rc) {
		pr_err("Failed to register for events from PCIe\n");
		return rc;
	}

	/* Set RESET field to 0 */
	mhi_dev_mmio_reset(mhi_ctx);

	return rc;
}

static void mhi_dev_scheduler(struct work_struct *work)
{
	struct mhi_dev *mhi = container_of(work,
@@ -1096,6 +1179,7 @@ static void mhi_dev_scheduler(struct work_struct *work)
	struct mhi_dev_ring *ring;
	enum mhi_dev_state state;
	enum mhi_dev_event event = 0;
	bool mhi_reset = false;

	mutex_lock(&mhi_ctx->mhi_lock);
	/* Check for interrupts */
@@ -1104,25 +1188,34 @@ static void mhi_dev_scheduler(struct work_struct *work)
	if (int_value & MHI_MMIO_CTRL_INT_STATUS_A7_MSK) {
		mhi_log(MHI_MSG_VERBOSE,
			"processing ctrl interrupt with %d\n", int_value);
		rc = mhi_dev_mmio_get_mhi_state(mhi, &state);
		rc = mhi_dev_mmio_get_mhi_state(mhi, &state, &mhi_reset);
		if (rc) {
			pr_err("%s: get mhi state failed\n", __func__);
			mutex_unlock(&mhi_ctx->mhi_lock);
			return;
		}

		if (mhi_reset) {
			mhi_log(MHI_MSG_VERBOSE,
				"processing mhi device reset\n");
			rc = mhi_dev_abort(mhi);
			if (rc)
				pr_err("device reset failed:%d\n", rc);
			mutex_unlock(&mhi_ctx->mhi_lock);
			queue_work(mhi->ring_init_wq, &mhi->re_init);
			return;
		}

		rc = mhi_dev_get_event_notify(state, &event);
		if (rc) {
			pr_err("unsupported state :%d\n", state);
			mutex_unlock(&mhi_ctx->mhi_lock);
			return;
			goto fail;
		}

		rc = mhi_dev_notify_sm_event(event);
		if (rc) {
			pr_err("error sending SM event\n");
			mutex_unlock(&mhi_ctx->mhi_lock);
			return;
			goto fail;
		}
	}

@@ -1137,6 +1230,7 @@ static void mhi_dev_scheduler(struct work_struct *work)
	/* get the specific channel interrupts */
	mhi_dev_check_channel_interrupt(mhi);

fail:
	mutex_unlock(&mhi_ctx->mhi_lock);

	if (mhi->config_iatu || mhi->mhi_int)
@@ -1389,7 +1483,6 @@ int mhi_dev_suspend(struct mhi_dev *mhi)
		mhi_dev_write_to_host(mhi, &data_transfer);

	}
	mhi_update_state_info(MHI_STATE_DISCONNECTED);

	atomic_set(&mhi->mhi_dev_wake, 0);
	pm_relax(mhi->dev);
@@ -1616,6 +1709,11 @@ int mhi_dev_read_channel(struct mhi_dev_client *handle_client,
		return -ENXIO;
	}

	if (mhi_ctx->ctrl_info != MHI_STATE_CONNECTED) {
		pr_err("Channel not connected:%d\n", mhi_ctx->ctrl_info);
		return -ENODEV;
	}

	ch = handle_client->channel;
	ring = ch->ring;
	ch_id = ch->ch_id;
@@ -1735,6 +1833,11 @@ int mhi_dev_write_channel(struct mhi_dev_client *handle_client,
		return -ENXIO;
	}

	if (mhi_ctx->ctrl_info != MHI_STATE_CONNECTED) {
		pr_err("Channel not connected:%d\n", mhi_ctx->ctrl_info);
		return -ENODEV;
	}

	mutex_lock(&mhi_ctx->mhi_write_test);

	if (atomic_read(&mhi_ctx->is_suspended)) {
@@ -1850,7 +1953,7 @@ static void mhi_dev_enable(struct work_struct *work)
	struct ep_pcie_msi_config msi_cfg;
	struct mhi_dev *mhi = container_of(work,
				struct mhi_dev, ring_init_cb_work);

	bool mhi_reset;
	enum mhi_dev_state state;
	uint32_t max_cnt = 0, bhi_intvec = 0;

@@ -1898,7 +2001,7 @@ static void mhi_dev_enable(struct work_struct *work)
		}
	}

	rc = mhi_dev_mmio_get_mhi_state(mhi, &state);
	rc = mhi_dev_mmio_get_mhi_state(mhi, &state, &mhi_reset);
	if (rc) {
		pr_err("%s: get mhi state failed\n", __func__);
		return;
@@ -1907,7 +2010,7 @@ static void mhi_dev_enable(struct work_struct *work)
	while (state != MHI_DEV_M0_STATE && max_cnt < MHI_SUSPEND_TIMEOUT) {
		/* Wait for Host to set the M0 state */
		msleep(MHI_SUSPEND_MIN);
		rc = mhi_dev_mmio_get_mhi_state(mhi, &state);
		rc = mhi_dev_mmio_get_mhi_state(mhi, &state, &mhi_reset);
		if (rc) {
			pr_err("%s: get mhi state failed\n", __func__);
			return;
@@ -2096,6 +2199,39 @@ static int get_device_tree_data(struct platform_device *pdev)
	return 0;
}

static int mhi_deinit(struct mhi_dev *mhi)
{
	int rc = 0, i = 0, ring_id = 0;
	struct mhi_dev_ring *ring;
	struct platform_device *pdev = mhi->pdev;

	ring_id = mhi->cfg.channels + mhi->cfg.event_rings + 1;

	for (i = 0; i < ring_id; i++) {
		ring = &mhi->ring[i];
		if (ring->state == RING_STATE_UINT)
			continue;

		dma_free_coherent(mhi->dev, ring->ring_size *
			sizeof(union mhi_dev_ring_element_type),
			ring->ring_cache,
			ring->ring_cache_dma_handle);
	}

	for (i = 0; i < mhi->cfg.channels; i++)
		mutex_destroy(&mhi->ch[i].ch_lock);

	devm_kfree(&pdev->dev, mhi->mmio_backup);
	devm_kfree(&pdev->dev, mhi->ch);
	devm_kfree(&pdev->dev, mhi->ring);

	mhi_dev_sm_exit(mhi);

	mhi->mmio_initialized = false;

	return rc;
}

static int mhi_init(struct mhi_dev *mhi)
{
	int rc = 0, i = 0;
@@ -2129,15 +2265,101 @@ static int mhi_init(struct mhi_dev *mhi)
	if (!mhi->mmio_backup)
		return -ENOMEM;

	mhi_ipc_log = ipc_log_context_create(MHI_IPC_LOG_PAGES, "mhi", 0);
	if (mhi_ipc_log == NULL) {
		dev_err(&pdev->dev,
				"Failed to create IPC logging context\n");
	return 0;
}

static int mhi_dev_resume_mmio_mhi_reinit(struct mhi_dev *mhi_ctx)
{
	int rc = 0;

	mutex_lock(&mhi_ctx->mhi_lock);
	if (atomic_read(&mhi_ctx->re_init_done)) {
		mhi_log(MHI_MSG_INFO, "Re_init done, return\n");
		mutex_unlock(&mhi_ctx->mhi_lock);
		return 0;
	}

	rc = mhi_init(mhi_ctx);
	if (rc) {
		pr_err("Error initializing MHI MMIO with %d\n", rc);
		goto fail;
	}

	mhi_ctx->event_reg.events = EP_PCIE_EVENT_PM_D3_HOT |
		EP_PCIE_EVENT_PM_D3_COLD |
		EP_PCIE_EVENT_PM_D0 |
		EP_PCIE_EVENT_PM_RST_DEAST |
		EP_PCIE_EVENT_MHI_A7 |
		EP_PCIE_EVENT_LINKDOWN;
	mhi_ctx->event_reg.user = mhi_ctx;
	mhi_ctx->event_reg.mode = EP_PCIE_TRIGGER_CALLBACK;
	mhi_ctx->event_reg.callback = mhi_dev_sm_pcie_handler;

	rc = ep_pcie_register_event(mhi_ctx->phandle, &mhi_ctx->event_reg);
	if (rc) {
		pr_err("Failed to register for events from PCIe\n");
		goto fail;
	}

	rc = ipa_register_ipa_ready_cb(mhi_ring_init_cb, mhi_ctx);
	if (rc < 0) {
		if (rc == -EEXIST) {
			mhi_ring_init_cb(mhi_ctx);
		} else {
			pr_err("Error calling IPA cb with %d\n", rc);
			goto fail;
		}
	}

	/* Invoke MHI SM when device is in RESET state */
	rc = mhi_dev_sm_init(mhi_ctx);
	if (rc) {
		pr_err("%s: Error during SM init\n", __func__);
		goto fail;
	}

	/* set the env before setting the ready bit */
	rc = mhi_dev_mmio_set_env(mhi_ctx, MHI_ENV_VALUE);
	if (rc) {
		pr_err("%s: env setting failed\n", __func__);
		goto fail;
	}

	/* All set, notify the host */
	rc = mhi_dev_sm_set_ready();
	if (rc) {
		pr_err("%s: unable to set ready bit\n", __func__);
		goto fail;
	}

	atomic_set(&mhi_ctx->is_suspended, 0);

fail:
	atomic_set(&mhi_ctx->re_init_done, 1);
	mutex_unlock(&mhi_ctx->mhi_lock);
	return rc;
}

static void mhi_dev_reinit(struct work_struct *work)
{
	struct mhi_dev *mhi_ctx = container_of(work,
				struct mhi_dev, re_init);
	enum ep_pcie_link_status link_state;
	int rc = 0;

	link_state = ep_pcie_get_linkstatus(mhi_ctx->phandle);
	if (link_state == EP_PCIE_LINK_ENABLED) {
		/* PCIe link is up with BME set */
		rc = mhi_dev_resume_mmio_mhi_reinit(mhi_ctx);
		if (rc) {
			pr_err("Failed to register for events from PCIe\n");
			return;
		}
	}

	mhi_log(MHI_MSG_VERBOSE, "Wait for PCIe linkup\n");
}

static int mhi_dev_resume_mmio_mhi_init(struct mhi_dev *mhi_ctx)
{
	struct platform_device *pdev;
@@ -2158,6 +2380,8 @@ static int mhi_dev_resume_mmio_mhi_init(struct mhi_dev *mhi_ctx)

	INIT_WORK(&mhi_ctx->ring_init_cb_work, mhi_dev_enable);

	INIT_WORK(&mhi_ctx->re_init, mhi_dev_reinit);

	mhi_ctx->ring_init_wq = alloc_workqueue("mhi_ring_init_cb_wq",
							WQ_HIGHPRI, 0);
	if (!mhi_ctx->ring_init_wq) {
@@ -2281,11 +2505,19 @@ void mhi_dev_resume_init_with_link_up(struct ep_pcie_notify *notify)
		return;
	}

	if (notify->options == MHI_INIT) {
		rc = mhi_dev_resume_mmio_mhi_init(mhi_ctx);
		if (rc) {
			pr_err("Error during MHI device initialization\n");
			return;
		}
	} else if (notify->options == MHI_REINIT) {
		rc = mhi_dev_resume_mmio_mhi_reinit(mhi_ctx);
		if (rc) {
			pr_err("Error during MHI device re-initialization\n");
			return;
		}
	}
}

static int mhi_dev_probe(struct platform_device *pdev)
@@ -2298,6 +2530,12 @@ static int mhi_dev_probe(struct platform_device *pdev)
			pr_err("Error reading MHI Dev DT\n");
			return rc;
		}
		mhi_ipc_log = ipc_log_context_create(MHI_IPC_LOG_PAGES,
								"mhi", 0);
		if (mhi_ipc_log == NULL) {
			dev_err(&pdev->dev,
				"Failed to create IPC logging context\n");
		}
		mhi_uci_init();
		mhi_update_state_info(MHI_STATE_CONFIGURED);
	}
@@ -2316,6 +2554,7 @@ static int mhi_dev_probe(struct platform_device *pdev)
		mhi_ctx->event_reg.user = mhi_ctx;
		mhi_ctx->event_reg.mode = EP_PCIE_TRIGGER_CALLBACK;
		mhi_ctx->event_reg.callback = mhi_dev_resume_init_with_link_up;
		mhi_ctx->event_reg.options = MHI_INIT;

		rc = ep_pcie_register_event(mhi_ctx->phandle,
							&mhi_ctx->event_reg);
+5 −1
Original line number Diff line number Diff line
@@ -520,6 +520,7 @@ struct mhi_dev {
	u32				ipa_clnt_hndl[4];
	struct workqueue_struct		*ring_init_wq;
	struct work_struct		ring_init_cb_work;
	struct work_struct		re_init;

	/* EP PCIe registration */
	struct ep_pcie_register_event	event_reg;
@@ -529,6 +530,7 @@ struct mhi_dev {
	atomic_t			write_active;
	atomic_t			is_suspended;
	atomic_t			mhi_dev_wake;
	atomic_t			re_init_done;
	struct mutex			mhi_write_test;
	u32				device_local_pa_base;
	u32				mhi_ep_msi_num;
@@ -1050,8 +1052,10 @@ int mhi_dev_get_mhi_addr(struct mhi_dev *dev);
 * mhi_dev_get_mhi_state() - Fetches the MHI state such as M0/M1/M2/M3.
 * @dev:	MHI device structure.
 * @state:	Pointer of type mhi_dev_state
 * @mhi_reset:	MHI device reset from host.
 */
int mhi_dev_mmio_get_mhi_state(struct mhi_dev *dev, enum mhi_dev_state *state);
int mhi_dev_mmio_get_mhi_state(struct mhi_dev *dev, enum mhi_dev_state *state,
						bool *mhi_reset);

/**
 * mhi_dev_mmio_init() - Initializes the MMIO and reads the Number of event
+5 −1
Original line number Diff line number Diff line
@@ -258,7 +258,8 @@ int mhi_dev_mmio_disable_erdb_a7(struct mhi_dev *dev, uint32_t erdb_id)
}
EXPORT_SYMBOL(mhi_dev_mmio_disable_erdb_a7);

int mhi_dev_mmio_get_mhi_state(struct mhi_dev *dev, enum mhi_dev_state *state)
int mhi_dev_mmio_get_mhi_state(struct mhi_dev *dev, enum mhi_dev_state *state,
						bool *mhi_reset)
{
	uint32_t reg_value = 0;
	int rc = 0;
@@ -277,6 +278,9 @@ int mhi_dev_mmio_get_mhi_state(struct mhi_dev *dev, enum mhi_dev_state *state)
	if (rc)
		return rc;

	if (reg_value & MHICTRL_RESET_MASK)
		*mhi_reset = true;

	pr_debug("MHICTRL is 0x%x\n", reg_value);

	return 0;
+21 −2
Original line number Diff line number Diff line
@@ -397,7 +397,8 @@ static bool mhi_sm_is_legal_pcie_event_on_state(enum mhi_dev_state curr_mstate,
		res = true;
		break;
	case EP_PCIE_EVENT_PM_D3_HOT:
		res = (curr_mstate == MHI_DEV_M3_STATE &&
		res = ((curr_mstate == MHI_DEV_M3_STATE ||
			curr_mstate == MHI_DEV_READY_STATE) &&
			curr_dstate != MHI_SM_EP_PCIE_LINK_DISABLE);
		break;
	case EP_PCIE_EVENT_PM_D3_COLD:
@@ -937,6 +938,24 @@ fail_init_wq:
}
EXPORT_SYMBOL(mhi_dev_sm_init);

int mhi_dev_sm_exit(struct mhi_dev *mhi_dev)
{
	MHI_SM_FUNC_ENTRY();

	atomic_set(&mhi_sm_ctx->pending_device_events, 0);
	atomic_set(&mhi_sm_ctx->pending_pcie_events, 0);
	mhi_sm_debugfs_destroy();
	flush_workqueue(mhi_sm_ctx->mhi_sm_wq);
	destroy_workqueue(mhi_sm_ctx->mhi_sm_wq);
	ipa_dma_destroy();
	mutex_destroy(&mhi_sm_ctx->mhi_state_lock);
	devm_kfree(mhi_dev->dev, mhi_sm_ctx);
	mhi_sm_ctx = NULL;

	return 0;
}
EXPORT_SYMBOL(mhi_dev_sm_exit);

/**
 * mhi_dev_sm_get_mhi_state() -Get current MHI state.
 * @state: return param
@@ -983,7 +1002,7 @@ EXPORT_SYMBOL(mhi_dev_sm_get_mhi_state);
 */
int mhi_dev_sm_set_ready(void)
{
	int res = -EAGAIN;
	int res = 0;
	int is_ready;
	enum mhi_dev_state state;

+2 −1
Original line number Diff line number Diff line
/* Copyright (c) 2015, The Linux Foundation. All rights reserved.
/* Copyright (c) 2015,2017 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
@@ -41,6 +41,7 @@ enum mhi_dev_event {
};

int mhi_dev_sm_init(struct mhi_dev *dev);
int mhi_dev_sm_exit(struct mhi_dev *dev);
int mhi_dev_sm_set_ready(void);
int mhi_dev_notify_sm_event(enum mhi_dev_event event);
int mhi_dev_sm_get_mhi_state(enum mhi_dev_state *state);