M7350/kernel/drivers/platform/msm/mhi/mhi_init.c

656 lines
21 KiB
C
Raw Normal View History

2024-09-09 08:57:42 +00:00
/* 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 "mhi_hwio.h"
#include "mhi_macros.h"
#include <linux/hrtimer.h>
#include <linux/cpu.h>
#include <linux/kthread.h>
#include <linux/slab.h>
#include <linux/completion.h>
#include <linux/platform_device.h>
static int mhi_init_sync(struct mhi_device_ctxt *mhi_dev_ctxt)
{
int i;
mhi_dev_ctxt->mhi_ev_spinlock_list = kmalloc(sizeof(spinlock_t) *
mhi_dev_ctxt->mmio_info.nr_event_rings,
GFP_KERNEL);
if (NULL == mhi_dev_ctxt->mhi_ev_spinlock_list)
goto ev_mutex_free;
mhi_dev_ctxt->mhi_chan_mutex = kmalloc(sizeof(struct mutex) *
MHI_MAX_CHANNELS, GFP_KERNEL);
if (NULL == mhi_dev_ctxt->mhi_chan_mutex)
goto chan_mutex_free;
mhi_dev_ctxt->mhi_cmd_mutex_list = kmalloc(sizeof(struct mutex) *
NR_OF_CMD_RINGS, GFP_KERNEL);
if (NULL == mhi_dev_ctxt->mhi_cmd_mutex_list)
goto cmd_mutex_free;
mhi_dev_ctxt->db_write_lock = kmalloc(sizeof(spinlock_t) *
MHI_MAX_CHANNELS, GFP_KERNEL);
if (NULL == mhi_dev_ctxt->db_write_lock)
goto db_write_lock_free;
for (i = 0; i < MHI_MAX_CHANNELS; ++i)
mutex_init(&mhi_dev_ctxt->mhi_chan_mutex[i]);
for (i = 0; i < mhi_dev_ctxt->mmio_info.nr_event_rings; ++i)
spin_lock_init(&mhi_dev_ctxt->mhi_ev_spinlock_list[i]);
for (i = 0; i < NR_OF_CMD_RINGS; ++i)
mutex_init(&mhi_dev_ctxt->mhi_cmd_mutex_list[i]);
for (i = 0; i < MHI_MAX_CHANNELS; ++i)
spin_lock_init(&mhi_dev_ctxt->db_write_lock[i]);
rwlock_init(&mhi_dev_ctxt->xfer_lock);
mutex_init(&mhi_dev_ctxt->mhi_link_state);
mutex_init(&mhi_dev_ctxt->pm_lock);
atomic_set(&mhi_dev_ctxt->flags.m2_transition, 0);
return 0;
db_write_lock_free:
kfree(mhi_dev_ctxt->mhi_cmd_mutex_list);
cmd_mutex_free:
kfree(mhi_dev_ctxt->mhi_chan_mutex);
chan_mutex_free:
kfree(mhi_dev_ctxt->mhi_ev_spinlock_list);
ev_mutex_free:
return -ENOMEM;
}
size_t calculate_mhi_space(struct mhi_device_ctxt *mhi_dev_ctxt)
{
int i, r;
size_t mhi_dev_mem = 0;
struct mhi_chan_info chan_info;
/* Calculate size needed for contexts */
mhi_dev_mem += (MHI_MAX_CHANNELS * sizeof(struct mhi_chan_ctxt)) +
(NR_OF_CMD_RINGS * sizeof(struct mhi_chan_ctxt)) +
(mhi_dev_ctxt->mmio_info.nr_event_rings *
sizeof(struct mhi_event_ctxt));
mhi_log(MHI_MSG_INFO, "Reserved %zd bytes for context info\n",
mhi_dev_mem);
/*Calculate size needed for cmd TREs */
mhi_dev_mem += (CMD_EL_PER_RING * sizeof(union mhi_cmd_pkt));
/* Calculate size needed for event TREs */
for (i = 0; i < mhi_dev_ctxt->mmio_info.nr_event_rings; ++i)
mhi_dev_mem += (sizeof(union mhi_event_pkt) *
mhi_dev_ctxt->ev_ring_props[i].nr_desc);
/* Calculate size needed for xfer TREs and bounce buffers */
for (i = 0; i < MHI_MAX_CHANNELS; ++i)
if (VALID_CHAN_NR(i)) {
r = get_chan_props(mhi_dev_ctxt, i, &chan_info);
if (r)
continue;
/* Add size of TREs */
mhi_dev_mem += (sizeof(union mhi_xfer_pkt) *
chan_info.max_desc);
/* Add bounce buffer size */
if (mhi_dev_ctxt->flags.bb_enabled) {
mhi_log(MHI_MSG_INFO,
"Enabling BB list, chan %d\n", i);
/*mhi_dev_mem += (MAX_BOUNCE_BUF_SIZE *
chan_info.max_desc); */
}
}
mhi_log(MHI_MSG_INFO, "Final bytes for MHI device space %zd\n",
mhi_dev_mem);
return mhi_dev_mem;
}
void init_dev_ev_ctxt(struct mhi_event_ctxt *ev_ctxt,
dma_addr_t p_base_addr, size_t len)
{
ev_ctxt->mhi_event_ring_base_addr = p_base_addr;
ev_ctxt->mhi_event_read_ptr = p_base_addr;
ev_ctxt->mhi_event_write_ptr = p_base_addr;
ev_ctxt->mhi_event_ring_len = len;
}
void init_local_ev_ctxt(struct mhi_ring *ev_ctxt,
void *v_base_addr, size_t len)
{
ev_ctxt->base = v_base_addr;
ev_ctxt->rp = v_base_addr;
ev_ctxt->wp = v_base_addr;
ev_ctxt->len = len;
ev_ctxt->el_size = sizeof(union mhi_event_pkt);
ev_ctxt->overwrite_en = 0;
}
void init_dev_chan_ctxt(struct mhi_chan_ctxt *chan_ctxt,
dma_addr_t p_base_addr, size_t len, int ev_index)
{
chan_ctxt->mhi_trb_ring_base_addr = p_base_addr;
chan_ctxt->mhi_trb_read_ptr = p_base_addr;
chan_ctxt->mhi_trb_write_ptr = p_base_addr;
chan_ctxt->mhi_trb_ring_len = len;
/* Prepulate the channel ctxt */
chan_ctxt->mhi_chan_state = MHI_CHAN_STATE_ENABLED;
chan_ctxt->mhi_event_ring_index = ev_index;
}
void init_local_chan_ctxt(struct mhi_ring *chan_ctxt,
void *v_base_addr, size_t len)
{
chan_ctxt->base = v_base_addr;
chan_ctxt->rp = v_base_addr;
chan_ctxt->wp = v_base_addr;
chan_ctxt->len = len;
chan_ctxt->el_size = sizeof(union mhi_event_pkt);
chan_ctxt->overwrite_en = 0;
}
int populate_bb_list(struct list_head *bb_list, int num_bb)
{
struct mhi_buf_info *mhi_buf = NULL;
int i;
for (i = 0; i < num_bb; ++i) {
mhi_buf = kzalloc(sizeof(struct mhi_buf_info), GFP_KERNEL);
if (!mhi_buf)
return -ENOMEM;
mhi_buf->bb_p_addr = 0;
mhi_buf->bb_v_addr = NULL;
mhi_log(MHI_MSG_INFO,
"Allocated BB v_addr 0x%p, p_addr 0x%llx\n",
mhi_buf->bb_v_addr, (u64)mhi_buf->bb_p_addr);
}
return 0;
}
/**
* mhi_cmd_ring_init- Initialization of the command ring
*
* @cmd_ctxt: command ring context to initialize
* @trb_list_phy_addr: Pointer to the dma address of the tre ring
* @trb_list_virt_addr: Pointer to the virtual address of the tre ring
* @ring_size: Ring size
* @ring: Pointer to the shadow command context
*
* @Return MHI_STATUS
*/
static int mhi_cmd_ring_init(struct mhi_cmd_ctxt *cmd_ctxt,
void *trb_list_virt_addr,
dma_addr_t trb_list_phy_addr,
size_t ring_size, struct mhi_ring *ring)
{
cmd_ctxt->mhi_cmd_ring_base_addr = trb_list_phy_addr;
cmd_ctxt->mhi_cmd_ring_read_ptr = trb_list_phy_addr;
cmd_ctxt->mhi_cmd_ring_write_ptr = trb_list_phy_addr;
cmd_ctxt->mhi_cmd_ring_len = ring_size;
ring[PRIMARY_CMD_RING].wp = trb_list_virt_addr;
ring[PRIMARY_CMD_RING].rp = trb_list_virt_addr;
ring[PRIMARY_CMD_RING].base = trb_list_virt_addr;
ring[PRIMARY_CMD_RING].len = ring_size;
ring[PRIMARY_CMD_RING].el_size = sizeof(union mhi_cmd_pkt);
ring[PRIMARY_CMD_RING].overwrite_en = 0;
return 0;
}
static int enable_bb_ctxt(struct mhi_ring *bb_ctxt, int nr_el)
{
bb_ctxt->el_size = sizeof(struct mhi_buf_info);
bb_ctxt->len = bb_ctxt->el_size * nr_el;
bb_ctxt->base = kzalloc(bb_ctxt->len, GFP_KERNEL);
bb_ctxt->wp = bb_ctxt->base;
bb_ctxt->rp = bb_ctxt->base;
bb_ctxt->ack_rp = bb_ctxt->base;
if (!bb_ctxt->base)
return -ENOMEM;
return 0;
}
static void calculate_mhi_addressing_window(
struct mhi_device_ctxt *mhi_dev_ctxt)
{
dma_addr_t dma_dev_mem_start;
dma_addr_t dma_seg_size = 0x1FF00000UL;
dma_addr_t dma_max_addr = (dma_addr_t)(-1);
dma_dev_mem_start = mhi_dev_ctxt->dev_space.dma_dev_mem_start;
if (dma_dev_mem_start < dma_seg_size) {
mhi_dev_ctxt->dev_space.start_win_addr = 0;
mhi_dev_ctxt->dev_space.end_win_addr =
dma_dev_mem_start + dma_seg_size +
(dma_seg_size - dma_dev_mem_start);
} else if (dma_dev_mem_start >= dma_seg_size &&
dma_dev_mem_start <= (dma_max_addr - dma_seg_size)) {
mhi_dev_ctxt->dev_space.start_win_addr =
dma_dev_mem_start - dma_seg_size;
mhi_dev_ctxt->dev_space.end_win_addr =
dma_dev_mem_start + dma_seg_size;
} else if (dma_dev_mem_start > (dma_max_addr - dma_seg_size)) {
mhi_dev_ctxt->dev_space.start_win_addr =
dma_dev_mem_start - (dma_seg_size +
(dma_seg_size - (dma_max_addr -
dma_dev_mem_start)));
mhi_dev_ctxt->dev_space.end_win_addr = dma_max_addr;
}
mhi_log(MHI_MSG_INFO,
"MHI start address at 0x%llx, Window Start 0x%llx Window End 0x%llx\n",
(u64)dma_dev_mem_start,
(u64)mhi_dev_ctxt->dev_space.start_win_addr,
(u64)mhi_dev_ctxt->dev_space.end_win_addr);
}
int init_mhi_dev_mem(struct mhi_device_ctxt *mhi_dev_ctxt)
{
size_t mhi_mem_index = 0, ring_len;
void *dev_mem_start;
dma_addr_t dma_dev_mem_start;
int i, r;
mhi_dev_ctxt->dev_space.dev_mem_len =
calculate_mhi_space(mhi_dev_ctxt);
mhi_dev_ctxt->dev_space.dev_mem_start =
dma_alloc_coherent(&mhi_dev_ctxt->dev_info->plat_dev->dev,
mhi_dev_ctxt->dev_space.dev_mem_len,
&mhi_dev_ctxt->dev_space.dma_dev_mem_start,
GFP_KERNEL);
if (!mhi_dev_ctxt->dev_space.dev_mem_start) {
mhi_log(MHI_MSG_ERROR,
"Failed to allocate memory of size %zd bytes\n",
mhi_dev_ctxt->dev_space.dev_mem_len);
return -ENOMEM;
}
dev_mem_start = mhi_dev_ctxt->dev_space.dev_mem_start;
dma_dev_mem_start = mhi_dev_ctxt->dev_space.dma_dev_mem_start;
memset(dev_mem_start, 0, mhi_dev_ctxt->dev_space.dev_mem_len);
calculate_mhi_addressing_window(mhi_dev_ctxt);
mhi_log(MHI_MSG_INFO, "Starting Seg address: virt 0x%p, dma 0x%llx\n",
dev_mem_start, (u64)dma_dev_mem_start);
mhi_log(MHI_MSG_INFO, "Initializing CCABAP at virt 0x%p, dma 0x%llx\n",
dev_mem_start + mhi_mem_index,
(u64)dma_dev_mem_start + mhi_mem_index);
mhi_dev_ctxt->dev_space.ring_ctxt.cc_list = dev_mem_start;
mhi_dev_ctxt->dev_space.ring_ctxt.dma_cc_list = dma_dev_mem_start;
mhi_mem_index += MHI_MAX_CHANNELS * sizeof(struct mhi_chan_ctxt);
mhi_log(MHI_MSG_INFO, "Initializing CRCBAP at virt 0x%p, dma 0x%llx\n",
dev_mem_start + mhi_mem_index,
(u64)dma_dev_mem_start + mhi_mem_index);
mhi_dev_ctxt->dev_space.ring_ctxt.cmd_ctxt =
dev_mem_start + mhi_mem_index;
mhi_dev_ctxt->dev_space.ring_ctxt.dma_cmd_ctxt =
dma_dev_mem_start + mhi_mem_index;
mhi_mem_index += NR_OF_CMD_RINGS * sizeof(struct mhi_chan_ctxt);
mhi_log(MHI_MSG_INFO, "Initializing ECABAP at virt 0x%p, dma 0x%llx\n",
dev_mem_start + mhi_mem_index,
(u64)dma_dev_mem_start + mhi_mem_index);
mhi_dev_ctxt->dev_space.ring_ctxt.ec_list =
dev_mem_start + mhi_mem_index;
mhi_dev_ctxt->dev_space.ring_ctxt.dma_ec_list =
dma_dev_mem_start + mhi_mem_index;
mhi_mem_index += mhi_dev_ctxt->mmio_info.nr_event_rings *
sizeof(struct mhi_event_ctxt);
mhi_log(MHI_MSG_INFO,
"Initializing CMD context at virt 0x%p, dma 0x%llx\n",
dev_mem_start + mhi_mem_index,
(u64)dma_dev_mem_start + mhi_mem_index);
/* TODO: Initialize both the local and device cmd context */
ring_len = (CMD_EL_PER_RING * sizeof(union mhi_cmd_pkt));
mhi_cmd_ring_init(mhi_dev_ctxt->dev_space.ring_ctxt.cmd_ctxt,
dev_mem_start + mhi_mem_index,
dma_dev_mem_start + mhi_mem_index,
ring_len,
mhi_dev_ctxt->mhi_local_cmd_ctxt);
mhi_mem_index += ring_len;
/* Initialize both the local and device event contexts */
for (i = 0; i < mhi_dev_ctxt->mmio_info.nr_event_rings; ++i) {
ring_len = sizeof(union mhi_event_pkt) *
mhi_dev_ctxt->ev_ring_props[i].nr_desc;
init_dev_ev_ctxt(&mhi_dev_ctxt->dev_space.ring_ctxt.ec_list[i],
dma_dev_mem_start + mhi_mem_index,
ring_len);
init_local_ev_ctxt(&mhi_dev_ctxt->mhi_local_event_ctxt[i],
dev_mem_start + mhi_mem_index,
ring_len);
mhi_log(MHI_MSG_INFO,
"Initializing EV_%d TRE list at virt 0x%p dma 0x%llx\n",
i, dev_mem_start + mhi_mem_index,
(u64)dma_dev_mem_start + mhi_mem_index);
mhi_mem_index += ring_len;
}
/* Initialize both the local and device xfer contexts */
for (i = 0; i < MHI_MAX_CHANNELS; ++i)
if (VALID_CHAN_NR(i)) {
struct mhi_chan_info chan_info;
r = get_chan_props(mhi_dev_ctxt, i, &chan_info);
if (r)
continue;
mhi_log(MHI_MSG_INFO, "Initializing chan ctxt %d\n", i);
ring_len = (sizeof(union mhi_xfer_pkt) *
chan_info.max_desc);
init_dev_chan_ctxt(
&mhi_dev_ctxt->dev_space.ring_ctxt.cc_list[i],
dma_dev_mem_start + mhi_mem_index,
ring_len, chan_info.ev_ring);
/* TODO: May not need to do this. It would be best for
* the client to set it during chan open */
mhi_dev_ctxt->dev_space.ring_ctxt.cc_list[i].
mhi_chan_type = (i % 2) + 1;
init_local_chan_ctxt(
&mhi_dev_ctxt->mhi_local_chan_ctxt[i],
dev_mem_start + mhi_mem_index,
ring_len);
/* TODO: May not need to do this. It would be best for
* the client to set it during chan open */
mhi_dev_ctxt->mhi_local_chan_ctxt[i].dir = (i % 2) + 1;
/* Add size of TREs */
mhi_mem_index += ring_len;
if (mhi_dev_ctxt->flags.bb_enabled) {
r = enable_bb_ctxt(
&mhi_dev_ctxt->chan_bb_list[i],
chan_info.max_desc);
if (r)
goto error_during_bb_list;
}
}
return 0;
error_during_bb_list:
for (; i >= 0; --i)
kfree(mhi_dev_ctxt->chan_bb_list[i].base);
dma_free_coherent(&mhi_dev_ctxt->dev_info->plat_dev->dev,
mhi_dev_ctxt->dev_space.dev_mem_len,
mhi_dev_ctxt->dev_space.dev_mem_start,
mhi_dev_ctxt->dev_space.dma_dev_mem_start);
return r;
}
static int mhi_init_events(struct mhi_device_ctxt *mhi_dev_ctxt)
{
mhi_dev_ctxt->mhi_ev_wq.mhi_event_wq = kmalloc(
sizeof(wait_queue_head_t),
GFP_KERNEL);
if (NULL == mhi_dev_ctxt->mhi_ev_wq.mhi_event_wq) {
mhi_log(MHI_MSG_ERROR, "Failed to init event");
return MHI_STATUS_ERROR;
}
mhi_dev_ctxt->mhi_ev_wq.state_change_event =
kmalloc(sizeof(wait_queue_head_t), GFP_KERNEL);
if (NULL == mhi_dev_ctxt->mhi_ev_wq.state_change_event) {
mhi_log(MHI_MSG_ERROR, "Failed to init event");
goto error_event_handle_alloc;
}
/* Initialize the event which signals M0 */
mhi_dev_ctxt->mhi_ev_wq.m0_event = kmalloc(sizeof(wait_queue_head_t),
GFP_KERNEL);
if (NULL == mhi_dev_ctxt->mhi_ev_wq.m0_event) {
mhi_log(MHI_MSG_ERROR, "Failed to init event");
goto error_state_change_event_handle;
}
/* Initialize the event which signals M0 */
mhi_dev_ctxt->mhi_ev_wq.m3_event = kmalloc(sizeof(wait_queue_head_t),
GFP_KERNEL);
if (NULL == mhi_dev_ctxt->mhi_ev_wq.m3_event) {
mhi_log(MHI_MSG_ERROR, "Failed to init event");
goto error_m0_event;
}
/* Initialize the event which signals M0 */
mhi_dev_ctxt->mhi_ev_wq.bhi_event = kmalloc(sizeof(wait_queue_head_t),
GFP_KERNEL);
if (NULL == mhi_dev_ctxt->mhi_ev_wq.bhi_event) {
mhi_log(MHI_MSG_ERROR, "Failed to init event");
goto error_bhi_event;
}
/* Initialize the event which starts the event parsing thread */
init_waitqueue_head(mhi_dev_ctxt->mhi_ev_wq.mhi_event_wq);
/* Initialize the event which starts the state change thread */
init_waitqueue_head(mhi_dev_ctxt->mhi_ev_wq.state_change_event);
/* Initialize the event which triggers clients waiting to send */
init_waitqueue_head(mhi_dev_ctxt->mhi_ev_wq.m0_event);
/* Initialize the event which triggers D3hot */
init_waitqueue_head(mhi_dev_ctxt->mhi_ev_wq.m3_event);
init_waitqueue_head(mhi_dev_ctxt->mhi_ev_wq.bhi_event);
return 0;
error_bhi_event:
kfree(mhi_dev_ctxt->mhi_ev_wq.m3_event);
error_m0_event:
kfree(mhi_dev_ctxt->mhi_ev_wq.m0_event);
error_state_change_event_handle:
kfree(mhi_dev_ctxt->mhi_ev_wq.state_change_event);
error_event_handle_alloc:
kfree(mhi_dev_ctxt->mhi_ev_wq.mhi_event_wq);
return MHI_STATUS_ERROR;
}
static int mhi_init_state_change_thread_work_queue(
struct mhi_state_work_queue *q)
{
bool lock_acquired = 0;
unsigned long flags;
if (NULL == q->q_lock) {
q->q_lock = kmalloc(sizeof(spinlock_t), GFP_KERNEL);
if (NULL == q->q_lock)
return -ENOMEM;
spin_lock_init(q->q_lock);
} else {
spin_lock_irqsave(q->q_lock, flags);
lock_acquired = 1;
}
q->queue_full_cntr = 0;
q->q_info.base = q->buf;
q->q_info.rp = q->buf;
q->q_info.wp = q->buf;
q->q_info.len = MHI_WORK_Q_MAX_SIZE * sizeof(enum STATE_TRANSITION);
q->q_info.el_size = sizeof(enum STATE_TRANSITION);
q->q_info.overwrite_en = 0;
if (lock_acquired)
spin_unlock_irqrestore(q->q_lock, flags);
return 0;
}
static void mhi_init_wakelock(struct mhi_device_ctxt *mhi_dev_ctxt)
{
wakeup_source_init(&mhi_dev_ctxt->w_lock, "mhi_wakeup_source");
}
static int mhi_spawn_threads(struct mhi_device_ctxt *mhi_dev_ctxt)
{
mhi_dev_ctxt->event_thread_handle = kthread_run(parse_event_thread,
mhi_dev_ctxt,
"mhi_ev_thrd");
if (IS_ERR(mhi_dev_ctxt->event_thread_handle))
return MHI_STATUS_ERROR;
mhi_dev_ctxt->st_thread_handle = kthread_run(mhi_state_change_thread,
mhi_dev_ctxt,
"mhi_st_thrd");
if (IS_ERR(mhi_dev_ctxt->event_thread_handle))
return MHI_STATUS_ERROR;
return 0;
}
/**
* @brief Main initialization function for a mhi struct device context
* All threads, events mutexes, mhi specific data structures
* are initialized here
*
* @param dev_info [IN ] pcie struct device information structure to
which this mhi context belongs
* @param mhi_struct device [IN/OUT] reference to a mhi context to be populated
*
* @return MHI_STATUS
*/
int mhi_init_device_ctxt(struct mhi_pcie_dev_info *dev_info,
struct mhi_device_ctxt *mhi_dev_ctxt)
{
int r = 0;
if (NULL == dev_info || NULL == mhi_dev_ctxt)
return -EINVAL;
mhi_log(MHI_MSG_VERBOSE, "Entered\n");
mhi_dev_ctxt->dev_info = dev_info;
mhi_dev_ctxt->dev_props = &dev_info->core;
r = mhi_populate_event_cfg(mhi_dev_ctxt);
if (r) {
mhi_log(MHI_MSG_ERROR,
"Failed to get event ring properties ret %d\n", r);
goto error_during_props;
}
r = mhi_init_sync(mhi_dev_ctxt);
if (r) {
mhi_log(MHI_MSG_ERROR, "Failed to initialize mhi sync\n");
goto error_during_sync;
}
r = create_local_ev_ctxt(mhi_dev_ctxt);
if (r) {
mhi_log(MHI_MSG_ERROR,
"Failed to initialize local event ctxt ret %d\n", r);
goto error_during_local_ev_ctxt;
}
r = init_mhi_dev_mem(mhi_dev_ctxt);
if (r) {
mhi_log(MHI_MSG_ERROR,
"Failed to initialize device memory ret %d\n", r);
goto error_during_dev_mem_init;
}
r = mhi_init_events(mhi_dev_ctxt);
if (r) {
mhi_log(MHI_MSG_ERROR,
"Failed to initialize mhi events ret %d\n", r);
goto error_wq_init;
}
r = mhi_reset_all_thread_queues(mhi_dev_ctxt);
if (r) {
mhi_log(MHI_MSG_ERROR,
"Failed to initialize work queues ret %d\n", r);
goto error_during_thread_init;
}
init_event_ctxt_array(mhi_dev_ctxt);
mhi_dev_ctxt->mhi_state = MHI_STATE_RESET;
mhi_dev_ctxt->enable_lpm = 1;
r = mhi_spawn_threads(mhi_dev_ctxt);
if (r) {
mhi_log(MHI_MSG_ERROR, "Failed to spawn threads ret %d\n", r);
goto error_during_thread_spawn;
}
mhi_init_wakelock(mhi_dev_ctxt);
return r;
error_during_thread_spawn:
kfree(mhi_dev_ctxt->state_change_work_item_list.q_lock);
error_during_thread_init:
kfree(mhi_dev_ctxt->mhi_ev_wq.mhi_event_wq);
kfree(mhi_dev_ctxt->mhi_ev_wq.state_change_event);
kfree(mhi_dev_ctxt->mhi_ev_wq.m0_event);
kfree(mhi_dev_ctxt->mhi_ev_wq.m3_event);
kfree(mhi_dev_ctxt->mhi_ev_wq.bhi_event);
error_wq_init:
dma_free_coherent(&mhi_dev_ctxt->dev_info->plat_dev->dev,
mhi_dev_ctxt->dev_space.dev_mem_len,
mhi_dev_ctxt->dev_space.dev_mem_start,
mhi_dev_ctxt->dev_space.dma_dev_mem_start);
error_during_dev_mem_init:
error_during_local_ev_ctxt:
kfree(mhi_dev_ctxt->mhi_cmd_mutex_list);
kfree(mhi_dev_ctxt->mhi_chan_mutex);
kfree(mhi_dev_ctxt->mhi_ev_spinlock_list);
error_during_sync:
kfree(mhi_dev_ctxt->ev_ring_props);
error_during_props:
return r;
}
/**
* @brief Initialize the channel context and shadow context
*
* @cc_list: Context to initialize
* @trb_list_phy: Physical base address for the TRE ring
* @trb_list_virt: Virtual base address for the TRE ring
* @el_per_ring: Number of TREs this ring will contain
* @chan_type: Type of channel IN/OUT
* @event_ring: Event ring to be mapped to this channel context
* @ring: Shadow context to be initialized alongside
*
* @Return MHI_STATUS
*/
int mhi_init_chan_ctxt(struct mhi_chan_ctxt *cc_list,
uintptr_t trb_list_phy, uintptr_t trb_list_virt,
u64 el_per_ring, enum MHI_CHAN_TYPE chan_type,
u32 event_ring, struct mhi_ring *ring,
enum MHI_CHAN_STATE chan_state)
{
cc_list->mhi_chan_state = chan_state;
cc_list->mhi_chan_type = chan_type;
cc_list->mhi_event_ring_index = event_ring;
cc_list->mhi_trb_ring_base_addr = trb_list_phy;
cc_list->mhi_trb_ring_len =
((size_t)(el_per_ring)*sizeof(struct mhi_tx_pkt));
cc_list->mhi_trb_read_ptr = trb_list_phy;
cc_list->mhi_trb_write_ptr = trb_list_phy;
ring->rp = (void *)(trb_list_virt);
ring->ack_rp = ring->rp;
ring->wp = (void *)(trb_list_virt);
ring->base = (void *)(trb_list_virt);
ring->len = ((size_t)(el_per_ring)*sizeof(struct mhi_tx_pkt));
ring->el_size = sizeof(struct mhi_tx_pkt);
ring->overwrite_en = 0;
ring->dir = chan_type;
/* Flush writes to MMIO */
wmb();
return 0;
}
int mhi_reset_all_thread_queues(
struct mhi_device_ctxt *mhi_dev_ctxt)
{
int ret_val = 0;
ret_val = mhi_init_state_change_thread_work_queue(
&mhi_dev_ctxt->state_change_work_item_list);
if (ret_val)
mhi_log(MHI_MSG_ERROR, "Failed to reset STT work queue\n");
return ret_val;
}
int mhi_reg_notifiers(struct mhi_device_ctxt *mhi_dev_ctxt)
{
u32 ret_val;
if (NULL == mhi_dev_ctxt)
return MHI_STATUS_ERROR;
mhi_dev_ctxt->mhi_cpu_notifier.notifier_call = mhi_cpu_notifier_cb;
ret_val = register_cpu_notifier(&mhi_dev_ctxt->mhi_cpu_notifier);
if (ret_val)
return MHI_STATUS_ERROR;
else
return 0;
}