From 21e70acc995c3934626cf72aa4b6a5108f443cca Mon Sep 17 00:00:00 2001 From: Prasadarao Durvasula Date: Mon, 8 Feb 2021 18:54:22 +0530 Subject: [PATCH] platform: msm: Add USB BAM support to ChipIdea/RMNET This change adds USB BAM driver which allows USB BAM to communicate with other peripherals'(e.g. QDSS) BAM. Change-Id: Id16613095564b5790076b12dea8aaa2398e65ab7 Signed-off-by: Prasadarao Durvasula Signed-off-by: Sivasri Kumar Vanka Signed-off-by: Swetha Chikkaboraiah --- drivers/platform/msm/usb_bam.c | 367 +++++++++++++++++++++++++++++++-- include/linux/usb_bam.h | 22 ++ 2 files changed, 368 insertions(+), 21 deletions(-) diff --git a/drivers/platform/msm/usb_bam.c b/drivers/platform/msm/usb_bam.c index f9eb90e8dab2..157385855c43 100644 --- a/drivers/platform/msm/usb_bam.c +++ b/drivers/platform/msm/usb_bam.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only /* - * Copyright (c) 2011-2020, The Linux Foundation. All rights reserved. + * Copyright (c) 2011-2020,2021 The Linux Foundation. All rights reserved. */ #include @@ -15,6 +15,7 @@ #include #include #include +#include #define USB_THRESHOLD 512 #define USB_BAM_MAX_STR_LEN 50 @@ -60,6 +61,9 @@ do { \ #define log_event_dbg(x, ...) log_event(LOGLEVEL_DEBUG, x, ##__VA_ARGS__) #define log_event_err(x, ...) log_event(LOGLEVEL_ERR, x, ##__VA_ARGS__) +/* Reset BAM with pipes connected */ +#define SPS_BAM_FORCE_RESET (1UL << 11) + enum usb_bam_event_type { USB_BAM_EVENT_WAKEUP_PIPE = 0, /* Wake a pipe */ USB_BAM_EVENT_WAKEUP, /* Wake a bam (first pipe waked) */ @@ -176,6 +180,7 @@ struct msm_usb_bam_data { bool reset_on_connect; bool reset_on_disconnect; bool disable_clk_gating; + bool ignore_core_reset_ack; u32 override_threshold; u32 max_mbps_highspeed; u32 max_mbps_superspeed; @@ -219,6 +224,8 @@ struct usb_bam_ctx_type { static char *bam_enable_strings[MAX_BAMS] = { [DWC3_CTRL] = "ssusb", + [CI_CTRL] = "hsusb", + [HSIC_CTRL] = "hsic", }; struct usb_bam_host_info { @@ -324,15 +331,25 @@ static void __maybe_unused put_timestamp(char *tbuf) nanosec_rem); } -static inline enum usb_ctrl get_bam_type_from_core_name(const char *name) +void msm_bam_set_hsic_host_dev(struct device *dev) { - if (strnstr(name, bam_enable_strings[DWC3_CTRL], - USB_BAM_MAX_STR_LEN) || - strnstr(name, "dwc3", USB_BAM_MAX_STR_LEN)) - return DWC3_CTRL; + if (dev) { + /* Hold the device until allowing lpm */ + log_event_dbg("%s: Getting hsic device %pK\n", __func__, dev); + pm_runtime_get(dev); + } else if (host_info[HSIC_CTRL].dev) { + log_event_dbg("%s: Try Putting hsic device %pK, lpm:%d\n", + __func__, host_info[HSIC_CTRL].dev, + host_info[HSIC_CTRL].in_lpm); + /* Just release previous device if not already done */ + if (!host_info[HSIC_CTRL].in_lpm) { + host_info[HSIC_CTRL].in_lpm = true; + pm_runtime_put(host_info[HSIC_CTRL].dev); + } + } - log_event_err("%s: invalid BAM name(%s)\n", __func__, name); - return -EINVAL; + host_info[HSIC_CTRL].dev = dev; + host_info[HSIC_CTRL].in_lpm = false; } static void usb_bam_set_inactivity_timer(enum usb_ctrl bam) @@ -379,6 +396,156 @@ void msm_bam_set_usb_host_dev(struct device *dev) host_info[CI_CTRL].in_lpm = false; } +static void _msm_bam_host_notify_on_resume(enum usb_ctrl bam_type) +{ + struct usb_bam_ctx_type *ctx = &msm_usb_bam[bam_type]; + + spin_lock(&ctx->usb_bam_lock); + log_event_dbg("%s: enter bam=%s\n", __func__, + bam_enable_strings[bam_type]); + + host_info[bam_type].in_lpm = false; + + /* + * This function is called to notify the usb bam driver + * that the hsic core and hsic bam hw are fully resumed + * and clocked on. Therefore we can now set the inactivity + * timer to the hsic bam hw. + */ + if (ctx->inactivity_timer_ms) + usb_bam_set_inactivity_timer(bam_type); + + spin_unlock(&ctx->usb_bam_lock); +} + +/** + * msm_bam_hsic_host_pipe_empty - Check all HSIC host BAM pipe state + * + * return true if all BAM pipe used for HSIC Host mode is empty. + */ +bool msm_bam_hsic_host_pipe_empty(void) +{ + struct usb_bam_pipe_connect *pipe_connect; + struct sps_pipe *pipe = NULL; + enum usb_ctrl bam = HSIC_CTRL; + struct usb_bam_ctx_type *ctx = &msm_usb_bam[bam]; + int i, ret; + u32 status; + + log_event_dbg("%s: enter\n", __func__); + + for (i = 0; i < ctx->max_connections; i++) { + pipe_connect = &ctx->usb_bam_connections[i]; + if (pipe_connect->enabled) { + pipe = ctx->usb_bam_sps.sps_pipes[i]; + ret = sps_is_pipe_empty(pipe, &status); + if (ret) { + log_event_err("%s(): sps_is_pipe_empty() failed\n", + __func__); + log_event_err("%s(): SRC index(%d), DEST index(%d):\n", + __func__, + pipe_connect->src_pipe_index, + pipe_connect->dst_pipe_index); + WARN_ON(1); + } + + if (!status) { + log_event_err("%s(): pipe is not empty.\n", + __func__); + log_event_err("%s(): SRC index(%d), DEST index(%d):\n", + __func__, + pipe_connect->src_pipe_index, + pipe_connect->dst_pipe_index); + return false; + } + + log_event_dbg("%s(): SRC index(%d), DEST index(%d):\n", + __func__, + pipe_connect->src_pipe_index, + pipe_connect->dst_pipe_index); + } + + } + + if (!pipe) + log_event_err("%s: Bam %s has no connected pipes\n", __func__, + bam_enable_strings[bam]); + + return true; +} +EXPORT_SYMBOL(msm_bam_hsic_host_pipe_empty); + +static bool msm_bam_host_lpm_ok(enum usb_ctrl bam_type) +{ + struct usb_bam_ctx_type *ctx = &msm_usb_bam[bam_type]; + struct usb_bam_pipe_connect *pipe_iter; + int i; + + log_event_dbg("%s: enter bam=%s\n", __func__, + bam_enable_strings[bam_type]); + + if (!host_info[bam_type].dev) + return true; + + log_event_dbg("%s: Starting hsic full suspend sequence\n", + __func__); + + log_event_dbg("%s(): checking HSIC Host pipe state\n", __func__); + if (!msm_bam_hsic_host_pipe_empty()) { + log_event_err("%s(): HSIC HOST Pipe is not empty\n", + __func__); + return false; + } + + /* HSIC host will go now to lpm */ + log_event_dbg("%s: vote for suspend hsic %pK\n", + __func__, host_info[bam_type].dev); + + for (i = 0; i < ctx->max_connections; i++) { + pipe_iter = &ctx->usb_bam_connections[i]; + if (pipe_iter->bam_type == bam_type && + pipe_iter->enabled && !pipe_iter->suspended) + pipe_iter->suspended = true; + } + + host_info[bam_type].in_lpm = true; + + return true; +} + +bool msm_bam_hsic_lpm_ok(void) +{ + log_event_dbg("%s: enter\n", __func__); + + return msm_bam_host_lpm_ok(HSIC_CTRL); + +} +EXPORT_SYMBOL(msm_bam_hsic_lpm_ok); + +void msm_bam_hsic_host_notify_on_resume(void) +{ + _msm_bam_host_notify_on_resume(HSIC_CTRL); +} + +static inline enum usb_ctrl get_bam_type_from_core_name(const char *name) +{ + if (strnstr(name, bam_enable_strings[DWC3_CTRL], + USB_BAM_MAX_STR_LEN) || + strnstr(name, "dwc3", USB_BAM_MAX_STR_LEN)) + return DWC3_CTRL; + else if (strnstr(name, bam_enable_strings[HSIC_CTRL], + USB_BAM_MAX_STR_LEN) || + strnstr(name, "ci13xxx_msm_hsic", USB_BAM_MAX_STR_LEN)) + return HSIC_CTRL; + else if (strnstr(name, bam_enable_strings[CI_CTRL], + USB_BAM_MAX_STR_LEN) || + strnstr(name, "ci", USB_BAM_MAX_STR_LEN)) + return CI_CTRL; + + log_event_err("%s: invalid BAM name(%s)\n", __func__, name); + return -EINVAL; +} + static int usb_bam_alloc_buffer(struct usb_bam_pipe_connect *pipe_connect) { int ret = 0; @@ -389,7 +556,7 @@ static int usb_bam_alloc_buffer(struct usb_bam_pipe_connect *pipe_connect) struct sg_table data_sgt, desc_sgt; dma_addr_t data_iova, desc_iova; - pr_debug("%s: data_fifo size:%x desc_fifo_size:%x\n", + log_event_dbg("%s: data_fifo size:%x desc_fifo_size:%x\n", __func__, pipe_connect->data_fifo_size, pipe_connect->desc_fifo_size); @@ -563,7 +730,7 @@ int usb_bam_free_fifos(enum usb_ctrl cur_bam, u8 idx) struct device *dev = &ctx->usb_bam_pdev->dev; u32 data_fifo_size; - pr_debug("%s(): data size:%x desc size:%x\n", + log_event_dbg("%s(): data size:%x desc size:%x\n", __func__, sps_connection->data.size, sps_connection->desc.size); @@ -1053,7 +1220,6 @@ static int usb_bam_disconnect_ipa_cons( if (!pipe_empty) { if (inject_zlt) { - pr_debug("%s: Inject ZLT\n", __func__); log_event_dbg("%s: Inject ZLT\n", __func__); inject_zlt = false; sps_pipe_inject_zlt(sps_connection->destination, @@ -2045,12 +2211,12 @@ static int usb_bam_set_ipa_perf(enum usb_ctrl cur_bam, ctx->usb_bam_data->max_mbps_highspeed; /* - * Having a max mbps property in dtsi file is a must - * for target with IPA capability. - */ + * Having a max mbps property in dtsi file is a must + * for target with IPA capability. + */ if (!ipa_rm_perf_prof.max_supported_bandwidth_mbps) { log_event_err("%s: Max mbps is required for speed %d\n", - __func__, usb_connection_speed); + __func__, usb_connection_speed); return -EINVAL; } @@ -2059,18 +2225,69 @@ static int usb_bam_set_ipa_perf(enum usb_ctrl cur_bam, __func__, ipa_rm_resource_prod[cur_bam], ipa_rm_perf_prof.max_supported_bandwidth_mbps); ret = ipa_rm_set_perf_profile(ipa_rm_resource_prod[cur_bam], - &ipa_rm_perf_prof); + &ipa_rm_perf_prof); } else { log_event_dbg("%s: vote ipa_perf resource=%d perf=%d mbps\n", __func__, ipa_rm_resource_cons[cur_bam], ipa_rm_perf_prof.max_supported_bandwidth_mbps); ret = ipa_rm_set_perf_profile(ipa_rm_resource_cons[cur_bam], - &ipa_rm_perf_prof); + &ipa_rm_perf_prof); } return ret; } +static bool _hsic_host_bam_resume_core(void) +{ + log_event_dbg("%s: enter\n", __func__); + + /* Exit from "full suspend" in case of hsic host */ + if (host_info[HSIC_CTRL].dev) { + log_event_dbg("%s: Getting hsic device %pK\n", __func__, + host_info[HSIC_CTRL].dev); + pm_runtime_get(host_info[HSIC_CTRL].dev); + return true; + } + return false; +} + +static bool usb_bam_resume_core(enum usb_ctrl bam_type, + enum usb_bam_mode bam_mode) +{ + log_event_dbg("%s: enter bam=%s\n", __func__, + bam_enable_strings[bam_type]); + + if ((bam_mode == USB_BAM_DEVICE) || (bam_type != HSIC_CTRL)) { + log_event_err("%s: Invalid BAM type %d\n", __func__, bam_type); + return false; + } + + return _hsic_host_bam_resume_core(); +} + +static void _msm_bam_wait_for_host_prod_granted(enum usb_ctrl bam_type) +{ + struct usb_bam_ctx_type *ctx = &msm_usb_bam[bam_type]; + + spin_lock(&ctx->usb_bam_lock); + + log_event_dbg("%s: enter bam=%s\n", __func__, + bam_enable_strings[bam_type]); + ctx->is_bam_inactivity = false; + + /* Get back to resume state including wakeup ipa */ + usb_bam_resume_core(bam_type, USB_BAM_HOST); + + spin_unlock(&ctx->usb_bam_lock); + +} + +void msm_bam_wait_for_hsic_host_prod_granted(void) +{ + log_event_dbg("%s: start\n", __func__); + _msm_bam_wait_for_host_prod_granted(HSIC_CTRL); +} + int usb_bam_connect_ipa(enum usb_ctrl cur_bam, struct usb_bam_connect_ipa_params *ipa_params) { @@ -2170,7 +2387,7 @@ int usb_bam_connect_ipa(enum usb_ctrl cur_bam, } else { spin_unlock(&ctx->usb_bam_lock); if (!ctx->pipes_enabled_per_bam) - pr_debug("No BAM reset on connect, just pipe reset\n"); + log_event_dbg("No BAM reset on connect, just pipe reset\n"); } if (ipa_params->dir == USB_TO_PEER_PERIPHERAL) { @@ -2669,6 +2886,17 @@ static struct msm_usb_bam_data *usb_bam_dt_to_data( if (!usb_bam_data) return NULL; + rc = of_property_read_u32(node, "qcom,bam-type", &bam); + if (rc) { + log_event_err("%s: bam type is missing in device tree\n", + __func__); + return NULL; + } + if (bam >= MAX_BAMS) { + log_event_err("%s: Invalid bam type %d in device tree\n", + __func__, bam); + return NULL; + } usb_bam_data->bam_type = bam; usb_bam_data->reset_on_connect = of_property_read_bool(node, @@ -2696,7 +2924,8 @@ static struct msm_usb_bam_data *usb_bam_dt_to_data( rc = of_property_read_u32(node, "qcom,usb-bam-fifo-baseaddr", &addr); if (rc) - pr_debug("%s: Invalid usb base address property\n", __func__); + log_event_dbg("%s: Invalid usb base address property\n", + __func__); else usb_bam_data->usb_bam_fifo_baseaddr = addr; @@ -2786,7 +3015,7 @@ static struct msm_usb_bam_data *usb_bam_dt_to_data( rc = of_property_read_u32(node, "qcom,pipe-connection-type", &usb_bam_connections[i].pipe_type); if (rc) - pr_debug("%s: pipe type is defaulting to bam2bam\n", + log_event_dbg("%s: pipe type is defaulting to bam2bam\n", __func__); of_property_read_u32(node, "qcom,peer-bam-physical-address", @@ -2832,6 +3061,54 @@ static struct msm_usb_bam_data *usb_bam_dt_to_data( return NULL; } +static void msm_usb_bam_update_props(struct sps_bam_props *props, + struct platform_device *pdev) +{ + struct usb_bam_ctx_type *ctx = dev_get_drvdata(&pdev->dev); + enum usb_ctrl bam_type = ctx->usb_bam_data->bam_type; + struct device *dev; + + props->phys_addr = ctx->io_res->start; + props->virt_addr = NULL; + props->virt_size = resource_size(ctx->io_res); + props->irq = ctx->irq; + props->summing_threshold = ctx->usb_bam_data->override_threshold; + props->event_threshold = ctx->usb_bam_data->override_threshold; + props->num_pipes = ctx->usb_bam_data->usb_bam_num_pipes; + props->callback = usb_bam_sps_events; + props->user = bam_enable_strings[bam_type]; + + /* + * HSUSB and HSIC Cores don't support RESET ACK signal to BAMs + * Hence, let BAM to ignore acknowledge from USB while resetting PIPE + */ + if (ctx->usb_bam_data->ignore_core_reset_ack && bam_type != DWC3_CTRL) + props->options = SPS_BAM_NO_EXT_P_RST; + + if (ctx->usb_bam_data->disable_clk_gating) + props->options |= SPS_BAM_NO_LOCAL_CLK_GATING; + + /* + * HSUSB BAM is not NDP BAM and it must be enabled before + * starting peripheral controller to avoid switching USB core mode + * from legacy to BAM with ongoing data transfers. + */ + if (bam_type == CI_CTRL) { + log_event_dbg("Register and enable HSUSB BAM\n"); + props->options |= SPS_BAM_OPT_ENABLE_AT_BOOT; + props->options |= SPS_BAM_FORCE_RESET; + } + + dev = &ctx->usb_bam_pdev->dev; + if (dev && dev->parent && device_property_present(dev->parent, "iommus") + && !device_property_present(dev->parent, + "qcom,smmu-s1-bypass")) { + pr_info("%s: setting SPS_BAM_SMMU_EN flag with (%s)\n", + __func__, dev_name(dev)); + props->options |= SPS_BAM_SMMU_EN; + } +} + static int usb_bam_init(struct platform_device *pdev) { int ret; @@ -2842,7 +3119,7 @@ static int usb_bam_init(struct platform_device *pdev) memset(&props, 0, sizeof(props)); - pr_debug("%s\n", __func__); + log_event_dbg("%s\n", __func__); props.phys_addr = ctx->io_res->start; props.virt_size = resource_size(ctx->io_res); @@ -3120,6 +3397,54 @@ enum usb_ctrl usb_bam_get_bam_type(const char *core_name) } EXPORT_SYMBOL(usb_bam_get_bam_type); +bool msm_usb_bam_enable(enum usb_ctrl bam, bool bam_enable) +{ + struct msm_usb_bam_data *usb_bam_data; + struct usb_bam_ctx_type *ctx = &msm_usb_bam[bam]; + static bool bam_enabled; + int ret; + + if (!ctx->usb_bam_pdev) + return false; + + usb_bam_data = ctx->usb_bam_data; + if (bam != CI_CTRL) + return false; + + if (bam_enabled == bam_enable) { + log_event_dbg("%s: USB BAM is already %s\n", __func__, + bam_enable ? "Registered" : "De-registered"); + return false; + } + + if (bam_enable) { + struct sps_bam_props props; + + memset(&props, 0, sizeof(props)); + msm_usb_bam_update_props(&props, ctx->usb_bam_pdev); + msm_hw_bam_disable(1); + ret = sps_register_bam_device(&props, &ctx->h_bam); + bam_enabled = true; + if (ret < 0) { + log_event_err("%s: register bam error %d\n", + __func__, ret); + return false; + } + log_event_dbg("%s: USB BAM Registered\n", __func__); + msm_hw_bam_disable(0); + } else { + msm_hw_soft_reset(); + msm_hw_bam_disable(1); + sps_device_reset(ctx->h_bam); + sps_deregister_bam_device(ctx->h_bam); + log_event_dbg("%s: USB BAM De-registered\n", __func__); + bam_enabled = false; + } + + return true; +} +EXPORT_SYMBOL(msm_usb_bam_enable); + static int usb_bam_remove(struct platform_device *pdev) { struct usb_bam_ctx_type *ctx = dev_get_drvdata(&pdev->dev); diff --git a/include/linux/usb_bam.h b/include/linux/usb_bam.h index 46f7cc23edab..ad462f49bf66 100644 --- a/include/linux/usb_bam.h +++ b/include/linux/usb_bam.h @@ -313,6 +313,12 @@ int usb_bam_alloc_fifos(enum usb_ctrl cur_bam, u8 idx); int usb_bam_free_fifos(enum usb_ctrl cur_bam, u8 idx); int get_qdss_bam_info(enum usb_ctrl cur_bam, u8 idx, phys_addr_t *p_addr, u32 *bam_size); +bool msm_usb_bam_enable(enum usb_ctrl ctrl, bool bam_enable); +void msm_bam_set_hsic_host_dev(struct device *dev); +bool msm_bam_hsic_lpm_ok(void); +void msm_bam_hsic_host_notify_on_resume(void); +void msm_bam_wait_for_hsic_host_prod_granted(void); +bool msm_bam_hsic_host_pipe_empty(void); #else static inline int usb_bam_connect(enum usb_ctrl bam, u8 idx, u32 *bam_pipe_idx, unsigned long iova) @@ -415,6 +421,22 @@ static inline bool msm_usb_bam_enable(enum usb_ctrl ctrl, bool bam_enable) return false; } +static inline void msm_bam_set_hsic_host_dev(struct device *dev) {} + +static inline bool msm_bam_hsic_lpm_ok(void) +{ + return false; +} + +static inline void msm_bam_hsic_host_notify_on_resume(void) {} + +static inline void msm_bam_wait_for_hsic_host_prod_granted(void) {} + +static inline bool msm_bam_hsic_host_pipe_empty(void) +{ + return false; +} + #endif /* CONFIG_PM */