From 021307244fc254bc5cb7cf5b359d20bf6d12e860 Mon Sep 17 00:00:00 2001 From: Chaitanya Pratapa Date: Fri, 31 Jan 2020 16:06:58 -0800 Subject: [PATCH] ipa: use common ring event ring for WLAN MCC pipes in auto config In IPA4.0 Auto configuration, all event rings are allocated for different use cases and if we use different event rings for WLAN MCC pipes event ring allocation fails resulting in WLAN enablement failure. Make changes to use common event ring for WLAN MCC pipes. Change-Id: I46a462a4ad1a9135b1fd28fbc627eb49017a11e3 Signed-off-by: Chaitanya Pratapa --- drivers/platform/msm/gsi/gsi.c | 44 ++++++++- drivers/platform/msm/gsi/gsi.h | 9 +- drivers/platform/msm/ipa/ipa_v3/ipa_dp.c | 112 +++++++++++++++++++---- drivers/platform/msm/ipa/ipa_v3/ipa_i.h | 8 +- include/linux/msm_gsi.h | 5 +- 5 files changed, 156 insertions(+), 22 deletions(-) diff --git a/drivers/platform/msm/gsi/gsi.c b/drivers/platform/msm/gsi/gsi.c index c38f1be7e3fb..03e20005d55d 100644 --- a/drivers/platform/msm/gsi/gsi.c +++ b/drivers/platform/msm/gsi/gsi.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2015-2018, The Linux Foundation. All rights reserved. +/* Copyright (c) 2015-2018, 2020, 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 @@ -944,6 +944,9 @@ int gsi_register_device(struct gsi_per_props *props, unsigned long *dev_hdl) __gsi_config_gen_irq(props->ee, ~0, ~GSI_EE_n_CNTXT_GSI_IRQ_CLR_GSI_BREAK_POINT_BMSK); + gsi_ctx->shared_ch_info.ch_id = gsi_ctx->max_ch; + gsi_ctx->shared_ch_info.evchid = gsi_ctx->max_ev; + gsi_writel(props->intr, gsi_ctx->base + GSI_EE_n_CNTXT_INTSET_OFFS(gsi_ctx->per.ee)); @@ -1801,7 +1804,8 @@ int gsi_alloc_channel(struct gsi_chan_props *props, unsigned long dev_hdl, if (atomic_read( &gsi_ctx->evtr[props->evt_ring_hdl].chan_ref_cnt) && - gsi_ctx->evtr[props->evt_ring_hdl].props.exclusive) { + gsi_ctx->evtr[props->evt_ring_hdl].props.exclusive && + !props->common_evt_ring) { GSIERR("evt ring=%lu exclusively used by chan_hdl=%p\n", props->evt_ring_hdl, chan_hdl); return -GSI_STATUS_UNSUPPORTED_OP; @@ -1870,6 +1874,10 @@ int gsi_alloc_channel(struct gsi_chan_props *props, unsigned long dev_hdl, ctx->user_data = user_data; *chan_hdl = props->ch_id; ctx->allocated = true; + if ((props->evt_ring_hdl != ~0) && props->common_evt_ring) { + gsi_ctx->shared_ch_info.ch_id = props->ch_id; + gsi_ctx->shared_ch_info.evchid = props->evt_ring_hdl; + } ctx->stats.dp.last_timestamp = jiffies_to_msecs(jiffies); atomic_inc(&gsi_ctx->num_chan); @@ -2375,6 +2383,11 @@ int gsi_dealloc_channel(unsigned long chan_hdl) atomic_dec(&ctx->evtr->chan_ref_cnt); atomic_dec(&gsi_ctx->num_chan); + if (gsi_ctx->shared_ch_info.ch_id == chan_hdl) { + gsi_ctx->shared_ch_info.ch_id = gsi_ctx->max_ch; + gsi_ctx->shared_ch_info.evchid = gsi_ctx->max_ev; + } + return GSI_STATUS_SUCCESS; } EXPORT_SYMBOL(gsi_dealloc_channel); @@ -2751,6 +2764,7 @@ EXPORT_SYMBOL(gsi_poll_channel); int gsi_config_channel_mode(unsigned long chan_hdl, enum gsi_chan_mode mode) { struct gsi_chan_ctx *ctx; + struct gsi_chan_ctx *shared_ch_ctx; enum gsi_chan_mode curr; unsigned long flags; @@ -2793,13 +2807,39 @@ int gsi_config_channel_mode(unsigned long chan_hdl, enum gsi_chan_mode mode) mode == GSI_CHAN_MODE_POLL) { __gsi_config_ieob_irq(gsi_ctx->per.ee, 1 << ctx->evtr->id, 0); atomic_set(&ctx->poll_mode, mode); + if ((ctx->props.common_evt_ring) && + (gsi_ctx->shared_ch_info.ch_id == chan_hdl)) { + atomic_set(&ctx->evtr->chan->poll_mode, mode); + } else if ((ctx->props.common_evt_ring) && + gsi_ctx->shared_ch_info.evchid == + ctx->evtr->id) { + shared_ch_ctx = + &gsi_ctx->chan[gsi_ctx->shared_ch_info.ch_id]; + if (shared_ch_ctx != NULL) + atomic_set(&shared_ch_ctx->poll_mode, mode); + } + GSIDBG("set gsi_ctx evtr_id %d to %d mode\n", + ctx->evtr->id, mode); ctx->stats.callback_to_poll++; } if (curr == GSI_CHAN_MODE_POLL && mode == GSI_CHAN_MODE_CALLBACK) { atomic_set(&ctx->poll_mode, mode); + if ((ctx->props.common_evt_ring) && + (gsi_ctx->shared_ch_info.ch_id == chan_hdl)) { + atomic_set(&ctx->evtr->chan->poll_mode, mode); + } else if ((ctx->props.common_evt_ring) && + gsi_ctx->shared_ch_info.evchid == + ctx->evtr->id) { + shared_ch_ctx = + &gsi_ctx->chan[gsi_ctx->shared_ch_info.ch_id]; + if (shared_ch_ctx != NULL) + atomic_set(&shared_ch_ctx->poll_mode, mode); + } __gsi_config_ieob_irq(gsi_ctx->per.ee, 1 << ctx->evtr->id, ~0); + GSIDBG("set gsi_ctx evtr_id %d to %d mode\n", + ctx->evtr->id, mode); ctx->stats.poll_to_callback++; } spin_unlock_irqrestore(&gsi_ctx->slock, flags); diff --git a/drivers/platform/msm/gsi/gsi.h b/drivers/platform/msm/gsi/gsi.h index 4a3f4ec82032..9bc7be9603cf 100644 --- a/drivers/platform/msm/gsi/gsi.h +++ b/drivers/platform/msm/gsi/gsi.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2015-2018, The Linux Foundation. All rights reserved. +/* Copyright (c) 2015-2018, 2020, 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 @@ -190,6 +190,11 @@ struct gsi_generic_ee_cmd_debug_stats { unsigned long halt_channel; }; +struct gsi_shared_chan_info { + uint8_t ch_id; + uint8_t evchid; +}; + struct gsi_ctx { void __iomem *base; struct device *dev; @@ -213,6 +218,8 @@ struct gsi_ctx { struct completion gen_ee_cmd_compl; void *ipc_logbuf; void *ipc_logbuf_low; + struct gsi_shared_chan_info shared_ch_info; + /* * The following used only on emulation systems. */ diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_dp.c b/drivers/platform/msm/ipa/ipa_v3/ipa_dp.c index c9c6281a13cd..3a7b25d38f4c 100644 --- a/drivers/platform/msm/ipa/ipa_v3/ipa_dp.c +++ b/drivers/platform/msm/ipa/ipa_v3/ipa_dp.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2012-2019, The Linux Foundation. All rights reserved. +/* Copyright (c) 2012-2020, 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 @@ -102,11 +102,14 @@ static int ipa_populate_tag_field(struct ipa3_desc *desc, struct ipa3_tx_pkt_wrapper *tx_pkt, struct ipahal_imm_cmd_pyld **tag_pyld_ret); static int ipa_poll_gsi_pkt(struct ipa3_sys_context *sys, - struct ipa_mem_buffer *mem_info); + struct ipa_mem_buffer *mem_info, + struct gsi_chan_xfer_notify *xfer_notify); static unsigned long tag_to_pointer_wa(uint64_t tag); static uint64_t pointer_to_tag_wa(struct ipa3_tx_pkt_wrapper *tx_pkt); static u32 ipa_adjust_ra_buff_base_sz(u32 aggr_byte_limit); +static bool ipa_update_common_evt_ring(enum ipa_client_type src, + enum ipa_client_type dst); static void ipa3_wq_write_done_common(struct ipa3_sys_context *sys, struct ipa3_tx_pkt_wrapper *tx_pkt) @@ -710,20 +713,24 @@ static int ipa3_handle_rx_core(struct ipa3_sys_context *sys, bool process_all, int ret; int cnt = 0; struct ipa_mem_buffer mem_info = { 0 }; + struct gsi_chan_xfer_notify notify; + memset(¬ify, 0, sizeof(struct gsi_chan_xfer_notify)); while ((in_poll_state ? atomic_read(&sys->curr_polling_state) : !atomic_read(&sys->curr_polling_state))) { if (cnt && !process_all) break; - ret = ipa_poll_gsi_pkt(sys, &mem_info); + ret = ipa_poll_gsi_pkt(sys, &mem_info, ¬ify); if (ret) break; if (IPA_CLIENT_IS_MEMCPY_DMA_CONS(sys->ep->client)) ipa3_dma_memcpy_notify(sys, &mem_info); else if (IPA_CLIENT_IS_WLAN_CONS(sys->ep->client)) - ipa3_wlan_wq_rx_common(sys, mem_info.size); + ipa3_wlan_wq_rx_common( + (struct ipa3_sys_context *)(notify.chan_user_data), + mem_info.size); else ipa3_wq_rx_common(sys, mem_info.size); @@ -738,6 +745,8 @@ static int ipa3_handle_rx_core(struct ipa3_sys_context *sys, bool process_all, static void ipa3_rx_switch_to_intr_mode(struct ipa3_sys_context *sys) { int ret; + u32 wlan_ep_idx; + struct ipa3_ep_context *wlan_ep; if (!atomic_read(&sys->curr_polling_state)) { IPAERR("already in intr mode\n"); @@ -751,6 +760,16 @@ static void ipa3_rx_switch_to_intr_mode(struct ipa3_sys_context *sys) IPAERR("Failed to switch to intr mode.\n"); goto fail; } + if (IPA_CLIENT_IS_WLAN_CONS(sys->ep->client)) { + wlan_ep_idx = ipa3_get_ep_mapping( + ((sys->ep->client == IPA_CLIENT_WLAN2_CONS) ? + IPA_CLIENT_WLAN3_CONS : IPA_CLIENT_WLAN2_CONS)); + if (wlan_ep_idx != IPA_EP_NOT_ALLOCATED && + ipa3_ctx->ep[wlan_ep_idx].valid == 1) { + wlan_ep = &ipa3_ctx->ep[wlan_ep_idx]; + atomic_set(&wlan_ep->sys->curr_polling_state, 0); + } + } return; fail: @@ -1182,6 +1201,12 @@ int ipa3_teardown_sys_pipe(u32 clnt_hdl) ipa3_ctx->gsi_evt_comm_ring_rem += ep->gsi_mem_info.chan_ring_len; } else if (ep->gsi_evt_ring_hdl != ~0) { + /* common event ring is used for WLAN2/WLAN3 pipes */ + if (IPA_IS_4_0_AUTO_CONFIG() && + ep->client == IPA_CLIENT_WLAN2_CONS) { + /* Skip resetting the channel. */ + goto teardown; + } result = gsi_reset_evt_ring(ep->gsi_evt_ring_hdl); if (result != GSI_STATUS_SUCCESS) { IPAERR("Failed to reset evt ring: %d.\n", @@ -1201,6 +1226,8 @@ int ipa3_teardown_sys_pipe(u32 clnt_hdl) return result; } } + +teardown: if (ep->sys->repl_wq) flush_workqueue(ep->sys->repl_wq); if (IPA_CLIENT_IS_CONS(ep->client)) @@ -3412,8 +3439,21 @@ static void ipa_gsi_irq_tx_notify_cb(struct gsi_chan_xfer_notify *notify) void __ipa_gsi_irq_rx_scedule_poll(struct ipa3_sys_context *sys) { bool clk_off; + u32 wlan_ep_idx; + struct ipa3_ep_context *wlan_ep; atomic_set(&sys->curr_polling_state, 1); + if (IPA_CLIENT_IS_WLAN_CONS(sys->ep->client)) { + wlan_ep_idx = ipa3_get_ep_mapping( + ((sys->ep->client == IPA_CLIENT_WLAN2_CONS) ? + IPA_CLIENT_WLAN3_CONS : IPA_CLIENT_WLAN2_CONS)); + if (wlan_ep_idx != IPA_EP_NOT_ALLOCATED && + ipa3_ctx->ep[wlan_ep_idx].valid == 1) { + wlan_ep = &ipa3_ctx->ep[wlan_ep_idx]; + /* Set the polling state. */ + atomic_set(&wlan_ep->sys->curr_polling_state, 1); + } + } ipa3_inc_acquire_wakelock(); /* @@ -3463,11 +3503,7 @@ static void ipa_gsi_irq_rx_notify_cb(struct gsi_chan_xfer_notify *notify) struct ipa3_rx_pkt_wrapper, link); rx_pkt_rcvd = (struct ipa3_rx_pkt_wrapper *)notify->xfer_user_data; - if (rx_pkt_expected != rx_pkt_rcvd) { - IPAERR("Pkt was not filled in head of rx buffer.\n"); - WARN_ON(1); - return; - } + sys->ep->xfer_notify = *notify; sys->ep->bytes_xfered_valid = true; sys->ep->bytes_xfered = notify->bytes_xfered; sys->ep->phys_base = rx_pkt_rcvd->data.dma_addr; @@ -3534,6 +3570,26 @@ static void ipa_dma_gsi_irq_rx_notify_cb(struct gsi_chan_xfer_notify *notify) } } +static bool ipa_update_common_evt_ring(enum ipa_client_type src, + enum ipa_client_type dst) +{ + u32 src_ep_idx, dst_ep_idx; + + src_ep_idx = ipa3_get_ep_mapping(src); + dst_ep_idx = ipa3_get_ep_mapping(dst); + if (src_ep_idx != IPA_EP_NOT_ALLOCATED && + ipa3_ctx->ep[src_ep_idx].valid == 1 && + dst_ep_idx != IPA_EP_NOT_ALLOCATED && + ipa3_ctx->ep[dst_ep_idx].valid == 1) { + /* copy event ring handle */ + ipa3_ctx->ep[dst_ep_idx].gsi_evt_ring_hdl = + ipa3_ctx->ep[src_ep_idx].gsi_evt_ring_hdl; + return true; + } + return false; +} + + int ipa3_alloc_common_event_ring(void) { struct gsi_evt_ring_props gsi_evt_ring_props; @@ -3611,6 +3667,13 @@ static int ipa_gsi_setup_channel(struct ipa_sys_connect_params *in, ep->gsi_evt_ring_hdl = ipa3_ctx->gsi_evt_comm_hdl; } else if (ep->sys->policy != IPA_POLICY_NOINTR_MODE || IPA_CLIENT_IS_CONS(ep->client)) { + /* Use common event ring in auto config for WLAN2/WLAN3 pipes */ + if (IPA_IS_4_0_AUTO_CONFIG() && + (ep->client == IPA_CLIENT_WLAN3_CONS) && + ipa_update_common_evt_ring(IPA_CLIENT_WLAN2_CONS, + ep->client)) { + goto setup_channel; + } gsi_evt_ring_props.intf = GSI_EVT_CHTYPE_GPI_EV; gsi_evt_ring_props.intr = GSI_INTR_IRQ; gsi_evt_ring_props.re_size = @@ -3623,6 +3686,14 @@ static int ipa_gsi_setup_channel(struct ipa_sys_connect_params *in, */ gsi_evt_ring_props.ring_len = 2 * in->desc_fifo_sz; + /* In Auto config, common event ring is used for WLAN sys pipes. + * Double the event ring size. + */ + if (IPA_IS_4_0_AUTO_CONFIG() && + IPA_CLIENT_IS_WLAN_CONS(ep->client)) + gsi_evt_ring_props.ring_len = + 2 * gsi_evt_ring_props.ring_len; + gsi_evt_ring_props.ring_base_vaddr = dma_alloc_coherent(ipa3_ctx->pdev, gsi_evt_ring_props.ring_len, @@ -3659,6 +3730,7 @@ static int ipa_gsi_setup_channel(struct ipa_sys_connect_params *in, goto fail_alloc_evt_ring; } +setup_channel: memset(&gsi_channel_props, 0, sizeof(gsi_channel_props)); gsi_channel_props.prot = GSI_CHAN_PROT_GPI; if (IPA_CLIENT_IS_PROD(ep->client)) { @@ -3731,6 +3803,12 @@ static int ipa_gsi_setup_channel(struct ipa_sys_connect_params *in, gsi_channel_props.xfer_cb = ipa_gsi_irq_rx_notify_cb; if (IPA_CLIENT_IS_MEMCPY_DMA_CONS(ep->client)) gsi_channel_props.xfer_cb = ipa_dma_gsi_irq_rx_notify_cb; + + /* In Auto config, common event ring is used for WLAN sys pipes.*/ + if (IPA_IS_4_0_AUTO_CONFIG() && + IPA_CLIENT_IS_WLAN_CONS(ep->client)) + gsi_channel_props.common_evt_ring = true; + result = gsi_alloc_channel(&gsi_channel_props, ipa3_ctx->gsi_dev_hdl, &ep->gsi_chan_hdl); if (result != GSI_STATUS_SUCCESS) @@ -3814,21 +3892,22 @@ static int ipa_populate_tag_field(struct ipa3_desc *desc, } static int ipa_poll_gsi_pkt(struct ipa3_sys_context *sys, - struct ipa_mem_buffer *mem_info) + struct ipa_mem_buffer *mem_info, + struct gsi_chan_xfer_notify *xfer_notify) { int ret; - struct gsi_chan_xfer_notify xfer_notify; struct ipa3_rx_pkt_wrapper *rx_pkt; if (sys->ep->bytes_xfered_valid) { mem_info->phys_base = sys->ep->phys_base; mem_info->size = (u32)sys->ep->bytes_xfered; sys->ep->bytes_xfered_valid = false; + *xfer_notify = sys->ep->xfer_notify; return GSI_STATUS_SUCCESS; } ret = gsi_poll_channel(sys->ep->gsi_chan_hdl, - &xfer_notify); + xfer_notify); if (ret == GSI_STATUS_POLL_EMPTY) return ret; else if (ret != GSI_STATUS_SUCCESS) { @@ -3837,9 +3916,9 @@ static int ipa_poll_gsi_pkt(struct ipa3_sys_context *sys, } rx_pkt = (struct ipa3_rx_pkt_wrapper *) - xfer_notify.xfer_user_data; + (xfer_notify->xfer_user_data); mem_info->phys_base = rx_pkt->data.dma_addr; - mem_info->size = xfer_notify.bytes_xfered; + mem_info->size = xfer_notify->bytes_xfered; return ret; } @@ -3861,6 +3940,7 @@ int ipa3_rx_poll(u32 clnt_hdl, int weight) struct ipa_mem_buffer mem_info = {0}; static int total_cnt; struct ipa_active_client_logging_info log; + struct gsi_chan_xfer_notify notify; IPA_ACTIVE_CLIENTS_PREP_SPECIAL(log, "NAPI"); @@ -3869,14 +3949,14 @@ int ipa3_rx_poll(u32 clnt_hdl, int weight) IPAERR("bad parm 0x%x\n", clnt_hdl); return cnt; } - + memset(¬ify, 0, sizeof(struct gsi_chan_xfer_notify)); ep = &ipa3_ctx->ep[clnt_hdl]; while (cnt < weight && atomic_read(&ep->sys->curr_polling_state)) { atomic_set(&ipa3_ctx->transport_pm.eot_activity, 1); - ret = ipa_poll_gsi_pkt(ep->sys, &mem_info); + ret = ipa_poll_gsi_pkt(ep->sys, &mem_info, ¬ify); if (ret) break; diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_i.h b/drivers/platform/msm/ipa/ipa_v3/ipa_i.h index c8769381ab94..277b6372f0ad 100644 --- a/drivers/platform/msm/ipa/ipa_v3/ipa_i.h +++ b/drivers/platform/msm/ipa/ipa_v3/ipa_i.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2012-2019, The Linux Foundation. All rights reserved. +/* Copyright (c) 2012-2020, 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 @@ -385,6 +385,10 @@ #define IPA_TZ_UNLOCK_ATTRIBUTE 0x0C0311 #define TZ_MEM_PROTECT_REGION_ID 0x10 +#define IPA_IS_4_0_AUTO_CONFIG() \ + ((ipa3_ctx->ipa_hw_type == IPA_HW_v4_0) && \ + (ipa3_ctx->ipa_config_is_auto)) + struct ipa3_active_client_htable_entry { struct hlist_node list; char id_string[IPA3_ACTIVE_CLIENTS_LOG_NAME_LEN]; @@ -728,6 +732,7 @@ struct ipa3_status_stats { * @gsi_evt_ring_hdl: EP's GSI channel event ring handle * @gsi_mem_info: EP's GSI channel rings info * @chan_scratch: EP's GSI channel scratch info + * @xfer_notify: transfer element * @cfg: EP cionfiguration * @dst_pipe_index: destination pipe index * @rt_tbl_idx: routing table index @@ -752,6 +757,7 @@ struct ipa3_ep_context { unsigned long gsi_evt_ring_hdl; struct ipa_gsi_ep_mem_info gsi_mem_info; union __packed gsi_channel_scratch chan_scratch; + struct gsi_chan_xfer_notify xfer_notify; bool bytes_xfered_valid; u16 bytes_xfered; dma_addr_t phys_base; diff --git a/include/linux/msm_gsi.h b/include/linux/msm_gsi.h index 79e7daa1ebf5..0b85d3b8144e 100644 --- a/include/linux/msm_gsi.h +++ b/include/linux/msm_gsi.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2015-2018, The Linux Foundation. All rights reserved. +/* Copyright (c) 2015-2018, 2020, 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 @@ -352,7 +352,7 @@ enum gsi_chan_use_db_eng { * * @err_cb: error notification callback * @chan_user_data: cookie used for notifications - * + * @common_evt_ring: Boolean indicating common event ring. * All the callbacks are in interrupt context * */ @@ -373,6 +373,7 @@ struct gsi_chan_props { void (*xfer_cb)(struct gsi_chan_xfer_notify *notify); void (*err_cb)(struct gsi_chan_err_notify *notify); void *chan_user_data; + bool common_evt_ring; }; enum gsi_xfer_flag {