diff options
| author | Sujeev Dias <sdias@codeaurora.org> | 2017-01-19 12:19:55 -0800 |
|---|---|---|
| committer | Sujeev Dias <sdias@codeaurora.org> | 2017-03-11 12:10:22 -0800 |
| commit | 5ce3f182b801cf3b958cc991b7a3af619968dd15 (patch) | |
| tree | 00c64ed7415f0f6d74d8263c69a8fb22d8fb8e1f | |
| parent | 75edc331a7bc105e5382f606d074f92b8d0a838c (diff) | |
mhi: core: add support for bounce buffer transfer
Not all MHI capable devices can access entire host
DDR. If an MHI client provide a transfer packet that’s
outside of supported address range, copy the packet to
a local bounce buffer prior to transfer.
CRs-Fixed 1110280
Change-Id: Ie7292a2c89cc6608d5360f8a330d78635d913c29
Signed-off-by: Sujeev Dias <sdias@codeaurora.org>
| -rw-r--r-- | Documentation/devicetree/bindings/mhi/msm_mhi.txt | 7 | ||||
| -rw-r--r-- | drivers/platform/msm/mhi/mhi.h | 7 | ||||
| -rw-r--r-- | drivers/platform/msm/mhi/mhi_iface.c | 6 | ||||
| -rw-r--r-- | drivers/platform/msm/mhi/mhi_main.c | 236 |
4 files changed, 162 insertions, 94 deletions
diff --git a/Documentation/devicetree/bindings/mhi/msm_mhi.txt b/Documentation/devicetree/bindings/mhi/msm_mhi.txt index ba333cfd9fab..6467f4d103e5 100644 --- a/Documentation/devicetree/bindings/mhi/msm_mhi.txt +++ b/Documentation/devicetree/bindings/mhi/msm_mhi.txt @@ -120,6 +120,13 @@ Main node properties: Value type: <u32> Definition: Segment size in bytes for each segment in bytes. +- qcom,mhi-bb-required + Usage: optional + Value type: bool + Definition: Determine whether MHI device require bounce buffer + during active transfer. If true, during channel open host + will pre-allocate transfer buffers. + ======== Example: ======== diff --git a/drivers/platform/msm/mhi/mhi.h b/drivers/platform/msm/mhi/mhi.h index ea724996cf11..de46fd39259a 100644 --- a/drivers/platform/msm/mhi/mhi.h +++ b/drivers/platform/msm/mhi/mhi.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2014-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2014-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 @@ -348,6 +348,7 @@ struct mhi_ring { u32 msi_disable_cntr; u32 msi_enable_cntr; spinlock_t ring_lock; + struct dma_pool *dma_pool; }; enum MHI_CMD_STATUS { @@ -446,9 +447,12 @@ struct mhi_state_work_queue { struct mhi_buf_info { dma_addr_t bb_p_addr; + dma_addr_t pre_alloc_p_addr; void *bb_v_addr; + void *pre_alloc_v_addr; void *client_buf; size_t buf_len; + size_t pre_alloc_len; size_t filled_size; enum dma_data_direction dir; int bb_active; @@ -479,6 +483,7 @@ struct mhi_flags { u32 kill_threads; u32 ev_thread_stopped; u32 st_thread_stopped; + bool bb_required; }; struct mhi_wait_queues { diff --git a/drivers/platform/msm/mhi/mhi_iface.c b/drivers/platform/msm/mhi/mhi_iface.c index 1fa32d435469..50e330a6742b 100644 --- a/drivers/platform/msm/mhi/mhi_iface.c +++ b/drivers/platform/msm/mhi/mhi_iface.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2014-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2014-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 @@ -456,6 +456,10 @@ static int mhi_plat_probe(struct platform_device *pdev) INIT_WORK(&bhi_ctxt->fw_load_work, bhi_firmware_download); } + mhi_dev_ctxt->flags.bb_required = + of_property_read_bool(pdev->dev.of_node, + "qcom,mhi-bb-required"); + mhi_dev_ctxt->plat_dev = pdev; platform_set_drvdata(pdev, mhi_dev_ctxt); diff --git a/drivers/platform/msm/mhi/mhi_main.c b/drivers/platform/msm/mhi/mhi_main.c index 1ce477966b77..889c4708692e 100644 --- a/drivers/platform/msm/mhi/mhi_main.c +++ b/drivers/platform/msm/mhi/mhi_main.c @@ -32,9 +32,18 @@ static int reset_chan_cmd(struct mhi_device_ctxt *mhi_dev_ctxt, union mhi_cmd_pkt *cmd_pkt); - -static int enable_bb_ctxt(struct mhi_ring *bb_ctxt, int nr_el) +static void disable_bb_ctxt(struct mhi_device_ctxt *mhi_dev_ctxt, + struct mhi_ring *bb_ctxt); + +static int enable_bb_ctxt(struct mhi_device_ctxt *mhi_dev_ctxt, + struct mhi_ring *bb_ctxt, + int nr_el, + int chan, + size_t max_payload) { + int i; + struct mhi_buf_info *mhi_buf_info; + 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); @@ -43,7 +52,46 @@ static int enable_bb_ctxt(struct mhi_ring *bb_ctxt, int nr_el) bb_ctxt->ack_rp = bb_ctxt->base; if (!bb_ctxt->base) return -ENOMEM; + + if (mhi_dev_ctxt->flags.bb_required) { + char pool_name[32]; + + snprintf(pool_name, sizeof(pool_name), "mhi%d_%d", + mhi_dev_ctxt->plat_dev->id, chan); + + mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, + "Creating pool %s for chan:%d payload: 0x%lx\n", + pool_name, chan, max_payload); + + bb_ctxt->dma_pool = dma_pool_create(pool_name, + &mhi_dev_ctxt->plat_dev->dev, max_payload, 0, 0); + if (unlikely(!bb_ctxt->dma_pool)) + goto dma_pool_error; + + mhi_buf_info = (struct mhi_buf_info *)bb_ctxt->base; + for (i = 0; i < nr_el; i++, mhi_buf_info++) { + mhi_buf_info->pre_alloc_v_addr = + dma_pool_alloc(bb_ctxt->dma_pool, GFP_KERNEL, + &mhi_buf_info->pre_alloc_p_addr); + if (unlikely(!mhi_buf_info->pre_alloc_v_addr)) + goto dma_alloc_error; + mhi_buf_info->pre_alloc_len = max_payload; + } + } + return 0; + +dma_alloc_error: + for (--i, --mhi_buf_info; i >= 0; i--, mhi_buf_info--) + dma_pool_free(bb_ctxt->dma_pool, mhi_buf_info->pre_alloc_v_addr, + mhi_buf_info->pre_alloc_p_addr); + + dma_pool_destroy(bb_ctxt->dma_pool); + bb_ctxt->dma_pool = NULL; +dma_pool_error: + kfree(bb_ctxt->base); + bb_ctxt->base = NULL; + return -ENOMEM; } static void mhi_write_db(struct mhi_device_ctxt *mhi_dev_ctxt, @@ -207,11 +255,9 @@ int mhi_release_chan_ctxt(struct mhi_device_ctxt *mhi_dev_ctxt, return 0; } -void free_tre_ring(struct mhi_client_config *client_config) +void free_tre_ring(struct mhi_device_ctxt *mhi_dev_ctxt, int chan) { struct mhi_chan_ctxt *chan_ctxt; - struct mhi_device_ctxt *mhi_dev_ctxt = client_config->mhi_dev_ctxt; - int chan = client_config->chan_info.chan_nr; int r; chan_ctxt = &mhi_dev_ctxt->dev_space.ring_ctxt.cc_list[chan]; @@ -276,11 +322,6 @@ int mhi_open_channel(struct mhi_client_handle *client_handle) return -EINVAL; mhi_dev_ctxt = client_config->mhi_dev_ctxt; - ret_val = get_chan_props(mhi_dev_ctxt, - client_config->chan_info.chan_nr, - &client_config->chan_info); - if (ret_val) - return ret_val; chan = client_config->chan_info.chan_nr; cfg = &mhi_dev_ctxt->mhi_chan_cfg[chan]; @@ -302,21 +343,11 @@ int mhi_open_channel(struct mhi_client_handle *client_handle) mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR, "Failed to initialize tre ring chan %d ret %d\n", chan, ret_val); - mutex_unlock(&cfg->chan_lock); - return ret_val; + goto error_tre_ring; } client_config->event_ring_index = mhi_dev_ctxt->dev_space.ring_ctxt. cc_list[chan].mhi_event_ring_index; - ret_val = enable_bb_ctxt(&mhi_dev_ctxt->chan_bb_list[chan], - client_config->chan_info.max_desc); - if (ret_val) { - mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR, - "Failed to initialize bb ctxt chan %d ret %d\n", - chan, ret_val); - mutex_unlock(&cfg->chan_lock); - return ret_val; - } client_config->msi_vec = mhi_dev_ctxt->dev_space.ring_ctxt.ec_list[ @@ -331,18 +362,13 @@ int mhi_open_channel(struct mhi_client_handle *client_handle) mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR, "MHI State is disabled\n"); read_unlock_bh(&mhi_dev_ctxt->pm_xfer_lock); - mutex_unlock(&cfg->chan_lock); - return -EIO; + ret_val = -EIO; + goto error_pm_state; } - - WARN_ON(mhi_dev_ctxt->mhi_pm_state == MHI_PM_DISABLE); mhi_dev_ctxt->assert_wake(mhi_dev_ctxt, false); read_unlock_bh(&mhi_dev_ctxt->pm_xfer_lock); mhi_dev_ctxt->runtime_get(mhi_dev_ctxt); - spin_lock_irq(&chan_ring->ring_lock); - chan_ring->ch_state = MHI_CHAN_STATE_ENABLED; - spin_unlock_irq(&chan_ring->ring_lock); ret_val = mhi_send_cmd(client_config->mhi_dev_ctxt, MHI_COMMAND_START_CHAN, chan); @@ -377,10 +403,11 @@ int mhi_open_channel(struct mhi_client_handle *client_handle) goto error_completion; } + spin_lock_irq(&chan_ring->ring_lock); + chan_ring->ch_state = MHI_CHAN_STATE_ENABLED; + spin_unlock_irq(&chan_ring->ring_lock); client_config->chan_status = 1; -error_completion: - read_lock_bh(&mhi_dev_ctxt->pm_xfer_lock); mhi_dev_ctxt->deassert_wake(mhi_dev_ctxt); read_unlock_bh(&mhi_dev_ctxt->pm_xfer_lock); @@ -388,6 +415,19 @@ error_completion: mutex_unlock(&cfg->chan_lock); mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, + "chan:%d opened successfully\n", chan); + return 0; + +error_completion: + read_lock_bh(&mhi_dev_ctxt->pm_xfer_lock); + mhi_dev_ctxt->deassert_wake(mhi_dev_ctxt); + read_unlock_bh(&mhi_dev_ctxt->pm_xfer_lock); + mhi_dev_ctxt->runtime_put(mhi_dev_ctxt); +error_pm_state: + free_tre_ring(mhi_dev_ctxt, chan); +error_tre_ring: + mutex_unlock(&cfg->chan_lock); + mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, "Exited chan 0x%x ret:%d\n", chan, ret_val); return ret_val; } @@ -431,6 +471,7 @@ int mhi_register_channel(struct mhi_client_handle **client_handle, struct mhi_client_config *client_config; const char *node_name; enum MHI_CLIENT_CHANNEL chan; + int ret; if (!client_info || client_info->dev->of_node == NULL) return -EINVAL; @@ -489,6 +530,17 @@ int mhi_register_channel(struct mhi_client_handle **client_handle, if (MHI_CLIENT_IP_HW_0_IN == chan) client_config->intmod_t = 10; + get_chan_props(mhi_dev_ctxt, chan, &client_config->chan_info); + ret = enable_bb_ctxt(mhi_dev_ctxt, &mhi_dev_ctxt->chan_bb_list[chan], + client_config->chan_info.max_desc, chan, + client_config->client_info.max_payload); + if (ret) { + kfree(mhi_dev_ctxt->client_handle_list[chan]->client_config); + kfree(mhi_dev_ctxt->client_handle_list[chan]); + mhi_dev_ctxt->client_handle_list[chan] = NULL; + return -ENOMEM; + } + if (mhi_dev_ctxt->dev_exec_env == MHI_EXEC_ENV_AMSS && mhi_dev_ctxt->flags.mhi_initialized) { mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, @@ -531,6 +583,14 @@ void mhi_close_channel(struct mhi_client_handle *client_handle) /* No more processing events for this channel */ spin_lock_irq(&chan_ring->ring_lock); + if (chan_ring->ch_state != MHI_CHAN_STATE_ENABLED) { + mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, + "Chan %d is not enabled, cur state:0x%x\n", + chan, chan_ring->ch_state); + spin_unlock_irq(&chan_ring->ring_lock); + mutex_unlock(&cfg->chan_lock); + return; + } chan_ring->ch_state = MHI_CHAN_STATE_DISABLED; spin_unlock_irq(&chan_ring->ring_lock); init_completion(&cfg->cmd_complete); @@ -565,22 +625,30 @@ void mhi_close_channel(struct mhi_client_handle *client_handle) mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR, "Error to receive event completion ev_cod:0x%x\n", ev_code); - goto error_completion; } +error_completion: ret_val = reset_chan_cmd(mhi_dev_ctxt, &cmd_pkt); if (ret_val) mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR, "Error resetting cmd ret:%d\n", ret_val); -error_completion: read_lock_bh(&mhi_dev_ctxt->pm_xfer_lock); mhi_dev_ctxt->deassert_wake(mhi_dev_ctxt); read_unlock_bh(&mhi_dev_ctxt->pm_xfer_lock); mhi_dev_ctxt->runtime_put(mhi_dev_ctxt); mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, + "resetting bb_ring for chan 0x%x\n", chan); + mhi_dev_ctxt->chan_bb_list[chan].rp = + mhi_dev_ctxt->chan_bb_list[chan].base; + mhi_dev_ctxt->chan_bb_list[chan].wp = + mhi_dev_ctxt->chan_bb_list[chan].base; + mhi_dev_ctxt->chan_bb_list[chan].ack_rp = + mhi_dev_ctxt->chan_bb_list[chan].base; + + mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, "Freeing ring for chan 0x%x\n", chan); - free_tre_ring(client_config); + free_tre_ring(mhi_dev_ctxt, chan); mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, "Chan 0x%x confirmed closed.\n", chan); client_config->chan_status = 0; @@ -639,6 +707,7 @@ static inline int mhi_queue_tre(struct mhi_device_ctxt } return 0; } + static int create_bb(struct mhi_device_ctxt *mhi_dev_ctxt, int chan, void *buf, size_t buf_len, enum dma_data_direction dir, struct mhi_buf_info **bb) @@ -674,6 +743,7 @@ static int create_bb(struct mhi_device_ctxt *mhi_dev_ctxt, bb_info->client_buf, bb_info->buf_len, bb_info->dir); + bb_info->bb_active = 0; if (!VALID_BUF(bb_info->bb_p_addr, bb_info->buf_len, mhi_dev_ctxt)) { mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, "Buffer outside DMA range 0x%lx, size 0x%zx\n", @@ -682,30 +752,48 @@ static int create_bb(struct mhi_device_ctxt *mhi_dev_ctxt, bb_info->bb_p_addr, bb_info->buf_len, bb_info->dir); - mhi_log(mhi_dev_ctxt, MHI_MSG_RAW, - "Allocating BB, chan %d\n", chan); - bb_info->bb_v_addr = dma_alloc_coherent( - &mhi_dev_ctxt->plat_dev->dev, - bb_info->buf_len, - &bb_info->bb_p_addr, - GFP_ATOMIC); - if (!bb_info->bb_v_addr) - return -ENOMEM; - mhi_dev_ctxt->counters.bb_used[chan]++; - if (dir == DMA_TO_DEVICE) { - mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, - "Copying client buf into BB.\n"); - memcpy(bb_info->bb_v_addr, buf, bb_info->buf_len); - /* Flush out data to bounce buffer */ - wmb(); - } - bb_info->bb_active = 1; + + if (likely((mhi_dev_ctxt->flags.bb_required && + bb_info->pre_alloc_len >= bb_info->buf_len))) { + bb_info->bb_p_addr = bb_info->pre_alloc_p_addr; + bb_info->bb_v_addr = bb_info->pre_alloc_v_addr; + mhi_dev_ctxt->counters.bb_used[chan]++; + if (dir == DMA_TO_DEVICE) { + mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, + "Copying client buf into BB.\n"); + memcpy(bb_info->bb_v_addr, buf, + bb_info->buf_len); + } + bb_info->bb_active = 1; + } else + mhi_log(mhi_dev_ctxt, MHI_MSG_ERROR, + "No BB allocated\n"); } *bb = bb_info; mhi_log(mhi_dev_ctxt, MHI_MSG_RAW, "Exited chan %d\n", chan); return 0; } +static void disable_bb_ctxt(struct mhi_device_ctxt *mhi_dev_ctxt, + struct mhi_ring *bb_ctxt) +{ + if (mhi_dev_ctxt->flags.bb_required) { + struct mhi_buf_info *bb = + (struct mhi_buf_info *)bb_ctxt->base; + int nr_el = bb_ctxt->len / bb_ctxt->el_size; + int i = 0; + + for (i = 0; i < nr_el; i++, bb++) + dma_pool_free(bb_ctxt->dma_pool, bb->pre_alloc_v_addr, + bb->pre_alloc_p_addr); + dma_pool_destroy(bb_ctxt->dma_pool); + bb_ctxt->dma_pool = NULL; + } + + kfree(bb_ctxt->base); + bb_ctxt->base = NULL; +} + static void free_bounce_buffer(struct mhi_device_ctxt *mhi_dev_ctxt, struct mhi_buf_info *bb) { @@ -714,44 +802,11 @@ static void free_bounce_buffer(struct mhi_device_ctxt *mhi_dev_ctxt, /* This buffer was maped directly to device */ dma_unmap_single(&mhi_dev_ctxt->plat_dev->dev, bb->bb_p_addr, bb->buf_len, bb->dir); - else - /* This buffer was bounced */ - dma_free_coherent(&mhi_dev_ctxt->plat_dev->dev, - bb->buf_len, - bb->bb_v_addr, - bb->bb_p_addr); + bb->bb_active = 0; mhi_log(mhi_dev_ctxt, MHI_MSG_RAW, "Exited\n"); } -void reset_bb_ctxt(struct mhi_device_ctxt *mhi_dev_ctxt, - struct mhi_ring *bb_ctxt) -{ - int r = 0; - struct mhi_buf_info *bb = NULL; - - mhi_log(mhi_dev_ctxt, MHI_MSG_VERBOSE, "Entered\n"); - /* - Assumption: No events are expected during or after - this operation is occurring for this channel. - If a bounce buffer was allocated, the coherent memory is - expected to be already freed. - If the user's bounce buffer was mapped, it is expected to be - already unmapped. - Failure of any of the above conditions will result in - a memory leak or subtle memory corruption. - */ - while (!r) { - r = ctxt_del_element(bb_ctxt, (void **)&bb); - if (bb) - free_bounce_buffer(mhi_dev_ctxt, bb); - } - bb_ctxt->ack_rp = bb_ctxt->base; - bb_ctxt->rp = bb_ctxt->base; - bb_ctxt->wp = bb_ctxt->base; - mhi_log(mhi_dev_ctxt, MHI_MSG_VERBOSE, "Exited\n"); -} - static int mhi_queue_dma_xfer( struct mhi_client_config *client_config, dma_addr_t buf, size_t buf_len, enum MHI_FLAGS mhi_flags) @@ -1342,7 +1397,6 @@ static int reset_chan_cmd(struct mhi_device_ctxt *mhi_dev_ctxt, struct mhi_chan_ctxt *chan_ctxt; struct mhi_event_ctxt *ev_ctxt = NULL; int pending_el = 0, i; - struct mhi_ring *bb_ctxt; unsigned long flags; union mhi_event_pkt *local_rp = NULL; union mhi_event_pkt *device_rp = NULL; @@ -1355,8 +1409,6 @@ static int reset_chan_cmd(struct mhi_device_ctxt *mhi_dev_ctxt, return -EINVAL; } - bb_ctxt = &mhi_dev_ctxt->chan_bb_list[chan]; - local_chan_ctxt = &mhi_dev_ctxt->mhi_local_chan_ctxt[chan]; chan_ctxt = &mhi_dev_ctxt->dev_space.ring_ctxt.cc_list[chan]; ev_ring = &mhi_dev_ctxt-> @@ -1426,9 +1478,6 @@ static int reset_chan_cmd(struct mhi_device_ctxt *mhi_dev_ctxt, chan_ctxt->mhi_trb_read_ptr = chan_ctxt->mhi_trb_ring_base_addr; chan_ctxt->mhi_trb_write_ptr = chan_ctxt->mhi_trb_ring_base_addr; - mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, "Cleaning up BB list\n"); - reset_bb_ctxt(mhi_dev_ctxt, bb_ctxt); - mhi_log(mhi_dev_ctxt, MHI_MSG_INFO, "Reset complete.\n"); return ret_val; } @@ -1672,14 +1721,17 @@ int mhi_deregister_channel(struct mhi_client_handle *client_handle) int ret_val = 0; int chan; struct mhi_client_config *client_config; + struct mhi_device_ctxt *mhi_dev_ctxt; if (!client_handle) return -EINVAL; client_config = client_handle->client_config; + mhi_dev_ctxt = client_config->mhi_dev_ctxt; chan = client_config->chan_info.chan_nr; client_config->magic = 0; - client_config->mhi_dev_ctxt->client_handle_list[chan] = NULL; + mhi_dev_ctxt->client_handle_list[chan] = NULL; + disable_bb_ctxt(mhi_dev_ctxt, &mhi_dev_ctxt->chan_bb_list[chan]); kfree(client_config); kfree(client_handle); return ret_val; |
