Loading drivers/platform/msm/mhi_dev/mhi.c +152 −71 Original line number Diff line number Diff line // SPDX-License-Identifier: GPL-2.0-only /* Copyright (c) 2015-2020, The Linux Foundation. All rights reserved.*/ /* Copyright (c) 2015-2021, The Linux Foundation. All rights reserved.*/ /* * MSM MHI device core driver. Loading Loading @@ -84,6 +84,10 @@ static int mhi_dev_pcie_notify_event; static void mhi_dev_transfer_completion_cb(void *mreq); static int mhi_dev_alloc_evt_buf_evt_req(struct mhi_dev *mhi, struct mhi_dev_channel *ch, struct mhi_dev_ring *evt_ring); static int mhi_dev_schedule_msi_ipa(struct mhi_dev *mhi, struct event_req *ereq); static void mhi_dev_event_msi_cb(void *req); static struct mhi_dev_uevent_info channel_state_info[MHI_MAX_CHANNELS]; static DECLARE_COMPLETION(read_from_host); static DECLARE_COMPLETION(write_to_host); Loading Loading @@ -165,6 +169,7 @@ void mhi_dev_write_to_host_ipa(struct mhi_dev *mhi, struct mhi_addr *transfer, int rc = 0; uint64_t bit_40 = ((u64) 1) << 40, host_addr_pa = 0, offset = 0; dma_addr_t dma; void (*cb_func)(void *req); if (WARN_ON(!mhi)) return; Loading @@ -183,11 +188,12 @@ void mhi_dev_write_to_host_ipa(struct mhi_dev *mhi, struct mhi_addr *transfer, (int) transfer->size); if (tr_type == MHI_DEV_DMA_ASYNC) { /* * Event read pointer memory is dma_alloc_coherent memory * don't need to dma_map. Assigns the physical address in * phy_addr. * Event read pointer memory and MSI buf are dma_alloc_coherent * memory so don't need to dma_map. Use the physical address * passed in phy_addr. */ if (transfer->phy_addr) if (ereq->event_type == SEND_EVENT_RD_OFFSET || ereq->event_type == SEND_MSI) dma = transfer->phy_addr; else dma = dma_map_single(&mhi->pdev->dev, Loading @@ -196,6 +202,7 @@ void mhi_dev_write_to_host_ipa(struct mhi_dev *mhi, struct mhi_addr *transfer, if (ereq->event_type == SEND_EVENT_BUFFER) { ereq->dma = dma; ereq->dma_len = transfer->size; cb_func = ereq->client_cb; } else if (ereq->event_type == SEND_EVENT_RD_OFFSET) { /* * Event read pointer memory is dma_alloc_coherent Loading @@ -205,10 +212,13 @@ void mhi_dev_write_to_host_ipa(struct mhi_dev *mhi, struct mhi_addr *transfer, ereq->event_rd_dma = 0; else ereq->event_rd_dma = dma; cb_func = ereq->rd_offset_cb; } else { cb_func = ereq->msi_cb; } rc = ipa_dma_async_memcpy(host_addr_pa, (uint64_t)dma, (int)transfer->size, ereq->client_cb, ereq); cb_func, ereq); if (rc) pr_err("error while writing to host:%d\n", rc); } else if (tr_type == MHI_DEV_DMA_SYNC) { Loading @@ -235,11 +245,55 @@ static void mhi_dev_event_buf_completion_cb(void *req) { struct event_req *ereq = req; if (ereq) if (!ereq) { mhi_log(MHI_MSG_ERROR, "Null ereq\n"); return; } if (ereq->dma && ereq->dma_len) dma_unmap_single(&mhi_ctx->pdev->dev, ereq->dma, ereq->dma_len, DMA_TO_DEVICE); else mhi_log(MHI_MSG_ERROR, "event req is null\n"); mhi_log(MHI_MSG_VERBOSE, "Event buf dma completed for flush req %d\n", ereq->flush_num); } static int mhi_dev_schedule_msi_ipa(struct mhi_dev *mhi, struct event_req *ereq) { struct ep_pcie_msi_config cfg; struct mhi_addr msi_addr; struct mhi_dev_channel *ch = ereq->context; uint64_t evnt_ring_idx = mhi->ev_ring_start + ereq->event_ring; struct mhi_dev_ring *ring = &mhi->ring[evnt_ring_idx]; union mhi_dev_ring_ctx *ctx; int rc; rc = ep_pcie_get_msi_config(mhi->phandle, &cfg); if (rc) { pr_err("Error retrieving pcie msi logic\n"); return rc; } ctx = (union mhi_dev_ring_ctx *)&mhi->ev_ctx_cache[ereq->event_ring]; msi_addr.size = sizeof(uint32_t); msi_addr.host_pa = (uint64_t)((uint64_t)cfg.upper << 32) | (uint64_t)cfg.lower; *ring->msi_buf = cfg.data + ctx->ev.msivec; msi_addr.phy_addr = ring->msi_buf_dma_handle; ereq->event_type = SEND_MSI; ereq->msi_cb = mhi_dev_event_msi_cb; ch->msi_cnt++; mhi_log(MHI_MSG_VERBOSE, "Sending MSI %d to 0x%llx as data = 0x%x for ch %d msi_count %d, ereq flush_num %d\n", ctx->ev.msivec, msi_addr.host_pa, *ring->msi_buf, ch->ch_id, ch->msi_cnt, ereq->flush_num); mhi_ctx->write_to_host(mhi, &msi_addr, ereq, MHI_DEV_DMA_ASYNC); return 0; } /* Loading @@ -250,25 +304,35 @@ static void mhi_dev_event_buf_completion_cb(void *req) */ static void mhi_dev_event_rd_offset_completion_cb(void *req) { union mhi_dev_ring_ctx *ctx; int rc; struct event_req *ereq = req; struct mhi_dev_channel *ch = ereq->context; struct mhi_dev *mhi = ch->ring->mhi_dev; unsigned long flags; mhi_log(MHI_MSG_VERBOSE, "Rd offset dma completed for flush req %d\n", ereq->flush_num); if (ereq->event_rd_dma) dma_unmap_single(&mhi_ctx->pdev->dev, ereq->event_rd_dma, sizeof(uint64_t), DMA_TO_DEVICE); /* rp update in host memory should be flushed before sending an MSI */ wmb(); ctx = (union mhi_dev_ring_ctx *)&mhi->ev_ctx_cache[ereq->event_ring]; if (mhi_ctx->use_ipa) { rc = ep_pcie_trigger_msi(mhi_ctx->phandle, ctx->ev.msivec); if (rc) pr_err("%s: error sending in msi\n", __func__); } static void mhi_dev_event_msi_cb(void *req) { struct event_req *ereq = req; struct mhi_dev_channel *ch; struct mhi_dev *mhi; unsigned long flags; if (!ereq) { mhi_log(MHI_MSG_WARNING, "Null ereq, valid only for sync dma and cmd ack to host\n"); return; } ch = ereq->context; mhi = ch->ring->mhi_dev; mhi_log(MHI_MSG_VERBOSE, "MSI completed for flush req %d\n", ereq->flush_num); /* Add back the flushed events space to the event buffer */ ch->evt_buf_wp = ereq->start + ereq->num_events; if (ch->evt_buf_wp == ch->evt_buf_size) Loading @@ -292,7 +356,7 @@ static int mhi_trigger_msi_edma(struct mhi_dev_ring *ring, u32 idx) { struct dma_async_tx_descriptor *descriptor; struct ep_pcie_msi_config cfg; struct msi_buf_cb_data *msi_buf; struct msi_buf_cb_data *msi_buffer; int rc; unsigned long flags; Loading @@ -313,12 +377,12 @@ static int mhi_trigger_msi_edma(struct mhi_dev_ring *ring, u32 idx) spin_lock_irqsave(&mhi_ctx->msi_lock, flags); msi_buf = &ring->msi_buf; msi_buf->buf[0] = (mhi_ctx->msi_data + idx); msi_buffer = &ring->msi_buffer; msi_buffer->buf[0] = (mhi_ctx->msi_data + idx); descriptor = dmaengine_prep_dma_memcpy(mhi_ctx->tx_dma_chan, (dma_addr_t)(mhi_ctx->msi_lower), msi_buf->dma_addr, msi_buffer->dma_addr, sizeof(u32), DMA_PREP_INTERRUPT); if (!descriptor) { Loading @@ -327,7 +391,7 @@ static int mhi_trigger_msi_edma(struct mhi_dev_ring *ring, u32 idx) return -EFAULT; } descriptor->callback_param = msi_buf; descriptor->callback_param = msi_buffer; descriptor->callback = msi_trigger_completion_cb; dma_async_issue_pending(mhi_ctx->tx_dma_chan); Loading Loading @@ -384,16 +448,22 @@ static int mhi_dev_send_multiple_tr_events(struct mhi_dev *mhi, int evnt_ring, /* add the events */ ereq->client_cb = mhi_dev_event_buf_completion_cb; ereq->event_type = SEND_EVENT_BUFFER; rc = mhi_dev_add_element(ring, ereq->tr_events, ereq, evt_len); if (rc) { pr_err("%s(): error in adding element rc %d\n", __func__, rc); mutex_unlock(&ring->event_lock); return rc; } ring->ring_ctx_shadow->ev.rp = (ring->rd_offset * sizeof(union mhi_dev_ring_element_type)) + ring->ring_ctx->generic.rbase; ring->evt_rp_cache[ring->rd_offset] = ring->ring_ctx_shadow->ev.rp; mhi_log(MHI_MSG_VERBOSE, "Caching rp %llx for rd offset %d\n", ring->evt_rp_cache[ring->rd_offset], ring->rd_offset); mhi_log(MHI_MSG_VERBOSE, "ev.rp = %llx for %lld\n", ring->ring_ctx_shadow->ev.rp, evnt_ring_idx); Loading @@ -402,35 +472,37 @@ static int mhi_dev_send_multiple_tr_events(struct mhi_dev *mhi, int evnt_ring, sizeof(struct mhi_dev_ev_ctx) * evnt_ring) + (size_t)&ring->ring_ctx->ev.rp - (size_t)ring->ring_ctx; /* * As ev_ctx_cache memory is dma_alloc_coherent, dma_map_single * should not be called. Pass physical address to write to host. */ transfer_addr.phy_addr = (mhi->ev_ctx_cache_dma_handle + sizeof(struct mhi_dev_ev_ctx) * evnt_ring) + (size_t)&ring->ring_ctx->ev.rp - (size_t)ring->ring_ctx; transfer_addr.phy_addr = ring->evt_rp_cache_dma_handle + (sizeof(uint64_t) * ring->rd_offset); mhi_log(MHI_MSG_VERBOSE, "RP phy addr = 0x%x for ring rd offset %d\n", transfer_addr.phy_addr, ring->rd_offset); } else { transfer_addr.device_va = (mhi->ev_ctx_shadow.device_va + sizeof(struct mhi_dev_ev_ctx) * evnt_ring) + (size_t)&ring->ring_ctx->ev.rp - (size_t)ring->ring_ctx; transfer_addr.device_va = (size_t)(ring->evt_rp_cache + ring->rd_offset); } transfer_addr.virt_addr = &ring->ring_ctx_shadow->ev.rp; transfer_addr.virt_addr = &ring->evt_rp_cache[ring->rd_offset]; transfer_addr.size = sizeof(uint64_t); ereq->event_type = SEND_EVENT_RD_OFFSET; ereq->client_cb = mhi_dev_event_rd_offset_completion_cb; ereq->rd_offset_cb = mhi_dev_event_rd_offset_completion_cb; ereq->event_ring = evnt_ring; /* Schedule DMA for event ring RP*/ mhi_ctx->write_to_host(mhi, &transfer_addr, ereq, MHI_DEV_DMA_ASYNC); mutex_unlock(&ring->event_lock); if (mhi_ctx->use_edma) { rc = mhi_trigger_msi_edma(ring, ctx->ev.msivec); if (rc) pr_err("%s: error sending in msi\n", __func__); } else { /* Schedule DMA for MSI*/ rc = mhi_dev_schedule_msi_ipa(mhi, ereq); if (rc) pr_err("%s: error sending in msi\n", __func__); } mutex_unlock(&ring->event_lock); return rc; } Loading Loading @@ -463,7 +535,10 @@ static int mhi_dev_flush_transfer_completion_events(struct mhi_dev *mhi, list_del_init(&flush_ereq->list); spin_unlock_irqrestore(&mhi->lock, flags); mhi_log(MHI_MSG_DBG, "Flush called for ch %d\n", ch->ch_id); ch->flush_req_cnt++; flush_ereq->flush_num = ch->flush_req_cnt; mhi_log(MHI_MSG_DBG, "Flush num %d called for ch %d\n", ch->flush_req_cnt, ch->ch_id); rc = mhi_dev_send_multiple_tr_events(mhi, mhi->ch_ctx_cache[ch->ch_id].err_indx, flush_ereq, Loading Loading @@ -526,9 +601,24 @@ static int mhi_dev_queue_transfer_completion(struct mhi_req *mreq, bool *flush) compl_ev->evt_tr_comp.ptr = ch->ring->ring_ctx->generic.rbase + mreq->rd_offset * TR_RING_ELEMENT_SZ; ch->evt_buf_rp++; /* Ensure new event is flushed to memory */ wmb(); if (ch->evt_buf_rp == ch->evt_buf_size) ch->evt_buf_rp = 0; ch->curr_ereq->num_events++; mhi_log(MHI_MSG_VERBOSE, "evnt ptr : 0x%llx\n", compl_ev->evt_tr_comp.ptr); mhi_log(MHI_MSG_VERBOSE, "evnt len : 0x%x\n", compl_ev->evt_tr_comp.len); mhi_log(MHI_MSG_VERBOSE, "evnt code :0x%x\n", compl_ev->evt_tr_comp.code); mhi_log(MHI_MSG_VERBOSE, "evnt type :0x%x\n", compl_ev->evt_tr_comp.type); mhi_log(MHI_MSG_VERBOSE, "evnt chid :0x%x\n", compl_ev->evt_tr_comp.chid); mhi_log(MHI_MSG_VERBOSE, "evt_buf_rp: 0x%x, curr_ereq:0x%x\n", ch->evt_buf_rp, ch->curr_ereq->num_events); /* * It is not necessary to flush when we need to wrap-around, if * we do have free space in the buffer upon wrap-around. Loading Loading @@ -2879,6 +2969,14 @@ int mhi_dev_read_channel(struct mhi_req *mreq) mhi_log(MHI_MSG_ERROR, "invalid mhi request\n"); return -ENXIO; } if (atomic_read(&mhi_ctx->is_suspended)) { mhi_log(MHI_MSG_ERROR, "mhi still in suspend, return %d for read ch:%d\n", rc, mreq->client->channel->ch_id); return -ENODEV; } handle_client = mreq->client; ch = handle_client->channel; usr_buf_remaining = mreq->len; Loading Loading @@ -3590,26 +3688,6 @@ static int get_device_tree_data(struct platform_device *pdev) static int mhi_deinit(struct mhi_dev *mhi) { int i = 0, ring_id = 0; struct mhi_dev_ring *ring; 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); if (mhi->use_edma) dma_free_coherent(mhi->dev, sizeof(u32), ring->msi_buf.buf, ring->msi_buf.dma_addr); } mhi_dev_sm_exit(mhi); Loading @@ -3629,6 +3707,7 @@ static int mhi_init(struct mhi_dev *mhi) return rc; } if (!mhi->ring) mhi->ring = devm_kzalloc(&pdev->dev, (sizeof(struct mhi_dev_ring) * (mhi->cfg.channels + mhi->cfg.event_rings + 1)), Loading @@ -3655,8 +3734,10 @@ static int mhi_init(struct mhi_dev *mhi) spin_lock_init(&mhi->lock); spin_lock_init(&mhi->msi_lock); mhi->mmio_backup = devm_kzalloc(&pdev->dev, MHI_DEV_MMIO_RANGE, GFP_KERNEL); if (!mhi->mmio_backup) mhi->mmio_backup = devm_kzalloc(&pdev->dev, MHI_DEV_MMIO_RANGE, GFP_KERNEL); if (!mhi->mmio_backup) return -ENOMEM; Loading drivers/platform/msm/mhi_dev/mhi.h +16 −2 Original line number Diff line number Diff line /* SPDX-License-Identifier: GPL-2.0-only */ /* Copyright (c) 2015-2020, The Linux Foundation. All rights reserved.*/ /* Copyright (c) 2015-2021, The Linux Foundation. All rights reserved.*/ #ifndef __MHI_H #define __MHI_H Loading Loading @@ -354,6 +354,7 @@ enum mhi_dev_ch_operation { enum mhi_dev_tr_compl_evt_type { SEND_EVENT_BUFFER, SEND_EVENT_RD_OFFSET, SEND_MSI }; enum mhi_dev_transfer_type { Loading Loading @@ -388,13 +389,21 @@ struct mhi_dev_ring { union mhi_dev_ring_element_type *ring_cache; /* Physical address of the cached ring copy on the device side */ dma_addr_t ring_cache_dma_handle; /* Device VA of read pointer array (used only for event rings) */ uint64_t *evt_rp_cache; /* PA of the read pointer array (used only for event rings) */ dma_addr_t evt_rp_cache_dma_handle; /* Device VA of msi buffer (used only for event rings) */ uint32_t *msi_buf; /* PA of msi buf (used only for event rings) */ dma_addr_t msi_buf_dma_handle; /* Physical address of the host where we will write/read to/from */ struct mhi_addr ring_shadow; /* Ring type - cmd, event, transfer ring and its rp/wp... */ union mhi_dev_ring_ctx *ring_ctx; /* ring_ctx_shadow -> tracking ring_ctx in the host */ union mhi_dev_ring_ctx *ring_ctx_shadow; struct msi_buf_cb_data msi_buf; struct msi_buf_cb_data msi_buffer; void (*ring_cb)(struct mhi_dev *dev, union mhi_dev_ring_element_type *el, void *ctx); Loading Loading @@ -434,7 +443,10 @@ struct event_req { enum mhi_dev_tr_compl_evt_type event_type; u32 event_ring; void (*client_cb)(void *req); void (*rd_offset_cb)(void *req); void (*msi_cb)(void *req); struct list_head list; u32 flush_num; }; struct mhi_dev_channel { Loading Loading @@ -478,6 +490,8 @@ struct mhi_dev_channel { /* td size being read/written from/to so far */ uint32_t td_size; uint32_t pend_wr_count; uint32_t msi_cnt; uint32_t flush_req_cnt; bool skip_td; }; Loading drivers/platform/msm/mhi_dev/mhi_ring.c +78 −12 Original line number Diff line number Diff line // SPDX-License-Identifier: GPL-2.0-only /* Copyright (c) 2015-2020, The Linux Foundation. All rights reserved.*/ /* Copyright (c) 2015-2021, The Linux Foundation. All rights reserved.*/ #include <linux/kernel.h> #include <linux/of.h> Loading Loading @@ -279,6 +279,7 @@ int mhi_dev_add_element(struct mhi_dev_ring *ring, uint32_t num_elem = 1; uint32_t num_free_elem; struct mhi_dev *mhi_ctx; uint32_t i; if (WARN_ON(!ring || !element)) return -EINVAL; Loading Loading @@ -310,6 +311,10 @@ int mhi_dev_add_element(struct mhi_dev_ring *ring, } else mhi_dev_ring_inc_index(ring, ring->rd_offset); mhi_log(MHI_MSG_VERBOSE, "Writing %d elements, ring old 0x%x, new 0x%x\n", num_elem, old_offset, ring->rd_offset); ring->ring_ctx->generic.rp = (ring->rd_offset * sizeof(union mhi_dev_ring_element_type)) + ring->ring_ctx->generic.rbase; Loading Loading @@ -340,6 +345,19 @@ int mhi_dev_add_element(struct mhi_dev_ring *ring, return 0; } // Log elements added to ring for (i = 0; i < num_elem; ++i) { mhi_log(MHI_MSG_VERBOSE, "evnt ptr : 0x%llx\n", (element + i)->evt_tr_comp.ptr); mhi_log(MHI_MSG_VERBOSE, "evnt len : 0x%x\n", (element + i)->evt_tr_comp.len); mhi_log(MHI_MSG_VERBOSE, "evnt code :0x%x\n", (element + i)->evt_tr_comp.code); mhi_log(MHI_MSG_VERBOSE, "evnt type :0x%x\n", (element + i)->evt_tr_comp.type); mhi_log(MHI_MSG_VERBOSE, "evnt chid :0x%x\n", (element + i)->evt_tr_comp.chid); } /* Adding multiple ring elements */ if (ring->rd_offset == 0 || (ring->rd_offset > old_offset)) { /* No wrap-around case */ Loading @@ -349,6 +367,7 @@ int mhi_dev_add_element(struct mhi_dev_ring *ring, mhi_ctx->write_to_host(ring->mhi_dev, &host_addr, ereq, MHI_DEV_DMA_ASYNC); } else { mhi_log(MHI_MSG_VERBOSE, "Wrap around case\n"); /* Wrap-around case - first chunk uses dma sync */ host_addr.virt_addr = element; host_addr.size = (ring->ring_size - old_offset) * Loading Loading @@ -385,17 +404,17 @@ EXPORT_SYMBOL(mhi_dev_add_element); static int mhi_dev_ring_alloc_msi_buf(struct mhi_dev_ring *ring) { if (ring->msi_buf.buf) { if (ring->msi_buffer.buf) { mhi_log(MHI_MSG_INFO, "MSI buf already allocated\n"); return 0; } ring->msi_buf.buf = dma_alloc_coherent(&ring->mhi_dev->pdev->dev, ring->msi_buffer.buf = dma_alloc_coherent(&ring->mhi_dev->pdev->dev, sizeof(u32), &ring->msi_buf.dma_addr, &ring->msi_buffer.dma_addr, GFP_KERNEL); if (!ring->msi_buf.buf) if (!ring->msi_buffer.buf) return -ENOMEM; return 0; Loading Loading @@ -424,13 +443,45 @@ int mhi_ring_start(struct mhi_dev_ring *ring, union mhi_dev_ring_ctx *ctx, wr_offset = mhi_dev_ring_addr2ofst(ring, ring->ring_ctx->generic.wp); if (!ring->ring_cache) { ring->ring_cache = dma_alloc_coherent(mhi->dev, ring->ring_size * sizeof(union mhi_dev_ring_element_type), &ring->ring_cache_dma_handle, GFP_KERNEL); if (!ring->ring_cache) if (!ring->ring_cache) { mhi_log(MHI_MSG_ERROR, "Failed to allocate ring cache\n"); return -ENOMEM; } } if (ring->type == RING_TYPE_ER) { if (!ring->evt_rp_cache) { ring->evt_rp_cache = dma_alloc_coherent(mhi->dev, sizeof(uint64_t) * ring->ring_size, &ring->evt_rp_cache_dma_handle, GFP_KERNEL); if (!ring->evt_rp_cache) { mhi_log(MHI_MSG_ERROR, "Failed to allocate evt rp cache\n"); rc = -ENOMEM; goto cleanup; } } if (!ring->msi_buf) { ring->msi_buf = dma_alloc_coherent(mhi->dev, sizeof(uint32_t), &ring->msi_buf_dma_handle, GFP_KERNEL); if (!ring->msi_buf) { mhi_log(MHI_MSG_ERROR, "Failed to allocate msi buf\n"); rc = -ENOMEM; goto cleanup; } } } offset = (size_t)(ring->ring_ctx->generic.rbase - mhi->ctrl_base.host_pa); Loading Loading @@ -471,7 +522,22 @@ int mhi_ring_start(struct mhi_dev_ring *ring, union mhi_dev_ring_ctx *ctx, if (rc) return rc; } return rc; cleanup: dma_free_coherent(mhi->dev, ring->ring_size * sizeof(union mhi_dev_ring_element_type), ring->ring_cache, ring->ring_cache_dma_handle); ring->ring_cache = NULL; if (ring->evt_rp_cache) { dma_free_coherent(mhi->dev, sizeof(uint64_t) * ring->ring_size, ring->evt_rp_cache, ring->evt_rp_cache_dma_handle); ring->evt_rp_cache = NULL; } return rc; } EXPORT_SYMBOL(mhi_ring_start); Loading Loading
drivers/platform/msm/mhi_dev/mhi.c +152 −71 Original line number Diff line number Diff line // SPDX-License-Identifier: GPL-2.0-only /* Copyright (c) 2015-2020, The Linux Foundation. All rights reserved.*/ /* Copyright (c) 2015-2021, The Linux Foundation. All rights reserved.*/ /* * MSM MHI device core driver. Loading Loading @@ -84,6 +84,10 @@ static int mhi_dev_pcie_notify_event; static void mhi_dev_transfer_completion_cb(void *mreq); static int mhi_dev_alloc_evt_buf_evt_req(struct mhi_dev *mhi, struct mhi_dev_channel *ch, struct mhi_dev_ring *evt_ring); static int mhi_dev_schedule_msi_ipa(struct mhi_dev *mhi, struct event_req *ereq); static void mhi_dev_event_msi_cb(void *req); static struct mhi_dev_uevent_info channel_state_info[MHI_MAX_CHANNELS]; static DECLARE_COMPLETION(read_from_host); static DECLARE_COMPLETION(write_to_host); Loading Loading @@ -165,6 +169,7 @@ void mhi_dev_write_to_host_ipa(struct mhi_dev *mhi, struct mhi_addr *transfer, int rc = 0; uint64_t bit_40 = ((u64) 1) << 40, host_addr_pa = 0, offset = 0; dma_addr_t dma; void (*cb_func)(void *req); if (WARN_ON(!mhi)) return; Loading @@ -183,11 +188,12 @@ void mhi_dev_write_to_host_ipa(struct mhi_dev *mhi, struct mhi_addr *transfer, (int) transfer->size); if (tr_type == MHI_DEV_DMA_ASYNC) { /* * Event read pointer memory is dma_alloc_coherent memory * don't need to dma_map. Assigns the physical address in * phy_addr. * Event read pointer memory and MSI buf are dma_alloc_coherent * memory so don't need to dma_map. Use the physical address * passed in phy_addr. */ if (transfer->phy_addr) if (ereq->event_type == SEND_EVENT_RD_OFFSET || ereq->event_type == SEND_MSI) dma = transfer->phy_addr; else dma = dma_map_single(&mhi->pdev->dev, Loading @@ -196,6 +202,7 @@ void mhi_dev_write_to_host_ipa(struct mhi_dev *mhi, struct mhi_addr *transfer, if (ereq->event_type == SEND_EVENT_BUFFER) { ereq->dma = dma; ereq->dma_len = transfer->size; cb_func = ereq->client_cb; } else if (ereq->event_type == SEND_EVENT_RD_OFFSET) { /* * Event read pointer memory is dma_alloc_coherent Loading @@ -205,10 +212,13 @@ void mhi_dev_write_to_host_ipa(struct mhi_dev *mhi, struct mhi_addr *transfer, ereq->event_rd_dma = 0; else ereq->event_rd_dma = dma; cb_func = ereq->rd_offset_cb; } else { cb_func = ereq->msi_cb; } rc = ipa_dma_async_memcpy(host_addr_pa, (uint64_t)dma, (int)transfer->size, ereq->client_cb, ereq); cb_func, ereq); if (rc) pr_err("error while writing to host:%d\n", rc); } else if (tr_type == MHI_DEV_DMA_SYNC) { Loading @@ -235,11 +245,55 @@ static void mhi_dev_event_buf_completion_cb(void *req) { struct event_req *ereq = req; if (ereq) if (!ereq) { mhi_log(MHI_MSG_ERROR, "Null ereq\n"); return; } if (ereq->dma && ereq->dma_len) dma_unmap_single(&mhi_ctx->pdev->dev, ereq->dma, ereq->dma_len, DMA_TO_DEVICE); else mhi_log(MHI_MSG_ERROR, "event req is null\n"); mhi_log(MHI_MSG_VERBOSE, "Event buf dma completed for flush req %d\n", ereq->flush_num); } static int mhi_dev_schedule_msi_ipa(struct mhi_dev *mhi, struct event_req *ereq) { struct ep_pcie_msi_config cfg; struct mhi_addr msi_addr; struct mhi_dev_channel *ch = ereq->context; uint64_t evnt_ring_idx = mhi->ev_ring_start + ereq->event_ring; struct mhi_dev_ring *ring = &mhi->ring[evnt_ring_idx]; union mhi_dev_ring_ctx *ctx; int rc; rc = ep_pcie_get_msi_config(mhi->phandle, &cfg); if (rc) { pr_err("Error retrieving pcie msi logic\n"); return rc; } ctx = (union mhi_dev_ring_ctx *)&mhi->ev_ctx_cache[ereq->event_ring]; msi_addr.size = sizeof(uint32_t); msi_addr.host_pa = (uint64_t)((uint64_t)cfg.upper << 32) | (uint64_t)cfg.lower; *ring->msi_buf = cfg.data + ctx->ev.msivec; msi_addr.phy_addr = ring->msi_buf_dma_handle; ereq->event_type = SEND_MSI; ereq->msi_cb = mhi_dev_event_msi_cb; ch->msi_cnt++; mhi_log(MHI_MSG_VERBOSE, "Sending MSI %d to 0x%llx as data = 0x%x for ch %d msi_count %d, ereq flush_num %d\n", ctx->ev.msivec, msi_addr.host_pa, *ring->msi_buf, ch->ch_id, ch->msi_cnt, ereq->flush_num); mhi_ctx->write_to_host(mhi, &msi_addr, ereq, MHI_DEV_DMA_ASYNC); return 0; } /* Loading @@ -250,25 +304,35 @@ static void mhi_dev_event_buf_completion_cb(void *req) */ static void mhi_dev_event_rd_offset_completion_cb(void *req) { union mhi_dev_ring_ctx *ctx; int rc; struct event_req *ereq = req; struct mhi_dev_channel *ch = ereq->context; struct mhi_dev *mhi = ch->ring->mhi_dev; unsigned long flags; mhi_log(MHI_MSG_VERBOSE, "Rd offset dma completed for flush req %d\n", ereq->flush_num); if (ereq->event_rd_dma) dma_unmap_single(&mhi_ctx->pdev->dev, ereq->event_rd_dma, sizeof(uint64_t), DMA_TO_DEVICE); /* rp update in host memory should be flushed before sending an MSI */ wmb(); ctx = (union mhi_dev_ring_ctx *)&mhi->ev_ctx_cache[ereq->event_ring]; if (mhi_ctx->use_ipa) { rc = ep_pcie_trigger_msi(mhi_ctx->phandle, ctx->ev.msivec); if (rc) pr_err("%s: error sending in msi\n", __func__); } static void mhi_dev_event_msi_cb(void *req) { struct event_req *ereq = req; struct mhi_dev_channel *ch; struct mhi_dev *mhi; unsigned long flags; if (!ereq) { mhi_log(MHI_MSG_WARNING, "Null ereq, valid only for sync dma and cmd ack to host\n"); return; } ch = ereq->context; mhi = ch->ring->mhi_dev; mhi_log(MHI_MSG_VERBOSE, "MSI completed for flush req %d\n", ereq->flush_num); /* Add back the flushed events space to the event buffer */ ch->evt_buf_wp = ereq->start + ereq->num_events; if (ch->evt_buf_wp == ch->evt_buf_size) Loading @@ -292,7 +356,7 @@ static int mhi_trigger_msi_edma(struct mhi_dev_ring *ring, u32 idx) { struct dma_async_tx_descriptor *descriptor; struct ep_pcie_msi_config cfg; struct msi_buf_cb_data *msi_buf; struct msi_buf_cb_data *msi_buffer; int rc; unsigned long flags; Loading @@ -313,12 +377,12 @@ static int mhi_trigger_msi_edma(struct mhi_dev_ring *ring, u32 idx) spin_lock_irqsave(&mhi_ctx->msi_lock, flags); msi_buf = &ring->msi_buf; msi_buf->buf[0] = (mhi_ctx->msi_data + idx); msi_buffer = &ring->msi_buffer; msi_buffer->buf[0] = (mhi_ctx->msi_data + idx); descriptor = dmaengine_prep_dma_memcpy(mhi_ctx->tx_dma_chan, (dma_addr_t)(mhi_ctx->msi_lower), msi_buf->dma_addr, msi_buffer->dma_addr, sizeof(u32), DMA_PREP_INTERRUPT); if (!descriptor) { Loading @@ -327,7 +391,7 @@ static int mhi_trigger_msi_edma(struct mhi_dev_ring *ring, u32 idx) return -EFAULT; } descriptor->callback_param = msi_buf; descriptor->callback_param = msi_buffer; descriptor->callback = msi_trigger_completion_cb; dma_async_issue_pending(mhi_ctx->tx_dma_chan); Loading Loading @@ -384,16 +448,22 @@ static int mhi_dev_send_multiple_tr_events(struct mhi_dev *mhi, int evnt_ring, /* add the events */ ereq->client_cb = mhi_dev_event_buf_completion_cb; ereq->event_type = SEND_EVENT_BUFFER; rc = mhi_dev_add_element(ring, ereq->tr_events, ereq, evt_len); if (rc) { pr_err("%s(): error in adding element rc %d\n", __func__, rc); mutex_unlock(&ring->event_lock); return rc; } ring->ring_ctx_shadow->ev.rp = (ring->rd_offset * sizeof(union mhi_dev_ring_element_type)) + ring->ring_ctx->generic.rbase; ring->evt_rp_cache[ring->rd_offset] = ring->ring_ctx_shadow->ev.rp; mhi_log(MHI_MSG_VERBOSE, "Caching rp %llx for rd offset %d\n", ring->evt_rp_cache[ring->rd_offset], ring->rd_offset); mhi_log(MHI_MSG_VERBOSE, "ev.rp = %llx for %lld\n", ring->ring_ctx_shadow->ev.rp, evnt_ring_idx); Loading @@ -402,35 +472,37 @@ static int mhi_dev_send_multiple_tr_events(struct mhi_dev *mhi, int evnt_ring, sizeof(struct mhi_dev_ev_ctx) * evnt_ring) + (size_t)&ring->ring_ctx->ev.rp - (size_t)ring->ring_ctx; /* * As ev_ctx_cache memory is dma_alloc_coherent, dma_map_single * should not be called. Pass physical address to write to host. */ transfer_addr.phy_addr = (mhi->ev_ctx_cache_dma_handle + sizeof(struct mhi_dev_ev_ctx) * evnt_ring) + (size_t)&ring->ring_ctx->ev.rp - (size_t)ring->ring_ctx; transfer_addr.phy_addr = ring->evt_rp_cache_dma_handle + (sizeof(uint64_t) * ring->rd_offset); mhi_log(MHI_MSG_VERBOSE, "RP phy addr = 0x%x for ring rd offset %d\n", transfer_addr.phy_addr, ring->rd_offset); } else { transfer_addr.device_va = (mhi->ev_ctx_shadow.device_va + sizeof(struct mhi_dev_ev_ctx) * evnt_ring) + (size_t)&ring->ring_ctx->ev.rp - (size_t)ring->ring_ctx; transfer_addr.device_va = (size_t)(ring->evt_rp_cache + ring->rd_offset); } transfer_addr.virt_addr = &ring->ring_ctx_shadow->ev.rp; transfer_addr.virt_addr = &ring->evt_rp_cache[ring->rd_offset]; transfer_addr.size = sizeof(uint64_t); ereq->event_type = SEND_EVENT_RD_OFFSET; ereq->client_cb = mhi_dev_event_rd_offset_completion_cb; ereq->rd_offset_cb = mhi_dev_event_rd_offset_completion_cb; ereq->event_ring = evnt_ring; /* Schedule DMA for event ring RP*/ mhi_ctx->write_to_host(mhi, &transfer_addr, ereq, MHI_DEV_DMA_ASYNC); mutex_unlock(&ring->event_lock); if (mhi_ctx->use_edma) { rc = mhi_trigger_msi_edma(ring, ctx->ev.msivec); if (rc) pr_err("%s: error sending in msi\n", __func__); } else { /* Schedule DMA for MSI*/ rc = mhi_dev_schedule_msi_ipa(mhi, ereq); if (rc) pr_err("%s: error sending in msi\n", __func__); } mutex_unlock(&ring->event_lock); return rc; } Loading Loading @@ -463,7 +535,10 @@ static int mhi_dev_flush_transfer_completion_events(struct mhi_dev *mhi, list_del_init(&flush_ereq->list); spin_unlock_irqrestore(&mhi->lock, flags); mhi_log(MHI_MSG_DBG, "Flush called for ch %d\n", ch->ch_id); ch->flush_req_cnt++; flush_ereq->flush_num = ch->flush_req_cnt; mhi_log(MHI_MSG_DBG, "Flush num %d called for ch %d\n", ch->flush_req_cnt, ch->ch_id); rc = mhi_dev_send_multiple_tr_events(mhi, mhi->ch_ctx_cache[ch->ch_id].err_indx, flush_ereq, Loading Loading @@ -526,9 +601,24 @@ static int mhi_dev_queue_transfer_completion(struct mhi_req *mreq, bool *flush) compl_ev->evt_tr_comp.ptr = ch->ring->ring_ctx->generic.rbase + mreq->rd_offset * TR_RING_ELEMENT_SZ; ch->evt_buf_rp++; /* Ensure new event is flushed to memory */ wmb(); if (ch->evt_buf_rp == ch->evt_buf_size) ch->evt_buf_rp = 0; ch->curr_ereq->num_events++; mhi_log(MHI_MSG_VERBOSE, "evnt ptr : 0x%llx\n", compl_ev->evt_tr_comp.ptr); mhi_log(MHI_MSG_VERBOSE, "evnt len : 0x%x\n", compl_ev->evt_tr_comp.len); mhi_log(MHI_MSG_VERBOSE, "evnt code :0x%x\n", compl_ev->evt_tr_comp.code); mhi_log(MHI_MSG_VERBOSE, "evnt type :0x%x\n", compl_ev->evt_tr_comp.type); mhi_log(MHI_MSG_VERBOSE, "evnt chid :0x%x\n", compl_ev->evt_tr_comp.chid); mhi_log(MHI_MSG_VERBOSE, "evt_buf_rp: 0x%x, curr_ereq:0x%x\n", ch->evt_buf_rp, ch->curr_ereq->num_events); /* * It is not necessary to flush when we need to wrap-around, if * we do have free space in the buffer upon wrap-around. Loading Loading @@ -2879,6 +2969,14 @@ int mhi_dev_read_channel(struct mhi_req *mreq) mhi_log(MHI_MSG_ERROR, "invalid mhi request\n"); return -ENXIO; } if (atomic_read(&mhi_ctx->is_suspended)) { mhi_log(MHI_MSG_ERROR, "mhi still in suspend, return %d for read ch:%d\n", rc, mreq->client->channel->ch_id); return -ENODEV; } handle_client = mreq->client; ch = handle_client->channel; usr_buf_remaining = mreq->len; Loading Loading @@ -3590,26 +3688,6 @@ static int get_device_tree_data(struct platform_device *pdev) static int mhi_deinit(struct mhi_dev *mhi) { int i = 0, ring_id = 0; struct mhi_dev_ring *ring; 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); if (mhi->use_edma) dma_free_coherent(mhi->dev, sizeof(u32), ring->msi_buf.buf, ring->msi_buf.dma_addr); } mhi_dev_sm_exit(mhi); Loading @@ -3629,6 +3707,7 @@ static int mhi_init(struct mhi_dev *mhi) return rc; } if (!mhi->ring) mhi->ring = devm_kzalloc(&pdev->dev, (sizeof(struct mhi_dev_ring) * (mhi->cfg.channels + mhi->cfg.event_rings + 1)), Loading @@ -3655,8 +3734,10 @@ static int mhi_init(struct mhi_dev *mhi) spin_lock_init(&mhi->lock); spin_lock_init(&mhi->msi_lock); mhi->mmio_backup = devm_kzalloc(&pdev->dev, MHI_DEV_MMIO_RANGE, GFP_KERNEL); if (!mhi->mmio_backup) mhi->mmio_backup = devm_kzalloc(&pdev->dev, MHI_DEV_MMIO_RANGE, GFP_KERNEL); if (!mhi->mmio_backup) return -ENOMEM; Loading
drivers/platform/msm/mhi_dev/mhi.h +16 −2 Original line number Diff line number Diff line /* SPDX-License-Identifier: GPL-2.0-only */ /* Copyright (c) 2015-2020, The Linux Foundation. All rights reserved.*/ /* Copyright (c) 2015-2021, The Linux Foundation. All rights reserved.*/ #ifndef __MHI_H #define __MHI_H Loading Loading @@ -354,6 +354,7 @@ enum mhi_dev_ch_operation { enum mhi_dev_tr_compl_evt_type { SEND_EVENT_BUFFER, SEND_EVENT_RD_OFFSET, SEND_MSI }; enum mhi_dev_transfer_type { Loading Loading @@ -388,13 +389,21 @@ struct mhi_dev_ring { union mhi_dev_ring_element_type *ring_cache; /* Physical address of the cached ring copy on the device side */ dma_addr_t ring_cache_dma_handle; /* Device VA of read pointer array (used only for event rings) */ uint64_t *evt_rp_cache; /* PA of the read pointer array (used only for event rings) */ dma_addr_t evt_rp_cache_dma_handle; /* Device VA of msi buffer (used only for event rings) */ uint32_t *msi_buf; /* PA of msi buf (used only for event rings) */ dma_addr_t msi_buf_dma_handle; /* Physical address of the host where we will write/read to/from */ struct mhi_addr ring_shadow; /* Ring type - cmd, event, transfer ring and its rp/wp... */ union mhi_dev_ring_ctx *ring_ctx; /* ring_ctx_shadow -> tracking ring_ctx in the host */ union mhi_dev_ring_ctx *ring_ctx_shadow; struct msi_buf_cb_data msi_buf; struct msi_buf_cb_data msi_buffer; void (*ring_cb)(struct mhi_dev *dev, union mhi_dev_ring_element_type *el, void *ctx); Loading Loading @@ -434,7 +443,10 @@ struct event_req { enum mhi_dev_tr_compl_evt_type event_type; u32 event_ring; void (*client_cb)(void *req); void (*rd_offset_cb)(void *req); void (*msi_cb)(void *req); struct list_head list; u32 flush_num; }; struct mhi_dev_channel { Loading Loading @@ -478,6 +490,8 @@ struct mhi_dev_channel { /* td size being read/written from/to so far */ uint32_t td_size; uint32_t pend_wr_count; uint32_t msi_cnt; uint32_t flush_req_cnt; bool skip_td; }; Loading
drivers/platform/msm/mhi_dev/mhi_ring.c +78 −12 Original line number Diff line number Diff line // SPDX-License-Identifier: GPL-2.0-only /* Copyright (c) 2015-2020, The Linux Foundation. All rights reserved.*/ /* Copyright (c) 2015-2021, The Linux Foundation. All rights reserved.*/ #include <linux/kernel.h> #include <linux/of.h> Loading Loading @@ -279,6 +279,7 @@ int mhi_dev_add_element(struct mhi_dev_ring *ring, uint32_t num_elem = 1; uint32_t num_free_elem; struct mhi_dev *mhi_ctx; uint32_t i; if (WARN_ON(!ring || !element)) return -EINVAL; Loading Loading @@ -310,6 +311,10 @@ int mhi_dev_add_element(struct mhi_dev_ring *ring, } else mhi_dev_ring_inc_index(ring, ring->rd_offset); mhi_log(MHI_MSG_VERBOSE, "Writing %d elements, ring old 0x%x, new 0x%x\n", num_elem, old_offset, ring->rd_offset); ring->ring_ctx->generic.rp = (ring->rd_offset * sizeof(union mhi_dev_ring_element_type)) + ring->ring_ctx->generic.rbase; Loading Loading @@ -340,6 +345,19 @@ int mhi_dev_add_element(struct mhi_dev_ring *ring, return 0; } // Log elements added to ring for (i = 0; i < num_elem; ++i) { mhi_log(MHI_MSG_VERBOSE, "evnt ptr : 0x%llx\n", (element + i)->evt_tr_comp.ptr); mhi_log(MHI_MSG_VERBOSE, "evnt len : 0x%x\n", (element + i)->evt_tr_comp.len); mhi_log(MHI_MSG_VERBOSE, "evnt code :0x%x\n", (element + i)->evt_tr_comp.code); mhi_log(MHI_MSG_VERBOSE, "evnt type :0x%x\n", (element + i)->evt_tr_comp.type); mhi_log(MHI_MSG_VERBOSE, "evnt chid :0x%x\n", (element + i)->evt_tr_comp.chid); } /* Adding multiple ring elements */ if (ring->rd_offset == 0 || (ring->rd_offset > old_offset)) { /* No wrap-around case */ Loading @@ -349,6 +367,7 @@ int mhi_dev_add_element(struct mhi_dev_ring *ring, mhi_ctx->write_to_host(ring->mhi_dev, &host_addr, ereq, MHI_DEV_DMA_ASYNC); } else { mhi_log(MHI_MSG_VERBOSE, "Wrap around case\n"); /* Wrap-around case - first chunk uses dma sync */ host_addr.virt_addr = element; host_addr.size = (ring->ring_size - old_offset) * Loading Loading @@ -385,17 +404,17 @@ EXPORT_SYMBOL(mhi_dev_add_element); static int mhi_dev_ring_alloc_msi_buf(struct mhi_dev_ring *ring) { if (ring->msi_buf.buf) { if (ring->msi_buffer.buf) { mhi_log(MHI_MSG_INFO, "MSI buf already allocated\n"); return 0; } ring->msi_buf.buf = dma_alloc_coherent(&ring->mhi_dev->pdev->dev, ring->msi_buffer.buf = dma_alloc_coherent(&ring->mhi_dev->pdev->dev, sizeof(u32), &ring->msi_buf.dma_addr, &ring->msi_buffer.dma_addr, GFP_KERNEL); if (!ring->msi_buf.buf) if (!ring->msi_buffer.buf) return -ENOMEM; return 0; Loading Loading @@ -424,13 +443,45 @@ int mhi_ring_start(struct mhi_dev_ring *ring, union mhi_dev_ring_ctx *ctx, wr_offset = mhi_dev_ring_addr2ofst(ring, ring->ring_ctx->generic.wp); if (!ring->ring_cache) { ring->ring_cache = dma_alloc_coherent(mhi->dev, ring->ring_size * sizeof(union mhi_dev_ring_element_type), &ring->ring_cache_dma_handle, GFP_KERNEL); if (!ring->ring_cache) if (!ring->ring_cache) { mhi_log(MHI_MSG_ERROR, "Failed to allocate ring cache\n"); return -ENOMEM; } } if (ring->type == RING_TYPE_ER) { if (!ring->evt_rp_cache) { ring->evt_rp_cache = dma_alloc_coherent(mhi->dev, sizeof(uint64_t) * ring->ring_size, &ring->evt_rp_cache_dma_handle, GFP_KERNEL); if (!ring->evt_rp_cache) { mhi_log(MHI_MSG_ERROR, "Failed to allocate evt rp cache\n"); rc = -ENOMEM; goto cleanup; } } if (!ring->msi_buf) { ring->msi_buf = dma_alloc_coherent(mhi->dev, sizeof(uint32_t), &ring->msi_buf_dma_handle, GFP_KERNEL); if (!ring->msi_buf) { mhi_log(MHI_MSG_ERROR, "Failed to allocate msi buf\n"); rc = -ENOMEM; goto cleanup; } } } offset = (size_t)(ring->ring_ctx->generic.rbase - mhi->ctrl_base.host_pa); Loading Loading @@ -471,7 +522,22 @@ int mhi_ring_start(struct mhi_dev_ring *ring, union mhi_dev_ring_ctx *ctx, if (rc) return rc; } return rc; cleanup: dma_free_coherent(mhi->dev, ring->ring_size * sizeof(union mhi_dev_ring_element_type), ring->ring_cache, ring->ring_cache_dma_handle); ring->ring_cache = NULL; if (ring->evt_rp_cache) { dma_free_coherent(mhi->dev, sizeof(uint64_t) * ring->ring_size, ring->evt_rp_cache, ring->evt_rp_cache_dma_handle); ring->evt_rp_cache = NULL; } return rc; } EXPORT_SYMBOL(mhi_ring_start); Loading