/* 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 #include #include #include #include #include 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; }