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 <pdurvasu@codeaurora.org>
Signed-off-by: Sivasri Kumar Vanka <sivasri@codeaurora.org>
Signed-off-by: Swetha Chikkaboraiah <schikk@codeaurora.org>
This commit is contained in:
Prasadarao Durvasula
2021-02-08 18:54:22 +05:30
committed by Swetha Chikkaboraiah
parent 2302cd24f3
commit 21e70acc99
2 changed files with 368 additions and 21 deletions

View File

@@ -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 <linux/kernel.h>
@@ -15,6 +15,7 @@
#include <linux/dma-mapping.h>
#include <linux/pm_runtime.h>
#include <linux/delay.h>
#include <linux/usb/msm_hsusb.h>
#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);

View File

@@ -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 */