} \
} while (0)
+#define IPA_API_DISPATCH_RETURN_BOOL(api, p...) \
+ do { \
+ if (!ipa_api_ctrl) { \
+ pr_err("IPA HW is not supported on this target\n"); \
+ ret = false; \
+ } \
+ else { \
+ if (ipa_api_ctrl->api) { \
+ ret = ipa_api_ctrl->api(p); \
+ } else { \
+ pr_err("%s not implemented for IPA ver %d\n", \
+ __func__, ipa_api_hw_type); \
+ WARN_ON(1); \
+ ret = false; \
+ } \
+ } \
+ } while (0)
+
static enum ipa_hw_type ipa_api_hw_type;
static struct ipa_api_controller *ipa_api_ctrl;
}
EXPORT_SYMBOL(ipa_dma_destroy);
-/**
- * ipa_mhi_init() - Initialize IPA MHI driver
- * @params: initialization params
- *
- * This function is called by MHI client driver on boot to initialize IPA MHI
- * Driver. When this function returns device can move to READY state.
- * This function is doing the following:
- * - Initialize MHI IPA internal data structures
- * - Create IPA RM resources
- * - Initialize debugfs
- *
- * Return codes: 0 : success
- * negative : error
- */
-int ipa_mhi_init(struct ipa_mhi_init_params *params)
+int ipa_mhi_init_engine(struct ipa_mhi_init_engine *params)
{
int ret;
- IPA_API_DISPATCH_RETURN(ipa_mhi_init, params);
+ IPA_API_DISPATCH_RETURN(ipa_mhi_init_engine, params);
return ret;
}
-EXPORT_SYMBOL(ipa_mhi_init);
+EXPORT_SYMBOL(ipa_mhi_init_engine);
/**
- * ipa_mhi_start() - Start IPA MHI engine
- * @params: pcie addresses for MHI
- *
- * This function is called by MHI client driver on MHI engine start for
- * handling MHI accelerated channels. This function is called after
- * ipa_mhi_init() was called and can be called after MHI reset to restart MHI
- * engine. When this function returns device can move to M0 state.
- * This function is doing the following:
- * - Send command to uC for initialization of MHI engine
- * - Add dependencies to IPA RM
- * - Request MHI_PROD in IPA RM
- *
- * Return codes: 0 : success
- * negative : error
- */
-int ipa_mhi_start(struct ipa_mhi_start_params *params)
-{
- int ret;
-
- IPA_API_DISPATCH_RETURN(ipa_mhi_start, params);
-
- return ret;
-}
-EXPORT_SYMBOL(ipa_mhi_start);
-
-/**
- * ipa_mhi_connect_pipe() - Connect pipe to IPA and start corresponding
+ * ipa_connect_mhi_pipe() - Connect pipe to IPA and start corresponding
* MHI channel
* @in: connect parameters
* @clnt_hdl: [out] client handle for this pipe
*
- * This function is called by MHI client driver on MHI channel start.
+ * This function is called by IPA MHI client driver on MHI channel start.
* This function is called after MHI engine was started.
* This function is doing the following:
* - Send command to uC to start corresponding MHI channel
* Return codes: 0 : success
* negative : error
*/
-int ipa_mhi_connect_pipe(struct ipa_mhi_connect_params *in, u32 *clnt_hdl)
+int ipa_connect_mhi_pipe(struct ipa_mhi_connect_params_internal *in,
+ u32 *clnt_hdl)
{
int ret;
- IPA_API_DISPATCH_RETURN(ipa_mhi_connect_pipe, in, clnt_hdl);
+ IPA_API_DISPATCH_RETURN(ipa_connect_mhi_pipe, in, clnt_hdl);
return ret;
}
-EXPORT_SYMBOL(ipa_mhi_connect_pipe);
+EXPORT_SYMBOL(ipa_connect_mhi_pipe);
/**
- * ipa_mhi_disconnect_pipe() - Disconnect pipe from IPA and reset corresponding
+ * ipa_disconnect_mhi_pipe() - Disconnect pipe from IPA and reset corresponding
* MHI channel
* @in: connect parameters
* @clnt_hdl: [out] client handle for this pipe
*
- * This function is called by MHI client driver on MHI channel reset.
+ * This function is called by IPA MHI client driver on MHI channel reset.
* This function is called after MHI channel was started.
* This function is doing the following:
* - Send command to uC to reset corresponding MHI channel
* Return codes: 0 : success
* negative : error
*/
-int ipa_mhi_disconnect_pipe(u32 clnt_hdl)
+int ipa_disconnect_mhi_pipe(u32 clnt_hdl)
{
int ret;
- IPA_API_DISPATCH_RETURN(ipa_mhi_disconnect_pipe, clnt_hdl);
+ IPA_API_DISPATCH_RETURN(ipa_disconnect_mhi_pipe, clnt_hdl);
return ret;
}
-EXPORT_SYMBOL(ipa_mhi_disconnect_pipe);
+EXPORT_SYMBOL(ipa_disconnect_mhi_pipe);
-/**
- * ipa_mhi_suspend() - Suspend MHI accelerated channels
- * @force:
- * false: in case of data pending in IPA, MHI channels will not be
- * suspended and function will fail.
- * true: in case of data pending in IPA, make sure no further access from
- * IPA to PCIe is possible. In this case suspend cannot fail.
- *
- * This function is called by MHI client driver on MHI suspend.
- * This function is called after MHI channel was started.
- * When this function returns device can move to M1/M2/M3/D3cold state.
- * This function is doing the following:
- * - Send command to uC to suspend corresponding MHI channel
- * - Make sure no further access is possible from IPA to PCIe
- * - Release MHI_PROD in IPA RM
- *
- * Return codes: 0 : success
- * negative : error
- */
-int ipa_mhi_suspend(bool force)
+bool ipa_mhi_stop_gsi_channel(enum ipa_client_type client)
+{
+ bool ret;
+
+ IPA_API_DISPATCH_RETURN_BOOL(ipa_mhi_stop_gsi_channel, client);
+
+ return ret;
+}
+
+int ipa_uc_mhi_reset_channel(int channelHandle)
{
int ret;
- IPA_API_DISPATCH_RETURN(ipa_mhi_suspend, force);
+ IPA_API_DISPATCH_RETURN(ipa_uc_mhi_reset_channel, channelHandle);
return ret;
}
-EXPORT_SYMBOL(ipa_mhi_suspend);
-/**
- * ipa_mhi_resume() - Resume MHI accelerated channels
- *
- * This function is called by MHI client driver on MHI resume.
- * This function is called after MHI channel was suspended.
- * When this function returns device can move to M0 state.
- * This function is doing the following:
- * - Send command to uC to resume corresponding MHI channel
- * - Request MHI_PROD in IPA RM
- * - Resume data to IPA
- *
- * Return codes: 0 : success
- * negative : error
- */
-int ipa_mhi_resume(void)
+bool ipa_mhi_sps_channel_empty(enum ipa_client_type client)
+{
+ bool ret;
+
+ IPA_API_DISPATCH_RETURN_BOOL(ipa_mhi_sps_channel_empty, client);
+
+ return ret;
+}
+
+int ipa_qmi_enable_force_clear_datapath_send(
+ struct ipa_enable_force_clear_datapath_req_msg_v01 *req)
+{
+ int ret;
+
+ IPA_API_DISPATCH_RETURN(ipa_qmi_enable_force_clear_datapath_send, req);
+
+ return ret;
+}
+
+int ipa_qmi_disable_force_clear_datapath_send(
+ struct ipa_disable_force_clear_datapath_req_msg_v01 *req)
+{
+ int ret;
+
+ IPA_API_DISPATCH_RETURN(ipa_qmi_disable_force_clear_datapath_send, req);
+
+ return ret;
+}
+
+int ipa_generate_tag_process(void)
+{
+ int ret;
+
+ IPA_API_DISPATCH_RETURN(ipa_generate_tag_process);
+
+ return ret;
+}
+
+int ipa_disable_sps_pipe(enum ipa_client_type client)
+{
+ int ret;
+
+ IPA_API_DISPATCH_RETURN(ipa_disable_sps_pipe, client);
+
+ return ret;
+}
+
+int ipa_mhi_reset_channel_internal(enum ipa_client_type client)
+{
+ int ret;
+
+ IPA_API_DISPATCH_RETURN(ipa_mhi_reset_channel_internal, client);
+
+ return ret;
+}
+
+int ipa_mhi_start_channel_internal(enum ipa_client_type client)
+{
+ int ret;
+
+ IPA_API_DISPATCH_RETURN(ipa_mhi_start_channel_internal, client);
+
+ return ret;
+}
+
+void ipa_get_holb(int ep_idx, struct ipa_ep_cfg_holb *holb)
+{
+ IPA_API_DISPATCH(ipa_get_holb, ep_idx, holb);
+}
+
+void ipa_set_tag_process_before_gating(bool val)
+{
+ IPA_API_DISPATCH(ipa_set_tag_process_before_gating, val);
+}
+
+int ipa_mhi_query_ch_info(enum ipa_client_type client,
+ struct gsi_chan_info *ch_info)
+{
+ int ret;
+
+ IPA_API_DISPATCH_RETURN(ipa_mhi_query_ch_info, client, ch_info);
+
+ return ret;
+}
+
+int ipa_uc_mhi_suspend_channel(int channelHandle)
+{
+ int ret;
+
+ IPA_API_DISPATCH_RETURN(ipa_uc_mhi_suspend_channel, channelHandle);
+
+ return ret;
+}
+
+int ipa_uc_mhi_stop_event_update_channel(int channelHandle)
+{
+ int ret;
+
+ IPA_API_DISPATCH_RETURN(ipa_uc_mhi_stop_event_update_channel,
+ channelHandle);
+
+ return ret;
+}
+
+bool ipa_has_open_aggr_frame(enum ipa_client_type client)
+{
+ bool ret;
+
+ IPA_API_DISPATCH_RETURN_BOOL(ipa_has_open_aggr_frame, client);
+
+ return ret;
+}
+
+int ipa_mhi_resume_channels_internal(enum ipa_client_type client,
+ bool LPTransitionRejected, bool brstmode_enabled,
+ union __packed gsi_channel_scratch ch_scratch, u8 index)
+{
+ int ret;
+
+ IPA_API_DISPATCH_RETURN(ipa_mhi_resume_channels_internal, client,
+ LPTransitionRejected, brstmode_enabled, ch_scratch,
+ index);
+
+ return ret;
+}
+
+int ipa_uc_mhi_send_dl_ul_sync_info(union IpaHwMhiDlUlSyncCmdData_t *cmd)
{
int ret;
- IPA_API_DISPATCH_RETURN(ipa_mhi_resume);
+ IPA_API_DISPATCH_RETURN(ipa_uc_mhi_send_dl_ul_sync_info,
+ cmd);
+
+ return ret;
+}
+
+int ipa_mhi_destroy_channel(enum ipa_client_type client)
+{
+ int ret;
+
+ IPA_API_DISPATCH_RETURN(ipa_mhi_destroy_channel, client);
+
+ return ret;
+}
+
+int ipa_uc_mhi_init(void (*ready_cb)(void),
+ void (*wakeup_request_cb)(void))
+{
+ int ret;
+
+ IPA_API_DISPATCH_RETURN(ipa_uc_mhi_init, ready_cb, wakeup_request_cb);
+
+ return ret;
+}
+
+void ipa_uc_mhi_cleanup(void)
+{
+ IPA_API_DISPATCH(ipa_uc_mhi_cleanup);
+}
+
+int ipa_uc_mhi_print_stats(char *dbg_buff, int size)
+{
+ int ret;
+
+ IPA_API_DISPATCH_RETURN(ipa_uc_mhi_print_stats, dbg_buff, size);
return ret;
}
-EXPORT_SYMBOL(ipa_mhi_resume);
/**
- * ipa_mhi_destroy() - Destroy MHI IPA
+ * ipa_uc_state_check() - Check the status of the uC interface
*
- * This function is called by MHI client driver on MHI reset to destroy all IPA
- * MHI resources.
+ * Return value: 0 if the uC is loaded, interface is initialized
+ * and there was no recent failure in one of the commands.
+ * A negative value is returned otherwise.
*/
-void ipa_mhi_destroy(void)
+int ipa_uc_state_check(void)
{
- IPA_API_DISPATCH(ipa_mhi_destroy);
+ int ret;
+ IPA_API_DISPATCH_RETURN(ipa_uc_state_check);
+
+ return ret;
}
-EXPORT_SYMBOL(ipa_mhi_destroy);
int ipa_write_qmap_id(struct ipa_ioc_write_qmapid *param_in)
{
* GNU General Public License for more details.
*/
+#include <linux/ipa_mhi.h>
#include "ipa_common_i.h"
#ifndef _IPA_API_H_
void (*ipa_dma_destroy)(void);
- int (*ipa_mhi_init)(struct ipa_mhi_init_params *params);
+ bool (*ipa_has_open_aggr_frame)(enum ipa_client_type client);
- int (*ipa_mhi_start)(struct ipa_mhi_start_params *params);
+ int (*ipa_generate_tag_process)(void);
- int (*ipa_mhi_connect_pipe)(struct ipa_mhi_connect_params *in,
+ int (*ipa_disable_sps_pipe)(enum ipa_client_type client);
+
+ void (*ipa_set_tag_process_before_gating)(bool val);
+
+ int (*ipa_mhi_init_engine)(struct ipa_mhi_init_engine *params);
+
+ int (*ipa_connect_mhi_pipe)(struct ipa_mhi_connect_params_internal *in,
u32 *clnt_hdl);
- int (*ipa_mhi_disconnect_pipe)(u32 clnt_hdl);
+ int (*ipa_disconnect_mhi_pipe)(u32 clnt_hdl);
+
+ bool (*ipa_mhi_stop_gsi_channel)(enum ipa_client_type client);
+
+ int (*ipa_qmi_disable_force_clear)(u32 request_id);
+
+ int (*ipa_qmi_enable_force_clear_datapath_send)(
+ struct ipa_enable_force_clear_datapath_req_msg_v01 *req);
+
+ int (*ipa_qmi_disable_force_clear_datapath_send)(
+ struct ipa_disable_force_clear_datapath_req_msg_v01 *req);
+
+ bool (*ipa_mhi_sps_channel_empty)(enum ipa_client_type client);
+
+ int (*ipa_mhi_reset_channel_internal)(enum ipa_client_type client);
+
+ int (*ipa_mhi_start_channel_internal)(enum ipa_client_type client);
+
+ void (*ipa_get_holb)(int ep_idx, struct ipa_ep_cfg_holb *holb);
+
+ int (*ipa_mhi_query_ch_info)(enum ipa_client_type client,
+ struct gsi_chan_info *ch_info);
+
+ int (*ipa_mhi_resume_channels_internal)(
+ enum ipa_client_type client,
+ bool LPTransitionRejected,
+ bool brstmode_enabled,
+ union __packed gsi_channel_scratch ch_scratch,
+ u8 index);
+
+ int (*ipa_mhi_destroy_channel)(enum ipa_client_type client);
+
+ int (*ipa_uc_mhi_send_dl_ul_sync_info)
+ (union IpaHwMhiDlUlSyncCmdData_t *cmd);
+
+ int (*ipa_uc_mhi_init)
+ (void (*ready_cb)(void), void (*wakeup_request_cb)(void));
+
+ void (*ipa_uc_mhi_cleanup)(void);
+
+ int (*ipa_uc_mhi_print_stats)(char *dbg_buff, int size);
+
+ int (*ipa_uc_mhi_reset_channel)(int channelHandle);
- int (*ipa_mhi_suspend)(bool force);
+ int (*ipa_uc_mhi_suspend_channel)(int channelHandle);
- int (*ipa_mhi_resume)(void);
+ int (*ipa_uc_mhi_stop_event_update_channel)(int channelHandle);
- void (*ipa_mhi_destroy)(void);
+ int (*ipa_uc_state_check)(void);
int (*ipa_write_qmap_id)(struct ipa_ioc_write_qmapid *param_in);
-obj-$(CONFIG_IPA3) += ipa_usb.o odu_bridge.o
-obj-$(CONFIG_IPA) += odu_bridge.o
\ No newline at end of file
+obj-$(CONFIG_IPA3) += ipa_usb.o odu_bridge.o ipa_mhi_client.o
+obj-$(CONFIG_IPA) += odu_bridge.o ipa_mhi_client.o
\ No newline at end of file
--- /dev/null
+/* Copyright (c) 2015, 2016 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
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#include <linux/debugfs.h>
+#include <linux/export.h>
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/ipa.h>
+#include <linux/msm_gsi.h>
+#include <linux/ipa_qmi_service_v01.h>
+#include <linux/ipa_mhi.h>
+#include "../ipa_common_i.h"
+
+#define IPA_MHI_DRV_NAME "ipa_mhi_client"
+#define IPA_MHI_DBG(fmt, args...) \
+ pr_debug(IPA_MHI_DRV_NAME " %s:%d " fmt, \
+ __func__, __LINE__, ## args)
+#define IPA_MHI_ERR(fmt, args...) \
+ pr_err(IPA_MHI_DRV_NAME " %s:%d " fmt, __func__, __LINE__, ## args)
+#define IPA_MHI_FUNC_ENTRY() \
+ IPA_MHI_DBG("ENTRY\n")
+#define IPA_MHI_FUNC_EXIT() \
+ IPA_MHI_DBG("EXIT\n")
+
+#define IPA_MHI_RM_TIMEOUT_MSEC 10000
+#define IPA_MHI_CH_EMPTY_TIMEOUT_MSEC 10
+
+#define IPA_MHI_SUSPEND_SLEEP_MIN 900
+#define IPA_MHI_SUSPEND_SLEEP_MAX 1100
+
+#define IPA_MHI_MAX_UL_CHANNELS 1
+#define IPA_MHI_MAX_DL_CHANNELS 1
+
+#define IPA_MHI_GSI_ER_START 10
+#define IPA_MHI_GSI_ER_END 16
+
+#if (IPA_MHI_MAX_UL_CHANNELS + IPA_MHI_MAX_DL_CHANNELS) > \
+ (IPA_MHI_GSI_ER_END - IPA_MHI_GSI_ER_START)
+#error not enought event rings for MHI
+#endif
+
+/* bit #40 in address should be asserted for MHI transfers over pcie */
+#define IPA_MHI_CLIENT_HOST_ADDR_COND(addr) \
+ ((ipa_mhi_client_ctx->assert_bit40)?(IPA_MHI_HOST_ADDR(addr)):(addr))
+
+enum ipa_mhi_rm_state {
+ IPA_MHI_RM_STATE_RELEASED,
+ IPA_MHI_RM_STATE_REQUESTED,
+ IPA_MHI_RM_STATE_GRANTED,
+ IPA_MHI_RM_STATE_MAX
+};
+
+enum ipa_mhi_state {
+ IPA_MHI_STATE_INITIALIZED,
+ IPA_MHI_STATE_READY,
+ IPA_MHI_STATE_STARTED,
+ IPA_MHI_STATE_SUSPEND_IN_PROGRESS,
+ IPA_MHI_STATE_SUSPENDED,
+ IPA_MHI_STATE_RESUME_IN_PROGRESS,
+ IPA_MHI_STATE_MAX
+};
+
+static char *ipa_mhi_state_str[] = {
+ __stringify(IPA_MHI_STATE_INITIALIZED),
+ __stringify(IPA_MHI_STATE_READY),
+ __stringify(IPA_MHI_STATE_STARTED),
+ __stringify(IPA_MHI_STATE_SUSPEND_IN_PROGRESS),
+ __stringify(IPA_MHI_STATE_SUSPENDED),
+ __stringify(IPA_MHI_STATE_RESUME_IN_PROGRESS),
+};
+
+#define MHI_STATE_STR(state) \
+ (((state) >= 0 && (state) < IPA_MHI_STATE_MAX) ? \
+ ipa_mhi_state_str[(state)] : \
+ "INVALID")
+
+enum ipa_mhi_dma_dir {
+ IPA_MHI_DMA_TO_HOST,
+ IPA_MHI_DMA_FROM_HOST,
+};
+
+/**
+ * struct ipa_mhi_channel_ctx - MHI Channel context
+ * @valid: entry is valid
+ * @id: MHI channel ID
+ * @hdl: channel handle for uC
+ * @client: IPA Client
+ * @state: Channel state
+ */
+struct ipa_mhi_channel_ctx {
+ bool valid;
+ u8 id;
+ u8 index;
+ enum ipa_client_type client;
+ enum ipa_hw_mhi_channel_states state;
+ bool stop_in_proc;
+ struct gsi_chan_info ch_info;
+ u64 channel_context_addr;
+ struct ipa_mhi_ch_ctx ch_ctx_host;
+ u64 event_context_addr;
+ struct ipa_mhi_ev_ctx ev_ctx_host;
+ bool brstmode_enabled;
+ union __packed gsi_channel_scratch ch_scratch;
+ unsigned long cached_gsi_evt_ring_hdl;
+};
+
+struct ipa_mhi_client_ctx {
+ enum ipa_mhi_state state;
+ spinlock_t state_lock;
+ mhi_client_cb cb_notify;
+ void *cb_priv;
+ struct completion rm_prod_granted_comp;
+ enum ipa_mhi_rm_state rm_cons_state;
+ struct completion rm_cons_comp;
+ bool trigger_wakeup;
+ bool wakeup_notified;
+ struct workqueue_struct *wq;
+ struct ipa_mhi_channel_ctx ul_channels[IPA_MHI_MAX_UL_CHANNELS];
+ struct ipa_mhi_channel_ctx dl_channels[IPA_MHI_MAX_DL_CHANNELS];
+ u32 total_channels;
+ struct ipa_mhi_msi_info msi;
+ u32 mmio_addr;
+ u32 first_ch_idx;
+ u32 first_er_idx;
+ u32 host_ctrl_addr;
+ u32 host_data_addr;
+ u64 channel_context_array_addr;
+ u64 event_context_array_addr;
+ u32 qmi_req_id;
+ u32 use_ipadma;
+ bool assert_bit40;
+ bool test_mode;
+};
+
+static struct ipa_mhi_client_ctx *ipa_mhi_client_ctx;
+
+#ifdef CONFIG_DEBUG_FS
+#define IPA_MHI_MAX_MSG_LEN 512
+static char dbg_buff[IPA_MHI_MAX_MSG_LEN];
+static struct dentry *dent;
+
+static char *ipa_mhi_channel_state_str[] = {
+ __stringify(IPA_HW_MHI_CHANNEL_STATE_DISABLE),
+ __stringify(IPA_HW_MHI_CHANNEL_STATE_ENABLE),
+ __stringify(IPA_HW_MHI_CHANNEL_STATE_RUN),
+ __stringify(IPA_HW_MHI_CHANNEL_STATE_SUSPEND),
+ __stringify(IPA_HW_MHI_CHANNEL_STATE_STOP),
+ __stringify(IPA_HW_MHI_CHANNEL_STATE_ERROR),
+};
+
+#define MHI_CH_STATE_STR(state) \
+ (((state) >= 0 && (state) <= IPA_HW_MHI_CHANNEL_STATE_ERROR) ? \
+ ipa_mhi_channel_state_str[(state)] : \
+ "INVALID")
+
+static int ipa_mhi_read_write_host(enum ipa_mhi_dma_dir dir, void *dev_addr,
+ u64 host_addr, int size)
+{
+ struct ipa_mem_buffer mem;
+ int res;
+ struct device *pdev;
+
+ IPA_MHI_FUNC_ENTRY();
+
+ if (ipa_mhi_client_ctx->use_ipadma) {
+ pdev = ipa_get_dma_dev();
+ host_addr = IPA_MHI_CLIENT_HOST_ADDR_COND(host_addr);
+
+ mem.size = size;
+ mem.base = dma_alloc_coherent(pdev, mem.size,
+ &mem.phys_base, GFP_KERNEL);
+ if (!mem.base) {
+ IPA_MHI_ERR(
+ "dma_alloc_coherent failed, DMA buff size %d\n"
+ , mem.size);
+ return -ENOMEM;
+ }
+
+ if (dir == IPA_MHI_DMA_FROM_HOST) {
+ res = ipa_dma_sync_memcpy(mem.phys_base, host_addr,
+ size);
+ if (res) {
+ IPA_MHI_ERR(
+ "ipa_dma_sync_memcpy from host fail%d\n"
+ , res);
+ goto fail_memcopy;
+ }
+ memcpy(dev_addr, mem.base, size);
+ } else {
+ memcpy(mem.base, dev_addr, size);
+ res = ipa_dma_sync_memcpy(host_addr, mem.phys_base,
+ size);
+ if (res) {
+ IPA_MHI_ERR(
+ "ipa_dma_sync_memcpy to host fail %d\n"
+ , res);
+ goto fail_memcopy;
+ }
+ }
+ dma_free_coherent(pdev, mem.size, mem.base,
+ mem.phys_base);
+ } else {
+ void *host_ptr;
+
+ if (!ipa_mhi_client_ctx->test_mode)
+ host_ptr = ioremap(host_addr, size);
+ else
+ host_ptr = phys_to_virt(host_addr);
+ if (!host_ptr) {
+ IPA_MHI_ERR("ioremap failed for 0x%llx\n", host_addr);
+ return -EFAULT;
+ }
+ if (dir == IPA_MHI_DMA_FROM_HOST)
+ memcpy(dev_addr, host_ptr, size);
+ else
+ memcpy(host_ptr, dev_addr, size);
+ if (!ipa_mhi_client_ctx->test_mode)
+ iounmap(host_ptr);
+ }
+
+ IPA_MHI_FUNC_EXIT();
+ return 0;
+
+fail_memcopy:
+ dma_free_coherent(ipa_get_dma_dev(), mem.size, mem.base,
+ mem.phys_base);
+ return res;
+}
+
+static int ipa_mhi_print_channel_info(struct ipa_mhi_channel_ctx *channel,
+ char *buff, int len)
+{
+ int nbytes = 0;
+
+ if (channel->valid) {
+ nbytes += scnprintf(&buff[nbytes],
+ len - nbytes,
+ "channel idx=%d ch_id=%d client=%d state=%s\n",
+ channel->index, channel->id, channel->client,
+ MHI_CH_STATE_STR(channel->state));
+
+ nbytes += scnprintf(&buff[nbytes],
+ len - nbytes,
+ " ch_ctx=%llx\n",
+ channel->channel_context_addr);
+
+ nbytes += scnprintf(&buff[nbytes],
+ len - nbytes,
+ " gsi_evt_ring_hdl=%ld ev_ctx=%llx\n",
+ channel->cached_gsi_evt_ring_hdl,
+ channel->event_context_addr);
+ }
+ return nbytes;
+}
+
+static int ipa_mhi_print_host_channel_ctx_info(
+ struct ipa_mhi_channel_ctx *channel, char *buff, int len)
+{
+ int res, nbytes = 0;
+ struct ipa_mhi_ch_ctx ch_ctx_host;
+
+ memset(&ch_ctx_host, 0, sizeof(ch_ctx_host));
+
+ /* reading ch context from host */
+ res = ipa_mhi_read_write_host(IPA_MHI_DMA_FROM_HOST,
+ &ch_ctx_host, channel->channel_context_addr,
+ sizeof(ch_ctx_host));
+ if (res) {
+ nbytes += scnprintf(&buff[nbytes], len - nbytes,
+ "Failed to read from host %d\n", res);
+ return nbytes;
+ }
+
+ nbytes += scnprintf(&buff[nbytes], len - nbytes,
+ "ch_id: %d\n", channel->id);
+ nbytes += scnprintf(&buff[nbytes], len - nbytes,
+ "chstate: 0x%x\n", ch_ctx_host.chstate);
+ nbytes += scnprintf(&buff[nbytes], len - nbytes,
+ "brstmode: 0x%x\n", ch_ctx_host.brstmode);
+ nbytes += scnprintf(&buff[nbytes], len - nbytes,
+ "chtype: 0x%x\n", ch_ctx_host.chtype);
+ nbytes += scnprintf(&buff[nbytes], len - nbytes,
+ "erindex: 0x%x\n", ch_ctx_host.erindex);
+ nbytes += scnprintf(&buff[nbytes], len - nbytes,
+ "rbase: 0x%llx\n", ch_ctx_host.rbase);
+ nbytes += scnprintf(&buff[nbytes], len - nbytes,
+ "rlen: 0x%llx\n", ch_ctx_host.rlen);
+ nbytes += scnprintf(&buff[nbytes], len - nbytes,
+ "rp: 0x%llx\n", ch_ctx_host.rp);
+ nbytes += scnprintf(&buff[nbytes], len - nbytes,
+ "wp: 0x%llx\n", ch_ctx_host.wp);
+
+ return nbytes;
+}
+
+static ssize_t ipa_mhi_debugfs_stats(struct file *file,
+ char __user *ubuf,
+ size_t count,
+ loff_t *ppos)
+{
+ int nbytes = 0;
+ int i;
+ struct ipa_mhi_channel_ctx *channel;
+
+ nbytes += scnprintf(&dbg_buff[nbytes],
+ IPA_MHI_MAX_MSG_LEN - nbytes,
+ "IPA MHI state: %s\n",
+ MHI_STATE_STR(ipa_mhi_client_ctx->state));
+
+ for (i = 0; i < IPA_MHI_MAX_UL_CHANNELS; i++) {
+ channel = &ipa_mhi_client_ctx->ul_channels[i];
+ nbytes += ipa_mhi_print_channel_info(channel,
+ &dbg_buff[nbytes], IPA_MHI_MAX_MSG_LEN - nbytes);
+ }
+
+ for (i = 0; i < IPA_MHI_MAX_DL_CHANNELS; i++) {
+ channel = &ipa_mhi_client_ctx->dl_channels[i];
+ nbytes += ipa_mhi_print_channel_info(channel,
+ &dbg_buff[nbytes], IPA_MHI_MAX_MSG_LEN - nbytes);
+ }
+
+ return simple_read_from_buffer(ubuf, count, ppos, dbg_buff, nbytes);
+}
+
+static ssize_t ipa_mhi_debugfs_uc_stats(struct file *file,
+ char __user *ubuf,
+ size_t count,
+ loff_t *ppos)
+{
+ int nbytes = 0;
+
+ nbytes += ipa_uc_mhi_print_stats(dbg_buff, IPA_MHI_MAX_MSG_LEN);
+ return simple_read_from_buffer(ubuf, count, ppos, dbg_buff, nbytes);
+}
+
+static ssize_t ipa_mhi_debugfs_dump_host_ch_ctx_arr(struct file *file,
+ char __user *ubuf,
+ size_t count,
+ loff_t *ppos)
+{
+ int i, nbytes = 0;
+ struct ipa_mhi_channel_ctx *channel;
+
+ if (ipa_mhi_client_ctx->state == IPA_MHI_STATE_INITIALIZED ||
+ ipa_mhi_client_ctx->state == IPA_MHI_STATE_READY) {
+ nbytes += scnprintf(&dbg_buff[nbytes],
+ IPA_MHI_MAX_MSG_LEN - nbytes,
+ "Cannot dump host channel context ");
+ nbytes += scnprintf(&dbg_buff[nbytes],
+ IPA_MHI_MAX_MSG_LEN - nbytes,
+ "before IPA MHI was STARTED\n");
+ return simple_read_from_buffer(ubuf, count, ppos,
+ dbg_buff, nbytes);
+ }
+ if (ipa_mhi_client_ctx->state == IPA_MHI_STATE_SUSPENDED) {
+ nbytes += scnprintf(&dbg_buff[nbytes],
+ IPA_MHI_MAX_MSG_LEN - nbytes,
+ "IPA MHI is suspended, cannot dump channel ctx array");
+ nbytes += scnprintf(&dbg_buff[nbytes],
+ IPA_MHI_MAX_MSG_LEN - nbytes,
+ " from host -PCIe can be in D3 state\n");
+ return simple_read_from_buffer(ubuf, count, ppos,
+ dbg_buff, nbytes);
+ }
+
+ nbytes += scnprintf(&dbg_buff[nbytes],
+ IPA_MHI_MAX_MSG_LEN - nbytes,
+ "channel contex array - dump from host\n");
+ nbytes += scnprintf(&dbg_buff[nbytes],
+ IPA_MHI_MAX_MSG_LEN - nbytes,
+ "***** UL channels *******\n");
+
+ for (i = 0; i < IPA_MHI_MAX_UL_CHANNELS; i++) {
+ channel = &ipa_mhi_client_ctx->ul_channels[i];
+ if (!channel->valid)
+ continue;
+ nbytes += ipa_mhi_print_host_channel_ctx_info(channel,
+ &dbg_buff[nbytes],
+ IPA_MHI_MAX_MSG_LEN - nbytes);
+ }
+
+ nbytes += scnprintf(&dbg_buff[nbytes],
+ IPA_MHI_MAX_MSG_LEN - nbytes,
+ "\n***** DL channels *******\n");
+
+ for (i = 0; i < IPA_MHI_MAX_DL_CHANNELS; i++) {
+ channel = &ipa_mhi_client_ctx->dl_channels[i];
+ if (!channel->valid)
+ continue;
+ nbytes += ipa_mhi_print_host_channel_ctx_info(channel,
+ &dbg_buff[nbytes], IPA_MHI_MAX_MSG_LEN - nbytes);
+ }
+
+ return simple_read_from_buffer(ubuf, count, ppos, dbg_buff, nbytes);
+}
+
+const struct file_operations ipa_mhi_stats_ops = {
+ .read = ipa_mhi_debugfs_stats,
+};
+
+const struct file_operations ipa_mhi_uc_stats_ops = {
+ .read = ipa_mhi_debugfs_uc_stats,
+};
+
+const struct file_operations ipa_mhi_dump_host_ch_ctx_ops = {
+ .read = ipa_mhi_debugfs_dump_host_ch_ctx_arr,
+};
+
+
+static void ipa_mhi_debugfs_init(void)
+{
+ const mode_t read_only_mode = S_IRUSR | S_IRGRP | S_IROTH;
+ const mode_t read_write_mode = S_IRUSR | S_IRGRP | S_IROTH |
+ S_IWUSR | S_IWGRP;
+ struct dentry *file;
+
+ IPA_MHI_FUNC_ENTRY();
+
+ dent = debugfs_create_dir("ipa_mhi", 0);
+ if (IS_ERR(dent)) {
+ IPA_MHI_ERR("fail to create folder ipa_mhi\n");
+ return;
+ }
+
+ file = debugfs_create_file("stats", read_only_mode, dent,
+ 0, &ipa_mhi_stats_ops);
+ if (!file || IS_ERR(file)) {
+ IPA_MHI_ERR("fail to create file stats\n");
+ goto fail;
+ }
+
+ file = debugfs_create_file("uc_stats", read_only_mode, dent,
+ 0, &ipa_mhi_uc_stats_ops);
+ if (!file || IS_ERR(file)) {
+ IPA_MHI_ERR("fail to create file uc_stats\n");
+ goto fail;
+ }
+
+ file = debugfs_create_u32("use_ipadma", read_write_mode, dent,
+ &ipa_mhi_client_ctx->use_ipadma);
+ if (!file || IS_ERR(file)) {
+ IPA_MHI_ERR("fail to create file use_ipadma\n");
+ goto fail;
+ }
+
+ file = debugfs_create_file("dump_host_channel_ctx_array",
+ read_only_mode, dent, 0, &ipa_mhi_dump_host_ch_ctx_ops);
+ if (!file || IS_ERR(file)) {
+ IPA_MHI_ERR("fail to create file dump_host_channel_ctx_arr\n");
+ goto fail;
+ }
+
+ IPA_MHI_FUNC_EXIT();
+ return;
+fail:
+ debugfs_remove_recursive(dent);
+}
+
+#else
+static void ipa_mhi_debugfs_init(void) {}
+static void ipa_mhi_debugfs_destroy(void) {}
+#endif /* CONFIG_DEBUG_FS */
+
+static union IpaHwMhiDlUlSyncCmdData_t ipa_cached_dl_ul_sync_info;
+
+static void ipa_mhi_wq_notify_wakeup(struct work_struct *work);
+static DECLARE_WORK(ipa_mhi_notify_wakeup_work, ipa_mhi_wq_notify_wakeup);
+
+static void ipa_mhi_wq_notify_ready(struct work_struct *work);
+static DECLARE_WORK(ipa_mhi_notify_ready_work, ipa_mhi_wq_notify_ready);
+
+/**
+ * ipa_mhi_notify_wakeup() - Schedule work to notify data available
+ *
+ * This function will schedule a work to notify data available event.
+ * In case this function is called more than once, only one notification will
+ * be sent to MHI client driver. No further notifications will be sent until
+ * IPA MHI state will become STARTED.
+ */
+static void ipa_mhi_notify_wakeup(void)
+{
+ IPA_MHI_FUNC_ENTRY();
+ if (ipa_mhi_client_ctx->wakeup_notified) {
+ IPA_MHI_DBG("wakeup already called\n");
+ return;
+ }
+ queue_work(ipa_mhi_client_ctx->wq, &ipa_mhi_notify_wakeup_work);
+ ipa_mhi_client_ctx->wakeup_notified = true;
+ IPA_MHI_FUNC_EXIT();
+}
+
+/**
+ * ipa_mhi_rm_cons_request() - callback function for IPA RM request resource
+ *
+ * In case IPA MHI is not suspended, MHI CONS will be granted immediately.
+ * In case IPA MHI is suspended, MHI CONS will be granted after resume.
+ */
+static int ipa_mhi_rm_cons_request(void)
+{
+ unsigned long flags;
+ int res;
+
+ IPA_MHI_FUNC_ENTRY();
+
+ IPA_MHI_DBG("%s\n", MHI_STATE_STR(ipa_mhi_client_ctx->state));
+ spin_lock_irqsave(&ipa_mhi_client_ctx->state_lock, flags);
+ ipa_mhi_client_ctx->rm_cons_state = IPA_MHI_RM_STATE_REQUESTED;
+ if (ipa_mhi_client_ctx->state == IPA_MHI_STATE_STARTED) {
+ ipa_mhi_client_ctx->rm_cons_state = IPA_MHI_RM_STATE_GRANTED;
+ res = 0;
+ } else if (ipa_mhi_client_ctx->state == IPA_MHI_STATE_SUSPENDED) {
+ ipa_mhi_notify_wakeup();
+ res = -EINPROGRESS;
+ } else if (ipa_mhi_client_ctx->state ==
+ IPA_MHI_STATE_SUSPEND_IN_PROGRESS) {
+ /* wakeup event will be trigger after suspend finishes */
+ ipa_mhi_client_ctx->trigger_wakeup = true;
+ res = -EINPROGRESS;
+ } else {
+ res = -EINPROGRESS;
+ }
+
+ spin_unlock_irqrestore(&ipa_mhi_client_ctx->state_lock, flags);
+ IPA_MHI_DBG("EXIT with %d\n", res);
+ return res;
+}
+
+static int ipa_mhi_rm_cons_release(void)
+{
+ unsigned long flags;
+
+ IPA_MHI_FUNC_ENTRY();
+
+ spin_lock_irqsave(&ipa_mhi_client_ctx->state_lock, flags);
+ ipa_mhi_client_ctx->rm_cons_state = IPA_MHI_RM_STATE_RELEASED;
+ complete_all(&ipa_mhi_client_ctx->rm_cons_comp);
+ spin_unlock_irqrestore(&ipa_mhi_client_ctx->state_lock, flags);
+
+ IPA_MHI_FUNC_EXIT();
+ return 0;
+}
+
+static void ipa_mhi_rm_prod_notify(void *user_data, enum ipa_rm_event event,
+ unsigned long data)
+{
+ IPA_MHI_FUNC_ENTRY();
+
+ switch (event) {
+ case IPA_RM_RESOURCE_GRANTED:
+ IPA_MHI_DBG("IPA_RM_RESOURCE_GRANTED\n");
+ complete_all(&ipa_mhi_client_ctx->rm_prod_granted_comp);
+ break;
+
+ case IPA_RM_RESOURCE_RELEASED:
+ IPA_MHI_DBG("IPA_RM_RESOURCE_RELEASED\n");
+ break;
+
+ default:
+ IPA_MHI_ERR("unexpected event %d\n", event);
+ WARN_ON(1);
+ break;
+ }
+
+ IPA_MHI_FUNC_EXIT();
+}
+
+/**
+ * ipa_mhi_wq_notify_wakeup() - Notify MHI client on data available
+ *
+ * This function is called from IPA MHI workqueue to notify
+ * MHI client driver on data available event.
+ */
+static void ipa_mhi_wq_notify_wakeup(struct work_struct *work)
+{
+ IPA_MHI_FUNC_ENTRY();
+ ipa_mhi_client_ctx->cb_notify(ipa_mhi_client_ctx->cb_priv,
+ IPA_MHI_EVENT_DATA_AVAILABLE, 0);
+ IPA_MHI_FUNC_EXIT();
+}
+
+/**
+ * ipa_mhi_wq_notify_ready() - Notify MHI client on ready
+ *
+ * This function is called from IPA MHI workqueue to notify
+ * MHI client driver on ready event when IPA uC is loaded
+ */
+static void ipa_mhi_wq_notify_ready(struct work_struct *work)
+{
+ IPA_MHI_FUNC_ENTRY();
+ ipa_mhi_client_ctx->cb_notify(ipa_mhi_client_ctx->cb_priv,
+ IPA_MHI_EVENT_READY, 0);
+ IPA_MHI_FUNC_EXIT();
+}
+
+/**
+ * ipa_mhi_notify_ready() - Schedule work to notify ready
+ *
+ * This function will schedule a work to notify ready event.
+ */
+static void ipa_mhi_notify_ready(void)
+{
+ IPA_MHI_FUNC_ENTRY();
+ queue_work(ipa_mhi_client_ctx->wq, &ipa_mhi_notify_ready_work);
+ IPA_MHI_FUNC_EXIT();
+}
+
+/**
+ * ipa_mhi_set_state() - Set new state to IPA MHI
+ * @state: new state
+ *
+ * Sets a new state to IPA MHI if possible according to IPA MHI state machine.
+ * In some state transitions a wakeup request will be triggered.
+ *
+ * Returns: 0 on success, -1 otherwise
+ */
+static int ipa_mhi_set_state(enum ipa_mhi_state new_state)
+{
+ unsigned long flags;
+ int res = -EPERM;
+
+ spin_lock_irqsave(&ipa_mhi_client_ctx->state_lock, flags);
+ IPA_MHI_DBG("Current state: %s\n",
+ MHI_STATE_STR(ipa_mhi_client_ctx->state));
+
+ switch (ipa_mhi_client_ctx->state) {
+ case IPA_MHI_STATE_INITIALIZED:
+ if (new_state == IPA_MHI_STATE_READY) {
+ ipa_mhi_notify_ready();
+ res = 0;
+ }
+ break;
+
+ case IPA_MHI_STATE_READY:
+ if (new_state == IPA_MHI_STATE_READY)
+ res = 0;
+ if (new_state == IPA_MHI_STATE_STARTED)
+ res = 0;
+ break;
+
+ case IPA_MHI_STATE_STARTED:
+ if (new_state == IPA_MHI_STATE_INITIALIZED)
+ res = 0;
+ else if (new_state == IPA_MHI_STATE_SUSPEND_IN_PROGRESS)
+ res = 0;
+ break;
+
+ case IPA_MHI_STATE_SUSPEND_IN_PROGRESS:
+ if (new_state == IPA_MHI_STATE_SUSPENDED) {
+ if (ipa_mhi_client_ctx->trigger_wakeup) {
+ ipa_mhi_client_ctx->trigger_wakeup = false;
+ ipa_mhi_notify_wakeup();
+ }
+ res = 0;
+ } else if (new_state == IPA_MHI_STATE_STARTED) {
+ ipa_mhi_client_ctx->wakeup_notified = false;
+ ipa_mhi_client_ctx->trigger_wakeup = false;
+ if (ipa_mhi_client_ctx->rm_cons_state ==
+ IPA_MHI_RM_STATE_REQUESTED) {
+ ipa_rm_notify_completion(
+ IPA_RM_RESOURCE_GRANTED,
+ IPA_RM_RESOURCE_MHI_CONS);
+ ipa_mhi_client_ctx->rm_cons_state =
+ IPA_MHI_RM_STATE_GRANTED;
+ }
+ res = 0;
+ }
+ break;
+
+ case IPA_MHI_STATE_SUSPENDED:
+ if (new_state == IPA_MHI_STATE_RESUME_IN_PROGRESS)
+ res = 0;
+ break;
+
+ case IPA_MHI_STATE_RESUME_IN_PROGRESS:
+ if (new_state == IPA_MHI_STATE_SUSPENDED) {
+ if (ipa_mhi_client_ctx->trigger_wakeup) {
+ ipa_mhi_client_ctx->trigger_wakeup = false;
+ ipa_mhi_notify_wakeup();
+ }
+ res = 0;
+ } else if (new_state == IPA_MHI_STATE_STARTED) {
+ ipa_mhi_client_ctx->trigger_wakeup = false;
+ ipa_mhi_client_ctx->wakeup_notified = false;
+ if (ipa_mhi_client_ctx->rm_cons_state ==
+ IPA_MHI_RM_STATE_REQUESTED) {
+ ipa_rm_notify_completion(
+ IPA_RM_RESOURCE_GRANTED,
+ IPA_RM_RESOURCE_MHI_CONS);
+ ipa_mhi_client_ctx->rm_cons_state =
+ IPA_MHI_RM_STATE_GRANTED;
+ }
+ res = 0;
+ }
+ break;
+
+ default:
+ IPA_MHI_ERR("Invalid state %d\n", ipa_mhi_client_ctx->state);
+ WARN_ON(1);
+ }
+
+ if (res)
+ IPA_MHI_ERR("Invalid state change to %s\n",
+ MHI_STATE_STR(new_state));
+ else {
+ IPA_MHI_DBG("New state change to %s\n",
+ MHI_STATE_STR(new_state));
+ ipa_mhi_client_ctx->state = new_state;
+ }
+ spin_unlock_irqrestore(&ipa_mhi_client_ctx->state_lock, flags);
+ return res;
+}
+
+static void ipa_mhi_uc_ready_cb(void)
+{
+ IPA_MHI_FUNC_ENTRY();
+ ipa_mhi_set_state(IPA_MHI_STATE_READY);
+ IPA_MHI_FUNC_EXIT();
+}
+
+static void ipa_mhi_uc_wakeup_request_cb(void)
+{
+ unsigned long flags;
+
+ IPA_MHI_FUNC_ENTRY();
+ IPA_MHI_DBG("MHI state: %s\n",
+ MHI_STATE_STR(ipa_mhi_client_ctx->state));
+ spin_lock_irqsave(&ipa_mhi_client_ctx->state_lock, flags);
+ if (ipa_mhi_client_ctx->state == IPA_MHI_STATE_SUSPENDED)
+ ipa_mhi_notify_wakeup();
+ else if (ipa_mhi_client_ctx->state ==
+ IPA_MHI_STATE_SUSPEND_IN_PROGRESS)
+ /* wakeup event will be triggered after suspend finishes */
+ ipa_mhi_client_ctx->trigger_wakeup = true;
+
+ spin_unlock_irqrestore(&ipa_mhi_client_ctx->state_lock, flags);
+ IPA_MHI_FUNC_EXIT();
+}
+
+static int ipa_mhi_request_prod(void)
+{
+ int res;
+
+ IPA_MHI_FUNC_ENTRY();
+
+ reinit_completion(&ipa_mhi_client_ctx->rm_prod_granted_comp);
+ IPA_MHI_DBG("requesting mhi prod\n");
+ res = ipa_rm_request_resource(IPA_RM_RESOURCE_MHI_PROD);
+ if (res) {
+ if (res != -EINPROGRESS) {
+ IPA_MHI_ERR("failed to request mhi prod %d\n", res);
+ return res;
+ }
+ res = wait_for_completion_timeout(
+ &ipa_mhi_client_ctx->rm_prod_granted_comp,
+ msecs_to_jiffies(IPA_MHI_RM_TIMEOUT_MSEC));
+ if (res == 0) {
+ IPA_MHI_ERR("timeout request mhi prod\n");
+ return -ETIME;
+ }
+ }
+
+ IPA_MHI_DBG("mhi prod granted\n");
+ IPA_MHI_FUNC_EXIT();
+ return 0;
+
+}
+
+static int ipa_mhi_release_prod(void)
+{
+ int res;
+
+ IPA_MHI_FUNC_ENTRY();
+
+ res = ipa_rm_release_resource(IPA_RM_RESOURCE_MHI_PROD);
+
+ IPA_MHI_FUNC_EXIT();
+ return res;
+
+}
+
+/**
+ * ipa_mhi_start() - Start IPA MHI engine
+ * @params: pcie addresses for MHI
+ *
+ * This function is called by MHI client driver on MHI engine start for
+ * handling MHI accelerated channels. This function is called after
+ * ipa_mhi_init() was called and can be called after MHI reset to restart MHI
+ * engine. When this function returns device can move to M0 state.
+ *
+ * Return codes: 0 : success
+ * negative : error
+ */
+int ipa_mhi_start(struct ipa_mhi_start_params *params)
+{
+ int res;
+ struct ipa_mhi_init_engine init_params;
+
+ IPA_MHI_FUNC_ENTRY();
+
+ if (!params) {
+ IPA_MHI_ERR("null args\n");
+ return -EINVAL;
+ }
+
+ if (!ipa_mhi_client_ctx) {
+ IPA_MHI_ERR("not initialized\n");
+ return -EPERM;
+ }
+
+ res = ipa_mhi_set_state(IPA_MHI_STATE_STARTED);
+ if (res) {
+ IPA_MHI_ERR("ipa_mhi_set_state %d\n", res);
+ return res;
+ }
+
+ ipa_mhi_client_ctx->host_ctrl_addr = params->host_ctrl_addr;
+ ipa_mhi_client_ctx->host_data_addr = params->host_data_addr;
+ ipa_mhi_client_ctx->channel_context_array_addr =
+ params->channel_context_array_addr;
+ ipa_mhi_client_ctx->event_context_array_addr =
+ params->event_context_array_addr;
+ IPA_MHI_DBG("host_ctrl_addr 0x%x\n",
+ ipa_mhi_client_ctx->host_ctrl_addr);
+ IPA_MHI_DBG("host_data_addr 0x%x\n",
+ ipa_mhi_client_ctx->host_data_addr);
+ IPA_MHI_DBG("channel_context_array_addr 0x%llx\n",
+ ipa_mhi_client_ctx->channel_context_array_addr);
+ IPA_MHI_DBG("event_context_array_addr 0x%llx\n",
+ ipa_mhi_client_ctx->event_context_array_addr);
+
+ /* Add MHI <-> Q6 dependencies to IPA RM */
+ res = ipa_rm_add_dependency(IPA_RM_RESOURCE_MHI_PROD,
+ IPA_RM_RESOURCE_Q6_CONS);
+ if (res && res != -EINPROGRESS) {
+ IPA_MHI_ERR("failed to add dependency %d\n", res);
+ goto fail_add_mhi_q6_dep;
+ }
+
+ res = ipa_rm_add_dependency(IPA_RM_RESOURCE_Q6_PROD,
+ IPA_RM_RESOURCE_MHI_CONS);
+ if (res && res != -EINPROGRESS) {
+ IPA_MHI_ERR("failed to add dependency %d\n", res);
+ goto fail_add_q6_mhi_dep;
+ }
+
+ res = ipa_mhi_request_prod();
+ if (res) {
+ IPA_MHI_ERR("failed request prod %d\n", res);
+ goto fail_request_prod;
+ }
+
+ /* gsi params */
+ init_params.gsi.first_ch_idx =
+ ipa_mhi_client_ctx->first_ch_idx;
+ /* uC params */
+ init_params.uC.first_ch_idx =
+ ipa_mhi_client_ctx->first_ch_idx;
+ init_params.uC.first_er_idx =
+ ipa_mhi_client_ctx->first_er_idx;
+ init_params.uC.host_ctrl_addr = params->host_ctrl_addr;
+ init_params.uC.host_data_addr = params->host_data_addr;
+ init_params.uC.mmio_addr = ipa_mhi_client_ctx->mmio_addr;
+ init_params.uC.msi = &ipa_mhi_client_ctx->msi;
+ init_params.uC.ipa_cached_dl_ul_sync_info =
+ &ipa_cached_dl_ul_sync_info;
+
+ res = ipa_mhi_init_engine(&init_params);
+ if (res) {
+ IPA_MHI_ERR("IPA core failed to start MHI %d\n", res);
+ goto fail_init_engine;
+ }
+
+ IPA_MHI_FUNC_EXIT();
+ return 0;
+
+fail_init_engine:
+ ipa_mhi_release_prod();
+fail_request_prod:
+ ipa_rm_delete_dependency(IPA_RM_RESOURCE_Q6_PROD,
+ IPA_RM_RESOURCE_MHI_CONS);
+fail_add_q6_mhi_dep:
+ ipa_rm_delete_dependency(IPA_RM_RESOURCE_MHI_PROD,
+ IPA_RM_RESOURCE_Q6_CONS);
+fail_add_mhi_q6_dep:
+ ipa_mhi_set_state(IPA_MHI_STATE_INITIALIZED);
+ return res;
+}
+
+/**
+ * ipa_mhi_get_channel_context() - Get corresponding channel context
+ * @ep: IPA ep
+ * @channel_id: Channel ID
+ *
+ * This function will return the corresponding channel context or allocate new
+ * one in case channel context for channel does not exist.
+ */
+static struct ipa_mhi_channel_ctx *ipa_mhi_get_channel_context(
+ enum ipa_client_type client, u8 channel_id)
+{
+ int ch_idx;
+ struct ipa_mhi_channel_ctx *channels;
+ int max_channels;
+
+ if (IPA_CLIENT_IS_PROD(client)) {
+ channels = ipa_mhi_client_ctx->ul_channels;
+ max_channels = IPA_MHI_MAX_UL_CHANNELS;
+ } else {
+ channels = ipa_mhi_client_ctx->dl_channels;
+ max_channels = IPA_MHI_MAX_DL_CHANNELS;
+ }
+
+ /* find the channel context according to channel id */
+ for (ch_idx = 0; ch_idx < max_channels; ch_idx++) {
+ if (channels[ch_idx].valid &&
+ channels[ch_idx].id == channel_id)
+ return &channels[ch_idx];
+ }
+
+ /* channel context does not exists, allocate a new one */
+ for (ch_idx = 0; ch_idx < max_channels; ch_idx++) {
+ if (!channels[ch_idx].valid)
+ break;
+ }
+
+ if (ch_idx == max_channels) {
+ IPA_MHI_ERR("no more channels available\n");
+ return NULL;
+ }
+
+ channels[ch_idx].valid = true;
+ channels[ch_idx].id = channel_id;
+ channels[ch_idx].index = ipa_mhi_client_ctx->total_channels++;
+ channels[ch_idx].client = client;
+ channels[ch_idx].state = IPA_HW_MHI_CHANNEL_STATE_INVALID;
+
+ return &channels[ch_idx];
+}
+
+/**
+ * ipa_mhi_get_channel_context_by_clnt_hdl() - Get corresponding channel
+ * context
+ * @clnt_hdl: client handle as provided in ipa_mhi_connect_pipe()
+ *
+ * This function will return the corresponding channel context or NULL in case
+ * that channel does not exist.
+ */
+static struct ipa_mhi_channel_ctx *ipa_mhi_get_channel_context_by_clnt_hdl(
+ u32 clnt_hdl)
+{
+ int ch_idx;
+
+ for (ch_idx = 0; ch_idx < IPA_MHI_MAX_UL_CHANNELS; ch_idx++) {
+ if (ipa_mhi_client_ctx->ul_channels[ch_idx].valid &&
+ ipa_get_ep_mapping(
+ ipa_mhi_client_ctx->ul_channels[ch_idx].client)
+ == clnt_hdl)
+ return &ipa_mhi_client_ctx->ul_channels[ch_idx];
+ }
+
+ for (ch_idx = 0; ch_idx < IPA_MHI_MAX_DL_CHANNELS; ch_idx++) {
+ if (ipa_mhi_client_ctx->dl_channels[ch_idx].valid &&
+ ipa_get_ep_mapping(
+ ipa_mhi_client_ctx->dl_channels[ch_idx].client)
+ == clnt_hdl)
+ return &ipa_mhi_client_ctx->dl_channels[ch_idx];
+ }
+
+ return NULL;
+}
+
+static void ipa_mhi_dump_ch_ctx(struct ipa_mhi_channel_ctx *channel)
+{
+ IPA_MHI_DBG("ch_id %d\n", channel->id);
+ IPA_MHI_DBG("chstate 0x%x\n", channel->ch_ctx_host.chstate);
+ IPA_MHI_DBG("brstmode 0x%x\n", channel->ch_ctx_host.brstmode);
+ IPA_MHI_DBG("pollcfg 0x%x\n", channel->ch_ctx_host.pollcfg);
+ IPA_MHI_DBG("chtype 0x%x\n", channel->ch_ctx_host.chtype);
+ IPA_MHI_DBG("erindex 0x%x\n", channel->ch_ctx_host.erindex);
+ IPA_MHI_DBG("rbase 0x%llx\n", channel->ch_ctx_host.rbase);
+ IPA_MHI_DBG("rlen 0x%llx\n", channel->ch_ctx_host.rlen);
+ IPA_MHI_DBG("rp 0x%llx\n", channel->ch_ctx_host.rp);
+ IPA_MHI_DBG("wp 0x%llx\n", channel->ch_ctx_host.wp);
+}
+
+static void ipa_mhi_dump_ev_ctx(struct ipa_mhi_channel_ctx *channel)
+{
+ IPA_MHI_DBG("ch_id %d event id %d\n", channel->id,
+ channel->ch_ctx_host.erindex);
+
+ IPA_MHI_DBG("intmodc 0x%x\n", channel->ev_ctx_host.intmodc);
+ IPA_MHI_DBG("intmodt 0x%x\n", channel->ev_ctx_host.intmodt);
+ IPA_MHI_DBG("ertype 0x%x\n", channel->ev_ctx_host.ertype);
+ IPA_MHI_DBG("msivec 0x%x\n", channel->ev_ctx_host.msivec);
+ IPA_MHI_DBG("rbase 0x%llx\n", channel->ev_ctx_host.rbase);
+ IPA_MHI_DBG("rlen 0x%llx\n", channel->ev_ctx_host.rlen);
+ IPA_MHI_DBG("rp 0x%llx\n", channel->ev_ctx_host.rp);
+ IPA_MHI_DBG("wp 0x%llx\n", channel->ev_ctx_host.wp);
+}
+
+static int ipa_mhi_read_ch_ctx(struct ipa_mhi_channel_ctx *channel)
+{
+ int res;
+
+ res = ipa_mhi_read_write_host(IPA_MHI_DMA_FROM_HOST,
+ &channel->ch_ctx_host, channel->channel_context_addr,
+ sizeof(channel->ch_ctx_host));
+ if (res) {
+ IPA_MHI_ERR("ipa_mhi_read_write_host failed %d\n", res);
+ return res;
+
+ }
+ ipa_mhi_dump_ch_ctx(channel);
+
+ channel->event_context_addr =
+ ipa_mhi_client_ctx->event_context_array_addr +
+ channel->ch_ctx_host.erindex * sizeof(struct ipa_mhi_ev_ctx);
+ IPA_MHI_DBG("ch %d event_context_addr 0x%llx\n", channel->id,
+ channel->event_context_addr);
+
+ res = ipa_mhi_read_write_host(IPA_MHI_DMA_FROM_HOST,
+ &channel->ev_ctx_host, channel->event_context_addr,
+ sizeof(channel->ev_ctx_host));
+ if (res) {
+ IPA_MHI_ERR("ipa_mhi_read_write_host failed %d\n", res);
+ return res;
+
+ }
+ ipa_mhi_dump_ev_ctx(channel);
+
+ return 0;
+}
+
+static void ipa_mhi_gsi_ev_err_cb(struct gsi_evt_err_notify *notify)
+{
+ struct ipa_mhi_channel_ctx *channel = notify->user_data;
+
+ IPA_MHI_ERR("channel id=%d client=%d state=%d\n",
+ channel->id, channel->client, channel->state);
+ switch (notify->evt_id) {
+ case GSI_EVT_OUT_OF_BUFFERS_ERR:
+ IPA_MHI_ERR("Received GSI_EVT_OUT_OF_BUFFERS_ERR\n");
+ break;
+ case GSI_EVT_OUT_OF_RESOURCES_ERR:
+ IPA_MHI_ERR("Received GSI_EVT_OUT_OF_RESOURCES_ERR\n");
+ break;
+ case GSI_EVT_UNSUPPORTED_INTER_EE_OP_ERR:
+ IPA_MHI_ERR("Received GSI_EVT_UNSUPPORTED_INTER_EE_OP_ERR\n");
+ break;
+ case GSI_EVT_EVT_RING_EMPTY_ERR:
+ IPA_MHI_ERR("Received GSI_EVT_EVT_RING_EMPTY_ERR\n");
+ break;
+ default:
+ IPA_MHI_ERR("Unexpected err evt: %d\n", notify->evt_id);
+ }
+ IPA_MHI_ERR("err_desc=0x%x\n", notify->err_desc);
+}
+
+static void ipa_mhi_gsi_ch_err_cb(struct gsi_chan_err_notify *notify)
+{
+ struct ipa_mhi_channel_ctx *channel = notify->chan_user_data;
+
+ IPA_MHI_ERR("channel id=%d client=%d state=%d\n",
+ channel->id, channel->client, channel->state);
+ switch (notify->evt_id) {
+ case GSI_CHAN_INVALID_TRE_ERR:
+ IPA_MHI_ERR("Received GSI_CHAN_INVALID_TRE_ERR\n");
+ break;
+ case GSI_CHAN_NON_ALLOCATED_EVT_ACCESS_ERR:
+ IPA_MHI_ERR("Received GSI_CHAN_NON_ALLOCATED_EVT_ACCESS_ERR\n");
+ break;
+ case GSI_CHAN_OUT_OF_BUFFERS_ERR:
+ IPA_MHI_ERR("Received GSI_CHAN_OUT_OF_BUFFERS_ERR\n");
+ break;
+ case GSI_CHAN_OUT_OF_RESOURCES_ERR:
+ IPA_MHI_ERR("Received GSI_CHAN_OUT_OF_RESOURCES_ERR\n");
+ break;
+ case GSI_CHAN_UNSUPPORTED_INTER_EE_OP_ERR:
+ IPA_MHI_ERR("Received GSI_CHAN_UNSUPPORTED_INTER_EE_OP_ERR\n");
+ break;
+ case GSI_CHAN_HWO_1_ERR:
+ IPA_MHI_ERR("Received GSI_CHAN_HWO_1_ERR\n");
+ break;
+ default:
+ IPA_MHI_ERR("Unexpected err evt: %d\n", notify->evt_id);
+ }
+ IPA_MHI_ERR("err_desc=0x%x\n", notify->err_desc);
+}
+
+
+static bool ipa_mhi_gsi_channel_empty(struct ipa_mhi_channel_ctx *channel)
+{
+ IPA_MHI_FUNC_ENTRY();
+
+ if (!channel->stop_in_proc) {
+ IPA_MHI_DBG("Channel is not in STOP_IN_PROC\n");
+ return true;
+ }
+
+ if (ipa_mhi_stop_gsi_channel(channel->client) == true) {
+ channel->stop_in_proc = false;
+ return true;
+ }
+
+ return false;
+}
+
+/**
+ * ipa_mhi_wait_for_ul_empty_timeout() - wait for pending packets in uplink
+ * @msecs: timeout to wait
+ *
+ * This function will poll until there are no packets pending in uplink channels
+ * or timeout occurred.
+ *
+ * Return code: true - no pending packets in uplink channels
+ * false - timeout occurred
+ */
+static bool ipa_mhi_wait_for_ul_empty_timeout(unsigned int msecs)
+{
+ unsigned long jiffies_timeout = msecs_to_jiffies(msecs);
+ unsigned long jiffies_start = jiffies;
+ bool empty = false;
+ int i;
+
+ IPA_MHI_FUNC_ENTRY();
+ while (!empty) {
+ empty = true;
+ for (i = 0; i < IPA_MHI_MAX_UL_CHANNELS; i++) {
+ if (!ipa_mhi_client_ctx->ul_channels[i].valid)
+ continue;
+ if (ipa_get_transport_type() ==
+ IPA_TRANSPORT_TYPE_GSI)
+ empty &= ipa_mhi_gsi_channel_empty(
+ &ipa_mhi_client_ctx->ul_channels[i]);
+ else
+ empty &= ipa_mhi_sps_channel_empty(
+ ipa_mhi_client_ctx->ul_channels[i].client);
+ }
+
+ if (time_after(jiffies, jiffies_start + jiffies_timeout)) {
+ IPA_MHI_DBG("finished waiting for UL empty\n");
+ break;
+ }
+
+ if (ipa_get_transport_type() == IPA_TRANSPORT_TYPE_GSI &&
+ IPA_MHI_MAX_UL_CHANNELS == 1)
+ usleep_range(IPA_GSI_CHANNEL_STOP_SLEEP_MIN_USEC,
+ IPA_GSI_CHANNEL_STOP_SLEEP_MAX_USEC);
+ }
+
+ IPA_MHI_DBG("IPA UL is %s\n", (empty) ? "empty" : "not empty");
+
+ IPA_MHI_FUNC_EXIT();
+ return empty;
+}
+
+static int ipa_mhi_enable_force_clear(u32 request_id, bool throttle_source)
+{
+ struct ipa_enable_force_clear_datapath_req_msg_v01 req;
+ int i;
+ int res;
+
+ IPA_MHI_FUNC_ENTRY();
+ memset(&req, 0, sizeof(req));
+ req.request_id = request_id;
+ req.source_pipe_bitmask = 0;
+ for (i = 0; i < IPA_MHI_MAX_UL_CHANNELS; i++) {
+ if (!ipa_mhi_client_ctx->ul_channels[i].valid)
+ continue;
+ req.source_pipe_bitmask |= 1 << ipa_get_ep_mapping(
+ ipa_mhi_client_ctx->ul_channels[i].client);
+ }
+ if (throttle_source) {
+ req.throttle_source_valid = 1;
+ req.throttle_source = 1;
+ }
+ IPA_MHI_DBG("req_id=0x%x src_pipe_btmk=0x%x throt_src=%d\n",
+ req.request_id, req.source_pipe_bitmask,
+ req.throttle_source);
+ res = ipa_qmi_enable_force_clear_datapath_send(&req);
+ if (res) {
+ IPA_MHI_ERR(
+ "ipa_qmi_enable_force_clear_datapath_send failed %d\n"
+ , res);
+ return res;
+ }
+
+ IPA_MHI_FUNC_EXIT();
+ return 0;
+}
+
+static int ipa_mhi_disable_force_clear(u32 request_id)
+{
+ struct ipa_disable_force_clear_datapath_req_msg_v01 req;
+ int res;
+
+ IPA_MHI_FUNC_ENTRY();
+ memset(&req, 0, sizeof(req));
+ req.request_id = request_id;
+ IPA_MHI_DBG("req_id=0x%x\n", req.request_id);
+ res = ipa_qmi_disable_force_clear_datapath_send(&req);
+ if (res) {
+ IPA_MHI_ERR(
+ "ipa_qmi_disable_force_clear_datapath_send failed %d\n"
+ , res);
+ return res;
+ }
+
+ IPA_MHI_FUNC_EXIT();
+ return 0;
+}
+
+static void ipa_mhi_set_holb_on_dl_channels(bool enable,
+ struct ipa_ep_cfg_holb old_holb[])
+{
+ int i;
+ struct ipa_ep_cfg_holb ep_holb;
+ int ep_idx;
+ int res;
+
+ for (i = 0; i < IPA_MHI_MAX_DL_CHANNELS; i++) {
+ if (!ipa_mhi_client_ctx->dl_channels[i].valid)
+ continue;
+ if (ipa_mhi_client_ctx->dl_channels[i].state ==
+ IPA_HW_MHI_CHANNEL_STATE_INVALID)
+ continue;
+ ep_idx = ipa_get_ep_mapping(
+ ipa_mhi_client_ctx->dl_channels[i].client);
+ if (-1 == ep_idx) {
+ IPA_MHI_ERR("Client %u is not mapped\n",
+ ipa_mhi_client_ctx->dl_channels[i].client);
+ ipa_assert();
+ return;
+ }
+ memset(&ep_holb, 0, sizeof(ep_holb));
+ if (enable) {
+ ipa_get_holb(ep_idx, &old_holb[i]);
+ ep_holb.en = 1;
+ ep_holb.tmr_val = 0;
+ } else {
+ ep_holb = old_holb[i];
+ }
+ res = ipa_cfg_ep_holb(ep_idx, &ep_holb);
+ if (res) {
+ IPA_MHI_ERR("ipa_cfg_ep_holb failed %d\n", res);
+ ipa_assert();
+ return;
+ }
+ }
+}
+
+static int ipa_mhi_suspend_gsi_channel(struct ipa_mhi_channel_ctx *channel)
+{
+ int clnt_hdl;
+ int res;
+
+ IPA_MHI_FUNC_ENTRY();
+ clnt_hdl = ipa_get_ep_mapping(channel->client);
+ if (clnt_hdl < 0)
+ return -EFAULT;
+
+ res = ipa_stop_gsi_channel(clnt_hdl);
+ if (res != 0 && res != -GSI_STATUS_AGAIN &&
+ res != -GSI_STATUS_TIMED_OUT) {
+ IPA_MHI_ERR("GSI stop channel failed %d\n", res);
+ return -EFAULT;
+ }
+
+ /* check if channel was stopped completely */
+ if (res)
+ channel->stop_in_proc = true;
+
+ IPA_MHI_DBG("GSI channel is %s\n", (channel->stop_in_proc) ?
+ "STOP_IN_PROC" : "STOP");
+
+ IPA_MHI_FUNC_EXIT();
+ return 0;
+}
+
+static int ipa_mhi_reset_ul_channel(struct ipa_mhi_channel_ctx *channel)
+{
+ int res;
+ bool empty;
+ struct ipa_ep_cfg_holb old_ep_holb[IPA_MHI_MAX_DL_CHANNELS];
+
+ IPA_MHI_FUNC_ENTRY();
+ if (ipa_get_transport_type() == IPA_TRANSPORT_TYPE_GSI) {
+ res = ipa_mhi_suspend_gsi_channel(channel);
+ if (res) {
+ IPA_MHI_ERR("ipa_mhi_suspend_gsi_channel failed %d\n",
+ res);
+ return res;
+ }
+ } else {
+ res = ipa_uc_mhi_reset_channel(channel->index);
+ if (res) {
+ IPA_MHI_ERR("ipa_uc_mhi_reset_channel failed %d\n",
+ res);
+ return res;
+ }
+ }
+
+ empty = ipa_mhi_wait_for_ul_empty_timeout(
+ IPA_MHI_CH_EMPTY_TIMEOUT_MSEC);
+ if (!empty) {
+ IPA_MHI_DBG("%s not empty\n",
+ (ipa_get_transport_type() ==
+ IPA_TRANSPORT_TYPE_GSI) ? "GSI" : "BAM");
+ res = ipa_mhi_enable_force_clear(
+ ipa_mhi_client_ctx->qmi_req_id, false);
+ if (res) {
+ IPA_MHI_ERR("ipa_mhi_enable_force_clear failed %d\n",
+ res);
+ ipa_assert();
+ return res;
+ }
+
+ if (ipa_get_transport_type() == IPA_TRANSPORT_TYPE_GSI) {
+ empty = ipa_mhi_wait_for_ul_empty_timeout(
+ IPA_MHI_CH_EMPTY_TIMEOUT_MSEC);
+
+ IPA_MHI_DBG("empty=%d\n", empty);
+ } else {
+ /* enable packet drop on all DL channels */
+ ipa_mhi_set_holb_on_dl_channels(true, old_ep_holb);
+ ipa_generate_tag_process();
+ /* disable packet drop on all DL channels */
+ ipa_mhi_set_holb_on_dl_channels(false, old_ep_holb);
+
+ res = ipa_disable_sps_pipe(channel->client);
+ if (res) {
+ IPA_MHI_ERR("sps_pipe_disable fail %d\n", res);
+ ipa_assert();
+ return res;
+ }
+ }
+
+ res =
+ ipa_mhi_disable_force_clear(ipa_mhi_client_ctx->qmi_req_id);
+ if (res) {
+ IPA_MHI_ERR("ipa_mhi_disable_force_clear failed %d\n",
+ res);
+ ipa_assert();
+ return res;
+ }
+ ipa_mhi_client_ctx->qmi_req_id++;
+ }
+
+ res = ipa_mhi_reset_channel_internal(channel->client);
+ if (res) {
+ IPA_MHI_ERR("ipa_mhi_reset_ul_channel_internal failed %d\n"
+ , res);
+ return res;
+ }
+
+ IPA_MHI_FUNC_EXIT();
+
+ return 0;
+}
+
+static int ipa_mhi_reset_dl_channel(struct ipa_mhi_channel_ctx *channel)
+{
+ int res;
+
+ IPA_MHI_FUNC_ENTRY();
+ if (ipa_get_transport_type() == IPA_TRANSPORT_TYPE_GSI) {
+ res = ipa_mhi_suspend_gsi_channel(channel);
+ if (res) {
+ IPA_MHI_ERR("ipa_mhi_suspend_gsi_channel failed %d\n"
+ , res);
+ return res;
+ }
+
+ res = ipa_mhi_reset_channel_internal(channel->client);
+ if (res) {
+ IPA_MHI_ERR(
+ "ipa_mhi_reset_ul_channel_internal failed %d\n"
+ , res);
+ return res;
+ }
+ } else {
+ res = ipa_mhi_reset_channel_internal(channel->client);
+ if (res) {
+ IPA_MHI_ERR(
+ "ipa_mhi_reset_ul_channel_internal failed %d\n"
+ , res);
+ return res;
+ }
+
+ res = ipa_uc_mhi_reset_channel(channel->index);
+ if (res) {
+ IPA_MHI_ERR("ipa_uc_mhi_reset_channel failed %d\n",
+ res);
+ ipa_mhi_start_channel_internal(channel->client);
+ return res;
+ }
+ }
+
+ IPA_MHI_FUNC_EXIT();
+ return 0;
+}
+
+static int ipa_mhi_reset_channel(struct ipa_mhi_channel_ctx *channel)
+{
+ int res;
+
+ IPA_MHI_FUNC_ENTRY();
+ if (IPA_CLIENT_IS_PROD(channel->client))
+ res = ipa_mhi_reset_ul_channel(channel);
+ else
+ res = ipa_mhi_reset_dl_channel(channel);
+ if (res) {
+ IPA_MHI_ERR("failed to reset channel error %d\n", res);
+ return res;
+ }
+
+ channel->state = IPA_HW_MHI_CHANNEL_STATE_DISABLE;
+
+ if (ipa_get_transport_type() == IPA_TRANSPORT_TYPE_GSI) {
+ res = ipa_mhi_read_write_host(IPA_MHI_DMA_TO_HOST,
+ &channel->state, channel->channel_context_addr +
+ offsetof(struct ipa_mhi_ch_ctx, chstate),
+ sizeof(((struct ipa_mhi_ch_ctx *)0)->chstate));
+ if (res) {
+ IPA_MHI_ERR("ipa_mhi_read_write_host failed %d\n", res);
+ return res;
+ }
+ }
+
+ IPA_MHI_FUNC_EXIT();
+ return 0;
+}
+
+/**
+ * ipa_mhi_connect_pipe() - Connect pipe to IPA and start corresponding
+ * MHI channel
+ * @in: connect parameters
+ * @clnt_hdl: [out] client handle for this pipe
+ *
+ * This function is called by MHI client driver on MHI channel start.
+ * This function is called after MHI engine was started.
+ *
+ * Return codes: 0 : success
+ * negative : error
+ */
+int ipa_mhi_connect_pipe(struct ipa_mhi_connect_params *in, u32 *clnt_hdl)
+{
+ int res;
+ unsigned long flags;
+ struct ipa_mhi_channel_ctx *channel = NULL;
+
+ IPA_MHI_FUNC_ENTRY();
+
+ if (!in || !clnt_hdl) {
+ IPA_MHI_ERR("NULL args\n");
+ return -EINVAL;
+ }
+
+ if (in->sys.client >= IPA_CLIENT_MAX) {
+ IPA_MHI_ERR("bad param client:%d\n", in->sys.client);
+ return -EINVAL;
+ }
+
+ if (!IPA_CLIENT_IS_MHI(in->sys.client)) {
+ IPA_MHI_ERR(
+ "Invalid MHI client, client: %d\n", in->sys.client);
+ return -EINVAL;
+ }
+
+ IPA_MHI_DBG("channel=%d\n", in->channel_id);
+
+ spin_lock_irqsave(&ipa_mhi_client_ctx->state_lock, flags);
+ if (!ipa_mhi_client_ctx ||
+ ipa_mhi_client_ctx->state != IPA_MHI_STATE_STARTED) {
+ IPA_MHI_ERR("IPA MHI was not started\n");
+ spin_unlock_irqrestore(&ipa_mhi_client_ctx->state_lock, flags);
+ return -EINVAL;
+ }
+ spin_unlock_irqrestore(&ipa_mhi_client_ctx->state_lock, flags);
+
+ channel = ipa_mhi_get_channel_context(in->sys.client, in->channel_id);
+ if (!channel) {
+ IPA_MHI_ERR("ipa_mhi_get_channel_context failed\n");
+ return -EINVAL;
+ }
+
+ if (channel->state != IPA_HW_MHI_CHANNEL_STATE_INVALID &&
+ channel->state != IPA_HW_MHI_CHANNEL_STATE_DISABLE) {
+ IPA_MHI_ERR("Invalid channel state %d\n", channel->state);
+ return -EFAULT;
+ }
+
+ channel->channel_context_addr =
+ ipa_mhi_client_ctx->channel_context_array_addr +
+ channel->id * sizeof(struct ipa_mhi_ch_ctx);
+
+ /* for event context address index needs to read from host */
+
+ IPA_MHI_DBG("client %d channelHandle %d channelIndex %d, state %d\n",
+ channel->client, channel->index, channel->id, channel->state);
+ IPA_MHI_DBG("channel_context_addr 0x%llx\n",
+ channel->channel_context_addr);
+
+ IPA_ACTIVE_CLIENTS_INC_EP(in->sys.client);
+
+ if (ipa_get_transport_type() == IPA_TRANSPORT_TYPE_GSI) {
+ struct ipa_mhi_connect_params_internal internal;
+
+ IPA_MHI_DBG("reading ch/ev context from host\n");
+ res = ipa_mhi_read_ch_ctx(channel);
+ if (res) {
+ IPA_MHI_ERR("ipa_mhi_read_ch_ctx failed %d\n", res);
+ goto fail_start_channel;
+ }
+
+ internal.channel_id = in->channel_id;
+ internal.sys = &in->sys;
+ internal.start.gsi.state = channel->state;
+ internal.start.gsi.msi = &ipa_mhi_client_ctx->msi;
+ internal.start.gsi.ev_ctx_host = &channel->ev_ctx_host;
+ internal.start.gsi.event_context_addr =
+ channel->event_context_addr;
+ internal.start.gsi.ch_ctx_host = &channel->ch_ctx_host;
+ internal.start.gsi.channel_context_addr =
+ channel->channel_context_addr;
+ internal.start.gsi.ch_err_cb = ipa_mhi_gsi_ch_err_cb;
+ internal.start.gsi.channel = (void *)channel;
+ internal.start.gsi.ev_err_cb = ipa_mhi_gsi_ev_err_cb;
+ internal.start.gsi.assert_bit40 =
+ ipa_mhi_client_ctx->assert_bit40;
+ internal.start.gsi.mhi = &channel->ch_scratch.mhi;
+ internal.start.gsi.cached_gsi_evt_ring_hdl =
+ &channel->cached_gsi_evt_ring_hdl;
+ internal.start.gsi.evchid =
+ channel->index + IPA_MHI_GSI_ER_START;
+
+ res = ipa_connect_mhi_pipe(&internal, clnt_hdl);
+ if (res) {
+ IPA_MHI_ERR("ipa_connect_mhi_pipe failed %d\n", res);
+ goto fail_connect_pipe;
+ }
+ channel->state = IPA_HW_MHI_CHANNEL_STATE_RUN;
+ channel->brstmode_enabled =
+ channel->ch_scratch.mhi.burst_mode_enabled;
+
+ res = ipa_mhi_read_write_host(IPA_MHI_DMA_TO_HOST,
+ &channel->state, channel->channel_context_addr +
+ offsetof(struct ipa_mhi_ch_ctx, chstate),
+ sizeof(channel->state));
+ if (res) {
+ IPA_MHI_ERR("ipa_mhi_read_write_host failed\n");
+ return res;
+
+ }
+ } else {
+ struct ipa_mhi_connect_params_internal internal;
+
+ internal.channel_id = in->channel_id;
+ internal.sys = &in->sys;
+ internal.start.uC.index = channel->index;
+ internal.start.uC.id = channel->id;
+ internal.start.uC.state = channel->state;
+ res = ipa_connect_mhi_pipe(&internal, clnt_hdl);
+ if (res) {
+ IPA_MHI_ERR("ipa_connect_mhi_pipe failed %d\n", res);
+ goto fail_connect_pipe;
+ }
+ channel->state = IPA_HW_MHI_CHANNEL_STATE_RUN;
+ }
+
+ if (!in->sys.keep_ipa_awake)
+ IPA_ACTIVE_CLIENTS_DEC_EP(in->sys.client);
+
+ IPA_MHI_FUNC_EXIT();
+
+ return 0;
+fail_connect_pipe:
+ ipa_mhi_reset_channel(channel);
+fail_start_channel:
+ IPA_ACTIVE_CLIENTS_DEC_EP(in->sys.client);
+ return -EPERM;
+}
+
+/**
+ * ipa_mhi_disconnect_pipe() - Disconnect pipe from IPA and reset corresponding
+ * MHI channel
+ * @clnt_hdl: client handle for this pipe
+ *
+ * This function is called by MHI client driver on MHI channel reset.
+ * This function is called after MHI channel was started.
+ * This function is doing the following:
+ * - Send command to uC/GSI to reset corresponding MHI channel
+ * - Configure IPA EP control
+ *
+ * Return codes: 0 : success
+ * negative : error
+ */
+int ipa_mhi_disconnect_pipe(u32 clnt_hdl)
+{
+ int res;
+ enum ipa_client_type client;
+ static struct ipa_mhi_channel_ctx *channel;
+
+ IPA_MHI_FUNC_ENTRY();
+
+ if (!ipa_mhi_client_ctx) {
+ IPA_MHI_ERR("IPA MHI was not initialized\n");
+ return -EINVAL;
+ }
+
+ client = ipa_get_client_mapping(clnt_hdl);
+
+ if (!IPA_CLIENT_IS_MHI(client)) {
+ IPA_MHI_ERR("invalid IPA MHI client, client: %d\n", client);
+ return -EINVAL;
+ }
+
+ channel = ipa_mhi_get_channel_context_by_clnt_hdl(clnt_hdl);
+ if (!channel) {
+ IPA_MHI_ERR("invalid clnt index\n");
+ return -EINVAL;
+ }
+
+ IPA_ACTIVE_CLIENTS_INC_EP(ipa_get_client_mapping(clnt_hdl));
+
+ res = ipa_mhi_reset_channel(channel);
+ if (res) {
+ IPA_MHI_ERR("ipa_mhi_reset_channel failed %d\n", res);
+ goto fail_reset_channel;
+ }
+
+ res = ipa_disconnect_mhi_pipe(clnt_hdl);
+ if (res) {
+ IPA_MHI_ERR(
+ "IPA core driver failed to disconnect the pipe hdl %d, res %d"
+ , clnt_hdl, res);
+ return res;
+ }
+
+ IPA_ACTIVE_CLIENTS_DEC_EP(ipa_get_client_mapping(clnt_hdl));
+
+ IPA_MHI_DBG("client (ep: %d) disconnected\n", clnt_hdl);
+ IPA_MHI_FUNC_EXIT();
+ return 0;
+fail_reset_channel:
+ IPA_ACTIVE_CLIENTS_DEC_EP(ipa_get_client_mapping(clnt_hdl));
+ return res;
+}
+
+static int ipa_mhi_wait_for_cons_release(void)
+{
+ unsigned long flags;
+ int res;
+
+ IPA_MHI_FUNC_ENTRY();
+ reinit_completion(&ipa_mhi_client_ctx->rm_cons_comp);
+ spin_lock_irqsave(&ipa_mhi_client_ctx->state_lock, flags);
+ if (ipa_mhi_client_ctx->rm_cons_state != IPA_MHI_RM_STATE_GRANTED) {
+ spin_unlock_irqrestore(&ipa_mhi_client_ctx->state_lock, flags);
+ return 0;
+ }
+ spin_unlock_irqrestore(&ipa_mhi_client_ctx->state_lock, flags);
+
+ res = wait_for_completion_timeout(
+ &ipa_mhi_client_ctx->rm_cons_comp,
+ msecs_to_jiffies(IPA_MHI_RM_TIMEOUT_MSEC));
+ if (res == 0) {
+ IPA_MHI_ERR("timeout release mhi cons\n");
+ return -ETIME;
+ }
+ IPA_MHI_FUNC_EXIT();
+ return 0;
+}
+
+static int ipa_mhi_suspend_channels(struct ipa_mhi_channel_ctx *channels)
+{
+ int i;
+ int res;
+
+ IPA_MHI_FUNC_ENTRY();
+ for (i = 0; i < IPA_MHI_MAX_UL_CHANNELS; i++) {
+ if (!channels[i].valid)
+ continue;
+ if (channels[i].state !=
+ IPA_HW_MHI_CHANNEL_STATE_RUN)
+ continue;
+ IPA_MHI_DBG("suspending channel %d\n",
+ channels[i].id);
+
+ if (ipa_get_transport_type() == IPA_TRANSPORT_TYPE_GSI)
+ res = ipa_mhi_suspend_gsi_channel(
+ &channels[i]);
+ else
+ res = ipa_uc_mhi_suspend_channel(
+ channels[i].index);
+
+ if (res) {
+ IPA_MHI_ERR("failed to suspend channel %d error %d\n",
+ i, res);
+ return res;
+ }
+ channels[i].state =
+ IPA_HW_MHI_CHANNEL_STATE_SUSPEND;
+ }
+
+ IPA_MHI_FUNC_EXIT();
+ return 0;
+}
+
+static int ipa_mhi_stop_event_update_channels(
+ struct ipa_mhi_channel_ctx *channels)
+{
+ int i;
+ int res;
+
+ if (ipa_get_transport_type() == IPA_TRANSPORT_TYPE_GSI)
+ return 0;
+
+ IPA_MHI_FUNC_ENTRY();
+ for (i = 0; i < IPA_MHI_MAX_UL_CHANNELS; i++) {
+ if (!channels[i].valid)
+ continue;
+ if (channels[i].state !=
+ IPA_HW_MHI_CHANNEL_STATE_SUSPEND)
+ continue;
+ IPA_MHI_DBG("stop update event channel %d\n",
+ channels[i].id);
+ res = ipa_uc_mhi_stop_event_update_channel(
+ channels[i].index);
+ if (res) {
+ IPA_MHI_ERR("failed stop event channel %d error %d\n",
+ i, res);
+ return res;
+ }
+ }
+
+ IPA_MHI_FUNC_EXIT();
+ return 0;
+}
+
+static bool ipa_mhi_check_pending_packets_from_host(void)
+{
+ int i;
+ int res;
+ struct ipa_mhi_channel_ctx *channel;
+
+ IPA_MHI_FUNC_ENTRY();
+ for (i = 0; i < IPA_MHI_MAX_UL_CHANNELS; i++) {
+ channel = &ipa_mhi_client_ctx->ul_channels[i];
+ if (!channel->valid)
+ continue;
+
+ res = ipa_mhi_query_ch_info(channel->client,
+ &channel->ch_info);
+ if (res) {
+ IPA_MHI_ERR("gsi_query_channel_info failed\n");
+ return true;
+ }
+ res = ipa_mhi_read_ch_ctx(channel);
+ if (res) {
+ IPA_MHI_ERR("ipa_mhi_read_ch_ctx failed %d\n", res);
+ return true;
+ }
+
+ if (channel->ch_info.rp != channel->ch_ctx_host.wp) {
+ IPA_MHI_DBG("There are pending packets from host\n");
+ IPA_MHI_DBG("device rp 0x%llx host 0x%llx\n",
+ channel->ch_info.rp, channel->ch_ctx_host.wp);
+
+ return true;
+ }
+ }
+
+ IPA_MHI_FUNC_EXIT();
+ return false;
+}
+
+static int ipa_mhi_resume_channels(bool LPTransitionRejected,
+ struct ipa_mhi_channel_ctx *channels)
+{
+ int i;
+ int res;
+ struct ipa_mhi_channel_ctx *channel;
+
+ IPA_MHI_FUNC_ENTRY();
+ for (i = 0; i < IPA_MHI_MAX_UL_CHANNELS; i++) {
+ if (!channels[i].valid)
+ continue;
+ if (channels[i].state !=
+ IPA_HW_MHI_CHANNEL_STATE_SUSPEND)
+ continue;
+ channel = &channels[i];
+ IPA_MHI_DBG("resuming channel %d\n", channel->id);
+
+ res = ipa_mhi_resume_channels_internal(channel->client,
+ LPTransitionRejected, channel->brstmode_enabled,
+ channel->ch_scratch, channel->index);
+
+ if (res) {
+ IPA_MHI_ERR("failed to resume channel %d error %d\n",
+ i, res);
+ return res;
+ }
+
+ channel->stop_in_proc = false;
+ channel->state = IPA_HW_MHI_CHANNEL_STATE_RUN;
+ }
+
+ IPA_MHI_FUNC_EXIT();
+ return 0;
+}
+
+/**
+ * ipa_mhi_suspend_ul() - Suspend MHI accelerated up link channels
+ * @force:
+ * false: in case of data pending in IPA, MHI channels will not be
+ * suspended and function will fail.
+ * true: in case of data pending in IPA, make sure no further access from
+ * IPA to PCIe is possible. In this case suspend cannot fail.
+ *
+ *
+ * This function is called by MHI client driver on MHI suspend.
+ * This function is called after MHI channel was started.
+ * When this function returns device can move to M1/M2/M3/D3cold state.
+ *
+ * Return codes: 0 : success
+ * negative : error
+ */
+static int ipa_mhi_suspend_ul(bool force, bool *empty, bool *force_clear)
+{
+ int res;
+
+ *force_clear = false;
+
+ res = ipa_mhi_suspend_channels(ipa_mhi_client_ctx->ul_channels);
+ if (res) {
+ IPA_MHI_ERR("ipa_mhi_suspend_ul_channels failed %d\n", res);
+ goto fail_suspend_ul_channel;
+ }
+
+ *empty = ipa_mhi_wait_for_ul_empty_timeout(
+ IPA_MHI_CH_EMPTY_TIMEOUT_MSEC);
+
+ if (!*empty) {
+ if (force) {
+ res = ipa_mhi_enable_force_clear(
+ ipa_mhi_client_ctx->qmi_req_id, false);
+ if (res) {
+ IPA_MHI_ERR("failed to enable force clear\n");
+ ipa_assert();
+ return res;
+ }
+ *force_clear = true;
+ IPA_MHI_DBG("force clear datapath enabled\n");
+
+ *empty = ipa_mhi_wait_for_ul_empty_timeout(
+ IPA_MHI_CH_EMPTY_TIMEOUT_MSEC);
+ IPA_MHI_DBG("empty=%d\n", *empty);
+ if (!*empty && ipa_get_transport_type()
+ == IPA_TRANSPORT_TYPE_GSI) {
+ IPA_MHI_ERR("Failed to suspend UL channels\n");
+ if (ipa_mhi_client_ctx->test_mode) {
+ res = -EAGAIN;
+ goto fail_suspend_ul_channel;
+ }
+
+ ipa_assert();
+ }
+ } else {
+ IPA_MHI_DBG("IPA not empty\n");
+ res = -EAGAIN;
+ goto fail_suspend_ul_channel;
+ }
+ }
+
+ if (*force_clear) {
+ res =
+ ipa_mhi_disable_force_clear(ipa_mhi_client_ctx->qmi_req_id);
+ if (res) {
+ IPA_MHI_ERR("failed to disable force clear\n");
+ ipa_assert();
+ return res;
+ }
+ IPA_MHI_DBG("force clear datapath disabled\n");
+ ipa_mhi_client_ctx->qmi_req_id++;
+ }
+
+ if (!force && ipa_get_transport_type() == IPA_TRANSPORT_TYPE_GSI) {
+ if (ipa_mhi_check_pending_packets_from_host()) {
+ res = -EAGAIN;
+ goto fail_suspend_ul_channel;
+ }
+ }
+
+ res = ipa_mhi_stop_event_update_channels(
+ ipa_mhi_client_ctx->ul_channels);
+ if (res) {
+ IPA_MHI_ERR(
+ "ipa_mhi_stop_event_update_ul_channels failed %d\n",
+ res);
+ goto fail_suspend_ul_channel;
+ }
+
+ return 0;
+
+fail_suspend_ul_channel:
+ return res;
+}
+
+static bool ipa_mhi_has_open_aggr_frame(void)
+{
+ struct ipa_mhi_channel_ctx *channel;
+ int i;
+
+ for (i = 0; i < IPA_MHI_MAX_DL_CHANNELS; i++) {
+ channel = &ipa_mhi_client_ctx->dl_channels[i];
+
+ if (!channel->valid)
+ continue;
+
+ if (ipa_has_open_aggr_frame(channel->client))
+ return true;
+ }
+
+ return false;
+}
+
+static void ipa_mhi_update_host_ch_state(bool update_rp)
+{
+ int i;
+ int res;
+ struct ipa_mhi_channel_ctx *channel;
+
+ for (i = 0; i < IPA_MHI_MAX_UL_CHANNELS; i++) {
+ channel = &ipa_mhi_client_ctx->ul_channels[i];
+ if (!channel->valid)
+ continue;
+
+ if (update_rp) {
+ res = ipa_mhi_query_ch_info(channel->client,
+ &channel->ch_info);
+ if (res) {
+ IPA_MHI_ERR("gsi_query_channel_info failed\n");
+ ipa_assert();
+ return;
+ }
+
+ res = ipa_mhi_read_write_host(IPA_MHI_DMA_TO_HOST,
+ &channel->ch_info.rp,
+ channel->channel_context_addr +
+ offsetof(struct ipa_mhi_ch_ctx, rp),
+ sizeof(channel->ch_info.rp));
+ if (res) {
+ IPA_MHI_ERR("ipa_mhi_read_write_host failed\n");
+ ipa_assert();
+ return;
+ }
+ }
+
+ res = ipa_mhi_read_write_host(IPA_MHI_DMA_TO_HOST,
+ &channel->state, channel->channel_context_addr +
+ offsetof(struct ipa_mhi_ch_ctx, chstate),
+ sizeof(((struct ipa_mhi_ch_ctx *)0)->chstate));
+ if (res) {
+ IPA_MHI_ERR("ipa_mhi_read_write_host failed\n");
+ ipa_assert();
+ return;
+ }
+ }
+
+ for (i = 0; i < IPA_MHI_MAX_DL_CHANNELS; i++) {
+ channel = &ipa_mhi_client_ctx->dl_channels[i];
+ if (!channel->valid)
+ continue;
+
+ if (update_rp) {
+ res = ipa_mhi_query_ch_info(channel->client,
+ &channel->ch_info);
+ if (res) {
+ IPA_MHI_ERR("gsi_query_channel_info failed\n");
+ ipa_assert();
+ return;
+ }
+
+ res = ipa_mhi_read_write_host(IPA_MHI_DMA_TO_HOST,
+ &channel->ch_info.rp,
+ channel->channel_context_addr +
+ offsetof(struct ipa_mhi_ch_ctx, rp),
+ sizeof(channel->ch_info.rp));
+ if (res) {
+ IPA_MHI_ERR("ipa_mhi_read_write_host failed\n");
+ ipa_assert();
+ return;
+ }
+ }
+
+ res = ipa_mhi_read_write_host(IPA_MHI_DMA_TO_HOST,
+ &channel->state, channel->channel_context_addr +
+ offsetof(struct ipa_mhi_ch_ctx, chstate),
+ sizeof(((struct ipa_mhi_ch_ctx *)0)->chstate));
+ if (res) {
+ IPA_MHI_ERR("ipa_mhi_read_write_host failed\n");
+ ipa_assert();
+ }
+ }
+}
+
+static int ipa_mhi_suspend_dl(bool force)
+{
+ int res;
+
+ res = ipa_mhi_suspend_channels(ipa_mhi_client_ctx->dl_channels);
+ if (res) {
+ IPA_MHI_ERR(
+ "ipa_mhi_suspend_channels for dl failed %d\n", res);
+ goto fail_suspend_dl_channel;
+ }
+
+ res = ipa_mhi_stop_event_update_channels
+ (ipa_mhi_client_ctx->dl_channels);
+ if (res) {
+ IPA_MHI_ERR("failed to stop event update on DL %d\n", res);
+ goto fail_stop_event_update_dl_channel;
+ }
+
+ if (ipa_get_transport_type() == IPA_TRANSPORT_TYPE_GSI) {
+ if (ipa_mhi_has_open_aggr_frame()) {
+ IPA_MHI_DBG("There is an open aggr frame\n");
+ if (force) {
+ ipa_mhi_client_ctx->trigger_wakeup = true;
+ } else {
+ res = -EAGAIN;
+ goto fail_stop_event_update_dl_channel;
+ }
+ }
+ }
+
+ if (ipa_get_transport_type() == IPA_TRANSPORT_TYPE_GSI)
+ ipa_mhi_update_host_ch_state(true);
+
+fail_stop_event_update_dl_channel:
+ ipa_mhi_resume_channels(true,
+ ipa_mhi_client_ctx->dl_channels);
+fail_suspend_dl_channel:
+ return res;
+}
+
+/**
+ * ipa_mhi_suspend() - Suspend MHI accelerated channels
+ * @force:
+ * false: in case of data pending in IPA, MHI channels will not be
+ * suspended and function will fail.
+ * true: in case of data pending in IPA, make sure no further access from
+ * IPA to PCIe is possible. In this case suspend cannot fail.
+ *
+ * This function is called by MHI client driver on MHI suspend.
+ * This function is called after MHI channel was started.
+ * When this function returns device can move to M1/M2/M3/D3cold state.
+ *
+ * Return codes: 0 : success
+ * negative : error
+ */
+int ipa_mhi_suspend(bool force)
+{
+ int res;
+ bool empty;
+ bool force_clear;
+
+ IPA_MHI_FUNC_ENTRY();
+
+ res = ipa_mhi_set_state(IPA_MHI_STATE_SUSPEND_IN_PROGRESS);
+ if (res) {
+ IPA_MHI_ERR("ipa_mhi_set_state failed %d\n", res);
+ return res;
+ }
+ res = ipa_mhi_suspend_ul(force, &empty, &force_clear);
+ if (res) {
+ IPA_MHI_ERR("ipa_mhi_suspend_ul failed %d\n", res);
+ goto fail_suspend_ul_channel;
+ }
+
+ /*
+ * hold IPA clocks and release them after all
+ * IPA RM resource are released to make sure tag process will not start
+ */
+ IPA_ACTIVE_CLIENTS_INC_SIMPLE();
+
+ IPA_MHI_DBG("release prod\n");
+ res = ipa_mhi_release_prod();
+ if (res) {
+ IPA_MHI_ERR("ipa_mhi_release_prod failed %d\n", res);
+ goto fail_release_prod;
+ }
+
+ IPA_MHI_DBG("wait for cons release\n");
+ res = ipa_mhi_wait_for_cons_release();
+ if (res) {
+ IPA_MHI_ERR("ipa_mhi_wait_for_cons_release failed %d\n", res);
+ goto fail_release_cons;
+ }
+
+ usleep_range(IPA_MHI_SUSPEND_SLEEP_MIN, IPA_MHI_SUSPEND_SLEEP_MAX);
+
+ res = ipa_mhi_suspend_dl(force);
+ if (res) {
+ IPA_MHI_ERR("ipa_mhi_suspend_dl failed %d\n", res);
+ goto fail_suspend_dl_channel;
+ }
+
+ if (!empty)
+ ipa_set_tag_process_before_gating(false);
+
+ res = ipa_mhi_set_state(IPA_MHI_STATE_SUSPENDED);
+ if (res) {
+ IPA_MHI_ERR("ipa_mhi_set_state failed %d\n", res);
+ goto fail_release_cons;
+ }
+
+ IPA_ACTIVE_CLIENTS_DEC_SIMPLE();
+ IPA_MHI_FUNC_EXIT();
+ return 0;
+
+fail_suspend_dl_channel:
+fail_release_cons:
+ ipa_mhi_request_prod();
+fail_release_prod:
+ IPA_ACTIVE_CLIENTS_DEC_SIMPLE();
+fail_suspend_ul_channel:
+ ipa_mhi_resume_channels(true, ipa_mhi_client_ctx->ul_channels);
+ ipa_mhi_set_state(IPA_MHI_STATE_STARTED);
+ if (force_clear) {
+ if (
+ ipa_mhi_disable_force_clear(ipa_mhi_client_ctx->qmi_req_id)) {
+ IPA_MHI_ERR("failed to disable force clear\n");
+ ipa_assert();
+ }
+ IPA_MHI_DBG("force clear datapath disabled\n");
+ ipa_mhi_client_ctx->qmi_req_id++;
+ }
+ return res;
+}
+
+/**
+ * ipa_mhi_resume() - Resume MHI accelerated channels
+ *
+ * This function is called by MHI client driver on MHI resume.
+ * This function is called after MHI channel was suspended.
+ * When this function returns device can move to M0 state.
+ * This function is doing the following:
+ * - Send command to uC/GSI to resume corresponding MHI channel
+ * - Request MHI_PROD in IPA RM
+ * - Resume data to IPA
+ *
+ * Return codes: 0 : success
+ * negative : error
+ */
+int ipa_mhi_resume(void)
+{
+ int res;
+ bool dl_channel_resumed = false;
+
+ IPA_MHI_FUNC_ENTRY();
+
+ res = ipa_mhi_set_state(IPA_MHI_STATE_RESUME_IN_PROGRESS);
+ if (res) {
+ IPA_MHI_ERR("ipa_mhi_set_state failed %d\n", res);
+ return res;
+ }
+
+ if (ipa_mhi_client_ctx->rm_cons_state == IPA_MHI_RM_STATE_REQUESTED) {
+ /* resume all DL channels */
+ res = ipa_mhi_resume_channels(false,
+ ipa_mhi_client_ctx->dl_channels);
+ if (res) {
+ IPA_MHI_ERR("ipa_mhi_resume_dl_channels failed %d\n",
+ res);
+ goto fail_resume_dl_channels;
+ }
+ dl_channel_resumed = true;
+
+ ipa_rm_notify_completion(IPA_RM_RESOURCE_GRANTED,
+ IPA_RM_RESOURCE_MHI_CONS);
+ ipa_mhi_client_ctx->rm_cons_state = IPA_MHI_RM_STATE_GRANTED;
+ }
+
+ res = ipa_mhi_request_prod();
+ if (res) {
+ IPA_MHI_ERR("ipa_mhi_request_prod failed %d\n", res);
+ goto fail_request_prod;
+ }
+
+ /* resume all UL channels */
+ res = ipa_mhi_resume_channels(false,
+ ipa_mhi_client_ctx->ul_channels);
+ if (res) {
+ IPA_MHI_ERR("ipa_mhi_resume_ul_channels failed %d\n", res);
+ goto fail_resume_ul_channels;
+ }
+
+ if (!dl_channel_resumed) {
+ res = ipa_mhi_resume_channels(false,
+ ipa_mhi_client_ctx->dl_channels);
+ if (res) {
+ IPA_MHI_ERR("ipa_mhi_resume_dl_channels failed %d\n",
+ res);
+ goto fail_resume_dl_channels2;
+ }
+ }
+
+ if (ipa_get_transport_type() == IPA_TRANSPORT_TYPE_GSI)
+ ipa_mhi_update_host_ch_state(false);
+
+ res = ipa_mhi_set_state(IPA_MHI_STATE_STARTED);
+ if (res) {
+ IPA_MHI_ERR("ipa_mhi_set_state failed %d\n", res);
+ goto fail_set_state;
+ }
+
+ IPA_MHI_FUNC_EXIT();
+ return 0;
+
+fail_set_state:
+ ipa_mhi_suspend_channels(ipa_mhi_client_ctx->dl_channels);
+fail_resume_dl_channels2:
+ ipa_mhi_suspend_channels(ipa_mhi_client_ctx->ul_channels);
+fail_resume_ul_channels:
+ ipa_mhi_release_prod();
+fail_request_prod:
+ ipa_mhi_suspend_channels(ipa_mhi_client_ctx->dl_channels);
+fail_resume_dl_channels:
+ ipa_mhi_set_state(IPA_MHI_STATE_SUSPENDED);
+ return res;
+}
+
+
+static int ipa_mhi_destroy_channels(struct ipa_mhi_channel_ctx *channels,
+ int num_of_channels)
+{
+ struct ipa_mhi_channel_ctx *channel;
+ int i, res;
+ u32 clnt_hdl;
+
+ for (i = 0; i < num_of_channels; i++) {
+ channel = &channels[i];
+ if (!channel->valid)
+ continue;
+ if (channel->state == IPA_HW_MHI_CHANNEL_STATE_INVALID)
+ continue;
+ if (channel->state != IPA_HW_MHI_CHANNEL_STATE_DISABLE) {
+ clnt_hdl = ipa_get_ep_mapping(channel->client);
+ IPA_MHI_DBG("disconnect pipe (ep: %d)\n", clnt_hdl);
+ res = ipa_mhi_disconnect_pipe(clnt_hdl);
+ if (res) {
+ IPA_MHI_ERR(
+ "failed to disconnect pipe %d, err %d\n"
+ , clnt_hdl, res);
+ goto fail;
+ }
+ }
+ res = ipa_mhi_destroy_channel(channel->client);
+ if (res) {
+ IPA_MHI_ERR(
+ "ipa_mhi_destroy_channel failed %d"
+ , res);
+ goto fail;
+ }
+ }
+ return 0;
+fail:
+ return res;
+}
+
+/**
+ * ipa_mhi_destroy_all_channels() - Destroy MHI IPA channels
+ *
+ * This function is called by IPA MHI client driver on MHI reset to destroy all
+ * IPA MHI channels.
+ */
+int ipa_mhi_destroy_all_channels(void)
+{
+ int res;
+
+ IPA_MHI_FUNC_ENTRY();
+ /* reset all UL and DL acc channels and its accociated event rings */
+ res = ipa_mhi_destroy_channels(ipa_mhi_client_ctx->ul_channels,
+ IPA_MHI_MAX_UL_CHANNELS);
+ if (res) {
+ IPA_MHI_ERR("ipa_mhi_destroy_channels(ul_channels) failed %d\n",
+ res);
+ return -EPERM;
+ }
+ IPA_MHI_DBG("All UL channels are disconnected\n");
+
+ res = ipa_mhi_destroy_channels(ipa_mhi_client_ctx->dl_channels,
+ IPA_MHI_MAX_DL_CHANNELS);
+ if (res) {
+ IPA_MHI_ERR("ipa_mhi_destroy_channels(dl_channels) failed %d\n",
+ res);
+ return -EPERM;
+ }
+ IPA_MHI_DBG("All DL channels are disconnected\n");
+
+ IPA_MHI_FUNC_EXIT();
+ return 0;
+}
+
+static void ipa_mhi_debugfs_destroy(void)
+{
+ debugfs_remove_recursive(dent);
+}
+
+/**
+ * ipa_mhi_destroy() - Destroy MHI IPA
+ *
+ * This function is called by MHI client driver on MHI reset to destroy all IPA
+ * MHI resources.
+ * When this function returns ipa_mhi can re-initialize.
+ */
+void ipa_mhi_destroy(void)
+{
+ int res;
+
+ IPA_MHI_FUNC_ENTRY();
+ if (!ipa_mhi_client_ctx) {
+ IPA_MHI_DBG("IPA MHI was not initialized, already destroyed\n");
+ return;
+ }
+ /* reset all UL and DL acc channels and its accociated event rings */
+ if (ipa_get_transport_type() == IPA_TRANSPORT_TYPE_GSI) {
+ res = ipa_mhi_destroy_all_channels();
+ if (res) {
+ IPA_MHI_ERR("ipa_mhi_destroy_all_channels failed %d\n",
+ res);
+ goto fail;
+ }
+ }
+ IPA_MHI_DBG("All channels are disconnected\n");
+
+ if (ipa_get_transport_type() == IPA_TRANSPORT_TYPE_SPS) {
+ IPA_MHI_DBG("cleanup uC MHI\n");
+ ipa_uc_mhi_cleanup();
+ }
+
+
+ if (ipa_mhi_client_ctx->state != IPA_MHI_STATE_INITIALIZED &&
+ ipa_mhi_client_ctx->state != IPA_MHI_STATE_READY) {
+ IPA_MHI_DBG("release prod\n");
+ res = ipa_mhi_release_prod();
+ if (res) {
+ IPA_MHI_ERR("ipa_mhi_release_prod failed %d\n", res);
+ goto fail;
+ }
+ IPA_MHI_DBG("wait for cons release\n");
+ res = ipa_mhi_wait_for_cons_release();
+ if (res) {
+ IPA_MHI_ERR("ipa_mhi_wait_for_cons_release failed %d\n",
+ res);
+ goto fail;
+ }
+ usleep_range(IPA_MHI_SUSPEND_SLEEP_MIN,
+ IPA_MHI_SUSPEND_SLEEP_MAX);
+
+ IPA_MHI_DBG("deleate dependency Q6_PROD->MHI_CONS\n");
+ res = ipa_rm_delete_dependency(IPA_RM_RESOURCE_Q6_PROD,
+ IPA_RM_RESOURCE_MHI_CONS);
+ if (res) {
+ IPA_MHI_ERR(
+ "Error deleting dependency %d->%d, res=%d\n"
+ , IPA_RM_RESOURCE_Q6_PROD,
+ IPA_RM_RESOURCE_MHI_CONS,
+ res);
+ goto fail;
+ }
+ IPA_MHI_DBG("deleate dependency MHI_PROD->Q6_CONS\n");
+ res = ipa_rm_delete_dependency(IPA_RM_RESOURCE_MHI_PROD,
+ IPA_RM_RESOURCE_Q6_CONS);
+ if (res) {
+ IPA_MHI_ERR(
+ "Error deleting dependency %d->%d, res=%d\n",
+ IPA_RM_RESOURCE_MHI_PROD,
+ IPA_RM_RESOURCE_Q6_CONS,
+ res);
+ goto fail;
+ }
+ }
+
+ res = ipa_rm_delete_resource(IPA_RM_RESOURCE_MHI_PROD);
+ if (res) {
+ IPA_MHI_ERR("Error deleting resource %d, res=%d\n",
+ IPA_RM_RESOURCE_MHI_PROD, res);
+ goto fail;
+ }
+
+ res = ipa_rm_delete_resource(IPA_RM_RESOURCE_MHI_CONS);
+ if (res) {
+ IPA_MHI_ERR("Error deleting resource %d, res=%d\n",
+ IPA_RM_RESOURCE_MHI_CONS, res);
+ goto fail;
+ }
+
+ ipa_mhi_debugfs_destroy();
+ destroy_workqueue(ipa_mhi_client_ctx->wq);
+ kfree(ipa_mhi_client_ctx);
+ ipa_mhi_client_ctx = NULL;
+ IPA_MHI_DBG("IPA MHI was reset, ready for re-init\n");
+
+ IPA_MHI_FUNC_EXIT();
+ return;
+fail:
+ ipa_assert();
+}
+
+/**
+ * ipa_mhi_init() - Initialize IPA MHI driver
+ * @params: initialization params
+ *
+ * This function is called by MHI client driver on boot to initialize IPA MHI
+ * Driver. When this function returns device can move to READY state.
+ * This function is doing the following:
+ * - Initialize MHI IPA internal data structures
+ * - Create IPA RM resources
+ * - Initialize debugfs
+ *
+ * Return codes: 0 : success
+ * negative : error
+ */
+int ipa_mhi_init(struct ipa_mhi_init_params *params)
+{
+ int res;
+ struct ipa_rm_create_params mhi_prod_params;
+ struct ipa_rm_create_params mhi_cons_params;
+
+ IPA_MHI_FUNC_ENTRY();
+
+ if (!params) {
+ IPA_MHI_ERR("null args\n");
+ return -EINVAL;
+ }
+
+ if (!params->notify) {
+ IPA_MHI_ERR("null notify function\n");
+ return -EINVAL;
+ }
+
+ if (ipa_mhi_client_ctx) {
+ IPA_MHI_ERR("already initialized\n");
+ return -EPERM;
+ }
+
+ IPA_MHI_DBG("notify = %pF priv = %p\n", params->notify, params->priv);
+ IPA_MHI_DBG("msi: addr_lo = 0x%x addr_hi = 0x%x\n",
+ params->msi.addr_low, params->msi.addr_hi);
+ IPA_MHI_DBG("msi: data = 0x%x mask = 0x%x\n",
+ params->msi.data, params->msi.mask);
+ IPA_MHI_DBG("mmio_addr = 0x%x\n", params->mmio_addr);
+ IPA_MHI_DBG("first_ch_idx = 0x%x\n", params->first_ch_idx);
+ IPA_MHI_DBG("first_er_idx = 0x%x\n", params->first_er_idx);
+ IPA_MHI_DBG("assert_bit40=%d\n", params->assert_bit40);
+ IPA_MHI_DBG("test_mode=%d\n", params->test_mode);
+
+ /* Initialize context */
+ ipa_mhi_client_ctx = kzalloc(sizeof(*ipa_mhi_client_ctx), GFP_KERNEL);
+ if (!ipa_mhi_client_ctx) {
+ IPA_MHI_ERR("no memory\n");
+ res = -EFAULT;
+ goto fail_alloc_ctx;
+ }
+
+ ipa_mhi_client_ctx->state = IPA_MHI_STATE_INITIALIZED;
+ ipa_mhi_client_ctx->cb_notify = params->notify;
+ ipa_mhi_client_ctx->cb_priv = params->priv;
+ ipa_mhi_client_ctx->rm_cons_state = IPA_MHI_RM_STATE_RELEASED;
+ init_completion(&ipa_mhi_client_ctx->rm_prod_granted_comp);
+ spin_lock_init(&ipa_mhi_client_ctx->state_lock);
+ init_completion(&ipa_mhi_client_ctx->rm_cons_comp);
+ ipa_mhi_client_ctx->msi = params->msi;
+ ipa_mhi_client_ctx->mmio_addr = params->mmio_addr;
+ ipa_mhi_client_ctx->first_ch_idx = params->first_ch_idx;
+ ipa_mhi_client_ctx->first_er_idx = params->first_er_idx;
+ ipa_mhi_client_ctx->qmi_req_id = 0;
+ ipa_mhi_client_ctx->use_ipadma = true;
+ ipa_mhi_client_ctx->assert_bit40 = !!params->assert_bit40;
+ ipa_mhi_client_ctx->test_mode = params->test_mode;
+
+ ipa_mhi_client_ctx->wq = create_singlethread_workqueue("ipa_mhi_wq");
+ if (!ipa_mhi_client_ctx->wq) {
+ IPA_MHI_ERR("failed to create workqueue\n");
+ res = -EFAULT;
+ goto fail_create_wq;
+ }
+
+ /* Create PROD in IPA RM */
+ memset(&mhi_prod_params, 0, sizeof(mhi_prod_params));
+ mhi_prod_params.name = IPA_RM_RESOURCE_MHI_PROD;
+ mhi_prod_params.floor_voltage = IPA_VOLTAGE_SVS;
+ mhi_prod_params.reg_params.notify_cb = ipa_mhi_rm_prod_notify;
+ res = ipa_rm_create_resource(&mhi_prod_params);
+ if (res) {
+ IPA_MHI_ERR("fail to create IPA_RM_RESOURCE_MHI_PROD\n");
+ goto fail_create_rm_prod;
+ }
+
+ /* Create CONS in IPA RM */
+ memset(&mhi_cons_params, 0, sizeof(mhi_cons_params));
+ mhi_cons_params.name = IPA_RM_RESOURCE_MHI_CONS;
+ mhi_cons_params.floor_voltage = IPA_VOLTAGE_SVS;
+ mhi_cons_params.request_resource = ipa_mhi_rm_cons_request;
+ mhi_cons_params.release_resource = ipa_mhi_rm_cons_release;
+ res = ipa_rm_create_resource(&mhi_cons_params);
+ if (res) {
+ IPA_MHI_ERR("fail to create IPA_RM_RESOURCE_MHI_CONS\n");
+ goto fail_create_rm_cons;
+ }
+
+ /* Initialize uC interface */
+ ipa_uc_mhi_init(ipa_mhi_uc_ready_cb,
+ ipa_mhi_uc_wakeup_request_cb);
+ if (ipa_uc_state_check() == 0)
+ ipa_mhi_set_state(IPA_MHI_STATE_READY);
+
+ /* Initialize debugfs */
+ ipa_mhi_debugfs_init();
+
+ IPA_MHI_FUNC_EXIT();
+ return 0;
+
+fail_create_rm_cons:
+ ipa_rm_delete_resource(IPA_RM_RESOURCE_MHI_PROD);
+fail_create_rm_prod:
+ destroy_workqueue(ipa_mhi_client_ctx->wq);
+fail_create_wq:
+ kfree(ipa_mhi_client_ctx);
+ ipa_mhi_client_ctx = NULL;
+fail_alloc_ctx:
+ return res;
+}
+
+static void ipa_mhi_cache_dl_ul_sync_info(
+ struct ipa_config_req_msg_v01 *config_req)
+{
+ ipa_cached_dl_ul_sync_info.params.isDlUlSyncEnabled = true;
+ ipa_cached_dl_ul_sync_info.params.UlAccmVal =
+ (config_req->ul_accumulation_time_limit_valid) ?
+ config_req->ul_accumulation_time_limit : 0;
+ ipa_cached_dl_ul_sync_info.params.ulMsiEventThreshold =
+ (config_req->ul_msi_event_threshold_valid) ?
+ config_req->ul_msi_event_threshold : 0;
+ ipa_cached_dl_ul_sync_info.params.dlMsiEventThreshold =
+ (config_req->dl_msi_event_threshold_valid) ?
+ config_req->dl_msi_event_threshold : 0;
+}
+
+/**
+ * ipa_mhi_handle_ipa_config_req() - hanle IPA CONFIG QMI message
+ *
+ * This function is called by by IPA QMI service to indicate that IPA CONFIG
+ * message was sent from modem. IPA MHI will update this information to IPA uC
+ * or will cache it until IPA MHI will be initialized.
+ *
+ * Return codes: 0 : success
+ * negative : error
+ */
+int ipa_mhi_handle_ipa_config_req(struct ipa_config_req_msg_v01 *config_req)
+{
+ IPA_MHI_FUNC_ENTRY();
+
+ if (ipa_get_transport_type() != IPA_TRANSPORT_TYPE_GSI) {
+ ipa_mhi_cache_dl_ul_sync_info(config_req);
+ if (ipa_mhi_client_ctx &&
+ ipa_mhi_client_ctx->state !=
+ IPA_MHI_STATE_INITIALIZED)
+ ipa_uc_mhi_send_dl_ul_sync_info(
+ &ipa_cached_dl_ul_sync_info);
+ }
+
+ IPA_MHI_FUNC_EXIT();
+ return 0;
+}
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("IPA MHI client driver");
* GNU General Public License for more details.
*/
+#include <linux/ipa_mhi.h>
+#include <linux/ipa_qmi_service_v01.h>
+
#ifndef _IPA_COMMON_I_H_
#define _IPA_COMMON_I_H_
#include <linux/ipc_logging.h>
ipa_assert();\
} while (0)
+#define IPA_CLIENT_IS_PROD(x) (x >= IPA_CLIENT_PROD && x < IPA_CLIENT_CONS)
+#define IPA_CLIENT_IS_CONS(x) (x >= IPA_CLIENT_CONS && x < IPA_CLIENT_MAX)
+
+#define IPA_GSI_CHANNEL_STOP_SLEEP_MIN_USEC (1000)
+#define IPA_GSI_CHANNEL_STOP_SLEEP_MAX_USEC (2000)
+
enum ipa_active_client_log_type {
EP,
SIMPLE,
enum ipa_active_client_log_type type;
};
+/**
+ * struct ipa_mem_buffer - IPA memory buffer
+ * @base: base
+ * @phys_base: physical base address
+ * @size: size of memory buffer
+ */
+struct ipa_mem_buffer {
+ void *base;
+ dma_addr_t phys_base;
+ u32 size;
+};
+
+/**
+ * enum ipa_hw_mhi_channel_states - MHI channel state machine
+ *
+ * Values are according to MHI specification
+ * @IPA_HW_MHI_CHANNEL_STATE_DISABLE: Channel is disabled and not processed by
+ * the host or device.
+ * @IPA_HW_MHI_CHANNEL_STATE_ENABLE: A channel is enabled after being
+ * initialized and configured by host, including its channel context and
+ * associated transfer ring. While this state, the channel is not active
+ * and the device does not process transfer.
+ * @IPA_HW_MHI_CHANNEL_STATE_RUN: The device processes transfers and doorbell
+ * for channels.
+ * @IPA_HW_MHI_CHANNEL_STATE_SUSPEND: Used to halt operations on the channel.
+ * The device does not process transfers for the channel in this state.
+ * This state is typically used to synchronize the transition to low power
+ * modes.
+ * @IPA_HW_MHI_CHANNEL_STATE_STOP: Used to halt operations on the channel.
+ * The device does not process transfers for the channel in this state.
+ * @IPA_HW_MHI_CHANNEL_STATE_ERROR: The device detected an error in an element
+ * from the transfer ring associated with the channel.
+ * @IPA_HW_MHI_CHANNEL_STATE_INVALID: Invalid state. Shall not be in use in
+ * operational scenario.
+ */
+enum ipa_hw_mhi_channel_states {
+ IPA_HW_MHI_CHANNEL_STATE_DISABLE = 0,
+ IPA_HW_MHI_CHANNEL_STATE_ENABLE = 1,
+ IPA_HW_MHI_CHANNEL_STATE_RUN = 2,
+ IPA_HW_MHI_CHANNEL_STATE_SUSPEND = 3,
+ IPA_HW_MHI_CHANNEL_STATE_STOP = 4,
+ IPA_HW_MHI_CHANNEL_STATE_ERROR = 5,
+ IPA_HW_MHI_CHANNEL_STATE_INVALID = 0xFF
+};
+
+/**
+ * Structure holding the parameters for IPA_CPU_2_HW_CMD_MHI_DL_UL_SYNC_INFO
+ * command. Parameters are sent as 32b immediate parameters.
+ * @isDlUlSyncEnabled: Flag to indicate if DL UL Syncronization is enabled
+ * @UlAccmVal: UL Timer Accumulation value (Period after which device will poll
+ * for UL data)
+ * @ulMsiEventThreshold: Threshold at which HW fires MSI to host for UL events
+ * @dlMsiEventThreshold: Threshold at which HW fires MSI to host for DL events
+ */
+union IpaHwMhiDlUlSyncCmdData_t {
+ struct IpaHwMhiDlUlSyncCmdParams_t {
+ u32 isDlUlSyncEnabled:8;
+ u32 UlAccmVal:8;
+ u32 ulMsiEventThreshold:8;
+ u32 dlMsiEventThreshold:8;
+ } params;
+ u32 raw32b;
+};
+
+struct ipa_mhi_ch_ctx {
+ u8 chstate;/*0-7*/
+ u8 brstmode:2;/*8-9*/
+ u8 pollcfg:6;/*10-15*/
+ u16 rsvd;/*16-31*/
+ u32 chtype;
+ u32 erindex;
+ u64 rbase;
+ u64 rlen;
+ u64 rp;
+ u64 wp;
+} __packed;
+
+struct ipa_mhi_ev_ctx {
+ u32 intmodc:16;
+ u32 intmodt:16;
+ u32 ertype;
+ u32 msivec;
+ u64 rbase;
+ u64 rlen;
+ u64 rp;
+ u64 wp;
+} __packed;
+
+struct ipa_mhi_init_uc_engine {
+ struct ipa_mhi_msi_info *msi;
+ u32 mmio_addr;
+ u32 host_ctrl_addr;
+ u32 host_data_addr;
+ u32 first_ch_idx;
+ u32 first_er_idx;
+ union IpaHwMhiDlUlSyncCmdData_t *ipa_cached_dl_ul_sync_info;
+};
+
+struct ipa_mhi_init_gsi_engine {
+ u32 first_ch_idx;
+};
+
+struct ipa_mhi_init_engine {
+ struct ipa_mhi_init_uc_engine uC;
+ struct ipa_mhi_init_gsi_engine gsi;
+};
+
+struct start_gsi_channel {
+ enum ipa_hw_mhi_channel_states state;
+ struct ipa_mhi_msi_info *msi;
+ struct ipa_mhi_ev_ctx *ev_ctx_host;
+ u64 event_context_addr;
+ struct ipa_mhi_ch_ctx *ch_ctx_host;
+ u64 channel_context_addr;
+ void (*ch_err_cb)(struct gsi_chan_err_notify *notify);
+ void (*ev_err_cb)(struct gsi_evt_err_notify *notify);
+ void *channel;
+ bool assert_bit40;
+ struct gsi_mhi_channel_scratch *mhi;
+ unsigned long *cached_gsi_evt_ring_hdl;
+ uint8_t evchid;
+};
+
+struct start_uc_channel {
+ enum ipa_hw_mhi_channel_states state;
+ u8 index;
+ u8 id;
+};
+
+struct start_mhi_channel {
+ struct start_uc_channel uC;
+ struct start_gsi_channel gsi;
+};
+
+struct ipa_mhi_connect_params_internal {
+ struct ipa_sys_connect_params *sys;
+ u8 channel_id;
+ struct start_mhi_channel start;
+};
+
extern const char *ipa_clients_strings[];
#define IPA_IPC_LOGGING(buf, fmt, args...) \
void *ipa_get_ipc_logbuf_low(void);
void ipa_assert(void);
+/* MHI */
+int ipa_mhi_init_engine(struct ipa_mhi_init_engine *params);
+int ipa_connect_mhi_pipe(struct ipa_mhi_connect_params_internal *in,
+ u32 *clnt_hdl);
+int ipa_disconnect_mhi_pipe(u32 clnt_hdl);
+bool ipa_mhi_stop_gsi_channel(enum ipa_client_type client);
+int ipa_qmi_enable_force_clear_datapath_send(
+ struct ipa_enable_force_clear_datapath_req_msg_v01 *req);
+int ipa_qmi_disable_force_clear_datapath_send(
+ struct ipa_disable_force_clear_datapath_req_msg_v01 *req);
+int ipa_generate_tag_process(void);
+int ipa_disable_sps_pipe(enum ipa_client_type client);
+int ipa_mhi_reset_channel_internal(enum ipa_client_type client);
+int ipa_mhi_start_channel_internal(enum ipa_client_type client);
+bool ipa_mhi_sps_channel_empty(enum ipa_client_type client);
+int ipa_mhi_resume_channels_internal(enum ipa_client_type client,
+ bool LPTransitionRejected, bool brstmode_enabled,
+ union __packed gsi_channel_scratch ch_scratch, u8 index);
+int ipa_mhi_handle_ipa_config_req(struct ipa_config_req_msg_v01 *config_req);
+int ipa_mhi_query_ch_info(enum ipa_client_type client,
+ struct gsi_chan_info *ch_info);
+int ipa_mhi_destroy_channel(enum ipa_client_type client);
+
+/* MHI uC */
+int ipa_uc_mhi_send_dl_ul_sync_info(union IpaHwMhiDlUlSyncCmdData_t *cmd);
+int ipa_uc_mhi_init
+ (void (*ready_cb)(void), void (*wakeup_request_cb)(void));
+void ipa_uc_mhi_cleanup(void);
+int ipa_uc_mhi_reset_channel(int channelHandle);
+int ipa_uc_mhi_suspend_channel(int channelHandle);
+int ipa_uc_mhi_stop_event_update_channel(int channelHandle);
+int ipa_uc_mhi_print_stats(char *dbg_buff, int size);
+
+/* uC */
+int ipa_uc_state_check(void);
+
+/* general */
+void ipa_get_holb(int ep_idx, struct ipa_ep_cfg_holb *holb);
+void ipa_set_tag_process_before_gating(bool val);
+bool ipa_has_open_aggr_frame(enum ipa_client_type client);
#endif /* _IPA_COMMON_I_H_ */
#define IPA_LAN_RX_HDR_NAME "ipa_lan_hdr"
#define IPA_INVALID_L4_PROTOCOL 0xFF
-#define IPA_CLIENT_IS_PROD(x) (x >= IPA_CLIENT_PROD && x < IPA_CLIENT_CONS)
-#define IPA_CLIENT_IS_CONS(x) (x >= IPA_CLIENT_CONS && x < IPA_CLIENT_MAX)
#define IPA_SETFIELD(val, shift, mask) (((val) << (shift)) & (mask))
#define IPA_SETFIELD_IN_REG(reg, val, shift, mask) \
(reg |= ((val) << (shift)) & (mask))
};
/**
- * struct ipa_mem_buffer - IPA memory buffer
- * @base: base
- * @phys_base: physical base address
- * @size: size of memory buffer
- */
-struct ipa_mem_buffer {
- void *base;
- dma_addr_t phys_base;
- u32 size;
-};
-
-/**
* struct ipa_flt_entry - IPA filtering table entry
* @link: entry's link in global filtering enrties list
* @rule: filter rule
};
/**
- * enum ipa_hw_mhi_channel_states - MHI channel state machine
- *
- * Values are according to MHI specification
- * @IPA_HW_MHI_CHANNEL_STATE_DISABLE: Channel is disabled and not processed by
- * the host or device.
- * @IPA_HW_MHI_CHANNEL_STATE_ENABLE: A channel is enabled after being
- * initialized and configured by host, including its channel context and
- * associated transfer ring. While this state, the channel is not active
- * and the device does not process transfer.
- * @IPA_HW_MHI_CHANNEL_STATE_RUN: The device processes transfers and doorbell
- * for channels.
- * @IPA_HW_MHI_CHANNEL_STATE_SUSPEND: Used to halt operations on the channel.
- * The device does not process transfers for the channel in this state.
- * This state is typically used to synchronize the transition to low power
- * modes.
- * @IPA_HW_MHI_CHANNEL_STATE_STOP: Used to halt operations on the channel.
- * The device does not process transfers for the channel in this state.
- * @IPA_HW_MHI_CHANNEL_STATE_ERROR: The device detected an error in an element
- * from the transfer ring associated with the channel.
- * @IPA_HW_MHI_CHANNEL_STATE_INVALID: Invalid state. Shall not be in use in
- * operational scenario.
- */
-enum ipa_hw_mhi_channel_states {
- IPA_HW_MHI_CHANNEL_STATE_DISABLE = 0,
- IPA_HW_MHI_CHANNEL_STATE_ENABLE = 1,
- IPA_HW_MHI_CHANNEL_STATE_RUN = 2,
- IPA_HW_MHI_CHANNEL_STATE_SUSPEND = 3,
- IPA_HW_MHI_CHANNEL_STATE_STOP = 4,
- IPA_HW_MHI_CHANNEL_STATE_ERROR = 5,
- IPA_HW_MHI_CHANNEL_STATE_INVALID = 0xFF
-};
-
-/**
- * Structure holding the parameters for IPA_CPU_2_HW_CMD_MHI_DL_UL_SYNC_INFO
- * command. Parameters are sent as 32b immediate parameters.
- * @isDlUlSyncEnabled: Flag to indicate if DL UL Syncronization is enabled
- * @UlAccmVal: UL Timer Accumulation value (Period after which device will poll
- * for UL data)
- * @ulMsiEventThreshold: Threshold at which HW fires MSI to host for UL events
- * @dlMsiEventThreshold: Threshold at which HW fires MSI to host for DL events
- */
-union IpaHwMhiDlUlSyncCmdData_t {
- struct IpaHwMhiDlUlSyncCmdParams_t {
- u32 isDlUlSyncEnabled:8;
- u32 UlAccmVal:8;
- u32 ulMsiEventThreshold:8;
- u32 dlMsiEventThreshold:8;
- } params;
- u32 raw32b;
-};
-
-/**
* struct ipa_uc_ctx - IPA uC context
* @uc_inited: Indicates if uC interface has been initialized
* @uc_loaded: Indicates if uC has loaded
void ipa2_dma_destroy(void);
/*
- * MHI
+ * MHI APIs for IPA MHI client driver
*/
-int ipa2_mhi_init(struct ipa_mhi_init_params *params);
+int ipa2_init_mhi(struct ipa_mhi_init_params *params);
-int ipa2_mhi_start(struct ipa_mhi_start_params *params);
+int ipa2_mhi_init_engine(struct ipa_mhi_init_engine *params);
-int ipa2_mhi_connect_pipe(struct ipa_mhi_connect_params *in, u32 *clnt_hdl);
+int ipa2_connect_mhi_pipe(struct ipa_mhi_connect_params_internal *in,
+ u32 *clnt_hdl);
-int ipa2_mhi_disconnect_pipe(u32 clnt_hdl);
+int ipa2_disconnect_mhi_pipe(u32 clnt_hdl);
-int ipa2_mhi_suspend(bool force);
+bool ipa2_mhi_sps_channel_empty(enum ipa_client_type client);
-int ipa2_mhi_resume(void);
+int ipa2_disable_sps_pipe(enum ipa_client_type client);
-void ipa2_mhi_destroy(void);
+int ipa2_mhi_reset_channel_internal(enum ipa_client_type client);
+
+int ipa2_mhi_start_channel_internal(enum ipa_client_type client);
+
+int ipa2_mhi_suspend_ul_channels(void);
+
+int ipa2_mhi_resume_channels_internal(enum ipa_client_type client,
+ bool LPTransitionRejected, bool brstmode_enabled,
+ union __packed gsi_channel_scratch ch_scratch, u8 index);
/*
* mux id
int ipa_sps_connect_safe(struct sps_pipe *h, struct sps_connect *connect,
enum ipa_client_type ipa_client);
-int ipa_mhi_handle_ipa_config_req(struct ipa_config_req_msg_v01 *config_req);
-
int ipa_uc_interface_init(void);
int ipa_uc_reset_pipe(enum ipa_client_type ipa_client);
int ipa_uc_monitor_holb(enum ipa_client_type ipa_client, bool enable);
-int ipa_uc_state_check(void);
+int ipa2_uc_state_check(void);
int ipa_uc_loaded_check(void);
int ipa_uc_send_cmd(u32 cmd, u32 opcode, u32 expected_status,
bool polling_mode, unsigned long timeout_jiffies);
int ipa_uc_update_hw_flags(u32 flags);
-int ipa_uc_mhi_init(void (*ready_cb)(void), void (*wakeup_request_cb)(void));
-int ipa_uc_mhi_send_dl_ul_sync_info(union IpaHwMhiDlUlSyncCmdData_t cmd);
+int ipa2_uc_mhi_init(void (*ready_cb)(void), void (*wakeup_request_cb)(void));
+void ipa2_uc_mhi_cleanup(void);
+int ipa2_uc_mhi_send_dl_ul_sync_info(union IpaHwMhiDlUlSyncCmdData_t *cmd);
int ipa_uc_mhi_init_engine(struct ipa_mhi_msi_info *msi, u32 mmio_addr,
u32 host_ctrl_addr, u32 host_data_addr, u32 first_ch_idx,
u32 first_evt_idx);
int ipa_uc_mhi_init_channel(int ipa_ep_idx, int channelHandle,
int contexArrayIndex, int channelDirection);
-int ipa_uc_mhi_reset_channel(int channelHandle);
-int ipa_uc_mhi_suspend_channel(int channelHandle);
+int ipa2_uc_mhi_reset_channel(int channelHandle);
+int ipa2_uc_mhi_suspend_channel(int channelHandle);
int ipa_uc_mhi_resume_channel(int channelHandle, bool LPTransitionRejected);
-int ipa_uc_mhi_stop_event_update_channel(int channelHandle);
-int ipa_uc_mhi_print_stats(char *dbg_buff, int size);
+int ipa2_uc_mhi_stop_event_update_channel(int channelHandle);
+int ipa2_uc_mhi_print_stats(char *dbg_buff, int size);
int ipa_uc_memcpy(phys_addr_t dest, phys_addr_t src, int len);
u32 ipa_get_num_pipes(void);
u32 ipa_get_sys_yellow_wm(void);
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/ipa.h>
+#include <linux/ipa_mhi.h>
#include "ipa_i.h"
#include "ipa_qmi_service.h"
#define IPA_MHI_FUNC_EXIT() \
IPA_MHI_DBG("EXIT\n")
-#define IPA_MHI_RM_TIMEOUT_MSEC 10000
-
-#define IPA_MHI_BAM_EMPTY_TIMEOUT_MSEC 5
-
-#define IPA_MHI_MAX_UL_CHANNELS 1
-#define IPA_MHI_MAX_DL_CHANNELS 1
-
-#define IPA_MHI_SUSPEND_SLEEP_MIN 900
-#define IPA_MHI_SUSPEND_SLEEP_MAX 1100
-
-enum ipa_mhi_state {
- IPA_MHI_STATE_INITIALIZED,
- IPA_MHI_STATE_READY,
- IPA_MHI_STATE_STARTED,
- IPA_MHI_STATE_SUSPEND_IN_PROGRESS,
- IPA_MHI_STATE_SUSPENDED,
- IPA_MHI_STATE_RESUME_IN_PROGRESS,
- IPA_MHI_STATE_MAX
-};
-
-static char *ipa_mhi_state_str[] = {
- __stringify(IPA_MHI_STATE_INITIALIZED),
- __stringify(IPA_MHI_STATE_READY),
- __stringify(IPA_MHI_STATE_STARTED),
- __stringify(IPA_MHI_STATE_SUSPEND_IN_PROGRESS),
- __stringify(IPA_MHI_STATE_SUSPENDED),
- __stringify(IPA_MHI_STATE_RESUME_IN_PROGRESS),
-};
-
-#define MHI_STATE_STR(state) \
- (((state) >= 0 && (state) < IPA_MHI_STATE_MAX) ? \
- ipa_mhi_state_str[(state)] : \
- "INVALID")
-
-/**
- * struct ipa_mhi_channel_ctx - MHI Channel context
- * @valid: entry is valid
- * @id: MHI channel ID
- * @hdl: channel handle for uC
- * @client: IPA Client
- * @state: Channel state
- */
-struct ipa_mhi_channel_ctx {
- bool valid;
- u8 id;
- u8 hdl;
- enum ipa_client_type client;
- enum ipa_hw_mhi_channel_states state;
-};
-
-enum ipa_mhi_rm_state {
- IPA_MHI_RM_STATE_RELEASED,
- IPA_MHI_RM_STATE_REQUESTED,
- IPA_MHI_RM_STATE_GRANTED,
- IPA_MHI_RM_STATE_MAX
-};
-
-/**
- * struct ipa_mhi_ctx - IPA MHI context
- * @state: IPA MHI state
- * @state_lock: lock for state read/write operations
- * @msi: Message Signaled Interrupts parameters
- * @mmio_addr: MHI MMIO physical address
- * @first_ch_idx: First channel ID for hardware accelerated channels.
- * @first_er_idx: First event ring ID for hardware accelerated channels.
- * @host_ctrl_addr: Base address of MHI control data structures
- * @host_data_addr: Base address of MHI data buffers
- * @cb_notify: client callback
- * @cb_priv: client private data to be provided in client callback
- * @ul_channels: IPA MHI uplink channel contexts
- * @dl_channels: IPA MHI downlink channel contexts
- * @total_channels: Total number of channels ever connected to IPA MHI
- * @rm_prod_granted_comp: Completion object for MHI producer resource in IPA RM
- * @rm_cons_state: MHI consumer resource state in IPA RM
- * @rm_cons_comp: Completion object for MHI consumer resource in IPA RM
- * @trigger_wakeup: trigger wakeup callback ?
- * @wakeup_notified: MHI Client wakeup function was called
- * @wq: workqueue for wakeup event
- * @qmi_req_id: QMI request unique id
- */
-struct ipa_mhi_ctx {
- enum ipa_mhi_state state;
- spinlock_t state_lock;
- struct ipa_mhi_msi_info msi;
- u32 mmio_addr;
- u32 first_ch_idx;
- u32 first_er_idx;
- u32 host_ctrl_addr;
- u32 host_data_addr;
- mhi_client_cb cb_notify;
- void *cb_priv;
- struct ipa_mhi_channel_ctx ul_channels[IPA_MHI_MAX_UL_CHANNELS];
- struct ipa_mhi_channel_ctx dl_channels[IPA_MHI_MAX_DL_CHANNELS];
- u32 total_channels;
- struct completion rm_prod_granted_comp;
- enum ipa_mhi_rm_state rm_cons_state;
- struct completion rm_cons_comp;
- bool trigger_wakeup;
- bool wakeup_notified;
- struct workqueue_struct *wq;
- u32 qmi_req_id;
-};
-
-static struct ipa_mhi_ctx *ipa_mhi_ctx;
-
-static void ipa_mhi_wq_notify_wakeup(struct work_struct *work);
-static DECLARE_WORK(ipa_mhi_notify_wakeup_work, ipa_mhi_wq_notify_wakeup);
-
-static void ipa_mhi_wq_notify_ready(struct work_struct *work);
-static DECLARE_WORK(ipa_mhi_notify_ready_work, ipa_mhi_wq_notify_ready);
-
-static union IpaHwMhiDlUlSyncCmdData_t cached_dl_ul_sync_info;
-
-#ifdef CONFIG_DEBUG_FS
-#define IPA_MHI_MAX_MSG_LEN 512
-static char dbg_buff[IPA_MHI_MAX_MSG_LEN];
-static struct dentry *dent;
-
-static char *ipa_mhi_channel_state_str[] = {
- __stringify(IPA_HW_MHI_CHANNEL_STATE_DISABLE),
- __stringify(IPA_HW_MHI_CHANNEL_STATE_ENABLE),
- __stringify(IPA_HW_MHI_CHANNEL_STATE_RUN),
- __stringify(IPA_HW_MHI_CHANNEL_STATE_SUSPEND),
- __stringify(IPA_HW_MHI_CHANNEL_STATE_STOP),
- __stringify(IPA_HW_MHI_CHANNEL_STATE_ERROR),
-};
-
-#define MHI_CH_STATE_STR(state) \
- (((state) >= 0 && (state) <= IPA_HW_MHI_CHANNEL_STATE_ERROR) ? \
- ipa_mhi_channel_state_str[(state)] : \
- "INVALID")
-
-static ssize_t ipa_mhi_debugfs_stats(struct file *file,
- char __user *ubuf,
- size_t count,
- loff_t *ppos)
-{
- int nbytes = 0;
- int i;
- struct ipa_mhi_channel_ctx *channel;
-
- nbytes += scnprintf(&dbg_buff[nbytes],
- IPA_MHI_MAX_MSG_LEN - nbytes,
- "IPA MHI state: %s\n", MHI_STATE_STR(ipa_mhi_ctx->state));
-
- for (i = 0; i < IPA_MHI_MAX_UL_CHANNELS; i++) {
- channel = &ipa_mhi_ctx->ul_channels[i];
- nbytes += scnprintf(&dbg_buff[nbytes],
- IPA_MHI_MAX_MSG_LEN - nbytes,
- "channel %d: ", i);
- if (channel->valid) {
- nbytes += scnprintf(&dbg_buff[nbytes],
- IPA_MHI_MAX_MSG_LEN - nbytes,
- "ch_id=%d client=%d state=%s",
- channel->id, channel->client,
- MHI_CH_STATE_STR(channel->state));
- } else {
- nbytes += scnprintf(&dbg_buff[nbytes],
- IPA_MHI_MAX_MSG_LEN - nbytes,
- "never connected");
- }
-
- nbytes += scnprintf(&dbg_buff[nbytes],
- IPA_MHI_MAX_MSG_LEN - nbytes, "\n");
- }
-
- for (i = 0; i < IPA_MHI_MAX_DL_CHANNELS; i++) {
- channel = &ipa_mhi_ctx->dl_channels[i];
- nbytes += scnprintf(&dbg_buff[nbytes],
- IPA_MHI_MAX_MSG_LEN - nbytes,
- "channel %d: ", i);
- if (channel->valid) {
- nbytes += scnprintf(&dbg_buff[nbytes],
- IPA_MHI_MAX_MSG_LEN - nbytes,
- "ch_id=%d client=%d state=%s",
- channel->id, channel->client,
- MHI_CH_STATE_STR(channel->state));
- } else {
- nbytes += scnprintf(&dbg_buff[nbytes],
- IPA_MHI_MAX_MSG_LEN - nbytes,
- "never connected");
- }
-
- nbytes += scnprintf(&dbg_buff[nbytes],
- IPA_MHI_MAX_MSG_LEN - nbytes, "\n");
- }
-
- return simple_read_from_buffer(ubuf, count, ppos, dbg_buff, nbytes);
-}
-
-static ssize_t ipa_mhi_debugfs_uc_stats(struct file *file,
- char __user *ubuf,
- size_t count,
- loff_t *ppos)
-{
- int nbytes = 0;
-
- nbytes += ipa_uc_mhi_print_stats(dbg_buff, IPA_MHI_MAX_MSG_LEN);
-
- return simple_read_from_buffer(ubuf, count, ppos, dbg_buff, nbytes);
-}
-
-const struct file_operations ipa_mhi_stats_ops = {
- .read = ipa_mhi_debugfs_stats,
-};
-
-const struct file_operations ipa_mhi_uc_stats_ops = {
- .read = ipa_mhi_debugfs_uc_stats,
-};
-
-static void ipa_mhi_debugfs_init(void)
+bool ipa2_mhi_sps_channel_empty(enum ipa_client_type client)
{
- const mode_t read_only_mode = S_IRUSR | S_IRGRP | S_IROTH;
- struct dentry *file;
-
- IPA_MHI_FUNC_ENTRY();
-
- dent = debugfs_create_dir("ipa_mhi", 0);
- if (IS_ERR(dent)) {
- IPA_MHI_ERR("fail to create folder ipa_mhi\n");
- return;
- }
-
- file = debugfs_create_file("stats", read_only_mode, dent,
- 0, &ipa_mhi_stats_ops);
- if (!file || IS_ERR(file)) {
- IPA_MHI_ERR("fail to create file stats\n");
- goto fail;
- }
-
- file = debugfs_create_file("uc_stats", read_only_mode, dent,
- 0, &ipa_mhi_uc_stats_ops);
- if (!file || IS_ERR(file)) {
- IPA_MHI_ERR("fail to create file stats\n");
- goto fail;
- }
-
- IPA_MHI_FUNC_EXIT();
- return;
-fail:
- debugfs_remove_recursive(dent);
-}
-
-static void ipa_mhi_debugfs_destroy(void)
-{
- debugfs_remove_recursive(dent);
-}
-
-#else
-static void ipa_mhi_debugfs_init(void) {}
-static void ipa_mhi_debugfs_destroy(void) {}
-#endif /* CONFIG_DEBUG_FS */
-
-
-static void ipa_mhi_cache_dl_ul_sync_info(
- struct ipa_config_req_msg_v01 *config_req)
-{
- cached_dl_ul_sync_info.params.isDlUlSyncEnabled = true;
- cached_dl_ul_sync_info.params.UlAccmVal =
- (config_req->ul_accumulation_time_limit_valid) ?
- config_req->ul_accumulation_time_limit : 0;
- cached_dl_ul_sync_info.params.ulMsiEventThreshold =
- (config_req->ul_msi_event_threshold_valid) ?
- config_req->ul_msi_event_threshold : 0;
- cached_dl_ul_sync_info.params.dlMsiEventThreshold =
- (config_req->dl_msi_event_threshold_valid) ?
- config_req->dl_msi_event_threshold : 0;
-}
-
-/**
- * ipa_mhi_wq_notify_wakeup() - Notify MHI client on data available
- *
- * This function is called from IPA MHI workqueue to notify
- * MHI client driver on data available event.
- */
-static void ipa_mhi_wq_notify_wakeup(struct work_struct *work)
-{
- IPA_MHI_FUNC_ENTRY();
- ipa_mhi_ctx->cb_notify(ipa_mhi_ctx->cb_priv,
- IPA_MHI_EVENT_DATA_AVAILABLE, 0);
- IPA_MHI_FUNC_EXIT();
-}
-
-/**
- * ipa_mhi_notify_wakeup() - Schedule work to notify data available
- *
- * This function will schedule a work to notify data available event.
- * In case this function is called more than once, only one notification will
- * be sent to MHI client driver. No further notifications will be sent until
- * IPA MHI state will become STARTED.
- */
-static void ipa_mhi_notify_wakeup(void)
-{
- IPA_MHI_FUNC_ENTRY();
- if (ipa_mhi_ctx->wakeup_notified) {
- IPADBG("wakeup already called\n");
- return;
- }
- queue_work(ipa_mhi_ctx->wq, &ipa_mhi_notify_wakeup_work);
- ipa_mhi_ctx->wakeup_notified = true;
- IPA_MHI_FUNC_EXIT();
-}
-
-/**
- * ipa_mhi_wq_notify_ready() - Notify MHI client on ready
- *
- * This function is called from IPA MHI workqueue to notify
- * MHI client driver on ready event when IPA uC is loaded
- */
-static void ipa_mhi_wq_notify_ready(struct work_struct *work)
-{
- IPA_MHI_FUNC_ENTRY();
- ipa_mhi_ctx->cb_notify(ipa_mhi_ctx->cb_priv,
- IPA_MHI_EVENT_READY, 0);
- IPA_MHI_FUNC_EXIT();
-}
-
-/**
- * ipa_mhi_notify_ready() - Schedule work to notify ready
- *
- * This function will schedule a work to notify ready event.
- */
-static void ipa_mhi_notify_ready(void)
-{
- IPA_MHI_FUNC_ENTRY();
- queue_work(ipa_mhi_ctx->wq, &ipa_mhi_notify_ready_work);
- IPA_MHI_FUNC_EXIT();
-}
-
-/**
- * ipa_mhi_set_state() - Set new state to IPA MHI
- * @state: new state
- *
- * Sets a new state to IPA MHI if possible according to IPA MHI state machine.
- * In some state transitions a wakeup request will be triggered.
- *
- * Returns: 0 on success, -1 otherwise
- */
-static int ipa_mhi_set_state(enum ipa_mhi_state new_state)
-{
- unsigned long flags;
- int res = -EPERM;
-
- spin_lock_irqsave(&ipa_mhi_ctx->state_lock, flags);
- IPA_MHI_DBG("Current state: %s\n", MHI_STATE_STR(ipa_mhi_ctx->state));
-
- switch (ipa_mhi_ctx->state) {
- case IPA_MHI_STATE_INITIALIZED:
- if (new_state == IPA_MHI_STATE_READY) {
- ipa_mhi_notify_ready();
- res = 0;
- }
- break;
-
- case IPA_MHI_STATE_READY:
- if (new_state == IPA_MHI_STATE_READY)
- res = 0;
- if (new_state == IPA_MHI_STATE_STARTED)
- res = 0;
- break;
-
- case IPA_MHI_STATE_STARTED:
- if (new_state == IPA_MHI_STATE_INITIALIZED)
- res = 0;
- else if (new_state == IPA_MHI_STATE_SUSPEND_IN_PROGRESS)
- res = 0;
- break;
-
- case IPA_MHI_STATE_SUSPEND_IN_PROGRESS:
- if (new_state == IPA_MHI_STATE_SUSPENDED) {
- if (ipa_mhi_ctx->trigger_wakeup) {
- ipa_mhi_ctx->trigger_wakeup = false;
- ipa_mhi_notify_wakeup();
- }
- res = 0;
- } else if (new_state == IPA_MHI_STATE_STARTED) {
- ipa_mhi_ctx->wakeup_notified = false;
- if (ipa_mhi_ctx->rm_cons_state ==
- IPA_MHI_RM_STATE_REQUESTED) {
- ipa_rm_notify_completion(
- IPA_RM_RESOURCE_GRANTED,
- IPA_RM_RESOURCE_MHI_CONS);
- ipa_mhi_ctx->rm_cons_state =
- IPA_MHI_RM_STATE_GRANTED;
- }
- res = 0;
- }
- break;
-
- case IPA_MHI_STATE_SUSPENDED:
- if (new_state == IPA_MHI_STATE_RESUME_IN_PROGRESS)
- res = 0;
- break;
-
- case IPA_MHI_STATE_RESUME_IN_PROGRESS:
- if (new_state == IPA_MHI_STATE_SUSPENDED) {
- if (ipa_mhi_ctx->trigger_wakeup) {
- ipa_mhi_ctx->trigger_wakeup = false;
- ipa_mhi_notify_wakeup();
- }
- res = 0;
- } else if (new_state == IPA_MHI_STATE_STARTED) {
- ipa_mhi_ctx->wakeup_notified = false;
- if (ipa_mhi_ctx->rm_cons_state ==
- IPA_MHI_RM_STATE_REQUESTED) {
- ipa_rm_notify_completion(
- IPA_RM_RESOURCE_GRANTED,
- IPA_RM_RESOURCE_MHI_CONS);
- ipa_mhi_ctx->rm_cons_state =
- IPA_MHI_RM_STATE_GRANTED;
- }
- res = 0;
- }
- break;
-
- default:
- IPA_MHI_ERR("invalied state %d\n", ipa_mhi_ctx->state);
- WARN_ON(1);
- }
-
- if (res)
- IPA_MHI_ERR("Invalid state change to %s\n",
- MHI_STATE_STR(new_state));
- else {
- IPA_MHI_DBG("New state change to %s\n",
- MHI_STATE_STR(new_state));
- ipa_mhi_ctx->state = new_state;
- }
- spin_unlock_irqrestore(&ipa_mhi_ctx->state_lock, flags);
- return res;
-}
-
-static void ipa_mhi_rm_prod_notify(void *user_data, enum ipa_rm_event event,
- unsigned long data)
-{
- IPA_MHI_FUNC_ENTRY();
-
- switch (event) {
- case IPA_RM_RESOURCE_GRANTED:
- IPA_MHI_DBG("IPA_RM_RESOURCE_GRANTED\n");
- complete_all(&ipa_mhi_ctx->rm_prod_granted_comp);
- break;
-
- case IPA_RM_RESOURCE_RELEASED:
- IPA_MHI_DBG("IPA_RM_RESOURCE_RELEASED\n");
- break;
+ u32 pipe_idx;
+ bool pending;
- default:
- IPA_MHI_ERR("unexpected event %d\n", event);
+ pipe_idx = ipa2_get_ep_mapping(client);
+ if (sps_pipe_pending_desc(ipa_ctx->bam_handle,
+ pipe_idx, &pending)) {
+ IPA_MHI_ERR("sps_pipe_pending_desc failed\n");
WARN_ON(1);
- break;
+ return false;
}
- IPA_MHI_FUNC_EXIT();
+ return !pending;
}
-static void ipa_mhi_uc_ready_cb(void)
+int ipa2_disable_sps_pipe(enum ipa_client_type client)
{
- IPA_MHI_FUNC_ENTRY();
- ipa_mhi_set_state(IPA_MHI_STATE_READY);
- IPA_MHI_FUNC_EXIT();
-}
-
-static void ipa_mhi_uc_wakeup_request_cb(void)
-{
- unsigned long flags;
-
- IPA_MHI_FUNC_ENTRY();
- IPA_MHI_DBG("MHI state: %s\n", MHI_STATE_STR(ipa_mhi_ctx->state));
- spin_lock_irqsave(&ipa_mhi_ctx->state_lock, flags);
- if (ipa_mhi_ctx->state == IPA_MHI_STATE_SUSPENDED) {
- ipa_mhi_notify_wakeup();
- } else if (ipa_mhi_ctx->state == IPA_MHI_STATE_SUSPEND_IN_PROGRESS) {
- /* wakeup event will be triggered after suspend finishes */
- ipa_mhi_ctx->trigger_wakeup = true;
- }
- spin_unlock_irqrestore(&ipa_mhi_ctx->state_lock, flags);
- IPA_MHI_FUNC_EXIT();
-}
-
-/**
- * ipa_mhi_rm_cons_request() - callback function for IPA RM request resource
- *
- * In case IPA MHI is not suspended, MHI CONS will be granted immediately.
- * In case IPA MHI is suspended, MHI CONS will be granted after resume.
- */
-static int ipa_mhi_rm_cons_request(void)
-{
- unsigned long flags;
+ int ipa_ep_index;
int res;
- IPA_MHI_FUNC_ENTRY();
-
- IPA_MHI_DBG("%s\n", MHI_STATE_STR(ipa_mhi_ctx->state));
- spin_lock_irqsave(&ipa_mhi_ctx->state_lock, flags);
- ipa_mhi_ctx->rm_cons_state = IPA_MHI_RM_STATE_REQUESTED;
- if (ipa_mhi_ctx->state == IPA_MHI_STATE_STARTED) {
- ipa_mhi_ctx->rm_cons_state = IPA_MHI_RM_STATE_GRANTED;
- res = 0;
- } else if (ipa_mhi_ctx->state == IPA_MHI_STATE_SUSPENDED) {
- ipa_mhi_notify_wakeup();
- res = -EINPROGRESS;
- } else if (ipa_mhi_ctx->state == IPA_MHI_STATE_SUSPEND_IN_PROGRESS) {
- /* wakeup event will be trigger after suspend finishes */
- ipa_mhi_ctx->trigger_wakeup = true;
- res = -EINPROGRESS;
- } else {
- res = -EINPROGRESS;
- }
-
- spin_unlock_irqrestore(&ipa_mhi_ctx->state_lock, flags);
- IPA_MHI_DBG("EXIT with %d\n", res);
- return res;
-}
+ ipa_ep_index = ipa2_get_ep_mapping(client);
-static int ipa_mhi_rm_cons_release(void)
-{
- unsigned long flags;
-
- IPA_MHI_FUNC_ENTRY();
-
- spin_lock_irqsave(&ipa_mhi_ctx->state_lock, flags);
- ipa_mhi_ctx->rm_cons_state = IPA_MHI_RM_STATE_RELEASED;
- complete_all(&ipa_mhi_ctx->rm_cons_comp);
- spin_unlock_irqrestore(&ipa_mhi_ctx->state_lock, flags);
- IPA_MHI_FUNC_EXIT();
- return 0;
-}
-
-static int ipa_mhi_wait_for_cons_release(void)
-{
- unsigned long flags;
- int res;
-
- IPA_MHI_FUNC_ENTRY();
- reinit_completion(&ipa_mhi_ctx->rm_cons_comp);
- spin_lock_irqsave(&ipa_mhi_ctx->state_lock, flags);
- if (ipa_mhi_ctx->rm_cons_state != IPA_MHI_RM_STATE_GRANTED) {
- spin_unlock_irqrestore(&ipa_mhi_ctx->state_lock, flags);
- return 0;
- }
- spin_unlock_irqrestore(&ipa_mhi_ctx->state_lock, flags);
-
- res = wait_for_completion_timeout(
- &ipa_mhi_ctx->rm_cons_comp,
- msecs_to_jiffies(IPA_MHI_RM_TIMEOUT_MSEC));
- if (res == 0) {
- IPA_MHI_ERR("timeout release mhi cons\n");
- return -ETIME;
- }
- IPA_MHI_FUNC_EXIT();
- return 0;
-}
-
-static int ipa_mhi_request_prod(void)
-{
- int res;
-
- IPA_MHI_FUNC_ENTRY();
-
- reinit_completion(&ipa_mhi_ctx->rm_prod_granted_comp);
- IPA_MHI_DBG("requesting mhi prod\n");
- res = ipa_rm_request_resource(IPA_RM_RESOURCE_MHI_PROD);
+ res = sps_pipe_disable(ipa_ctx->bam_handle, ipa_ep_index);
if (res) {
- if (res != -EINPROGRESS) {
- IPA_MHI_ERR("failed to request mhi prod %d\n", res);
- return res;
- }
- res = wait_for_completion_timeout(
- &ipa_mhi_ctx->rm_prod_granted_comp,
- msecs_to_jiffies(IPA_MHI_RM_TIMEOUT_MSEC));
- if (res == 0) {
- IPA_MHI_ERR("timeout request mhi prod\n");
- return -ETIME;
- }
- }
-
- IPA_MHI_DBG("mhi prod granted\n");
- IPA_MHI_FUNC_EXIT();
- return 0;
-
-}
-
-static int ipa_mhi_release_prod(void)
-{
- int res;
-
- IPA_MHI_FUNC_ENTRY();
-
- res = ipa_rm_release_resource(IPA_RM_RESOURCE_MHI_PROD);
-
- IPA_MHI_FUNC_EXIT();
- return res;
-
-}
-
-/**
- * ipa_mhi_get_channel_context() - Get corresponding channel context
- * @client: IPA client
- * @channel_id: Channel ID
- *
- * This function will return the corresponding channel context or allocate new
- * one in case channel context for channel does not exist.
- */
-static struct ipa_mhi_channel_ctx *ipa_mhi_get_channel_context(
- enum ipa_client_type client, u8 channel_id)
-{
- int ch_idx;
- struct ipa_mhi_channel_ctx *channels;
- int max_channels;
-
- if (IPA_CLIENT_IS_PROD(client)) {
- channels = ipa_mhi_ctx->ul_channels;
- max_channels = IPA_MHI_MAX_UL_CHANNELS;
- } else {
- channels = ipa_mhi_ctx->dl_channels;
- max_channels = IPA_MHI_MAX_DL_CHANNELS;
- }
-
- /* find the channel context according to channel id */
- for (ch_idx = 0; ch_idx < max_channels; ch_idx++) {
- if (channels[ch_idx].valid &&
- channels[ch_idx].id == channel_id)
- return &channels[ch_idx];
- }
-
- /* channel context does not exists, allocate a new one */
- for (ch_idx = 0; ch_idx < max_channels; ch_idx++) {
- if (!channels[ch_idx].valid)
- break;
- }
-
- if (ch_idx == max_channels) {
- IPA_MHI_ERR("no more channels available\n");
- return NULL;
- }
-
- channels[ch_idx].valid = true;
- channels[ch_idx].id = channel_id;
- channels[ch_idx].hdl = ipa_mhi_ctx->total_channels++;
- channels[ch_idx].client = client;
- channels[ch_idx].state = IPA_HW_MHI_CHANNEL_STATE_INVALID;
-
- return &channels[ch_idx];
-}
-
-/**
- * ipa_mhi_get_channel_context_by_clnt_hdl() - Get corresponding channel context
- * @clnt_hdl: client handle as provided in ipa2_mhi_connect_pipe()
- *
- * This function will return the corresponding channel context or NULL in case
- * that channel does not exist.
- */
-static struct ipa_mhi_channel_ctx *ipa_mhi_get_channel_context_by_clnt_hdl(
- u32 clnt_hdl)
-{
- int ch_idx;
-
- for (ch_idx = 0; ch_idx < IPA_MHI_MAX_UL_CHANNELS; ch_idx++) {
- if (ipa_mhi_ctx->ul_channels[ch_idx].valid &&
- ipa2_get_ep_mapping(
- ipa_mhi_ctx->ul_channels[ch_idx].client) == clnt_hdl)
- return &ipa_mhi_ctx->ul_channels[ch_idx];
- }
-
- for (ch_idx = 0; ch_idx < IPA_MHI_MAX_DL_CHANNELS; ch_idx++) {
- if (ipa_mhi_ctx->dl_channels[ch_idx].valid &&
- ipa2_get_ep_mapping(
- ipa_mhi_ctx->dl_channels[ch_idx].client) == clnt_hdl)
- return &ipa_mhi_ctx->dl_channels[ch_idx];
- }
-
- return NULL;
-}
-
-static int ipa_mhi_enable_force_clear(u32 request_id, bool throttle_source)
-{
- struct ipa_enable_force_clear_datapath_req_msg_v01 req;
- int i;
- int res;
-
- IPA_MHI_FUNC_ENTRY();
- memset(&req, 0, sizeof(req));
- req.request_id = request_id;
- req.source_pipe_bitmask = 0;
- for (i = 0; i < IPA_MHI_MAX_UL_CHANNELS; i++) {
- if (!ipa_mhi_ctx->ul_channels[i].valid)
- continue;
- req.source_pipe_bitmask |= 1 << ipa2_get_ep_mapping(
- ipa_mhi_ctx->ul_channels[i].client);
- }
- if (throttle_source) {
- req.throttle_source_valid = 1;
- req.throttle_source = 1;
- }
- IPA_MHI_DBG("req_id=0x%x src_pipe_btmk=0x%x throt_src=%d\n",
- req.request_id, req.source_pipe_bitmask,
- req.throttle_source);
- res = qmi_enable_force_clear_datapath_send(&req);
- if (res) {
- IPA_MHI_ERR("qmi_enable_force_clear_datapath_send failed %d\n",
- res);
+ IPA_MHI_ERR("sps_pipe_disable fail %d\n", res);
return res;
}
- IPA_MHI_FUNC_EXIT();
return 0;
}
-static int ipa_mhi_disable_force_clear(u32 request_id)
-{
- struct ipa_disable_force_clear_datapath_req_msg_v01 req;
- int res;
-
- IPA_MHI_FUNC_ENTRY();
- memset(&req, 0, sizeof(req));
- req.request_id = request_id;
- IPA_MHI_DBG("req_id=0x%x\n", req.request_id);
- res = qmi_disable_force_clear_datapath_send(&req);
- if (res) {
- IPA_MHI_ERR("qmi_disable_force_clear_datapath_send failed %d\n",
- res);
- return res;
- }
-
- IPA_MHI_FUNC_EXIT();
- return 0;
-}
-
-/**
- * ipa_mhi_wait_for_bam_empty_timeout() - wait for pending packets in uplink
- * @msecs: timeout to wait
- *
- * This function will poll until there are no packets pending in uplink channels
- * or timeout occurred.
- *
- * Return code: true - no pending packets in uplink channels
- * false - timeout occurred
- */
-static bool ipa_mhi_wait_for_bam_empty_timeout(unsigned int msecs)
-{
- unsigned long jiffies_timeout = msecs_to_jiffies(msecs);
- unsigned long jiffies_start = jiffies;
- bool empty = false;
- bool pending;
- int i;
- u32 pipe_idx;
-
- IPA_MHI_FUNC_ENTRY();
- while (!empty) {
- empty = true;
- for (i = 0; i < IPA_MHI_MAX_UL_CHANNELS; i++) {
- if (!ipa_mhi_ctx->ul_channels[i].valid)
- continue;
- pipe_idx = ipa2_get_ep_mapping(
- ipa_mhi_ctx->ul_channels[i].client);
- if (sps_pipe_pending_desc(ipa_ctx->bam_handle,
- pipe_idx, &pending)) {
- IPA_MHI_ERR("sps_pipe_pending_desc failed\n");
- WARN_ON(1);
- return false;
- }
- empty &= !pending;
- }
-
- if (time_after(jiffies, jiffies_start + jiffies_timeout)) {
- IPA_MHI_DBG("timeout waiting for BAM empty\n");
- break;
- }
- }
- IPA_MHI_DBG("Bam is %s\n", (empty) ? "empty" : "not empty");
- IPA_MHI_FUNC_EXIT();
- return empty;
-}
-
-static int ipa_mhi_reset_ul_channel(struct ipa_mhi_channel_ctx *channel)
+int ipa2_mhi_reset_channel_internal(enum ipa_client_type client)
{
int res;
- int i;
- int ep_idx;
- struct ipa_ep_cfg_holb ep_holb;
- struct ipa_ep_cfg_holb old_ep_holb[IPA_MHI_MAX_DL_CHANNELS];
- bool empty;
IPA_MHI_FUNC_ENTRY();
- res = ipa_uc_mhi_reset_channel(channel->hdl);
- if (res) {
- IPA_MHI_ERR("ipa_uc_mhi_reset_channel failed %d\n", res);
- return res;
- }
- empty = ipa_mhi_wait_for_bam_empty_timeout(
- IPA_MHI_BAM_EMPTY_TIMEOUT_MSEC);
- if (!empty) {
- IPA_MHI_DBG("BAM not empty\n");
- res = ipa_mhi_enable_force_clear(ipa_mhi_ctx->qmi_req_id,
- true);
- if (res) {
- IPA_MHI_ERR("ipa_mhi_enable_force_clear failed %d\n",
- res);
- BUG();
- return res;
- }
-
- /* enable packet drop on all DL channels */
- for (i = 0; i < IPA_MHI_MAX_DL_CHANNELS; i++) {
- if (!ipa_mhi_ctx->dl_channels[i].valid)
- continue;
- if (ipa_mhi_ctx->dl_channels[i].state ==
- IPA_HW_MHI_CHANNEL_STATE_INVALID)
- continue;
- ep_idx = ipa2_get_ep_mapping(
- ipa_mhi_ctx->dl_channels[i].client);
- if (-1 == ep_idx) {
- IPA_MHI_ERR("Client %u is not mapped\n",
- ipa_mhi_ctx->dl_channels[i].client);
- BUG();
- return -EFAULT;
- }
- memset(&ep_holb, 0, sizeof(ep_holb));
- ep_holb.en = 1;
- ep_holb.tmr_val = 0;
- old_ep_holb[i] = ipa_ctx->ep[ep_idx].holb;
- res = ipa2_cfg_ep_holb(ep_idx, &ep_holb);
- if (res) {
- IPA_MHI_ERR("ipa2_cfg_ep_holb failed %d\n",
- res);
- BUG();
- return res;
- }
- }
-
- res = ipa_tag_process(NULL, 0, HZ);
- if (res)
- IPAERR("TAG process failed\n");
-
- /* disable packet drop on all DL channels */
- for (i = 0; i < IPA_MHI_MAX_DL_CHANNELS; i++) {
- if (!ipa_mhi_ctx->dl_channels[i].valid)
- continue;
- if (ipa_mhi_ctx->dl_channels[i].state ==
- IPA_HW_MHI_CHANNEL_STATE_INVALID)
- continue;
- ep_idx = ipa2_get_ep_mapping(
- ipa_mhi_ctx->dl_channels[i].client);
- res = ipa2_cfg_ep_holb(ep_idx, &old_ep_holb[i]);
- if (res) {
- IPA_MHI_ERR("ipa2_cfg_ep_holb failed %d\n",
- res);
- BUG();
- return res;
- }
- }
-
- res = sps_pipe_disable(ipa_ctx->bam_handle,
- ipa2_get_ep_mapping(channel->client));
- if (res) {
- IPA_MHI_ERR("sps_pipe_disable failed %d\n", res);
- BUG();
- return res;
- }
- res = ipa_mhi_disable_force_clear(ipa_mhi_ctx->qmi_req_id);
- if (res) {
- IPA_MHI_ERR("ipa_mhi_disable_force_clear failed %d\n",
- res);
- BUG();
- return res;
- }
- ipa_mhi_ctx->qmi_req_id++;
- }
-
- res = ipa_disable_data_path(ipa2_get_ep_mapping(channel->client));
+ res = ipa_disable_data_path(ipa2_get_ep_mapping(client));
if (res) {
IPA_MHI_ERR("ipa_disable_data_path failed %d\n", res);
return res;
return 0;
}
-static int ipa_mhi_reset_dl_channel(struct ipa_mhi_channel_ctx *channel)
-{
- int res;
-
- IPA_MHI_FUNC_ENTRY();
- res = ipa_disable_data_path(ipa2_get_ep_mapping(channel->client));
- if (res) {
- IPA_MHI_ERR("ipa_disable_data_path failed %d\n", res);
- return res;
- }
-
- res = ipa_uc_mhi_reset_channel(channel->hdl);
- if (res) {
- IPA_MHI_ERR("ipa_uc_mhi_reset_channel failed %d\n", res);
- goto fail_reset_channel;
- }
- IPA_MHI_FUNC_EXIT();
-
- return 0;
-
-fail_reset_channel:
- ipa_enable_data_path(ipa2_get_ep_mapping(channel->client));
- return res;
-}
-
-static int ipa_mhi_reset_channel(struct ipa_mhi_channel_ctx *channel)
-{
- int res;
-
- IPA_MHI_FUNC_ENTRY();
- if (IPA_CLIENT_IS_PROD(channel->client))
- res = ipa_mhi_reset_ul_channel(channel);
- else
- res = ipa_mhi_reset_dl_channel(channel);
- if (res) {
- IPA_MHI_ERR("failed to reset channel error %d\n", res);
- return res;
- }
-
- channel->state = IPA_HW_MHI_CHANNEL_STATE_DISABLE;
- IPA_MHI_FUNC_EXIT();
- return 0;
-}
-
-/**
- * ipa2_mhi_init() - Initialize IPA MHI driver
- * @params: initialization params
- *
- * This function is called by MHI client driver on boot to initialize IPA MHI
- * Driver. When this function returns device can move to READY state.
- * This function is doing the following:
- * - Initialize MHI IPA internal data structures
- * - Create IPA RM resources
- * - Initialize debugfs
- *
- * Return codes: 0 : success
- * negative : error
- */
-int ipa2_mhi_init(struct ipa_mhi_init_params *params)
-{
- int res;
- struct ipa_rm_create_params mhi_prod_params;
- struct ipa_rm_create_params mhi_cons_params;
-
- IPA_MHI_FUNC_ENTRY();
-
- if (!params) {
- IPA_MHI_ERR("null args\n");
- return -EINVAL;
- }
-
- if (!params->notify) {
- IPA_MHI_ERR("null notify function\n");
- return -EINVAL;
- }
-
- if (ipa_mhi_ctx) {
- IPA_MHI_ERR("already initialized\n");
- return -EPERM;
- }
-
- IPA_MHI_DBG("msi: addr_lo = 0x%x addr_hi = 0x%x\n",
- params->msi.addr_low, params->msi.addr_hi);
- IPA_MHI_DBG("msi: data = 0x%x mask = 0x%x\n",
- params->msi.data, params->msi.mask);
- IPA_MHI_DBG("mmio_addr = 0x%x\n", params->mmio_addr);
- IPA_MHI_DBG("first_ch_idx = 0x%x\n", params->first_ch_idx);
- IPA_MHI_DBG("first_er_idx = 0x%x\n", params->first_er_idx);
- IPA_MHI_DBG("notify = %pF priv = %p\n", params->notify, params->priv);
-
- /* Initialize context */
- ipa_mhi_ctx = kzalloc(sizeof(*ipa_mhi_ctx), GFP_KERNEL);
- if (!ipa_mhi_ctx) {
- IPA_MHI_ERR("no memory\n");
- res = -EFAULT;
- goto fail_alloc_ctx;
- }
-
- ipa_mhi_ctx->state = IPA_MHI_STATE_INITIALIZED;
- ipa_mhi_ctx->msi = params->msi;
- ipa_mhi_ctx->mmio_addr = params->mmio_addr;
- ipa_mhi_ctx->first_ch_idx = params->first_ch_idx;
- ipa_mhi_ctx->first_er_idx = params->first_er_idx;
- ipa_mhi_ctx->cb_notify = params->notify;
- ipa_mhi_ctx->cb_priv = params->priv;
- ipa_mhi_ctx->rm_cons_state = IPA_MHI_RM_STATE_RELEASED;
- ipa_mhi_ctx->qmi_req_id = 0;
- init_completion(&ipa_mhi_ctx->rm_prod_granted_comp);
- spin_lock_init(&ipa_mhi_ctx->state_lock);
- init_completion(&ipa_mhi_ctx->rm_cons_comp);
-
- ipa_mhi_ctx->wq = create_singlethread_workqueue("ipa_mhi_wq");
- if (!ipa_mhi_ctx->wq) {
- IPA_MHI_ERR("failed to create workqueue\n");
- res = -EFAULT;
- goto fail_create_wq;
- }
-
- /* Initialize debugfs */
- ipa_mhi_debugfs_init();
+int ipa2_mhi_start_channel_internal(enum ipa_client_type client)
+{
+ int res;
- /* Create PROD in IPA RM */
- memset(&mhi_prod_params, 0, sizeof(mhi_prod_params));
- mhi_prod_params.name = IPA_RM_RESOURCE_MHI_PROD;
- mhi_prod_params.floor_voltage = IPA_VOLTAGE_SVS;
- mhi_prod_params.reg_params.notify_cb = ipa_mhi_rm_prod_notify;
- res = ipa_rm_create_resource(&mhi_prod_params);
- if (res) {
- IPA_MHI_ERR("fail to create IPA_RM_RESOURCE_MHI_PROD\n");
- goto fail_create_rm_prod;
- }
+ IPA_MHI_FUNC_ENTRY();
- /* Create CONS in IPA RM */
- memset(&mhi_cons_params, 0, sizeof(mhi_cons_params));
- mhi_cons_params.name = IPA_RM_RESOURCE_MHI_CONS;
- mhi_cons_params.floor_voltage = IPA_VOLTAGE_SVS;
- mhi_cons_params.request_resource = ipa_mhi_rm_cons_request;
- mhi_cons_params.release_resource = ipa_mhi_rm_cons_release;
- res = ipa_rm_create_resource(&mhi_cons_params);
+ res = ipa_enable_data_path(ipa2_get_ep_mapping(client));
if (res) {
- IPA_MHI_ERR("fail to create IPA_RM_RESOURCE_MHI_CONS\n");
- goto fail_create_rm_cons;
+ IPA_MHI_ERR("ipa_enable_data_path failed %d\n", res);
+ return res;
}
-
- /* Initialize uC interface */
- ipa_uc_mhi_init(ipa_mhi_uc_ready_cb, ipa_mhi_uc_wakeup_request_cb);
- if (ipa_uc_state_check() == 0)
- ipa_mhi_set_state(IPA_MHI_STATE_READY);
-
IPA_MHI_FUNC_EXIT();
return 0;
-
-fail_create_rm_cons:
- ipa_rm_delete_resource(IPA_RM_RESOURCE_MHI_PROD);
-fail_create_rm_prod:
- destroy_workqueue(ipa_mhi_ctx->wq);
-fail_create_wq:
- kfree(ipa_mhi_ctx);
- ipa_mhi_ctx = NULL;
-fail_alloc_ctx:
- return res;
}
-/**
- * ipa2_mhi_start() - Start IPA MHI engine
- * @params: pcie addresses for MHI
- *
- * This function is called by MHI client driver on MHI engine start for
- * handling MHI accelerated channels. This function is called after
- * ipa2_mhi_init() was called and can be called after MHI reset to restart MHI
- * engine. When this function returns device can move to M0 state.
- * This function is doing the following:
- * - Send command to uC for initialization of MHI engine
- * - Add dependencies to IPA RM
- * - Request MHI_PROD in IPA RM
- *
- * Return codes: 0 : success
- * negative : error
- */
-int ipa2_mhi_start(struct ipa_mhi_start_params *params)
+int ipa2_mhi_init_engine(struct ipa_mhi_init_engine *params)
{
int res;
return -EINVAL;
}
- if (unlikely(!ipa_mhi_ctx)) {
- IPA_MHI_ERR("IPA MHI was not initialized\n");
- return -EINVAL;
- }
-
- if (ipa_uc_state_check()) {
+ if (ipa2_uc_state_check()) {
IPA_MHI_ERR("IPA uc is not loaded\n");
return -EAGAIN;
}
- res = ipa_mhi_set_state(IPA_MHI_STATE_STARTED);
- if (res) {
- IPA_MHI_ERR("ipa_mhi_set_state %d\n", res);
- return res;
- }
-
- ipa_mhi_ctx->host_ctrl_addr = params->host_ctrl_addr;
- ipa_mhi_ctx->host_data_addr = params->host_data_addr;
-
- /* Add MHI <-> Q6 dependencies to IPA RM */
- res = ipa_rm_add_dependency(IPA_RM_RESOURCE_MHI_PROD,
- IPA_RM_RESOURCE_Q6_CONS);
- if (res && res != -EINPROGRESS) {
- IPA_MHI_ERR("failed to add dependency %d\n", res);
- goto fail_add_mhi_q6_dep;
- }
-
- res = ipa_rm_add_dependency(IPA_RM_RESOURCE_Q6_PROD,
- IPA_RM_RESOURCE_MHI_CONS);
- if (res && res != -EINPROGRESS) {
- IPA_MHI_ERR("failed to add dependency %d\n", res);
- goto fail_add_q6_mhi_dep;
- }
-
- res = ipa_mhi_request_prod();
- if (res) {
- IPA_MHI_ERR("failed request prod %d\n", res);
- goto fail_request_prod;
- }
-
/* Initialize IPA MHI engine */
- res = ipa_uc_mhi_init_engine(&ipa_mhi_ctx->msi, ipa_mhi_ctx->mmio_addr,
- ipa_mhi_ctx->host_ctrl_addr, ipa_mhi_ctx->host_data_addr,
- ipa_mhi_ctx->first_ch_idx, ipa_mhi_ctx->first_er_idx);
+ res = ipa_uc_mhi_init_engine(params->uC.msi, params->uC.mmio_addr,
+ params->uC.host_ctrl_addr, params->uC.host_data_addr,
+ params->uC.first_ch_idx, params->uC.first_er_idx);
if (res) {
IPA_MHI_ERR("failed to start MHI engine %d\n", res);
goto fail_init_engine;
}
/* Update UL/DL sync if valid */
- res = ipa_uc_mhi_send_dl_ul_sync_info(cached_dl_ul_sync_info);
+ res = ipa2_uc_mhi_send_dl_ul_sync_info(
+ params->uC.ipa_cached_dl_ul_sync_info);
if (res) {
IPA_MHI_ERR("failed to update ul/dl sync %d\n", res);
goto fail_init_engine;
return 0;
fail_init_engine:
- ipa_mhi_release_prod();
-fail_request_prod:
- ipa_rm_delete_dependency(IPA_RM_RESOURCE_Q6_PROD,
- IPA_RM_RESOURCE_MHI_CONS);
-fail_add_q6_mhi_dep:
- ipa_rm_delete_dependency(IPA_RM_RESOURCE_MHI_PROD,
- IPA_RM_RESOURCE_Q6_CONS);
-fail_add_mhi_q6_dep:
- ipa_mhi_set_state(IPA_MHI_STATE_INITIALIZED);
return res;
}
/**
- * ipa2_mhi_connect_pipe() - Connect pipe to IPA and start corresponding
+ * ipa2_connect_mhi_pipe() - Connect pipe to IPA and start corresponding
* MHI channel
* @in: connect parameters
* @clnt_hdl: [out] client handle for this pipe
*
- * This function is called by MHI client driver on MHI channel start.
+ * This function is called by IPA MHI client driver on MHI channel start.
* This function is called after MHI engine was started.
* This function is doing the following:
* - Send command to uC to start corresponding MHI channel
* Return codes: 0 : success
* negative : error
*/
-int ipa2_mhi_connect_pipe(struct ipa_mhi_connect_params *in, u32 *clnt_hdl)
+int ipa2_connect_mhi_pipe(struct ipa_mhi_connect_params_internal *in,
+ u32 *clnt_hdl)
{
struct ipa_ep_context *ep;
int ipa_ep_idx;
int res;
- struct ipa_mhi_channel_ctx *channel = NULL;
- unsigned long flags;
IPA_MHI_FUNC_ENTRY();
return -EINVAL;
}
- if (in->sys.client >= IPA_CLIENT_MAX) {
- IPA_MHI_ERR("bad parm client:%d\n", in->sys.client);
+ if (in->sys->client >= IPA_CLIENT_MAX) {
+ IPA_MHI_ERR("bad parm client:%d\n", in->sys->client);
return -EINVAL;
}
- if (unlikely(!ipa_mhi_ctx)) {
- IPA_MHI_ERR("IPA MHI was not initialized\n");
- return -EINVAL;
- }
-
- spin_lock_irqsave(&ipa_mhi_ctx->state_lock, flags);
- if (!ipa_mhi_ctx || ipa_mhi_ctx->state != IPA_MHI_STATE_STARTED) {
- IPA_MHI_ERR("IPA MHI was not started\n");
- spin_unlock_irqrestore(&ipa_mhi_ctx->state_lock, flags);
- return -EINVAL;
- }
- spin_unlock_irqrestore(&ipa_mhi_ctx->state_lock, flags);
-
- ipa_ep_idx = ipa2_get_ep_mapping(in->sys.client);
+ ipa_ep_idx = ipa2_get_ep_mapping(in->sys->client);
if (ipa_ep_idx == -1) {
IPA_MHI_ERR("Invalid client.\n");
return -EINVAL;
ep = &ipa_ctx->ep[ipa_ep_idx];
- channel = ipa_mhi_get_channel_context(in->sys.client,
- in->channel_id);
- if (!channel) {
- IPA_MHI_ERR("ipa_mhi_get_channel_context failed\n");
- return -EINVAL;
- }
-
IPA_MHI_DBG("client %d channelHandle %d channelIndex %d\n",
- channel->client, channel->hdl, channel->id);
-
- IPA_ACTIVE_CLIENTS_INC_EP(in->sys.client);
+ in->sys->client, in->start.uC.index, in->start.uC.id);
if (ep->valid == 1) {
IPA_MHI_ERR("EP already allocated.\n");
memset(ep, 0, offsetof(struct ipa_ep_context, sys));
ep->valid = 1;
- ep->skip_ep_cfg = in->sys.skip_ep_cfg;
- ep->client = in->sys.client;
- ep->client_notify = in->sys.notify;
- ep->priv = in->sys.priv;
- ep->keep_ipa_awake = in->sys.keep_ipa_awake;
+ ep->skip_ep_cfg = in->sys->skip_ep_cfg;
+ ep->client = in->sys->client;
+ ep->client_notify = in->sys->notify;
+ ep->priv = in->sys->priv;
+ ep->keep_ipa_awake = in->sys->keep_ipa_awake;
/* start channel in uC */
- if (channel->state == IPA_HW_MHI_CHANNEL_STATE_INVALID) {
+ if (in->start.uC.state == IPA_HW_MHI_CHANNEL_STATE_INVALID) {
IPA_MHI_DBG("Initializing channel\n");
- res = ipa_uc_mhi_init_channel(ipa_ep_idx, channel->hdl,
- channel->id, (IPA_CLIENT_IS_PROD(ep->client) ? 1 : 2));
+ res = ipa_uc_mhi_init_channel(ipa_ep_idx, in->start.uC.index,
+ in->start.uC.id,
+ (IPA_CLIENT_IS_PROD(ep->client) ? 1 : 2));
if (res) {
IPA_MHI_ERR("init_channel failed %d\n", res);
goto fail_init_channel;
}
- } else if (channel->state == IPA_HW_MHI_CHANNEL_STATE_DISABLE) {
- if (channel->client != ep->client) {
- IPA_MHI_ERR("previous channel client was %d\n",
- ep->client);
- goto fail_init_channel;
- }
+ } else if (in->start.uC.state == IPA_HW_MHI_CHANNEL_STATE_DISABLE) {
IPA_MHI_DBG("Starting channel\n");
- res = ipa_uc_mhi_resume_channel(channel->hdl, false);
+ res = ipa_uc_mhi_resume_channel(in->start.uC.index, false);
if (res) {
IPA_MHI_ERR("init_channel failed %d\n", res);
goto fail_init_channel;
}
} else {
- IPA_MHI_ERR("Invalid channel state %d\n", channel->state);
+ IPA_MHI_ERR("Invalid channel state %d\n", in->start.uC.state);
goto fail_init_channel;
}
- channel->state = IPA_HW_MHI_CHANNEL_STATE_RUN;
-
res = ipa_enable_data_path(ipa_ep_idx);
if (res) {
IPA_MHI_ERR("enable data path failed res=%d clnt=%d.\n", res,
}
if (!ep->skip_ep_cfg) {
- if (ipa2_cfg_ep(ipa_ep_idx, &in->sys.ipa_ep_cfg)) {
+ if (ipa2_cfg_ep(ipa_ep_idx, &in->sys->ipa_ep_cfg)) {
IPAERR("fail to configure EP.\n");
goto fail_ep_cfg;
}
*clnt_hdl = ipa_ep_idx;
- if (!ep->skip_ep_cfg && IPA_CLIENT_IS_PROD(in->sys.client))
+ if (!ep->skip_ep_cfg && IPA_CLIENT_IS_PROD(in->sys->client))
ipa_install_dflt_flt_rules(ipa_ep_idx);
- if (!ep->keep_ipa_awake)
- IPA_ACTIVE_CLIENTS_DEC_EP(in->sys.client);
-
ipa_ctx->skip_ep_cfg_shadow[ipa_ep_idx] = ep->skip_ep_cfg;
- IPA_MHI_DBG("client %d (ep: %d) connected\n", in->sys.client,
+ IPA_MHI_DBG("client %d (ep: %d) connected\n", in->sys->client,
ipa_ep_idx);
IPA_MHI_FUNC_EXIT();
fail_ep_cfg:
ipa_disable_data_path(ipa_ep_idx);
fail_enable_dp:
- ipa_uc_mhi_reset_channel(channel->hdl);
- channel->state = IPA_HW_MHI_CHANNEL_STATE_DISABLE;
+ ipa_uc_mhi_reset_channel(in->start.uC.index);
fail_init_channel:
memset(ep, 0, offsetof(struct ipa_ep_context, sys));
fail_ep_exists:
- IPA_ACTIVE_CLIENTS_DEC_EP(in->sys.client);
return -EPERM;
}
/**
- * ipa2_mhi_disconnect_pipe() - Disconnect pipe from IPA and reset corresponding
+ * ipa2_disconnect_mhi_pipe() - Disconnect pipe from IPA and reset corresponding
* MHI channel
* @in: connect parameters
* @clnt_hdl: [out] client handle for this pipe
*
- * This function is called by MHI client driver on MHI channel reset.
+ * This function is called by IPA MHI client driver on MHI channel reset.
* This function is called after MHI channel was started.
* This function is doing the following:
* - Send command to uC to reset corresponding MHI channel
* Return codes: 0 : success
* negative : error
*/
-int ipa2_mhi_disconnect_pipe(u32 clnt_hdl)
+int ipa2_disconnect_mhi_pipe(u32 clnt_hdl)
{
struct ipa_ep_context *ep;
- static struct ipa_mhi_channel_ctx *channel;
- int res;
IPA_MHI_FUNC_ENTRY();
return -EINVAL;
}
- if (unlikely(!ipa_mhi_ctx)) {
- IPA_MHI_ERR("IPA MHI was not initialized\n");
- return -EINVAL;
- }
-
- channel = ipa_mhi_get_channel_context_by_clnt_hdl(clnt_hdl);
- if (!channel) {
- IPAERR("invalid clnt hdl\n");
- return -EINVAL;
- }
-
- ep = &ipa_ctx->ep[clnt_hdl];
-
- if (!ep->keep_ipa_awake)
- IPA_ACTIVE_CLIENTS_INC_EP(ipa2_get_client_mapping(clnt_hdl));
-
- res = ipa_mhi_reset_channel(channel);
- if (res) {
- IPA_MHI_ERR("ipa_mhi_reset_channel failed %d\n", res);
- goto fail_reset_channel;
- }
-
ep->valid = 0;
ipa_delete_dflt_flt_rules(clnt_hdl);
- IPA_ACTIVE_CLIENTS_DEC_EP(ipa2_get_client_mapping(clnt_hdl));
-
IPA_MHI_DBG("client (ep: %d) disconnected\n", clnt_hdl);
IPA_MHI_FUNC_EXIT();
return 0;
-
-fail_reset_channel:
- if (!ep->keep_ipa_awake)
- IPA_ACTIVE_CLIENTS_DEC_EP(ipa2_get_client_mapping(clnt_hdl));
- return res;
-}
-
-static int ipa_mhi_suspend_ul_channels(void)
-{
- int i;
- int res;
-
- IPA_MHI_FUNC_ENTRY();
- for (i = 0; i < IPA_MHI_MAX_UL_CHANNELS; i++) {
- if (!ipa_mhi_ctx->ul_channels[i].valid)
- continue;
- if (ipa_mhi_ctx->ul_channels[i].state !=
- IPA_HW_MHI_CHANNEL_STATE_RUN)
- continue;
- IPA_MHI_DBG("suspending channel %d\n",
- ipa_mhi_ctx->ul_channels[i].hdl);
- res = ipa_uc_mhi_suspend_channel(
- ipa_mhi_ctx->ul_channels[i].hdl);
- if (res) {
- IPA_MHI_ERR("failed to suspend channel %d error %d\n",
- i, res);
- return res;
- }
- ipa_mhi_ctx->ul_channels[i].state =
- IPA_HW_MHI_CHANNEL_STATE_SUSPEND;
- }
-
- IPA_MHI_FUNC_EXIT();
- return 0;
-}
-
-static int ipa_mhi_resume_ul_channels(bool LPTransitionRejected)
-{
- int i;
- int res;
-
- IPA_MHI_FUNC_ENTRY();
- for (i = 0; i < IPA_MHI_MAX_UL_CHANNELS; i++) {
- if (!ipa_mhi_ctx->ul_channels[i].valid)
- continue;
- if (ipa_mhi_ctx->ul_channels[i].state !=
- IPA_HW_MHI_CHANNEL_STATE_SUSPEND)
- continue;
- IPA_MHI_DBG("suspending channel %d\n",
- ipa_mhi_ctx->ul_channels[i].hdl);
- res = ipa_uc_mhi_resume_channel(ipa_mhi_ctx->ul_channels[i].hdl,
- LPTransitionRejected);
- if (res) {
- IPA_MHI_ERR("failed to suspend channel %d error %d\n",
- i, res);
- return res;
- }
- ipa_mhi_ctx->ul_channels[i].state =
- IPA_HW_MHI_CHANNEL_STATE_RUN;
- }
-
- IPA_MHI_FUNC_EXIT();
- return 0;
-}
-
-static int ipa_mhi_stop_event_update_ul_channels(void)
-{
- int i;
- int res;
-
- IPA_MHI_FUNC_ENTRY();
- for (i = 0; i < IPA_MHI_MAX_UL_CHANNELS; i++) {
- if (!ipa_mhi_ctx->ul_channels[i].valid)
- continue;
- if (ipa_mhi_ctx->ul_channels[i].state !=
- IPA_HW_MHI_CHANNEL_STATE_SUSPEND)
- continue;
- IPA_MHI_DBG("stop update event channel %d\n",
- ipa_mhi_ctx->ul_channels[i].hdl);
- res = ipa_uc_mhi_stop_event_update_channel(
- ipa_mhi_ctx->ul_channels[i].hdl);
- if (res) {
- IPA_MHI_ERR("failed stop event channel %d error %d\n",
- i, res);
- return res;
- }
- }
-
- IPA_MHI_FUNC_EXIT();
- return 0;
-}
-
-static int ipa_mhi_suspend_dl_channels(void)
-{
- int i;
- int res;
-
- IPA_MHI_FUNC_ENTRY();
- for (i = 0; i < IPA_MHI_MAX_DL_CHANNELS; i++) {
- if (!ipa_mhi_ctx->dl_channels[i].valid)
- continue;
- if (ipa_mhi_ctx->dl_channels[i].state !=
- IPA_HW_MHI_CHANNEL_STATE_RUN)
- continue;
- IPA_MHI_DBG("suspending channel %d\n",
- ipa_mhi_ctx->dl_channels[i].hdl);
- res = ipa_uc_mhi_suspend_channel(
- ipa_mhi_ctx->dl_channels[i].hdl);
- if (res) {
- IPA_MHI_ERR("failed to suspend channel %d error %d\n",
- i, res);
- return res;
- }
- ipa_mhi_ctx->dl_channels[i].state =
- IPA_HW_MHI_CHANNEL_STATE_SUSPEND;
- }
-
- IPA_MHI_FUNC_EXIT();
- return 0;
-}
-
-static int ipa_mhi_resume_dl_channels(bool LPTransitionRejected)
-{
- int i;
- int res;
-
- IPA_MHI_FUNC_ENTRY();
- for (i = 0; i < IPA_MHI_MAX_DL_CHANNELS; i++) {
- if (!ipa_mhi_ctx->dl_channels[i].valid)
- continue;
- if (ipa_mhi_ctx->dl_channels[i].state !=
- IPA_HW_MHI_CHANNEL_STATE_SUSPEND)
- continue;
- IPA_MHI_DBG("suspending channel %d\n",
- ipa_mhi_ctx->dl_channels[i].hdl);
- res = ipa_uc_mhi_resume_channel(ipa_mhi_ctx->dl_channels[i].hdl,
- LPTransitionRejected);
- if (res) {
- IPA_MHI_ERR("failed to suspend channel %d error %d\n",
- i, res);
- return res;
- }
- ipa_mhi_ctx->dl_channels[i].state =
- IPA_HW_MHI_CHANNEL_STATE_RUN;
- }
-
- IPA_MHI_FUNC_EXIT();
- return 0;
}
-static int ipa_mhi_stop_event_update_dl_channels(void)
+int ipa2_mhi_resume_channels_internal(enum ipa_client_type client,
+ bool LPTransitionRejected, bool brstmode_enabled,
+ union __packed gsi_channel_scratch ch_scratch, u8 index)
{
int i;
int res;
IPA_MHI_FUNC_ENTRY();
- for (i = 0; i < IPA_MHI_MAX_DL_CHANNELS; i++) {
- if (!ipa_mhi_ctx->dl_channels[i].valid)
- continue;
- if (ipa_mhi_ctx->dl_channels[i].state !=
- IPA_HW_MHI_CHANNEL_STATE_SUSPEND)
- continue;
- IPA_MHI_DBG("stop update event channel %d\n",
- ipa_mhi_ctx->dl_channels[i].hdl);
- res = ipa_uc_mhi_stop_event_update_channel(
- ipa_mhi_ctx->dl_channels[i].hdl);
- if (res) {
- IPA_MHI_ERR("failed stop event channel %d error %d\n",
- i, res);
- return res;
- }
- }
-
- IPA_MHI_FUNC_EXIT();
- return 0;
-}
-
-/**
- * ipa2_mhi_suspend() - Suspend MHI accelerated channels
- * @force:
- * false: in case of data pending in IPA, MHI channels will not be
- * suspended and function will fail.
- * true: in case of data pending in IPA, make sure no further access from
- * IPA to PCIe is possible. In this case suspend cannot fail.
- *
- * This function is called by MHI client driver on MHI suspend.
- * This function is called after MHI channel was started.
- * When this function returns device can move to M1/M2/M3/D3cold state.
- * This function is doing the following:
- * - Send command to uC to suspend corresponding MHI channel
- * - Make sure no further access is possible from IPA to PCIe
- * - Release MHI_PROD in IPA RM
- *
- * Return codes: 0 : success
- * negative : error
- */
-int ipa2_mhi_suspend(bool force)
-{
- int res;
- bool bam_empty;
- bool force_clear = false;
-
- IPA_MHI_FUNC_ENTRY();
-
- if (unlikely(!ipa_mhi_ctx)) {
- IPA_MHI_ERR("IPA MHI was not initialized\n");
- return -EINVAL;
- }
-
- res = ipa_mhi_set_state(IPA_MHI_STATE_SUSPEND_IN_PROGRESS);
- if (res) {
- IPA_MHI_ERR("ipa_mhi_set_state failed %d\n", res);
- return res;
- }
-
- res = ipa_mhi_suspend_ul_channels();
- if (res) {
- IPA_MHI_ERR("ipa_mhi_suspend_ul_channels failed %d\n", res);
- goto fail_suspend_ul_channel;
- }
-
- bam_empty = ipa_mhi_wait_for_bam_empty_timeout(
- IPA_MHI_BAM_EMPTY_TIMEOUT_MSEC);
- if (!bam_empty) {
- if (force) {
- res = ipa_mhi_enable_force_clear(
- ipa_mhi_ctx->qmi_req_id, false);
- if (res) {
- IPA_MHI_ERR("failed to enable force clear\n");
- BUG();
- return res;
- }
- force_clear = true;
- IPA_MHI_DBG("force clear datapath enabled\n");
-
- bam_empty = ipa_mhi_wait_for_bam_empty_timeout(
- IPA_MHI_BAM_EMPTY_TIMEOUT_MSEC);
- IPADBG("bam_empty=%d\n", bam_empty);
-
- } else {
- IPA_MHI_DBG("BAM not empty\n");
- res = -EAGAIN;
- goto fail_suspend_ul_channel;
- }
- }
-
- res = ipa_mhi_stop_event_update_ul_channels();
- if (res) {
- IPA_MHI_ERR("ipa_mhi_stop_event_update_ul_channels failed %d\n",
- res);
- goto fail_suspend_ul_channel;
- }
-
- /*
- * in case BAM not empty, hold IPA clocks and release them after all
- * IPA RM resource are released to make sure tag process will not start
- */
- if (!bam_empty)
- IPA_ACTIVE_CLIENTS_INC_SIMPLE();
-
- IPA_MHI_DBG("release prod\n");
- res = ipa_mhi_release_prod();
- if (res) {
- IPA_MHI_ERR("ipa_mhi_release_prod failed %d\n", res);
- goto fail_release_prod;
- }
-
- IPA_MHI_DBG("wait for cons release\n");
- res = ipa_mhi_wait_for_cons_release();
- if (res) {
- IPA_MHI_ERR("ipa_mhi_wait_for_cons_release failed %d\n", res);
- goto fail_release_cons;
- }
-
- usleep_range(IPA_MHI_SUSPEND_SLEEP_MIN, IPA_MHI_SUSPEND_SLEEP_MAX);
-
- res = ipa_mhi_suspend_dl_channels();
- if (res) {
- IPA_MHI_ERR("ipa_mhi_suspend_dl_channels failed %d\n", res);
- goto fail_suspend_dl_channel;
- }
-
- res = ipa_mhi_stop_event_update_dl_channels();
- if (res) {
- IPA_MHI_ERR("failed to stop event update on DL %d\n", res);
- goto fail_stop_event_update_dl_channel;
- }
-
- if (force_clear) {
- res = ipa_mhi_disable_force_clear(ipa_mhi_ctx->qmi_req_id);
- if (res) {
- IPA_MHI_ERR("failed to disable force clear\n");
- BUG();
- return res;
- }
- IPA_MHI_DBG("force clear datapath disabled\n");
- ipa_mhi_ctx->qmi_req_id++;
- }
-
- if (!bam_empty) {
- ipa_ctx->tag_process_before_gating = false;
- IPA_ACTIVE_CLIENTS_DEC_SIMPLE();
- }
-
- res = ipa_mhi_set_state(IPA_MHI_STATE_SUSPENDED);
- if (res) {
- IPA_MHI_ERR("ipa_mhi_set_state failed %d\n", res);
- goto fail_release_cons;
- }
-
- IPA_MHI_FUNC_EXIT();
- return 0;
-
-fail_stop_event_update_dl_channel:
- ipa_mhi_resume_dl_channels(true);
-fail_suspend_dl_channel:
-fail_release_cons:
- ipa_mhi_request_prod();
-fail_release_prod:
-fail_suspend_ul_channel:
- ipa_mhi_resume_ul_channels(true);
- ipa_mhi_set_state(IPA_MHI_STATE_STARTED);
- return res;
-}
-
-/**
- * ipa2_mhi_resume() - Resume MHI accelerated channels
- *
- * This function is called by MHI client driver on MHI resume.
- * This function is called after MHI channel was suspended.
- * When this function returns device can move to M0 state.
- * This function is doing the following:
- * - Send command to uC to resume corresponding MHI channel
- * - Request MHI_PROD in IPA RM
- * - Resume data to IPA
- *
- * Return codes: 0 : success
- * negative : error
- */
-int ipa2_mhi_resume(void)
-{
- int res;
- bool dl_channel_resumed = false;
-
- IPA_MHI_FUNC_ENTRY();
-
- if (unlikely(!ipa_mhi_ctx)) {
- IPA_MHI_ERR("IPA MHI was not initialized\n");
- return -EINVAL;
- }
-
- res = ipa_mhi_set_state(IPA_MHI_STATE_RESUME_IN_PROGRESS);
+ res = ipa_uc_mhi_resume_channel(index, LPTransitionRejected);
if (res) {
- IPA_MHI_ERR("ipa_mhi_set_state failed %d\n", res);
+ IPA_MHI_ERR("failed to suspend channel %d error %d\n",
+ i, res);
return res;
}
- if (ipa_mhi_ctx->rm_cons_state == IPA_MHI_RM_STATE_REQUESTED) {
- /* resume all DL channels */
- res = ipa_mhi_resume_dl_channels(false);
- if (res) {
- IPA_MHI_ERR("ipa_mhi_resume_dl_channels failed %d\n",
- res);
- goto fail_resume_dl_channels;
- }
- dl_channel_resumed = true;
-
- ipa_rm_notify_completion(IPA_RM_RESOURCE_GRANTED,
- IPA_RM_RESOURCE_MHI_CONS);
- ipa_mhi_ctx->rm_cons_state = IPA_MHI_RM_STATE_GRANTED;
- }
-
- res = ipa_mhi_request_prod();
- if (res) {
- IPA_MHI_ERR("ipa_mhi_request_prod failed %d\n", res);
- goto fail_request_prod;
- }
-
- /* resume all UL channels */
- res = ipa_mhi_resume_ul_channels(false);
- if (res) {
- IPA_MHI_ERR("ipa_mhi_resume_ul_channels failed %d\n", res);
- goto fail_resume_ul_channels;
- }
-
- if (!dl_channel_resumed) {
- res = ipa_mhi_resume_dl_channels(true);
- if (res) {
- IPA_MHI_ERR("ipa_mhi_resume_dl_channels failed %d\n",
- res);
- goto fail_resume_dl_channels2;
- }
- }
-
- res = ipa_mhi_set_state(IPA_MHI_STATE_STARTED);
- if (res) {
- IPA_MHI_ERR("ipa_mhi_set_state failed %d\n", res);
- goto fail_set_state;
- }
-
- IPA_MHI_FUNC_EXIT();
- return 0;
-
-fail_set_state:
- ipa_mhi_suspend_dl_channels();
-fail_resume_dl_channels2:
- ipa_mhi_suspend_ul_channels();
-fail_resume_ul_channels:
- ipa_mhi_release_prod();
-fail_request_prod:
- ipa_mhi_suspend_dl_channels();
-fail_resume_dl_channels:
- ipa_mhi_set_state(IPA_MHI_STATE_SUSPENDED);
- return res;
-}
-
-
-/**
- * ipa2_mhi_destroy() - Destroy MHI IPA
- *
- * This function is called by MHI client driver on MHI reset to destroy all IPA
- * MHI resources.
- */
-void ipa2_mhi_destroy(void)
-{
- IPA_MHI_FUNC_ENTRY();
-
- if (unlikely(!ipa_mhi_ctx)) {
- IPA_MHI_ERR("IPA MHI was not initialized\n");
- return;
- }
-
- IPAERR("Not implemented Yet!\n");
- ipa_mhi_debugfs_destroy();
-
- IPA_MHI_FUNC_EXIT();
-}
-
-/**
- * ipa_mhi_handle_ipa_config_req() - hanle IPA CONFIG QMI message
- *
- * This function is called by by IPA QMI service to indicate that IPA CONFIG
- * message was sent from modem. IPA MHI will update this information to IPA uC
- * or will cache it until IPA MHI will be initialized.
- *
- * Return codes: 0 : success
- * negative : error
- */
-int ipa_mhi_handle_ipa_config_req(struct ipa_config_req_msg_v01 *config_req)
-{
- IPA_MHI_FUNC_ENTRY();
- ipa_mhi_cache_dl_ul_sync_info(config_req);
-
- if (ipa_mhi_ctx && ipa_mhi_ctx->state != IPA_MHI_STATE_INITIALIZED)
- ipa_uc_mhi_send_dl_ul_sync_info(cached_dl_ul_sync_info);
-
IPA_MHI_FUNC_EXIT();
return 0;
}
#include "ipa_qmi_service.h"
#include "ipa_ram_mmap.h"
+#include "../ipa_common_i.h"
#define IPA_Q6_SVC_VERS 1
#define IPA_A5_SVC_VERS 1
}
/**
- * ipa_uc_state_check() - Check the status of the uC interface
+ * ipa2_uc_state_check() - Check the status of the uC interface
*
* Return value: 0 if the uC is loaded, interface is initialized
* and there was no recent failure in one of the commands.
* A negative value is returned otherwise.
*/
-int ipa_uc_state_check(void)
+int ipa2_uc_state_check(void)
{
if (!ipa_ctx->uc_ctx.uc_inited) {
IPAERR("uC interface not initialized\n");
return 0;
}
-EXPORT_SYMBOL(ipa_uc_state_check);
+EXPORT_SYMBOL(ipa2_uc_state_check);
/**
* ipa_uc_loaded_check() - Check the uC has been loaded
IPADBG("this=%p evt=%lu ptr=%p\n", this, event, ptr);
- result = ipa_uc_state_check();
+ result = ipa2_uc_state_check();
if (result)
goto fail;
IPA_ACTIVE_CLIENTS_PREP_SIMPLE(log_info);
mutex_lock(&ipa_ctx->uc_ctx.uc_lock);
- if (ipa_uc_state_check()) {
+ if (ipa2_uc_state_check()) {
IPADBG("uC send command aborted\n");
mutex_unlock(&ipa_ctx->uc_ctx.uc_lock);
return -EBADF;
* continue with the sequence without resetting the
* pipe.
*/
- if (ipa_uc_state_check()) {
+ if (ipa2_uc_state_check()) {
IPADBG("uC interface will not be used to reset %s pipe %d\n",
IPA_CLIENT_IS_PROD(ipa_client) ? "CONS" : "PROD",
ep_idx);
* continue with the sequence without resetting the
* pipe.
*/
- if (ipa_uc_state_check()) {
+ if (ipa2_uc_state_check()) {
IPADBG("uC interface will not be used to reset %s pipe %d\n",
IPA_CLIENT_IS_PROD(ipa_client) ? "CONS" : "PROD",
ep_idx);
* If the uC interface has not been initialized yet,
* don't notify the uC on the enable/disable
*/
- if (ipa_uc_state_check()) {
+ if (ipa2_uc_state_check()) {
IPADBG("uC interface will not notify the UC on clock state\n");
return 0;
}
}
}
-int ipa_uc_mhi_init(void (*ready_cb)(void), void (*wakeup_request_cb)(void))
+int ipa2_uc_mhi_init(void (*ready_cb)(void), void (*wakeup_request_cb)(void))
{
struct ipa_uc_hdlrs hdlrs;
return 0;
}
+void ipa2_uc_mhi_cleanup(void)
+{
+ struct ipa_uc_hdlrs null_hdlrs = { 0 };
+
+ IPADBG("Enter\n");
+
+ if (!ipa_uc_mhi_ctx) {
+ IPAERR("ipa3_uc_mhi_ctx is not initialized\n");
+ return;
+ }
+ ipa_uc_register_handlers(IPA_HW_FEATURE_MHI, &null_hdlrs);
+ kfree(ipa_uc_mhi_ctx);
+ ipa_uc_mhi_ctx = NULL;
+
+ IPADBG("Done\n");
+}
+
int ipa_uc_mhi_init_engine(struct ipa_mhi_msi_info *msi, u32 mmio_addr,
u32 host_ctrl_addr, u32 host_data_addr, u32 first_ch_idx,
u32 first_evt_idx)
}
-int ipa_uc_mhi_reset_channel(int channelHandle)
+int ipa2_uc_mhi_reset_channel(int channelHandle)
{
union IpaHwMhiChangeChannelStateCmdData_t cmd;
union IpaHwMhiChangeChannelStateResponseData_t uc_rsp;
return res;
}
-int ipa_uc_mhi_suspend_channel(int channelHandle)
+int ipa2_uc_mhi_suspend_channel(int channelHandle)
{
union IpaHwMhiChangeChannelStateCmdData_t cmd;
union IpaHwMhiChangeChannelStateResponseData_t uc_rsp;
return res;
}
-int ipa_uc_mhi_stop_event_update_channel(int channelHandle)
+int ipa2_uc_mhi_stop_event_update_channel(int channelHandle)
{
union IpaHwMhiStopEventUpdateData_t cmd;
int res;
return res;
}
-int ipa_uc_mhi_send_dl_ul_sync_info(union IpaHwMhiDlUlSyncCmdData_t cmd)
+int ipa2_uc_mhi_send_dl_ul_sync_info(union IpaHwMhiDlUlSyncCmdData_t *cmd)
{
int res;
}
IPADBG("isDlUlSyncEnabled=0x%x UlAccmVal=0x%x\n",
- cmd.params.isDlUlSyncEnabled, cmd.params.UlAccmVal);
+ cmd->params.isDlUlSyncEnabled, cmd->params.UlAccmVal);
IPADBG("ulMsiEventThreshold=0x%x dlMsiEventThreshold=0x%x\n",
- cmd.params.ulMsiEventThreshold, cmd.params.dlMsiEventThreshold);
+ cmd->params.ulMsiEventThreshold,
+ cmd->params.dlMsiEventThreshold);
IPA_ACTIVE_CLIENTS_INC_SIMPLE();
- res = ipa_uc_send_cmd(cmd.raw32b,
+ res = ipa_uc_send_cmd(cmd->raw32b,
IPA_CPU_2_HW_CMD_MHI_DL_UL_SYNC_INFO, 0, false, HZ);
if (res) {
IPAERR("ipa_uc_send_cmd failed %d\n", res);
return res;
}
-int ipa_uc_mhi_print_stats(char *dbg_buff, int size)
+int ipa2_uc_mhi_print_stats(char *dbg_buff, int size)
{
int nBytes = 0;
int i;
}
}
- result = ipa_uc_state_check();
+ result = ipa2_uc_state_check();
if (result)
return result;
return -EINVAL;
}
- result = ipa_uc_state_check();
+ result = ipa2_uc_state_check();
if (result)
return result;
return -EINVAL;
}
- result = ipa_uc_state_check();
+ result = ipa2_uc_state_check();
if (result)
return result;
return -EINVAL;
}
- result = ipa_uc_state_check();
+ result = ipa2_uc_state_check();
if (result)
return result;
return -EINVAL;
}
- result = ipa_uc_state_check();
+ result = ipa2_uc_state_check();
if (result)
return result;
return -EINVAL;
}
- result = ipa_uc_state_check();
+ result = ipa2_uc_state_check();
if (result)
return result;
return -EINVAL;
}
- result = ipa_uc_state_check();
+ result = ipa2_uc_state_check();
if (result)
return result;
return -EINVAL;
}
- result = ipa_uc_state_check();
+ result = ipa2_uc_state_check();
if (result) {
inout->is_uC_ready = false;
ipa_ctx->uc_wdi_ctx.uc_ready_cb = inout->notify;
return NULL;
}
+static void ipa2_get_holb(int ep_idx, struct ipa_ep_cfg_holb *holb)
+{
+ *holb = ipa_ctx->ep[ep_idx].holb;
+}
+
+static int ipa2_generate_tag_process(void)
+{
+ int res;
+
+ res = ipa_tag_process(NULL, 0, HZ);
+ if (res)
+ IPAERR("TAG process failed\n");
+
+ return res;
+}
+
+static void ipa2_set_tag_process_before_gating(bool val)
+{
+ ipa_ctx->tag_process_before_gating = val;
+}
int ipa2_bind_api_controller(enum ipa_hw_type ipa_hw_type,
struct ipa_api_controller *api_ctrl)
api_ctrl->ipa_cfg_ep_deaggr = ipa2_cfg_ep_deaggr;
api_ctrl->ipa_cfg_ep_route = ipa2_cfg_ep_route;
api_ctrl->ipa_cfg_ep_holb = ipa2_cfg_ep_holb;
+ api_ctrl->ipa_get_holb = ipa2_get_holb;
+ api_ctrl->ipa_set_tag_process_before_gating =
+ ipa2_set_tag_process_before_gating;
api_ctrl->ipa_cfg_ep_cfg = ipa2_cfg_ep_cfg;
api_ctrl->ipa_cfg_ep_metadata_mask = ipa2_cfg_ep_metadata_mask;
api_ctrl->ipa_cfg_ep_holb_by_client = ipa2_cfg_ep_holb_by_client;
api_ctrl->ipa_dma_async_memcpy = ipa2_dma_async_memcpy;
api_ctrl->ipa_dma_uc_memcpy = ipa2_dma_uc_memcpy;
api_ctrl->ipa_dma_destroy = ipa2_dma_destroy;
- api_ctrl->ipa_mhi_init = ipa2_mhi_init;
- api_ctrl->ipa_mhi_start = ipa2_mhi_start;
- api_ctrl->ipa_mhi_connect_pipe = ipa2_mhi_connect_pipe;
- api_ctrl->ipa_mhi_disconnect_pipe = ipa2_mhi_disconnect_pipe;
- api_ctrl->ipa_mhi_suspend = ipa2_mhi_suspend;
- api_ctrl->ipa_mhi_resume = ipa2_mhi_resume;
- api_ctrl->ipa_mhi_destroy = ipa2_mhi_destroy;
+ api_ctrl->ipa_mhi_init_engine = ipa2_mhi_init_engine;
+ api_ctrl->ipa_connect_mhi_pipe = ipa2_connect_mhi_pipe;
+ api_ctrl->ipa_disconnect_mhi_pipe = ipa2_disconnect_mhi_pipe;
+ api_ctrl->ipa_uc_mhi_reset_channel = ipa2_uc_mhi_reset_channel;
+ api_ctrl->ipa_mhi_sps_channel_empty = ipa2_mhi_sps_channel_empty;
+ api_ctrl->ipa_generate_tag_process = ipa2_generate_tag_process;
+ api_ctrl->ipa_disable_sps_pipe = ipa2_disable_sps_pipe;
+ api_ctrl->ipa_qmi_enable_force_clear_datapath_send =
+ qmi_enable_force_clear_datapath_send;
+ api_ctrl->ipa_qmi_disable_force_clear_datapath_send =
+ qmi_disable_force_clear_datapath_send;
+ api_ctrl->ipa_mhi_reset_channel_internal =
+ ipa2_mhi_reset_channel_internal;
+ api_ctrl->ipa_mhi_start_channel_internal =
+ ipa2_mhi_start_channel_internal;
+ api_ctrl->ipa_mhi_resume_channels_internal =
+ ipa2_mhi_resume_channels_internal;
+ api_ctrl->ipa_uc_mhi_send_dl_ul_sync_info =
+ ipa2_uc_mhi_send_dl_ul_sync_info;
+ api_ctrl->ipa_uc_mhi_init = ipa2_uc_mhi_init;
+ api_ctrl->ipa_uc_mhi_suspend_channel = ipa2_uc_mhi_suspend_channel;
+ api_ctrl->ipa_uc_mhi_stop_event_update_channel =
+ ipa2_uc_mhi_stop_event_update_channel;
+ api_ctrl->ipa_uc_mhi_cleanup = ipa2_uc_mhi_cleanup;
+ api_ctrl->ipa_uc_mhi_print_stats = ipa2_uc_mhi_print_stats;
+ api_ctrl->ipa_uc_state_check = ipa2_uc_state_check;
api_ctrl->ipa_write_qmap_id = ipa2_write_qmap_id;
api_ctrl->ipa_add_interrupt_handler = ipa2_add_interrupt_handler;
api_ctrl->ipa_remove_interrupt_handler = ipa2_remove_interrupt_handler;
struct ipahal_imm_cmd_dma_shared_mem cmd;
struct ipahal_imm_cmd_pyld *cmd_pyld;
struct ipa3_desc desc;
- struct ipa3_mem_buffer mem;
+ struct ipa_mem_buffer mem;
int rc;
if (memory_region_size == 0)
}
static int ipa3_q6_clean_q6_flt_tbls(enum ipa_ip_type ip,
- enum ipa_rule_type rlt, const struct ipa3_mem_buffer *mem)
+ enum ipa_rule_type rlt, const struct ipa_mem_buffer *mem)
{
struct ipa3_desc *desc;
struct ipahal_imm_cmd_dma_shared_mem cmd;
}
static int ipa3_q6_clean_q6_rt_tbls(enum ipa_ip_type ip,
- enum ipa_rule_type rlt, const struct ipa3_mem_buffer *mem)
+ enum ipa_rule_type rlt, const struct ipa_mem_buffer *mem)
{
struct ipa3_desc *desc;
struct ipahal_imm_cmd_dma_shared_mem cmd;
struct ipahal_imm_cmd_pyld *cmd_pyld;
struct ipahal_imm_cmd_register_write reg_write_cmd = {0};
int retval;
- struct ipa3_mem_buffer mem = { 0 };
+ struct ipa_mem_buffer mem = { 0 };
struct ipahal_reg_fltrt_hash_flush flush;
struct ipahal_reg_valmask valmask;
u64 *entry;
int _ipa_init_hdr_v3_0(void)
{
struct ipa3_desc desc = { 0 };
- struct ipa3_mem_buffer mem;
+ struct ipa_mem_buffer mem;
struct ipahal_imm_cmd_hdr_init_local cmd = {0};
struct ipahal_imm_cmd_pyld *cmd_pyld;
struct ipahal_imm_cmd_dma_shared_mem dma_cmd = { 0 };
int _ipa_init_rt4_v3(void)
{
struct ipa3_desc desc = { 0 };
- struct ipa3_mem_buffer mem;
+ struct ipa_mem_buffer mem;
struct ipahal_imm_cmd_ip_v4_routing_init v4_cmd;
struct ipahal_imm_cmd_pyld *cmd_pyld;
u64 *entry;
int _ipa_init_rt6_v3(void)
{
struct ipa3_desc desc = { 0 };
- struct ipa3_mem_buffer mem;
+ struct ipa_mem_buffer mem;
struct ipahal_imm_cmd_ip_v6_routing_init v6_cmd;
struct ipahal_imm_cmd_pyld *cmd_pyld;
u64 *entry;
int _ipa_init_flt4_v3(void)
{
struct ipa3_desc desc = { 0 };
- struct ipa3_mem_buffer mem;
+ struct ipa_mem_buffer mem;
struct ipahal_imm_cmd_ip_v4_filter_init v4_cmd;
struct ipahal_imm_cmd_pyld *cmd_pyld;
u64 *entry;
int _ipa_init_flt6_v3(void)
{
struct ipa3_desc desc = { 0 };
- struct ipa3_mem_buffer mem;
+ struct ipa_mem_buffer mem;
struct ipahal_imm_cmd_ip_v6_filter_init v6_cmd;
struct ipahal_imm_cmd_pyld *cmd_pyld;
u64 *entry;
static int ipa3_reconfigure_channel_to_gpi(struct ipa3_ep_context *ep,
struct gsi_chan_props *orig_chan_props,
- struct ipa3_mem_buffer *chan_dma)
+ struct ipa_mem_buffer *chan_dma)
{
struct gsi_chan_props chan_props;
enum gsi_status gsi_res;
enum gsi_status gsi_res;
struct gsi_chan_props orig_chan_props;
union gsi_channel_scratch orig_chan_scratch;
- struct ipa3_mem_buffer chan_dma;
+ struct ipa_mem_buffer chan_dma;
void *buff;
dma_addr_t dma_addr;
struct gsi_xfer_elem xfer_elem;
IPAERR("Error getting channel properties: %d\n", gsi_res);
return -EFAULT;
}
- memset(&chan_dma, 0, sizeof(struct ipa3_mem_buffer));
+ memset(&chan_dma, 0, sizeof(struct ipa_mem_buffer));
result = ipa3_reconfigure_channel_to_gpi(ep, &orig_chan_props,
&chan_dma);
if (result)
*
* @priv -not in use.
* @evt - event name - IPA_RECIVE.
- * @data -the ipa3_mem_buffer.
+ * @data -the ipa_mem_buffer.
*/
void ipa3_dma_async_memcpy_notify_cb(void *priv
, enum ipa_dp_evt_type evt, unsigned long data)
struct ipa3_dma_xfer_wrapper *xfer_descr_expected;
struct ipa3_sys_context *sys;
unsigned long flags;
- struct ipa3_mem_buffer *mem_info;
+ struct ipa_mem_buffer *mem_info;
IPADMA_FUNC_ENTRY();
- mem_info = (struct ipa3_mem_buffer *)data;
+ mem_info = (struct ipa_mem_buffer *)data;
ep_idx = ipa3_get_ep_mapping(IPA_CLIENT_MEMCPY_DMA_ASYNC_CONS);
sys = ipa3_ctx->ep[ep_idx].sys;
static void ipa3_cleanup_wlan_rx_common_cache(void);
static void ipa3_wq_repl_rx(struct work_struct *work);
static void ipa3_dma_memcpy_notify(struct ipa3_sys_context *sys,
- struct ipa3_mem_buffer *mem_info);
+ struct ipa_mem_buffer *mem_info);
static int ipa_gsi_setup_channel(struct ipa_sys_connect_params *in,
struct ipa3_ep_context *ep);
static int ipa_populate_tag_field(struct ipa3_desc *desc,
}
static void ipa3_dma_memcpy_notify(struct ipa3_sys_context *sys,
- struct ipa3_mem_buffer *mem_info)
+ struct ipa_mem_buffer *mem_info)
{
IPADBG_LOW("ENTER.\n");
if (unlikely(list_empty(&sys->head_desc_list))) {
struct ipa3_sys_context *sys_ptr;
struct ipa3_rx_pkt_wrapper *rx_pkt;
struct gsi_chan_xfer_notify xfer_notify;
- struct ipa3_mem_buffer mem_info = {0};
+ struct ipa_mem_buffer mem_info = {0};
enum ipa_client_type client;
if (sys->ep->bytes_xfered_valid) {
struct sps_iovec iov;
int ret;
int cnt = 0;
- struct ipa3_mem_buffer mem_info = {0};
+ struct ipa_mem_buffer mem_info = {0};
while ((in_poll_state ? atomic_read(&sys->curr_polling_state) :
!atomic_read(&sys->curr_polling_state))) {
* Return: 0 on success, negative on failure
*/
static int ipa_alloc_init_flt_tbl_hdr(enum ipa_ip_type ip,
- struct ipa3_mem_buffer *hash_hdr, struct ipa3_mem_buffer *nhash_hdr)
+ struct ipa_mem_buffer *hash_hdr, struct ipa_mem_buffer *nhash_hdr)
{
int num_hdrs;
u64 *hash_entr;
int res;
struct ipa3_flt_entry *entry;
u8 *tbl_mem_buf;
- struct ipa3_mem_buffer tbl_mem;
+ struct ipa_mem_buffer tbl_mem;
struct ipa3_flt_tbl *tbl;
int i;
int hdr_idx = 0;
* Return: 0 on success, negative on failure
*/
static int ipa_generate_flt_hw_tbl_img(enum ipa_ip_type ip,
- struct ipa3_mem_buffer *hash_hdr, struct ipa3_mem_buffer *nhash_hdr,
- struct ipa3_mem_buffer *hash_bdy, struct ipa3_mem_buffer *nhash_bdy)
+ struct ipa_mem_buffer *hash_hdr, struct ipa_mem_buffer *nhash_hdr,
+ struct ipa_mem_buffer *hash_bdy, struct ipa_mem_buffer *nhash_bdy)
{
u32 hash_bdy_start_ofst, nhash_bdy_start_ofst;
u32 hash_bdy_sz;
* Return: true if enough space available or false in other cases
*/
static bool ipa_flt_valid_lcl_tbl_size(enum ipa_ip_type ipt,
- enum ipa_rule_type rlt, struct ipa3_mem_buffer *bdy)
+ enum ipa_rule_type rlt, struct ipa_mem_buffer *bdy)
{
u16 avail;
*/
int __ipa_commit_flt_v3(enum ipa_ip_type ip)
{
- struct ipa3_mem_buffer hash_bdy, nhash_bdy;
- struct ipa3_mem_buffer hash_hdr, nhash_hdr;
+ struct ipa_mem_buffer hash_bdy, nhash_bdy;
+ struct ipa_mem_buffer hash_hdr, nhash_hdr;
int rc = 0;
struct ipa3_desc *desc;
struct ipahal_imm_cmd_register_write reg_write_cmd = {0};
*
* Returns: 0 on success, negative on failure
*/
-static int ipa3_generate_hdr_hw_tbl(struct ipa3_mem_buffer *mem)
+static int ipa3_generate_hdr_hw_tbl(struct ipa_mem_buffer *mem)
{
struct ipa3_hdr_entry *entry;
return 0;
}
-static int ipa3_hdr_proc_ctx_to_hw_format(struct ipa3_mem_buffer *mem,
+static int ipa3_hdr_proc_ctx_to_hw_format(struct ipa_mem_buffer *mem,
u32 hdr_base_addr)
{
struct ipa3_hdr_proc_ctx_entry *entry;
* Returns: 0 on success, negative on failure
*/
static int ipa3_generate_hdr_proc_ctx_hw_tbl(u32 hdr_sys_addr,
- struct ipa3_mem_buffer *mem, struct ipa3_mem_buffer *aligned_mem)
+ struct ipa_mem_buffer *mem, struct ipa_mem_buffer *aligned_mem)
{
u32 hdr_base_addr;
int __ipa_commit_hdr_v3_0(void)
{
struct ipa3_desc desc[2];
- struct ipa3_mem_buffer hdr_mem;
- struct ipa3_mem_buffer ctx_mem;
- struct ipa3_mem_buffer aligned_ctx_mem;
+ struct ipa_mem_buffer hdr_mem;
+ struct ipa_mem_buffer ctx_mem;
+ struct ipa_mem_buffer aligned_ctx_mem;
struct ipahal_imm_cmd_dma_shared_mem dma_cmd_hdr = {0};
struct ipahal_imm_cmd_dma_shared_mem dma_cmd_ctx = {0};
struct ipahal_imm_cmd_register_write reg_write_cmd = {0};
#define IPA_LAN_RX_HDR_NAME "ipa_lan_hdr"
#define IPA_INVALID_L4_PROTOCOL 0xFF
-#define IPA_CLIENT_IS_PROD(x) (x >= IPA_CLIENT_PROD && x < IPA_CLIENT_CONS)
-#define IPA_CLIENT_IS_CONS(x) (x >= IPA_CLIENT_CONS && x < IPA_CLIENT_MAX)
-
#define IPA_HW_TABLE_ALIGNMENT(start_ofst) \
(((start_ofst) + 127) & ~127)
#define IPA_RT_FLT_HW_RULE_BUF_SIZE (256)
#define IPA_GSI_CHANNEL_STOP_MAX_RETRY 10
#define IPA_GSI_CHANNEL_STOP_PKT_SIZE 1
-#define IPA_GSI_CHANNEL_STOP_SLEEP_MIN_USEC (1000)
-#define IPA_GSI_CHANNEL_STOP_SLEEP_MAX_USEC (2000)
#define IPA_GSI_CHANNEL_EMPTY_MAX_RETRY 15
#define IPA_GSI_CHANNEL_EMPTY_SLEEP_MIN_USEC (1000)
};
/**
- * struct ipa3_mem_buffer - IPA memory buffer
- * @base: base
- * @phys_base: physical base address
- * @size: size of memory buffer
- */
-struct ipa3_mem_buffer {
- void *base;
- dma_addr_t phys_base;
- u32 size;
-};
-
-/**
* struct ipa3_flt_entry - IPA filtering table entry
* @link: entry's link in global filtering enrties list
* @rule: filter rule
u32 cookie;
bool in_sys[IPA_RULE_TYPE_MAX];
u32 sz[IPA_RULE_TYPE_MAX];
- struct ipa3_mem_buffer curr_mem[IPA_RULE_TYPE_MAX];
- struct ipa3_mem_buffer prev_mem[IPA_RULE_TYPE_MAX];
+ struct ipa_mem_buffer curr_mem[IPA_RULE_TYPE_MAX];
+ struct ipa_mem_buffer prev_mem[IPA_RULE_TYPE_MAX];
int id;
struct idr rule_ids;
};
u32 rule_cnt;
bool in_sys[IPA_RULE_TYPE_MAX];
u32 sz[IPA_RULE_TYPE_MAX];
- struct ipa3_mem_buffer curr_mem[IPA_RULE_TYPE_MAX];
- struct ipa3_mem_buffer prev_mem[IPA_RULE_TYPE_MAX];
+ struct ipa_mem_buffer curr_mem[IPA_RULE_TYPE_MAX];
+ struct ipa_mem_buffer prev_mem[IPA_RULE_TYPE_MAX];
bool sticky_rear;
struct idr rule_ids;
};
*/
struct ipa3_tx_pkt_wrapper {
enum ipa3_desc_type type;
- struct ipa3_mem_buffer mem;
+ struct ipa_mem_buffer mem;
struct work_struct work;
struct list_head link;
void (*callback)(void *user1, int user2);
void *user1;
int user2;
struct ipa3_sys_context *sys;
- struct ipa3_mem_buffer mult;
+ struct ipa_mem_buffer mult;
u32 cnt;
void *bounce;
bool no_unmap_dma;
};
/**
- * enum ipa3_hw_mhi_channel_states - MHI channel state machine
- *
- * Values are according to MHI specification
- * @IPA_HW_MHI_CHANNEL_STATE_DISABLE: Channel is disabled and not processed by
- * the host or device.
- * @IPA_HW_MHI_CHANNEL_STATE_ENABLE: A channel is enabled after being
- * initialized and configured by host, including its channel context and
- * associated transfer ring. While this state, the channel is not active
- * and the device does not process transfer.
- * @IPA_HW_MHI_CHANNEL_STATE_RUN: The device processes transfers and doorbell
- * for channels.
- * @IPA_HW_MHI_CHANNEL_STATE_SUSPEND: Used to halt operations on the channel.
- * The device does not process transfers for the channel in this state.
- * This state is typically used to synchronize the transition to low power
- * modes.
- * @IPA_HW_MHI_CHANNEL_STATE_STOP: Used to halt operations on the channel.
- * The device does not process transfers for the channel in this state.
- * @IPA_HW_MHI_CHANNEL_STATE_ERROR: The device detected an error in an element
- * from the transfer ring associated with the channel.
- * @IPA_HW_MHI_CHANNEL_STATE_INVALID: Invalid state. Shall not be in use in
- * operational scenario.
- */
-enum ipa3_hw_mhi_channel_states {
- IPA_HW_MHI_CHANNEL_STATE_DISABLE = 0,
- IPA_HW_MHI_CHANNEL_STATE_ENABLE = 1,
- IPA_HW_MHI_CHANNEL_STATE_RUN = 2,
- IPA_HW_MHI_CHANNEL_STATE_SUSPEND = 3,
- IPA_HW_MHI_CHANNEL_STATE_STOP = 4,
- IPA_HW_MHI_CHANNEL_STATE_ERROR = 5,
- IPA_HW_MHI_CHANNEL_STATE_INVALID = 0xFF
-};
-
-/**
- * Structure holding the parameters for IPA_CPU_2_HW_CMD_MHI_DL_UL_SYNC_INFO
- * command. Parameters are sent as 32b immediate parameters.
- * @isDlUlSyncEnabled: Flag to indicate if DL UL Syncronization is enabled
- * @UlAccmVal: UL Timer Accumulation value (Period after which device will poll
- * for UL data)
- * @ulMsiEventThreshold: Threshold at which HW fires MSI to host for UL events
- * @dlMsiEventThreshold: Threshold at which HW fires MSI to host for DL events
- */
-union IpaHwMhiDlUlSyncCmdData_t {
- struct IpaHwMhiDlUlSyncCmdParams_t {
- u32 isDlUlSyncEnabled:8;
- u32 UlAccmVal:8;
- u32 ulMsiEventThreshold:8;
- u32 dlMsiEventThreshold:8;
- } params;
- u32 raw32b;
-};
-
-/**
* struct ipa3_uc_ctx - IPA uC context
* @uc_inited: Indicates if uC interface has been initialized
* @uc_loaded: Indicates if uC has loaded
uint aggregation_time_limit;
bool hdr_tbl_lcl;
bool hdr_proc_ctx_tbl_lcl;
- struct ipa3_mem_buffer hdr_mem;
- struct ipa3_mem_buffer hdr_proc_ctx_mem;
+ struct ipa_mem_buffer hdr_mem;
+ struct ipa_mem_buffer hdr_proc_ctx_mem;
bool ip4_rt_tbl_hash_lcl;
bool ip4_rt_tbl_nhash_lcl;
bool ip6_rt_tbl_hash_lcl;
bool ip4_flt_tbl_nhash_lcl;
bool ip6_flt_tbl_hash_lcl;
bool ip6_flt_tbl_nhash_lcl;
- struct ipa3_mem_buffer empty_rt_tbl_mem;
+ struct ipa_mem_buffer empty_rt_tbl_mem;
struct gen_pool *pipe_mem_pool;
struct dma_pool *dma_pool;
struct ipa3_active_clients ipa3_active_clients;
/*
* MHI
*/
-int ipa3_mhi_init(struct ipa_mhi_init_params *params);
-int ipa3_mhi_start(struct ipa_mhi_start_params *params);
+int ipa3_mhi_init_engine(struct ipa_mhi_init_engine *params);
+
+int ipa3_connect_mhi_pipe(
+ struct ipa_mhi_connect_params_internal *in,
+ u32 *clnt_hdl);
+
+int ipa3_disconnect_mhi_pipe(u32 clnt_hdl);
+
+bool ipa3_mhi_stop_gsi_channel(enum ipa_client_type client);
-int ipa3_mhi_connect_pipe(struct ipa_mhi_connect_params *in, u32 *clnt_hdl);
+int ipa3_mhi_reset_channel_internal(enum ipa_client_type client);
-int ipa3_mhi_disconnect_pipe(u32 clnt_hdl);
+int ipa3_mhi_start_channel_internal(enum ipa_client_type client);
-int ipa3_mhi_suspend(bool force);
+bool ipa3_has_open_aggr_frame(enum ipa_client_type client);
-int ipa3_mhi_resume(void);
+int ipa3_mhi_resume_channels_internal(enum ipa_client_type client,
+ bool LPTransitionRejected, bool brstmode_enabled,
+ union __packed gsi_channel_scratch ch_scratch, u8 index);
-void ipa3_mhi_destroy(void);
+int ipa3_mhi_destroy_channel(enum ipa_client_type client);
/*
* mux id
enum ipa_client_type ipa_client);
int ipa3_mhi_handle_ipa_config_req(struct ipa_config_req_msg_v01 *config_req);
+int ipa3_mhi_query_ch_info(enum ipa_client_type client,
+ struct gsi_chan_info *ch_info);
int ipa3_uc_interface_init(void);
int ipa3_uc_reset_pipe(enum ipa_client_type ipa_client);
int ipa3_uc_mhi_init(void (*ready_cb)(void), void (*wakeup_request_cb)(void));
void ipa3_uc_mhi_cleanup(void);
-int ipa3_uc_mhi_send_dl_ul_sync_info(union IpaHwMhiDlUlSyncCmdData_t cmd);
+int ipa3_uc_mhi_send_dl_ul_sync_info(union IpaHwMhiDlUlSyncCmdData_t *cmd);
int ipa3_uc_mhi_init_engine(struct ipa_mhi_msi_info *msi, u32 mmio_addr,
u32 host_ctrl_addr, u32 host_data_addr, u32 first_ch_idx,
u32 first_evt_idx);
#include <linux/mutex.h>
#include <linux/ipa.h>
#include <linux/msm_gsi.h>
+#include <linux/ipa_mhi.h>
+#include "../ipa_common_i.h"
#include "ipa_i.h"
#include "ipa_qmi_service.h"
#define IPA_MHI_FUNC_EXIT() \
IPA_MHI_DBG_LOW("EXIT\n")
-#define IPA_MHI_GSI_ER_START 10
-#define IPA_MHI_GSI_ER_END 16
-
-#define IPA_MHI_RM_TIMEOUT_MSEC 10000
-
-#define IPA_MHI_CH_EMPTY_TIMEOUT_MSEC 10
-
#define IPA_MHI_MAX_UL_CHANNELS 1
#define IPA_MHI_MAX_DL_CHANNELS 1
-#if (IPA_MHI_MAX_UL_CHANNELS + IPA_MHI_MAX_DL_CHANNELS) > \
- (IPA_MHI_GSI_ER_END - IPA_MHI_GSI_ER_START)
-#error not enought event rings for MHI
-#endif
-
-#define IPA_MHI_SUSPEND_SLEEP_MIN 900
-#define IPA_MHI_SUSPEND_SLEEP_MAX 1100
-
/* bit #40 in address should be asserted for MHI transfers over pcie */
#define IPA_MHI_HOST_ADDR_COND(addr) \
- ((ipa3_mhi_ctx->assert_bit40)?(IPA_MHI_HOST_ADDR(addr)):(addr))
-
-enum ipa3_mhi_state {
- IPA_MHI_STATE_INITIALIZED,
- IPA_MHI_STATE_READY,
- IPA_MHI_STATE_STARTED,
- IPA_MHI_STATE_SUSPEND_IN_PROGRESS,
- IPA_MHI_STATE_SUSPENDED,
- IPA_MHI_STATE_RESUME_IN_PROGRESS,
- IPA_MHI_STATE_MAX
-};
-
-static char *ipa3_mhi_state_str[] = {
- __stringify(IPA_MHI_STATE_INITIALIZED),
- __stringify(IPA_MHI_STATE_READY),
- __stringify(IPA_MHI_STATE_STARTED),
- __stringify(IPA_MHI_STATE_SUSPEND_IN_PROGRESS),
- __stringify(IPA_MHI_STATE_SUSPENDED),
- __stringify(IPA_MHI_STATE_RESUME_IN_PROGRESS),
-};
-
-#define MHI_STATE_STR(state) \
- (((state) >= 0 && (state) < IPA_MHI_STATE_MAX) ? \
- ipa3_mhi_state_str[(state)] : \
- "INVALID")
-
-enum ipa_mhi_dma_dir {
- IPA_MHI_DMA_TO_HOST,
- IPA_MHI_DMA_FROM_HOST,
-};
+ ((params->assert_bit40)?(IPA_MHI_HOST_ADDR(addr)):(addr))
/**
* enum ipa3_mhi_burst_mode - MHI channel burst mode state
IPA_MHI_POLLING_MODE_POLL_MODE,
};
-struct ipa3_mhi_ch_ctx {
- u8 chstate;/*0-7*/
- u8 brstmode:2;/*8-9*/
- u8 pollcfg:6;/*10-15*/
- u16 rsvd;/*16-31*/
- u32 chtype;
- u32 erindex;
- u64 rbase;
- u64 rlen;
- u64 rp;
- u64 wp;
-} __packed;
-
-struct ipa3_mhi_ev_ctx {
- u32 intmodc:16;
- u32 intmodt:16;
- u32 ertype;
- u32 msivec;
- u64 rbase;
- u64 rlen;
- u64 rp;
- u64 wp;
-} __packed;
-
-/**
- * struct ipa3_mhi_channel_ctx - MHI Channel context
- * @valid: entry is valid
- * @id: MHI channel ID
- * @index: channel handle for uC
- * @ep: IPA endpoint context
- * @state: Channel state
- * @stop_in_proc: flag to indicate if channel was stopped completely
- * @ch_info: information about channel occupancy
- * @channel_context_addr : the channel context address in host address space
- * @ch_ctx_host: MHI Channel context
- * @event_context_addr: the event context address in host address space
- * @ev_ctx_host: MHI event context
- * @cached_gsi_evt_ring_hdl: GSI channel event ring handle
- * @brstmode_enabled: is burst mode enabled for this channel?
- * @ch_scratch: the channel scratch configuration
- */
-struct ipa3_mhi_channel_ctx {
- bool valid;
- u8 id;
- u8 index;
- struct ipa3_ep_context *ep;
- enum ipa3_hw_mhi_channel_states state;
- bool stop_in_proc;
- struct gsi_chan_info ch_info;
- u64 channel_context_addr;
- struct ipa3_mhi_ch_ctx ch_ctx_host;
- u64 event_context_addr;
- struct ipa3_mhi_ev_ctx ev_ctx_host;
- unsigned long cached_gsi_evt_ring_hdl;
- bool brstmode_enabled;
- union __packed gsi_channel_scratch ch_scratch;
-};
-
-enum ipa3_mhi_rm_state {
- IPA_MHI_RM_STATE_RELEASED,
- IPA_MHI_RM_STATE_REQUESTED,
- IPA_MHI_RM_STATE_GRANTED,
- IPA_MHI_RM_STATE_MAX
-};
-
-/**
- * struct ipa3_mhi_ctx - IPA MHI context
- * @state: IPA MHI state
- * @state_lock: lock for state read/write operations
- * @msi: Message Signaled Interrupts parameters
- * @mmio_addr: MHI MMIO physical address
- * @first_ch_idx: First channel ID for hardware accelerated channels.
- * @first_er_idx: First event ring ID for hardware accelerated channels.
- * @host_ctrl_addr: Base address of MHI control data structures
- * @host_data_addr: Base address of MHI data buffers
- * @channel_context_addr: channel context array address in host address space
- * @event_context_addr: event context array address in host address space
- * @cb_notify: client callback
- * @cb_priv: client private data to be provided in client callback
- * @ul_channels: IPA MHI uplink channel contexts
- * @dl_channels: IPA MHI downlink channel contexts
- * @total_channels: Total number of channels ever connected to IPA MHI
- * @rm_prod_granted_comp: Completion object for MHI producer resource in IPA RM
- * @rm_cons_state: MHI consumer resource state in IPA RM
- * @rm_cons_comp: Completion object for MHI consumer resource in IPA RM
- * @trigger_wakeup: trigger wakeup callback ?
- * @wakeup_notified: MHI Client wakeup function was called
- * @wq: workqueue for wakeup event
- * @qmi_req_id: QMI request unique id
- * @use_ipadma: use IPADMA to access host space
- * @assert_bit40: should assert bit 40 in order to access hots space.
- * if PCIe iATU is configured then not need to assert bit40
- * @ test_mode: flag to indicate if IPA MHI is in unit test mode
- */
-struct ipa3_mhi_ctx {
- enum ipa3_mhi_state state;
- spinlock_t state_lock;
- struct ipa_mhi_msi_info msi;
- u32 mmio_addr;
- u32 first_ch_idx;
- u32 first_er_idx;
- u32 host_ctrl_addr;
- u32 host_data_addr;
- u64 channel_context_array_addr;
- u64 event_context_array_addr;
- mhi_client_cb cb_notify;
- void *cb_priv;
- struct ipa3_mhi_channel_ctx ul_channels[IPA_MHI_MAX_UL_CHANNELS];
- struct ipa3_mhi_channel_ctx dl_channels[IPA_MHI_MAX_DL_CHANNELS];
- u32 total_channels;
- struct completion rm_prod_granted_comp;
- enum ipa3_mhi_rm_state rm_cons_state;
- struct completion rm_cons_comp;
- bool trigger_wakeup;
- bool wakeup_notified;
- struct workqueue_struct *wq;
- u32 qmi_req_id;
- u32 use_ipadma;
- bool assert_bit40;
- bool test_mode;
-};
-
-static struct ipa3_mhi_ctx *ipa3_mhi_ctx;
-
-static void ipa3_mhi_wq_notify_wakeup(struct work_struct *work);
-static DECLARE_WORK(ipa_mhi_notify_wakeup_work, ipa3_mhi_wq_notify_wakeup);
-
-static void ipa3_mhi_wq_notify_ready(struct work_struct *work);
-static DECLARE_WORK(ipa_mhi_notify_ready_work, ipa3_mhi_wq_notify_ready);
-
-static union IpaHwMhiDlUlSyncCmdData_t ipa3_cached_dl_ul_sync_info;
-
-#ifdef CONFIG_DEBUG_FS
-#define IPA_MHI_MAX_MSG_LEN 512
-static char dbg_buff[IPA_MHI_MAX_MSG_LEN];
-static struct dentry *dent;
-
-static char *ipa3_mhi_channel_state_str[] = {
- __stringify(IPA_HW_MHI_CHANNEL_STATE_DISABLE),
- __stringify(IPA_HW_MHI_CHANNEL_STATE_ENABLE),
- __stringify(IPA_HW_MHI_CHANNEL_STATE_RUN),
- __stringify(IPA_HW_MHI_CHANNEL_STATE_SUSPEND),
- __stringify(IPA_HW_MHI_CHANNEL_STATE_STOP),
- __stringify(IPA_HW_MHI_CHANNEL_STATE_ERROR),
-};
-
-#define MHI_CH_STATE_STR(state) \
- (((state) >= 0 && (state) <= IPA_HW_MHI_CHANNEL_STATE_ERROR) ? \
- ipa3_mhi_channel_state_str[(state)] : \
- "INVALID")
-
-static int ipa_mhi_read_write_host(enum ipa_mhi_dma_dir dir, void *dev_addr,
- u64 host_addr, int size)
+bool ipa3_mhi_stop_gsi_channel(enum ipa_client_type client)
{
- struct ipa3_mem_buffer mem;
int res;
+ int ipa_ep_idx;
+ struct ipa3_ep_context *ep;
IPA_MHI_FUNC_ENTRY();
-
- if (ipa3_mhi_ctx->use_ipadma) {
- host_addr = IPA_MHI_HOST_ADDR_COND(host_addr);
-
- mem.size = size;
- mem.base = dma_alloc_coherent(ipa3_ctx->pdev, mem.size,
- &mem.phys_base, GFP_KERNEL);
- if (!mem.base) {
- IPAERR("dma_alloc_coherent failed, DMA buff size %d\n",
- mem.size);
- return -ENOMEM;
- }
-
- if (dir == IPA_MHI_DMA_FROM_HOST) {
- res = ipa_dma_sync_memcpy(mem.phys_base, host_addr,
- size);
- if (res) {
- IPAERR("ipa_dma_sync_memcpy from host fail%d\n",
- res);
- goto fail_memcopy;
- }
- memcpy(dev_addr, mem.base, size);
- } else {
- memcpy(mem.base, dev_addr, size);
- res = ipa_dma_sync_memcpy(host_addr, mem.phys_base,
- size);
- if (res) {
- IPAERR("ipa_dma_sync_memcpy to host fail %d\n",
- res);
- goto fail_memcopy;
- }
- }
- dma_free_coherent(ipa3_ctx->pdev, mem.size, mem.base,
- mem.phys_base);
- } else {
- void *host_ptr;
-
- if (!ipa3_mhi_ctx->test_mode)
- host_ptr = ioremap(host_addr, size);
- else
- host_ptr = phys_to_virt(host_addr);
- if (!host_ptr) {
- IPAERR("ioremap failed for 0x%llx\n", host_addr);
- return -EFAULT;
- }
- if (dir == IPA_MHI_DMA_FROM_HOST)
- memcpy(dev_addr, host_ptr, size);
- else
- memcpy(host_ptr, dev_addr, size);
- if (!ipa3_mhi_ctx->test_mode)
- iounmap(host_ptr);
- }
-
- IPA_MHI_FUNC_EXIT();
- return 0;
-
-fail_memcopy:
- dma_free_coherent(ipa3_ctx->pdev, mem.size, mem.base,
- mem.phys_base);
- return res;
-}
-
-static int ipa3_mhi_print_channel_info(struct ipa3_mhi_channel_ctx *channel,
- char *buff, int len)
-{
- int nbytes = 0;
-
- if (channel->valid) {
- nbytes += scnprintf(&buff[nbytes],
- len - nbytes,
- "channel idx=%d ch_id=%d client=%d state=%s\n",
- channel->index, channel->id, channel->ep->client,
- MHI_CH_STATE_STR(channel->state));
-
- nbytes += scnprintf(&buff[nbytes],
- len - nbytes,
- " stop_in_proc=%d gsi_chan_hdl=%ld\n",
- channel->stop_in_proc, channel->ep->gsi_chan_hdl);
-
- nbytes += scnprintf(&buff[nbytes],
- len - nbytes,
- " ch_ctx=%llx\n",
- channel->channel_context_addr);
-
- nbytes += scnprintf(&buff[nbytes],
- len - nbytes,
- " gsi_evt_ring_hdl=%ld ev_ctx=%llx\n",
- channel->ep->gsi_evt_ring_hdl,
- channel->event_context_addr);
- }
- return nbytes;
-}
-
-static int ipa3_mhi_print_host_channel_ctx_info(
- struct ipa3_mhi_channel_ctx *channel, char *buff, int len)
-{
- int res, nbytes = 0;
- struct ipa3_mhi_ch_ctx ch_ctx_host;
-
- memset(&ch_ctx_host, 0, sizeof(ch_ctx_host));
-
- /* reading ch context from host */
- res = ipa_mhi_read_write_host(IPA_MHI_DMA_FROM_HOST,
- &ch_ctx_host, channel->channel_context_addr,
- sizeof(ch_ctx_host));
- if (res) {
- nbytes += scnprintf(&buff[nbytes], len - nbytes,
- "Failed to read from host %d\n", res);
- return nbytes;
+ ipa_ep_idx = ipa3_get_ep_mapping(client);
+ if (ipa_ep_idx == -1) {
+ IPA_MHI_ERR("Invalid client.\n");
+ return -EINVAL;
}
- nbytes += scnprintf(&buff[nbytes], len - nbytes,
- "ch_id: %d\n", channel->id);
- nbytes += scnprintf(&buff[nbytes], len - nbytes,
- "chstate: 0x%x\n", ch_ctx_host.chstate);
- nbytes += scnprintf(&buff[nbytes], len - nbytes,
- "brstmode: 0x%x\n", ch_ctx_host.brstmode);
- nbytes += scnprintf(&buff[nbytes], len - nbytes,
- "chtype: 0x%x\n", ch_ctx_host.chtype);
- nbytes += scnprintf(&buff[nbytes], len - nbytes,
- "erindex: 0x%x\n", ch_ctx_host.erindex);
- nbytes += scnprintf(&buff[nbytes], len - nbytes,
- "rbase: 0x%llx\n", ch_ctx_host.rbase);
- nbytes += scnprintf(&buff[nbytes], len - nbytes,
- "rlen: 0x%llx\n", ch_ctx_host.rlen);
- nbytes += scnprintf(&buff[nbytes], len - nbytes,
- "rp: 0x%llx\n", ch_ctx_host.rp);
- nbytes += scnprintf(&buff[nbytes], len - nbytes,
- "wp: 0x%llx\n", ch_ctx_host.wp);
-
- return nbytes;
-}
-
-static ssize_t ipa3_mhi_debugfs_stats(struct file *file,
- char __user *ubuf,
- size_t count,
- loff_t *ppos)
-{
- int nbytes = 0;
- int i;
- struct ipa3_mhi_channel_ctx *channel;
-
- nbytes += scnprintf(&dbg_buff[nbytes],
- IPA_MHI_MAX_MSG_LEN - nbytes,
- "IPA MHI state: %s\n", MHI_STATE_STR(ipa3_mhi_ctx->state));
-
- for (i = 0; i < IPA_MHI_MAX_UL_CHANNELS; i++) {
- channel = &ipa3_mhi_ctx->ul_channels[i];
- nbytes += ipa3_mhi_print_channel_info(channel,
- &dbg_buff[nbytes], IPA_MHI_MAX_MSG_LEN - nbytes);
+ ep = &ipa3_ctx->ep[ipa_ep_idx];
+ IPA_MHI_DBG_LOW("Stopping GSI channel %ld\n", ep->gsi_chan_hdl);
+ res = gsi_stop_channel(ep->gsi_chan_hdl);
+ if (res != 0 &&
+ res != -GSI_STATUS_AGAIN &&
+ res != -GSI_STATUS_TIMED_OUT) {
+ IPA_MHI_ERR("GSI stop channel failed %d\n",
+ res);
+ WARN_ON(1);
+ return false;
}
- for (i = 0; i < IPA_MHI_MAX_DL_CHANNELS; i++) {
- channel = &ipa3_mhi_ctx->dl_channels[i];
- nbytes += ipa3_mhi_print_channel_info(channel,
- &dbg_buff[nbytes], IPA_MHI_MAX_MSG_LEN - nbytes);
+ if (res == 0) {
+ IPA_MHI_DBG_LOW("GSI channel %ld STOP\n",
+ ep->gsi_chan_hdl);
+ return true;
}
- return simple_read_from_buffer(ubuf, count, ppos, dbg_buff, nbytes);
-}
-
-static ssize_t ipa3_mhi_debugfs_uc_stats(struct file *file,
- char __user *ubuf,
- size_t count,
- loff_t *ppos)
-{
- int nbytes = 0;
-
- nbytes += ipa3_uc_mhi_print_stats(dbg_buff, IPA_MHI_MAX_MSG_LEN);
- return simple_read_from_buffer(ubuf, count, ppos, dbg_buff, nbytes);
+ return false;
}
-static ssize_t ipa3_mhi_debugfs_dump_host_ch_ctx_arr(struct file *file,
- char __user *ubuf,
- size_t count,
- loff_t *ppos)
+static int ipa3_mhi_reset_gsi_channel(enum ipa_client_type client)
{
- int i, nbytes = 0;
- struct ipa3_mhi_channel_ctx *channel;
-
- if (ipa3_mhi_ctx->state == IPA_MHI_STATE_INITIALIZED ||
- ipa3_mhi_ctx->state == IPA_MHI_STATE_READY) {
- nbytes += scnprintf(&dbg_buff[nbytes],
- IPA_MHI_MAX_MSG_LEN - nbytes,
- "Cannot dump host channel context ");
- nbytes += scnprintf(&dbg_buff[nbytes],
- IPA_MHI_MAX_MSG_LEN - nbytes,
- "before IPA MHI was STARTED\n");
- return simple_read_from_buffer(ubuf, count, ppos,
- dbg_buff, nbytes);
- }
- if (ipa3_mhi_ctx->state == IPA_MHI_STATE_SUSPENDED) {
- nbytes += scnprintf(&dbg_buff[nbytes],
- IPA_MHI_MAX_MSG_LEN - nbytes,
- "IPA MHI is suspended, cannot dump channel ctx array");
- nbytes += scnprintf(&dbg_buff[nbytes],
- IPA_MHI_MAX_MSG_LEN - nbytes,
- " from host -PCIe can be in D3 state\n");
- return simple_read_from_buffer(ubuf, count, ppos,
- dbg_buff, nbytes);
- }
+ int res;
+ int clnt_hdl;
- nbytes += scnprintf(&dbg_buff[nbytes],
- IPA_MHI_MAX_MSG_LEN - nbytes,
- "channel contex array - dump from host\n");
- nbytes += scnprintf(&dbg_buff[nbytes],
- IPA_MHI_MAX_MSG_LEN - nbytes,
- "***** UL channels *******\n");
-
- for (i = 0; i < IPA_MHI_MAX_UL_CHANNELS; i++) {
- channel = &ipa3_mhi_ctx->ul_channels[i];
- if (!channel->valid)
- continue;
- nbytes += ipa3_mhi_print_host_channel_ctx_info(channel,
- &dbg_buff[nbytes],
- IPA_MHI_MAX_MSG_LEN - nbytes);
- }
+ IPA_MHI_FUNC_ENTRY();
- nbytes += scnprintf(&dbg_buff[nbytes],
- IPA_MHI_MAX_MSG_LEN - nbytes,
- "\n***** DL channels *******\n");
+ clnt_hdl = ipa3_get_ep_mapping(client);
+ if (clnt_hdl < 0)
+ return -EFAULT;
- for (i = 0; i < IPA_MHI_MAX_DL_CHANNELS; i++) {
- channel = &ipa3_mhi_ctx->dl_channels[i];
- if (!channel->valid)
- continue;
- nbytes += ipa3_mhi_print_host_channel_ctx_info(channel,
- &dbg_buff[nbytes], IPA_MHI_MAX_MSG_LEN - nbytes);
+ res = ipa3_reset_gsi_channel(clnt_hdl);
+ if (res) {
+ IPA_MHI_ERR("ipa3_reset_gsi_channel failed %d\n", res);
+ return -EFAULT;
}
- return simple_read_from_buffer(ubuf, count, ppos, dbg_buff, nbytes);
+ IPA_MHI_FUNC_EXIT();
+ return 0;
}
-const struct file_operations ipa3_mhi_stats_ops = {
- .read = ipa3_mhi_debugfs_stats,
-};
-
-const struct file_operations ipa3_mhi_uc_stats_ops = {
- .read = ipa3_mhi_debugfs_uc_stats,
-};
-
-const struct file_operations ipa3_mhi_dump_host_ch_ctx_ops = {
- .read = ipa3_mhi_debugfs_dump_host_ch_ctx_arr,
-};
-
-
-static void ipa3_mhi_debugfs_init(void)
+int ipa3_mhi_reset_channel_internal(enum ipa_client_type client)
{
- const mode_t read_only_mode = S_IRUSR | S_IRGRP | S_IROTH;
- const mode_t read_write_mode = S_IRUSR | S_IRGRP | S_IROTH |
- S_IWUSR | S_IWGRP;
- struct dentry *file;
+ int res;
IPA_MHI_FUNC_ENTRY();
- dent = debugfs_create_dir("ipa_mhi", 0);
- if (IS_ERR(dent)) {
- IPA_MHI_ERR("fail to create folder ipa_mhi\n");
- return;
- }
-
- file = debugfs_create_file("stats", read_only_mode, dent,
- 0, &ipa3_mhi_stats_ops);
- if (!file || IS_ERR(file)) {
- IPA_MHI_ERR("fail to create file stats\n");
- goto fail;
- }
-
- file = debugfs_create_file("uc_stats", read_only_mode, dent,
- 0, &ipa3_mhi_uc_stats_ops);
- if (!file || IS_ERR(file)) {
- IPA_MHI_ERR("fail to create file uc_stats\n");
- goto fail;
- }
-
- file = debugfs_create_u32("use_ipadma", read_write_mode, dent,
- &ipa3_mhi_ctx->use_ipadma);
- if (!file || IS_ERR(file)) {
- IPA_MHI_ERR("fail to create file use_ipadma\n");
- goto fail;
+ res = ipa3_mhi_reset_gsi_channel(client);
+ if (res) {
+ IPAERR("ipa3_mhi_reset_gsi_channel failed\n");
+ ipa_assert();
+ return res;
}
- file = debugfs_create_file("dump_host_channel_ctx_array",
- read_only_mode, dent, 0, &ipa3_mhi_dump_host_ch_ctx_ops);
- if (!file || IS_ERR(file)) {
- IPA_MHI_ERR("fail to create file dump_host_channel_ctx_arr\n");
- goto fail;
+ res = ipa3_disable_data_path(ipa3_get_ep_mapping(client));
+ if (res) {
+ IPA_MHI_ERR("ipa3_disable_data_path failed %d\n", res);
+ return res;
}
-
IPA_MHI_FUNC_EXIT();
- return;
-fail:
- debugfs_remove_recursive(dent);
-}
-static void ipa3_mhi_debugfs_destroy(void)
-{
- debugfs_remove_recursive(dent);
+ return 0;
}
-#else
-static void ipa3_mhi_debugfs_init(void) {}
-static void ipa3_mhi_debugfs_destroy(void) {}
-#endif /* CONFIG_DEBUG_FS */
-
-
-static void ipa3_mhi_cache_dl_ul_sync_info(
- struct ipa_config_req_msg_v01 *config_req)
+int ipa3_mhi_start_channel_internal(enum ipa_client_type client)
{
- ipa3_cached_dl_ul_sync_info.params.isDlUlSyncEnabled = true;
- ipa3_cached_dl_ul_sync_info.params.UlAccmVal =
- (config_req->ul_accumulation_time_limit_valid) ?
- config_req->ul_accumulation_time_limit : 0;
- ipa3_cached_dl_ul_sync_info.params.ulMsiEventThreshold =
- (config_req->ul_msi_event_threshold_valid) ?
- config_req->ul_msi_event_threshold : 0;
- ipa3_cached_dl_ul_sync_info.params.dlMsiEventThreshold =
- (config_req->dl_msi_event_threshold_valid) ?
- config_req->dl_msi_event_threshold : 0;
-}
+ int res;
-/**
- * ipa3_mhi_wq_notify_wakeup() - Notify MHI client on data available
- *
- * This function is called from IPA MHI workqueue to notify
- * MHI client driver on data available event.
- */
-static void ipa3_mhi_wq_notify_wakeup(struct work_struct *work)
-{
IPA_MHI_FUNC_ENTRY();
- ipa3_mhi_ctx->cb_notify(ipa3_mhi_ctx->cb_priv,
- IPA_MHI_EVENT_DATA_AVAILABLE, 0);
- IPA_MHI_FUNC_EXIT();
-}
-/**
- * ipa3_mhi_notify_wakeup() - Schedule work to notify data available
- *
- * This function will schedule a work to notify data available event.
- * In case this function is called more than once, only one notification will
- * be sent to MHI client driver. No further notifications will be sent until
- * IPA MHI state will become STARTED.
- */
-static void ipa3_mhi_notify_wakeup(void)
-{
- IPA_MHI_FUNC_ENTRY();
- if (ipa3_mhi_ctx->wakeup_notified) {
- IPADBG("wakeup already called\n");
- return;
+ res = ipa3_enable_data_path(ipa3_get_ep_mapping(client));
+ if (res) {
+ IPA_MHI_ERR("ipa3_enable_data_path failed %d\n", res);
+ return res;
}
- queue_work(ipa3_mhi_ctx->wq, &ipa_mhi_notify_wakeup_work);
- ipa3_mhi_ctx->wakeup_notified = true;
- IPA_MHI_FUNC_EXIT();
-}
-
-/**
- * ipa3_mhi_wq_notify_ready() - Notify MHI client on ready
- *
- * This function is called from IPA MHI workqueue to notify
- * MHI client driver on ready event when IPA uC is loaded
- */
-static void ipa3_mhi_wq_notify_ready(struct work_struct *work)
-{
- IPA_MHI_FUNC_ENTRY();
- ipa3_mhi_ctx->cb_notify(ipa3_mhi_ctx->cb_priv,
- IPA_MHI_EVENT_READY, 0);
- IPA_MHI_FUNC_EXIT();
-}
-
-/**
- * ipa3_mhi_notify_ready() - Schedule work to notify ready
- *
- * This function will schedule a work to notify ready event.
- */
-static void ipa3_mhi_notify_ready(void)
-{
- IPA_MHI_FUNC_ENTRY();
- queue_work(ipa3_mhi_ctx->wq, &ipa_mhi_notify_ready_work);
IPA_MHI_FUNC_EXIT();
-}
-
-/**
- * ipa3_mhi_set_state() - Set new state to IPA MHI
- * @state: new state
- *
- * Sets a new state to IPA MHI if possible according to IPA MHI state machine.
- * In some state transitions a wakeup request will be triggered.
- *
- * Returns: 0 on success, -1 otherwise
- */
-static int ipa3_mhi_set_state(enum ipa3_mhi_state new_state)
-{
- unsigned long flags;
- int res = -EPERM;
-
- spin_lock_irqsave(&ipa3_mhi_ctx->state_lock, flags);
- IPA_MHI_DBG("Current state: %s\n", MHI_STATE_STR(ipa3_mhi_ctx->state));
-
- switch (ipa3_mhi_ctx->state) {
- case IPA_MHI_STATE_INITIALIZED:
- if (new_state == IPA_MHI_STATE_READY) {
- ipa3_mhi_notify_ready();
- res = 0;
- }
- break;
-
- case IPA_MHI_STATE_READY:
- if (new_state == IPA_MHI_STATE_READY)
- res = 0;
- if (new_state == IPA_MHI_STATE_STARTED)
- res = 0;
- break;
-
- case IPA_MHI_STATE_STARTED:
- if (new_state == IPA_MHI_STATE_INITIALIZED)
- res = 0;
- else if (new_state == IPA_MHI_STATE_SUSPEND_IN_PROGRESS)
- res = 0;
- break;
-
- case IPA_MHI_STATE_SUSPEND_IN_PROGRESS:
- if (new_state == IPA_MHI_STATE_SUSPENDED) {
- if (ipa3_mhi_ctx->trigger_wakeup) {
- ipa3_mhi_ctx->trigger_wakeup = false;
- ipa3_mhi_notify_wakeup();
- }
- res = 0;
- } else if (new_state == IPA_MHI_STATE_STARTED) {
- ipa3_mhi_ctx->wakeup_notified = false;
- ipa3_mhi_ctx->trigger_wakeup = false;
- if (ipa3_mhi_ctx->rm_cons_state ==
- IPA_MHI_RM_STATE_REQUESTED) {
- ipa_rm_notify_completion(
- IPA_RM_RESOURCE_GRANTED,
- IPA_RM_RESOURCE_MHI_CONS);
- ipa3_mhi_ctx->rm_cons_state =
- IPA_MHI_RM_STATE_GRANTED;
- }
- res = 0;
- }
- break;
-
- case IPA_MHI_STATE_SUSPENDED:
- if (new_state == IPA_MHI_STATE_RESUME_IN_PROGRESS)
- res = 0;
- break;
-
- case IPA_MHI_STATE_RESUME_IN_PROGRESS:
- if (new_state == IPA_MHI_STATE_SUSPENDED) {
- if (ipa3_mhi_ctx->trigger_wakeup) {
- ipa3_mhi_ctx->trigger_wakeup = false;
- ipa3_mhi_notify_wakeup();
- }
- res = 0;
- } else if (new_state == IPA_MHI_STATE_STARTED) {
- ipa3_mhi_ctx->trigger_wakeup = false;
- ipa3_mhi_ctx->wakeup_notified = false;
- if (ipa3_mhi_ctx->rm_cons_state ==
- IPA_MHI_RM_STATE_REQUESTED) {
- ipa_rm_notify_completion(
- IPA_RM_RESOURCE_GRANTED,
- IPA_RM_RESOURCE_MHI_CONS);
- ipa3_mhi_ctx->rm_cons_state =
- IPA_MHI_RM_STATE_GRANTED;
- }
- res = 0;
- }
- break;
-
- default:
- IPA_MHI_ERR("Invalid state %d\n", ipa3_mhi_ctx->state);
- WARN_ON(1);
- }
- if (res)
- IPA_MHI_ERR("Invalid state change to %s\n",
- MHI_STATE_STR(new_state));
- else {
- IPA_MHI_DBG("New state change to %s\n",
- MHI_STATE_STR(new_state));
- ipa3_mhi_ctx->state = new_state;
- }
- spin_unlock_irqrestore(&ipa3_mhi_ctx->state_lock, flags);
- return res;
+ return 0;
}
-static void ipa3_mhi_rm_prod_notify(void *user_data, enum ipa_rm_event event,
- unsigned long data)
+static int ipa3_mhi_get_ch_poll_cfg(enum ipa_client_type client,
+ struct ipa_mhi_ch_ctx *ch_ctx_host, int ring_size)
{
- IPA_MHI_FUNC_ENTRY();
-
- switch (event) {
- case IPA_RM_RESOURCE_GRANTED:
- IPA_MHI_DBG_LOW("IPA_RM_RESOURCE_GRANTED\n");
- complete_all(&ipa3_mhi_ctx->rm_prod_granted_comp);
- break;
-
- case IPA_RM_RESOURCE_RELEASED:
- IPA_MHI_DBG_LOW("IPA_RM_RESOURCE_RELEASED\n");
+ switch (ch_ctx_host->pollcfg) {
+ case 0:
+ /*set default polling configuration according to MHI spec*/
+ if (IPA_CLIENT_IS_PROD(client))
+ return 7;
+ else
+ return (ring_size/2)/8;
break;
-
default:
- IPA_MHI_ERR("unexpected event %d\n", event);
- WARN_ON(1);
- break;
+ return ch_ctx_host->pollcfg;
}
-
- IPA_MHI_FUNC_EXIT();
-}
-
-static void ipa3_mhi_uc_ready_cb(void)
-{
- IPA_MHI_FUNC_ENTRY();
- ipa3_mhi_set_state(IPA_MHI_STATE_READY);
- IPA_MHI_FUNC_EXIT();
-}
-
-static void ipa3_mhi_uc_wakeup_request_cb(void)
-{
- unsigned long flags;
-
- IPA_MHI_FUNC_ENTRY();
- IPA_MHI_DBG("MHI state: %s\n", MHI_STATE_STR(ipa3_mhi_ctx->state));
- spin_lock_irqsave(&ipa3_mhi_ctx->state_lock, flags);
- if (ipa3_mhi_ctx->state == IPA_MHI_STATE_SUSPENDED)
- ipa3_mhi_notify_wakeup();
- else if (ipa3_mhi_ctx->state == IPA_MHI_STATE_SUSPEND_IN_PROGRESS)
- /* wakeup event will be triggered after suspend finishes */
- ipa3_mhi_ctx->trigger_wakeup = true;
-
- spin_unlock_irqrestore(&ipa3_mhi_ctx->state_lock, flags);
- IPA_MHI_FUNC_EXIT();
}
-/**
- * ipa3_mhi_rm_cons_request() - callback function for IPA RM request resource
- *
- * In case IPA MHI is not suspended, MHI CONS will be granted immediately.
- * In case IPA MHI is suspended, MHI CONS will be granted after resume.
- */
-static int ipa3_mhi_rm_cons_request(void)
+static int ipa_mhi_start_gsi_channel(enum ipa_client_type client,
+ int ipa_ep_idx, struct start_gsi_channel *params)
{
- unsigned long flags;
int res;
+ struct gsi_evt_ring_props ev_props;
+ struct ipa_mhi_msi_info *msi;
+ struct gsi_chan_props ch_props;
+ union __packed gsi_channel_scratch ch_scratch;
+ struct ipa3_ep_context *ep;
+ struct ipa_gsi_ep_config *ep_cfg;
IPA_MHI_FUNC_ENTRY();
- IPA_MHI_DBG("%s\n", MHI_STATE_STR(ipa3_mhi_ctx->state));
- spin_lock_irqsave(&ipa3_mhi_ctx->state_lock, flags);
- ipa3_mhi_ctx->rm_cons_state = IPA_MHI_RM_STATE_REQUESTED;
- if (ipa3_mhi_ctx->state == IPA_MHI_STATE_STARTED) {
- ipa3_mhi_ctx->rm_cons_state = IPA_MHI_RM_STATE_GRANTED;
- res = 0;
- } else if (ipa3_mhi_ctx->state == IPA_MHI_STATE_SUSPENDED) {
- ipa3_mhi_notify_wakeup();
- res = -EINPROGRESS;
- } else if (ipa3_mhi_ctx->state == IPA_MHI_STATE_SUSPEND_IN_PROGRESS) {
- /* wakeup event will be trigger after suspend finishes */
- ipa3_mhi_ctx->trigger_wakeup = true;
- res = -EINPROGRESS;
- } else {
- res = -EINPROGRESS;
- }
-
- spin_unlock_irqrestore(&ipa3_mhi_ctx->state_lock, flags);
- IPA_MHI_DBG_LOW("EXIT with %d\n", res);
- return res;
-}
+ ep = &ipa3_ctx->ep[ipa_ep_idx];
-static int ipa3_mhi_rm_cons_release(void)
-{
- unsigned long flags;
+ msi = params->msi;
+ ep_cfg = ipa_get_gsi_ep_info(ipa_ep_idx);
+ if (!ep_cfg) {
+ IPA_MHI_ERR("Wrong parameter, ep_cfg is NULL\n");
+ return -EPERM;
+ }
- IPA_MHI_FUNC_ENTRY();
+ /* allocate event ring only for the first time pipe is connected */
+ if (params->state == IPA_HW_MHI_CHANNEL_STATE_INVALID) {
+ IPA_MHI_DBG("allocating event ring\n");
+ memset(&ev_props, 0, sizeof(ev_props));
+ ev_props.intf = GSI_EVT_CHTYPE_MHI_EV;
+ ev_props.intr = GSI_INTR_MSI;
+ ev_props.re_size = GSI_EVT_RING_RE_SIZE_16B;
+ ev_props.ring_len = params->ev_ctx_host->rlen;
+ ev_props.ring_base_addr = IPA_MHI_HOST_ADDR_COND(
+ params->ev_ctx_host->rbase);
+ ev_props.int_modt = params->ev_ctx_host->intmodt *
+ IPA_SLEEP_CLK_RATE_KHZ;
+ ev_props.int_modc = params->ev_ctx_host->intmodc;
+ ev_props.intvec = ((msi->data & ~msi->mask) |
+ (params->ev_ctx_host->msivec & msi->mask));
+ ev_props.msi_addr = IPA_MHI_HOST_ADDR_COND(
+ (((u64)msi->addr_hi << 32) | msi->addr_low));
+ ev_props.rp_update_addr = IPA_MHI_HOST_ADDR_COND(
+ params->event_context_addr +
+ offsetof(struct ipa_mhi_ev_ctx, rp));
+ ev_props.exclusive = true;
+ ev_props.err_cb = params->ev_err_cb;
+ ev_props.user_data = params->channel;
+ ev_props.evchid_valid = true;
+ ev_props.evchid = params->evchid;
+ res = gsi_alloc_evt_ring(&ev_props, ipa3_ctx->gsi_dev_hdl,
+ &ep->gsi_evt_ring_hdl);
+ if (res) {
+ IPA_MHI_ERR("gsi_alloc_evt_ring failed %d\n", res);
+ goto fail_alloc_evt;
+ return res;
+ }
+ IPA_MHI_DBG("client %d, caching event ring hdl %lu\n",
+ client,
+ ep->gsi_evt_ring_hdl);
+ *params->cached_gsi_evt_ring_hdl =
+ ep->gsi_evt_ring_hdl;
- spin_lock_irqsave(&ipa3_mhi_ctx->state_lock, flags);
- ipa3_mhi_ctx->rm_cons_state = IPA_MHI_RM_STATE_RELEASED;
- complete_all(&ipa3_mhi_ctx->rm_cons_comp);
- spin_unlock_irqrestore(&ipa3_mhi_ctx->state_lock, flags);
- IPA_MHI_FUNC_EXIT();
- return 0;
-}
+ }
-static int ipa3_mhi_wait_for_cons_release(void)
-{
- unsigned long flags;
- int res;
-
- IPA_MHI_FUNC_ENTRY();
- reinit_completion(&ipa3_mhi_ctx->rm_cons_comp);
- spin_lock_irqsave(&ipa3_mhi_ctx->state_lock, flags);
- if (ipa3_mhi_ctx->rm_cons_state != IPA_MHI_RM_STATE_GRANTED) {
- spin_unlock_irqrestore(&ipa3_mhi_ctx->state_lock, flags);
- return 0;
- }
- spin_unlock_irqrestore(&ipa3_mhi_ctx->state_lock, flags);
-
- res = wait_for_completion_timeout(
- &ipa3_mhi_ctx->rm_cons_comp,
- msecs_to_jiffies(IPA_MHI_RM_TIMEOUT_MSEC));
- if (res == 0) {
- IPA_MHI_ERR("timeout release mhi cons\n");
- return -ETIME;
- }
- IPA_MHI_FUNC_EXIT();
- return 0;
-}
-
-static int ipa3_mhi_request_prod(void)
-{
- int res;
-
- IPA_MHI_FUNC_ENTRY();
-
- reinit_completion(&ipa3_mhi_ctx->rm_prod_granted_comp);
- IPA_MHI_DBG_LOW("requesting mhi prod\n");
- res = ipa_rm_request_resource(IPA_RM_RESOURCE_MHI_PROD);
- if (res) {
- if (res != -EINPROGRESS) {
- IPA_MHI_ERR("failed to request mhi prod %d\n", res);
- return res;
- }
- res = wait_for_completion_timeout(
- &ipa3_mhi_ctx->rm_prod_granted_comp,
- msecs_to_jiffies(IPA_MHI_RM_TIMEOUT_MSEC));
- if (res == 0) {
- IPA_MHI_ERR("timeout request mhi prod\n");
- return -ETIME;
- }
- }
-
- IPA_MHI_DBG_LOW("mhi prod granted\n");
- IPA_MHI_FUNC_EXIT();
- return 0;
-
-}
-
-static int ipa3_mhi_release_prod(void)
-{
- int res;
-
- IPA_MHI_FUNC_ENTRY();
-
- res = ipa_rm_release_resource(IPA_RM_RESOURCE_MHI_PROD);
-
- IPA_MHI_FUNC_EXIT();
- return res;
-
-}
-
-/**
- * ipa3_mhi_get_channel_context() - Get corresponding channel context
- * @ep: IPA ep
- * @channel_id: Channel ID
- *
- * This function will return the corresponding channel context or allocate new
- * one in case channel context for channel does not exist.
- */
-static struct ipa3_mhi_channel_ctx *ipa3_mhi_get_channel_context(
- struct ipa3_ep_context *ep, u8 channel_id)
-{
- int ch_idx;
- struct ipa3_mhi_channel_ctx *channels;
- int max_channels;
-
- if (IPA_CLIENT_IS_PROD(ep->client)) {
- channels = ipa3_mhi_ctx->ul_channels;
- max_channels = IPA_MHI_MAX_UL_CHANNELS;
- } else {
- channels = ipa3_mhi_ctx->dl_channels;
- max_channels = IPA_MHI_MAX_DL_CHANNELS;
- }
-
- /* find the channel context according to channel id */
- for (ch_idx = 0; ch_idx < max_channels; ch_idx++) {
- if (channels[ch_idx].valid &&
- channels[ch_idx].id == channel_id)
- return &channels[ch_idx];
- }
-
- /* channel context does not exists, allocate a new one */
- for (ch_idx = 0; ch_idx < max_channels; ch_idx++) {
- if (!channels[ch_idx].valid)
- break;
- }
-
- if (ch_idx == max_channels) {
- IPA_MHI_ERR("no more channels available\n");
- return NULL;
- }
-
- channels[ch_idx].valid = true;
- channels[ch_idx].id = channel_id;
- channels[ch_idx].index = ipa3_mhi_ctx->total_channels++;
- channels[ch_idx].ep = ep;
- channels[ch_idx].state = IPA_HW_MHI_CHANNEL_STATE_INVALID;
-
- return &channels[ch_idx];
-}
-
-/**
- * ipa3_mhi_get_channel_context_by_clnt_hdl() - Get corresponding channel
- * context
- * @clnt_hdl: client handle as provided in ipa3_mhi_connect_pipe()
- *
- * This function will return the corresponding channel context or NULL in case
- * that channel does not exist.
- */
-static struct ipa3_mhi_channel_ctx *ipa3_mhi_get_channel_context_by_clnt_hdl(
- u32 clnt_hdl)
-{
- int ch_idx;
-
- for (ch_idx = 0; ch_idx < IPA_MHI_MAX_UL_CHANNELS; ch_idx++) {
- if (ipa3_mhi_ctx->ul_channels[ch_idx].valid &&
- ipa3_get_ep_mapping(
- ipa3_mhi_ctx->ul_channels[ch_idx].ep->client) == clnt_hdl)
- return &ipa3_mhi_ctx->ul_channels[ch_idx];
- }
-
- for (ch_idx = 0; ch_idx < IPA_MHI_MAX_DL_CHANNELS; ch_idx++) {
- if (ipa3_mhi_ctx->dl_channels[ch_idx].valid &&
- ipa3_get_ep_mapping(
- ipa3_mhi_ctx->dl_channels[ch_idx].ep->client) == clnt_hdl)
- return &ipa3_mhi_ctx->dl_channels[ch_idx];
- }
-
- return NULL;
-}
-
-static int ipa3_mhi_enable_force_clear(u32 request_id, bool throttle_source)
-{
- struct ipa_enable_force_clear_datapath_req_msg_v01 req;
- int i;
- int res;
-
- IPA_MHI_FUNC_ENTRY();
- memset(&req, 0, sizeof(req));
- req.request_id = request_id;
- req.source_pipe_bitmask = 0;
- for (i = 0; i < IPA_MHI_MAX_UL_CHANNELS; i++) {
- if (!ipa3_mhi_ctx->ul_channels[i].valid)
- continue;
- req.source_pipe_bitmask |= 1 << ipa3_get_ep_mapping(
- ipa3_mhi_ctx->ul_channels[i].ep->client);
- }
- if (throttle_source) {
- req.throttle_source_valid = 1;
- req.throttle_source = 1;
- }
- IPA_MHI_DBG("req_id=0x%x src_pipe_btmk=0x%x throt_src=%d\n",
- req.request_id, req.source_pipe_bitmask,
- req.throttle_source);
- res = ipa3_qmi_enable_force_clear_datapath_send(&req);
- if (res) {
- IPA_MHI_ERR(
- "ipa3_qmi_enable_force_clear_datapath_send failed %d\n",
- res);
- return res;
- }
-
- IPA_MHI_FUNC_EXIT();
- return 0;
-}
-
-static int ipa3_mhi_disable_force_clear(u32 request_id)
-{
- struct ipa_disable_force_clear_datapath_req_msg_v01 req;
- int res;
-
- IPA_MHI_FUNC_ENTRY();
- memset(&req, 0, sizeof(req));
- req.request_id = request_id;
- IPA_MHI_DBG("req_id=0x%x\n", req.request_id);
- res = ipa3_qmi_disable_force_clear_datapath_send(&req);
+ memset(&ch_props, 0, sizeof(ch_props));
+ ch_props.prot = GSI_CHAN_PROT_MHI;
+ ch_props.dir = IPA_CLIENT_IS_PROD(client) ?
+ GSI_CHAN_DIR_TO_GSI : GSI_CHAN_DIR_FROM_GSI;
+ ch_props.ch_id = ep_cfg->ipa_gsi_chan_num;
+ ch_props.evt_ring_hdl = *params->cached_gsi_evt_ring_hdl;
+ ch_props.re_size = GSI_CHAN_RE_SIZE_16B;
+ ch_props.ring_len = params->ch_ctx_host->rlen;
+ ch_props.ring_base_addr = IPA_MHI_HOST_ADDR_COND(
+ params->ch_ctx_host->rbase);
+ ch_props.use_db_eng = GSI_CHAN_DB_MODE;
+ ch_props.max_prefetch = GSI_ONE_PREFETCH_SEG;
+ ch_props.low_weight = 1;
+ ch_props.err_cb = params->ch_err_cb;
+ ch_props.chan_user_data = params->channel;
+ res = gsi_alloc_channel(&ch_props, ipa3_ctx->gsi_dev_hdl,
+ &ep->gsi_chan_hdl);
if (res) {
- IPA_MHI_ERR(
- "ipa3_qmi_disable_force_clear_datapath_send failed %d\n",
- res);
- return res;
- }
-
- IPA_MHI_FUNC_EXIT();
- return 0;
-}
-
-static bool ipa3_mhi_sps_channel_empty(struct ipa3_mhi_channel_ctx *channel)
-{
- u32 pipe_idx;
- bool pending;
-
- pipe_idx = ipa3_get_ep_mapping(channel->ep->client);
- if (sps_pipe_pending_desc(ipa3_ctx->bam_handle,
- pipe_idx, &pending)) {
- IPA_MHI_ERR("sps_pipe_pending_desc failed\n");
- WARN_ON(1);
- return false;
- }
-
- return !pending;
-}
-
-static bool ipa3_mhi_gsi_channel_empty(struct ipa3_mhi_channel_ctx *channel)
-{
- int res;
- IPA_MHI_FUNC_ENTRY();
-
- if (!channel->stop_in_proc) {
- IPA_MHI_DBG_LOW("Channel is not in STOP_IN_PROC\n");
- return true;
- }
-
- IPA_MHI_DBG_LOW("Stopping GSI channel %ld\n",
- channel->ep->gsi_chan_hdl);
- res = gsi_stop_channel(channel->ep->gsi_chan_hdl);
- if (res != 0 &&
- res != -GSI_STATUS_AGAIN &&
- res != -GSI_STATUS_TIMED_OUT) {
- IPA_MHI_ERR("GSI stop channel failed %d\n",
+ IPA_MHI_ERR("gsi_alloc_channel failed %d\n",
res);
- WARN_ON(1);
- return false;
- }
-
- if (res == 0) {
- IPA_MHI_DBG_LOW("GSI channel %ld STOP\n",
- channel->ep->gsi_chan_hdl);
- channel->stop_in_proc = false;
- return true;
- }
-
- return false;
-}
-
-/**
- * ipa3_mhi_wait_for_ul_empty_timeout() - wait for pending packets in uplink
- * @msecs: timeout to wait
- *
- * This function will poll until there are no packets pending in uplink channels
- * or timeout occurred.
- *
- * Return code: true - no pending packets in uplink channels
- * false - timeout occurred
- */
-static bool ipa3_mhi_wait_for_ul_empty_timeout(unsigned int msecs)
-{
- unsigned long jiffies_timeout = msecs_to_jiffies(msecs);
- unsigned long jiffies_start = jiffies;
- bool empty = false;
- int i;
-
- IPA_MHI_FUNC_ENTRY();
- while (!empty) {
- empty = true;
- for (i = 0; i < IPA_MHI_MAX_UL_CHANNELS; i++) {
- if (!ipa3_mhi_ctx->ul_channels[i].valid)
- continue;
- if (ipa3_ctx->transport_prototype ==
- IPA_TRANSPORT_TYPE_GSI)
- empty &= ipa3_mhi_gsi_channel_empty(
- &ipa3_mhi_ctx->ul_channels[i]);
- else
- empty &= ipa3_mhi_sps_channel_empty(
- &ipa3_mhi_ctx->ul_channels[i]);
- }
-
- if (time_after(jiffies, jiffies_start + jiffies_timeout)) {
- IPA_MHI_DBG_LOW("timeout waiting for UL empty\n");
- break;
- }
-
- if (ipa3_ctx->transport_prototype == IPA_TRANSPORT_TYPE_GSI &&
- IPA_MHI_MAX_UL_CHANNELS == 1)
- usleep_range(IPA_GSI_CHANNEL_STOP_SLEEP_MIN_USEC,
- IPA_GSI_CHANNEL_STOP_SLEEP_MAX_USEC);
- }
-
- IPA_MHI_DBG("IPA UL is %s\n", (empty) ? "empty" : "not empty");
-
- IPA_MHI_FUNC_EXIT();
- return empty;
-}
-
-static void ipa3_mhi_set_holb_on_dl_channels(bool enable,
- struct ipa_ep_cfg_holb old_holb[])
-{
- int i;
- struct ipa_ep_cfg_holb ep_holb;
- int ep_idx;
- int res;
-
- for (i = 0; i < IPA_MHI_MAX_DL_CHANNELS; i++) {
- if (!ipa3_mhi_ctx->dl_channels[i].valid)
- continue;
- if (ipa3_mhi_ctx->dl_channels[i].state ==
- IPA_HW_MHI_CHANNEL_STATE_INVALID)
- continue;
- ep_idx = ipa3_get_ep_mapping(
- ipa3_mhi_ctx->dl_channels[i].ep->client);
- if (-1 == ep_idx) {
- IPA_MHI_ERR("Client %u is not mapped\n",
- ipa3_mhi_ctx->dl_channels[i].ep->client);
- BUG();
- return;
- }
- memset(&ep_holb, 0, sizeof(ep_holb));
- if (enable) {
- ep_holb.en = 1;
- ep_holb.tmr_val = 0;
- old_holb[i] = ipa3_ctx->ep[ep_idx].holb;
- } else {
- ep_holb = old_holb[i];
- }
- res = ipa3_cfg_ep_holb(ep_idx, &ep_holb);
- if (res) {
- IPA_MHI_ERR("ipa3_cfg_ep_holb failed %d\n", res);
- BUG();
- return;
- }
- }
-}
-
-static int ipa3_mhi_suspend_gsi_channel(struct ipa3_mhi_channel_ctx *channel)
-{
- int res;
- int clnt_hdl;
-
- IPA_MHI_FUNC_ENTRY();
-
- clnt_hdl = ipa3_get_ep_mapping(channel->ep->client);
- if (clnt_hdl < 0)
- return -EFAULT;
-
- res = ipa3_stop_gsi_channel(clnt_hdl);
- if (res != 0 && res != -GSI_STATUS_AGAIN &&
- res != -GSI_STATUS_TIMED_OUT) {
- IPA_MHI_ERR("GSI stop channel failed %d\n", res);
- return -EFAULT;
- }
-
- /* check if channel was stopped completely */
- if (res)
- channel->stop_in_proc = true;
-
- IPA_MHI_DBG("GSI channel is %s\n", (channel->stop_in_proc) ?
- "STOP_IN_PROC" : "STOP");
-
- IPA_MHI_FUNC_EXIT();
- return 0;
-}
-
-static int ipa3_mhi_reset_gsi_channel(struct ipa3_mhi_channel_ctx *channel)
-{
- int res;
- int clnt_hdl;
-
- IPA_MHI_FUNC_ENTRY();
-
- clnt_hdl = ipa3_get_ep_mapping(channel->ep->client);
- if (clnt_hdl < 0)
- return -EFAULT;
-
- res = ipa3_reset_gsi_channel(clnt_hdl);
- if (res) {
- IPA_MHI_ERR("ipa3_reset_gsi_channel failed %d\n", res);
- return -EFAULT;
+ goto fail_alloc_ch;
}
- IPA_MHI_FUNC_EXIT();
- return 0;
-}
-
-static int ipa3_mhi_reset_ul_channel(struct ipa3_mhi_channel_ctx *channel)
-{
- int res;
- struct ipa_ep_cfg_holb old_ep_holb[IPA_MHI_MAX_DL_CHANNELS];
- bool empty;
-
- IPA_MHI_FUNC_ENTRY();
- if (ipa3_ctx->transport_prototype == IPA_TRANSPORT_TYPE_GSI) {
- res = ipa3_mhi_suspend_gsi_channel(channel);
- if (res) {
- IPA_MHI_ERR("ipa3_mhi_suspend_gsi_channel failed %d\n",
- res);
- return res;
- }
+ memset(&ch_scratch, 0, sizeof(ch_scratch));
+ ch_scratch.mhi.mhi_host_wp_addr = IPA_MHI_HOST_ADDR_COND(
+ params->channel_context_addr +
+ offsetof(struct ipa_mhi_ch_ctx, wp));
+ ch_scratch.mhi.assert_bit40 = params->assert_bit40;
+ ch_scratch.mhi.max_outstanding_tre =
+ ep_cfg->ipa_if_tlv * ch_props.re_size;
+ ch_scratch.mhi.outstanding_threshold =
+ min(ep_cfg->ipa_if_tlv / 2, 8) * ch_props.re_size;
+ ch_scratch.mhi.oob_mod_threshold = 4;
+ if (params->ch_ctx_host->brstmode == IPA_MHI_BURST_MODE_DEFAULT ||
+ params->ch_ctx_host->brstmode == IPA_MHI_BURST_MODE_ENABLE) {
+ ch_scratch.mhi.burst_mode_enabled = true;
+ ch_scratch.mhi.polling_configuration =
+ ipa3_mhi_get_ch_poll_cfg(client, params->ch_ctx_host,
+ (ch_props.ring_len / ch_props.re_size));
+ ch_scratch.mhi.polling_mode = IPA_MHI_POLLING_MODE_DB_MODE;
} else {
- res = ipa3_uc_mhi_reset_channel(channel->index);
- if (res) {
- IPA_MHI_ERR("ipa3_uc_mhi_reset_channel failed %d\n",
- res);
- return res;
- }
- }
-
- empty = ipa3_mhi_wait_for_ul_empty_timeout(
- IPA_MHI_CH_EMPTY_TIMEOUT_MSEC);
- if (!empty) {
- IPA_MHI_DBG("%s not empty\n",
- (ipa3_ctx->transport_prototype ==
- IPA_TRANSPORT_TYPE_GSI) ? "GSI" : "BAM");
- res = ipa3_mhi_enable_force_clear(ipa3_mhi_ctx->qmi_req_id,
- false);
- if (res) {
- IPA_MHI_ERR("ipa3_mhi_enable_force_clear failed %d\n",
- res);
- BUG();
- return res;
- }
-
- if (ipa3_ctx->transport_prototype == IPA_TRANSPORT_TYPE_GSI) {
- empty = ipa3_mhi_wait_for_ul_empty_timeout(
- IPA_MHI_CH_EMPTY_TIMEOUT_MSEC);
-
- IPADBG("empty=%d\n", empty);
- } else {
- /* enable packet drop on all DL channels */
- ipa3_mhi_set_holb_on_dl_channels(true, old_ep_holb);
- res = ipa3_tag_process(NULL, 0, HZ);
- if (res)
- IPAERR("TAG process failed\n");
-
- /* disable packet drop on all DL channels */
- ipa3_mhi_set_holb_on_dl_channels(false, old_ep_holb);
- res = sps_pipe_disable(ipa3_ctx->bam_handle,
- ipa3_get_ep_mapping(channel->ep->client));
- if (res) {
- IPA_MHI_ERR("sps_pipe_disable fail %d\n", res);
- BUG();
- return res;
- }
- }
-
- res = ipa3_mhi_disable_force_clear(ipa3_mhi_ctx->qmi_req_id);
- if (res) {
- IPA_MHI_ERR("ipa3_mhi_disable_force_clear failed %d\n",
- res);
- BUG();
- return res;
- }
- ipa3_mhi_ctx->qmi_req_id++;
- }
-
- if (ipa3_ctx->transport_prototype == IPA_TRANSPORT_TYPE_GSI) {
- res = ipa3_mhi_reset_gsi_channel(channel);
- if (res) {
- IPAERR("ipa3_mhi_reset_gsi_channel failed\n");
- BUG();
- return res;
- }
+ ch_scratch.mhi.burst_mode_enabled = false;
}
-
- res = ipa3_disable_data_path(ipa3_get_ep_mapping(channel->ep->client));
- if (res) {
- IPA_MHI_ERR("ipa3_disable_data_path failed %d\n", res);
- return res;
- }
- IPA_MHI_FUNC_EXIT();
-
- return 0;
-}
-
-static int ipa3_mhi_reset_dl_channel(struct ipa3_mhi_channel_ctx *channel)
-{
- int res;
-
- IPA_MHI_FUNC_ENTRY();
- if (ipa3_ctx->transport_prototype == IPA_TRANSPORT_TYPE_GSI) {
- res = ipa3_mhi_suspend_gsi_channel(channel);
- if (res) {
- IPAERR("ipa3_mhi_suspend_gsi_channel failed %d\n", res);
- return res;
- }
-
- res = ipa3_mhi_reset_gsi_channel(channel);
- if (res) {
- IPAERR("ipa3_mhi_reset_gsi_channel failed\n");
- return res;
- }
-
- res = ipa3_disable_data_path(
- ipa3_get_ep_mapping(channel->ep->client));
- if (res) {
- IPA_MHI_ERR("ipa3_disable_data_path failed\n");
- return res;
- }
- } else {
- res = ipa3_disable_data_path(
- ipa3_get_ep_mapping(channel->ep->client));
- if (res) {
- IPA_MHI_ERR("ipa3_disable_data_path failed %d\n", res);
- return res;
- }
-
- res = ipa3_uc_mhi_reset_channel(channel->index);
- if (res) {
- IPA_MHI_ERR("ipa3_uc_mhi_reset_channel failed %d\n",
- res);
- ipa3_enable_data_path(
- ipa3_get_ep_mapping(channel->ep->client));
- return res;
- }
- }
-
- IPA_MHI_FUNC_EXIT();
- return 0;
-}
-
-static int ipa3_mhi_reset_channel(struct ipa3_mhi_channel_ctx *channel)
-{
- int res;
-
- IPA_MHI_FUNC_ENTRY();
- if (IPA_CLIENT_IS_PROD(channel->ep->client))
- res = ipa3_mhi_reset_ul_channel(channel);
- else
- res = ipa3_mhi_reset_dl_channel(channel);
- if (res) {
- IPA_MHI_ERR("failed to reset channel error %d\n", res);
- return res;
- }
-
- channel->state = IPA_HW_MHI_CHANNEL_STATE_DISABLE;
-
- if (ipa3_ctx->transport_prototype == IPA_TRANSPORT_TYPE_GSI) {
- res = ipa_mhi_read_write_host(IPA_MHI_DMA_TO_HOST,
- &channel->state, channel->channel_context_addr +
- offsetof(struct ipa3_mhi_ch_ctx, chstate),
- sizeof(((struct ipa3_mhi_ch_ctx *)0)->chstate));
- if (res) {
- IPAERR("ipa_mhi_read_write_host failed %d\n", res);
- return res;
- }
- }
-
- IPA_MHI_FUNC_EXIT();
- return 0;
-}
-
-static int ipa_mhi_start_uc_channel(struct ipa3_mhi_channel_ctx *channel,
- int ipa_ep_idx)
-{
- int res;
- struct ipa3_ep_context *ep;
-
- IPA_MHI_FUNC_ENTRY();
-
- ep = channel->ep;
- if (channel->state == IPA_HW_MHI_CHANNEL_STATE_INVALID) {
- IPA_MHI_DBG("Initializing channel\n");
- res = ipa3_uc_mhi_init_channel(ipa_ep_idx, channel->index,
- channel->id, (IPA_CLIENT_IS_PROD(ep->client) ? 1 : 2));
- if (res) {
- IPA_MHI_ERR("init_channel failed %d\n", res);
- return res;
- }
- } else if (channel->state == IPA_HW_MHI_CHANNEL_STATE_DISABLE) {
- if (channel->ep != ep) {
- IPA_MHI_ERR("previous channel client was %d\n",
- ep->client);
- return res;
- }
- IPA_MHI_DBG("Starting channel\n");
- res = ipa3_uc_mhi_resume_channel(channel->index, false);
- if (res) {
- IPA_MHI_ERR("init_channel failed %d\n", res);
- return res;
- }
- } else {
- IPA_MHI_ERR("Invalid channel state %d\n", channel->state);
- return -EFAULT;
- }
-
- IPA_MHI_FUNC_EXIT();
- return 0;
-}
-
-static void ipa_mhi_dump_ch_ctx(struct ipa3_mhi_channel_ctx *channel)
-{
- IPA_MHI_DBG_LOW("ch_id %d\n", channel->id);
- IPA_MHI_DBG_LOW("chstate 0x%x\n", channel->ch_ctx_host.chstate);
- IPA_MHI_DBG_LOW("brstmode 0x%x\n", channel->ch_ctx_host.brstmode);
- IPA_MHI_DBG_LOW("pollcfg 0x%x\n", channel->ch_ctx_host.pollcfg);
- IPA_MHI_DBG_LOW("chtype 0x%x\n", channel->ch_ctx_host.chtype);
- IPA_MHI_DBG_LOW("erindex 0x%x\n", channel->ch_ctx_host.erindex);
- IPA_MHI_DBG_LOW("rbase 0x%llx\n", channel->ch_ctx_host.rbase);
- IPA_MHI_DBG_LOW("rlen 0x%llx\n", channel->ch_ctx_host.rlen);
- IPA_MHI_DBG_LOW("rp 0x%llx\n", channel->ch_ctx_host.rp);
- IPA_MHI_DBG_LOW("wp 0x%llx\n", channel->ch_ctx_host.wp);
-}
-
-static void ipa_mhi_dump_ev_ctx(struct ipa3_mhi_channel_ctx *channel)
-{
- IPA_MHI_DBG_LOW("ch_id %d event id %d\n", channel->id,
- channel->ch_ctx_host.erindex);
-
- IPA_MHI_DBG_LOW("intmodc 0x%x\n", channel->ev_ctx_host.intmodc);
- IPA_MHI_DBG_LOW("intmodt 0x%x\n", channel->ev_ctx_host.intmodt);
- IPA_MHI_DBG_LOW("ertype 0x%x\n", channel->ev_ctx_host.ertype);
- IPA_MHI_DBG_LOW("msivec 0x%x\n", channel->ev_ctx_host.msivec);
- IPA_MHI_DBG_LOW("rbase 0x%llx\n", channel->ev_ctx_host.rbase);
- IPA_MHI_DBG_LOW("rlen 0x%llx\n", channel->ev_ctx_host.rlen);
- IPA_MHI_DBG_LOW("rp 0x%llx\n", channel->ev_ctx_host.rp);
- IPA_MHI_DBG_LOW("wp 0x%llx\n", channel->ev_ctx_host.wp);
-}
-
-static int ipa_mhi_read_ch_ctx(struct ipa3_mhi_channel_ctx *channel)
-{
- int res;
-
- res = ipa_mhi_read_write_host(IPA_MHI_DMA_FROM_HOST,
- &channel->ch_ctx_host, channel->channel_context_addr,
- sizeof(channel->ch_ctx_host));
- if (res) {
- IPAERR("ipa_mhi_read_write_host failed %d\n", res);
- return res;
-
- }
- ipa_mhi_dump_ch_ctx(channel);
-
- channel->event_context_addr = ipa3_mhi_ctx->event_context_array_addr +
- channel->ch_ctx_host.erindex * sizeof(struct ipa3_mhi_ev_ctx);
- IPA_MHI_DBG("ch %d event_context_addr 0x%llx\n", channel->id,
- channel->event_context_addr);
-
- res = ipa_mhi_read_write_host(IPA_MHI_DMA_FROM_HOST,
- &channel->ev_ctx_host, channel->event_context_addr,
- sizeof(channel->ev_ctx_host));
- if (res) {
- IPAERR("ipa_mhi_read_write_host failed %d\n", res);
- return res;
-
- }
- ipa_mhi_dump_ev_ctx(channel);
-
- return 0;
-}
-
-static void ipa_mhi_gsi_ev_err_cb(struct gsi_evt_err_notify *notify)
-{
- struct ipa3_mhi_channel_ctx *channel = notify->user_data;
-
- IPAERR("channel id=%d client=%d state=%d\n",
- channel->id, channel->ep->client, channel->state);
- switch (notify->evt_id) {
- case GSI_EVT_OUT_OF_BUFFERS_ERR:
- IPA_MHI_ERR("Received GSI_EVT_OUT_OF_BUFFERS_ERR\n");
- break;
- case GSI_EVT_OUT_OF_RESOURCES_ERR:
- IPA_MHI_ERR("Received GSI_EVT_OUT_OF_RESOURCES_ERR\n");
- break;
- case GSI_EVT_UNSUPPORTED_INTER_EE_OP_ERR:
- IPA_MHI_ERR("Received GSI_EVT_UNSUPPORTED_INTER_EE_OP_ERR\n");
- break;
- case GSI_EVT_EVT_RING_EMPTY_ERR:
- IPA_MHI_ERR("Received GSI_EVT_EVT_RING_EMPTY_ERR\n");
- break;
- default:
- IPA_MHI_ERR("Unexpected err evt: %d\n", notify->evt_id);
- }
- IPA_MHI_ERR("err_desc=0x%x\n", notify->err_desc);
-}
-
-static void ipa_mhi_gsi_ch_err_cb(struct gsi_chan_err_notify *notify)
-{
- struct ipa3_mhi_channel_ctx *channel = notify->chan_user_data;
-
- IPAERR("channel id=%d client=%d state=%d\n",
- channel->id, channel->ep->client, channel->state);
- switch (notify->evt_id) {
- case GSI_CHAN_INVALID_TRE_ERR:
- IPA_MHI_ERR("Received GSI_CHAN_INVALID_TRE_ERR\n");
- break;
- case GSI_CHAN_NON_ALLOCATED_EVT_ACCESS_ERR:
- IPA_MHI_ERR("Received GSI_CHAN_NON_ALLOCATED_EVT_ACCESS_ERR\n");
- break;
- case GSI_CHAN_OUT_OF_BUFFERS_ERR:
- IPA_MHI_ERR("Received GSI_CHAN_OUT_OF_BUFFERS_ERR\n");
- break;
- case GSI_CHAN_OUT_OF_RESOURCES_ERR:
- IPA_MHI_ERR("Received GSI_CHAN_OUT_OF_RESOURCES_ERR\n");
- break;
- case GSI_CHAN_UNSUPPORTED_INTER_EE_OP_ERR:
- IPA_MHI_ERR("Received GSI_CHAN_UNSUPPORTED_INTER_EE_OP_ERR\n");
- break;
- case GSI_CHAN_HWO_1_ERR:
- IPA_MHI_ERR("Received GSI_CHAN_HWO_1_ERR\n");
- break;
- default:
- IPAERR("Unexpected err evt: %d\n", notify->evt_id);
- }
- IPA_MHI_ERR("err_desc=0x%x\n", notify->err_desc);
-}
-
-static int ipa3_mhi_get_ch_poll_cfg(struct ipa3_mhi_channel_ctx *channel,
- int ring_size)
-{
- switch (channel->ch_ctx_host.pollcfg) {
- case 0:
- /*set default polling configuration according to MHI spec*/
- if (IPA_CLIENT_IS_PROD(channel->ep->client))
- return 7;
- else
- return (ring_size/2)/8;
- break;
- default:
- return channel->ch_ctx_host.pollcfg;
- }
-}
-
-static int ipa_mhi_start_gsi_channel(struct ipa3_mhi_channel_ctx *channel,
- int ipa_ep_idx)
-{
- int res;
- struct ipa3_ep_context *ep;
- struct gsi_evt_ring_props ev_props;
- struct ipa_mhi_msi_info msi;
- struct gsi_chan_props ch_props;
- union __packed gsi_channel_scratch ch_scratch;
- struct ipa_gsi_ep_config *ep_cfg;
-
- IPA_MHI_FUNC_ENTRY();
-
- if (channel->state != IPA_HW_MHI_CHANNEL_STATE_INVALID &&
- channel->state != IPA_HW_MHI_CHANNEL_STATE_DISABLE) {
- IPA_MHI_ERR("Invalid channel state %d\n", channel->state);
- return -EFAULT;
- }
-
- msi = ipa3_mhi_ctx->msi;
- ep = channel->ep;
- ep_cfg = ipa_get_gsi_ep_info(ipa_ep_idx);
- if (!ep_cfg) {
- IPA_MHI_ERR("Wrong parameter, ep_cfg is NULL\n");
- return -EPERM;
- }
- IPA_MHI_DBG("reading ch/ev context from host\n");
- res = ipa_mhi_read_ch_ctx(channel);
- if (res) {
- IPA_MHI_ERR("ipa_mhi_read_ch_ctx failed %d\n", res);
- return res;
- }
-
- /* allocate event ring only for the first time pipe is connected */
- if (channel->state == IPA_HW_MHI_CHANNEL_STATE_INVALID) {
- IPA_MHI_DBG("allocating event ring\n");
- memset(&ev_props, 0, sizeof(ev_props));
- ev_props.intf = GSI_EVT_CHTYPE_MHI_EV;
- ev_props.intr = GSI_INTR_MSI;
- ev_props.re_size = GSI_EVT_RING_RE_SIZE_16B;
- ev_props.ring_len = channel->ev_ctx_host.rlen;
- ev_props.ring_base_addr = IPA_MHI_HOST_ADDR_COND(
- channel->ev_ctx_host.rbase);
- ev_props.int_modt = channel->ev_ctx_host.intmodt *
- IPA_SLEEP_CLK_RATE_KHZ;
- ev_props.int_modc = channel->ev_ctx_host.intmodc;
- ev_props.intvec = ((msi.data & ~msi.mask) |
- (channel->ev_ctx_host.msivec & msi.mask));
- ev_props.msi_addr = IPA_MHI_HOST_ADDR_COND(
- (((u64)msi.addr_hi << 32) | msi.addr_low));
- ev_props.rp_update_addr = IPA_MHI_HOST_ADDR_COND(
- channel->event_context_addr +
- offsetof(struct ipa3_mhi_ev_ctx, rp));
- ev_props.exclusive = true;
- ev_props.err_cb = ipa_mhi_gsi_ev_err_cb;
- ev_props.user_data = channel;
- ev_props.evchid_valid = true;
- ev_props.evchid = channel->index + IPA_MHI_GSI_ER_START;
- res = gsi_alloc_evt_ring(&ev_props, ipa3_ctx->gsi_dev_hdl,
- &channel->ep->gsi_evt_ring_hdl);
- if (res) {
- IPA_MHI_ERR("gsi_alloc_evt_ring failed %d\n", res);
- goto fail_alloc_evt;
- return res;
- }
-
- channel->cached_gsi_evt_ring_hdl =
- channel->ep->gsi_evt_ring_hdl;
-
- }
-
- memset(&ch_props, 0, sizeof(ch_props));
- ch_props.prot = GSI_CHAN_PROT_MHI;
- ch_props.dir = IPA_CLIENT_IS_PROD(ep->client) ?
- GSI_CHAN_DIR_TO_GSI : GSI_CHAN_DIR_FROM_GSI;
- ch_props.ch_id = ep_cfg->ipa_gsi_chan_num;
- ch_props.evt_ring_hdl = channel->cached_gsi_evt_ring_hdl;
- ch_props.re_size = GSI_CHAN_RE_SIZE_16B;
- ch_props.ring_len = channel->ch_ctx_host.rlen;
- ch_props.ring_base_addr = IPA_MHI_HOST_ADDR_COND(
- channel->ch_ctx_host.rbase);
- ch_props.use_db_eng = GSI_CHAN_DB_MODE;
- ch_props.max_prefetch = GSI_ONE_PREFETCH_SEG;
- ch_props.low_weight = 1;
- ch_props.err_cb = ipa_mhi_gsi_ch_err_cb;
- ch_props.chan_user_data = channel;
- res = gsi_alloc_channel(&ch_props, ipa3_ctx->gsi_dev_hdl,
- &channel->ep->gsi_chan_hdl);
- if (res) {
- IPA_MHI_ERR("gsi_alloc_channel failed %d\n",
- res);
- goto fail_alloc_ch;
- }
-
- memset(&ch_scratch, 0, sizeof(ch_scratch));
- ch_scratch.mhi.mhi_host_wp_addr = IPA_MHI_HOST_ADDR_COND(
- channel->channel_context_addr +
- offsetof(struct ipa3_mhi_ch_ctx, wp));
- ch_scratch.mhi.assert_bit40 = ipa3_mhi_ctx->assert_bit40;
- ch_scratch.mhi.max_outstanding_tre =
- ep_cfg->ipa_if_tlv * ch_props.re_size;
- ch_scratch.mhi.outstanding_threshold =
- min(ep_cfg->ipa_if_tlv / 2, 8) * ch_props.re_size;
- ch_scratch.mhi.oob_mod_threshold = 4;
- if (channel->ch_ctx_host.brstmode == IPA_MHI_BURST_MODE_DEFAULT ||
- channel->ch_ctx_host.brstmode == IPA_MHI_BURST_MODE_ENABLE) {
- ch_scratch.mhi.burst_mode_enabled = true;
- ch_scratch.mhi.polling_configuration =
- ipa3_mhi_get_ch_poll_cfg(channel,
- (ch_props.ring_len / ch_props.re_size));
- ch_scratch.mhi.polling_mode = IPA_MHI_POLLING_MODE_DB_MODE;
- } else {
- ch_scratch.mhi.burst_mode_enabled = false;
- }
- res = gsi_write_channel_scratch(channel->ep->gsi_chan_hdl,
- ch_scratch);
+ res = gsi_write_channel_scratch(ep->gsi_chan_hdl,
+ ch_scratch);
if (res) {
IPA_MHI_ERR("gsi_write_channel_scratch failed %d\n",
res);
- goto fail_ch_scratch;
- }
- channel->brstmode_enabled = ch_scratch.mhi.burst_mode_enabled;
- channel->ch_scratch.mhi = ch_scratch.mhi;
-
- IPA_MHI_DBG("Starting channel\n");
- res = gsi_start_channel(channel->ep->gsi_chan_hdl);
- if (res) {
- IPA_MHI_ERR("gsi_start_channel failed %d\n", res);
- goto fail_ch_start;
- }
-
- IPA_MHI_FUNC_EXIT();
- return 0;
-
-fail_ch_start:
-fail_ch_scratch:
- gsi_dealloc_channel(channel->ep->gsi_chan_hdl);
-fail_alloc_ch:
- gsi_dealloc_evt_ring(channel->ep->gsi_evt_ring_hdl);
- channel->ep->gsi_evt_ring_hdl = ~0;
-fail_alloc_evt:
- return res;
-}
-
-/**
- * ipa3_mhi_init() - Initialize IPA MHI driver
- * @params: initialization params
- *
- * This function is called by MHI client driver on boot to initialize IPA MHI
- * Driver. When this function returns device can move to READY state.
- * This function is doing the following:
- * - Initialize MHI IPA internal data structures
- * - Create IPA RM resources
- * - Initialize debugfs
- *
- * Return codes: 0 : success
- * negative : error
- */
-int ipa3_mhi_init(struct ipa_mhi_init_params *params)
-{
- int res;
- struct ipa_rm_create_params mhi_prod_params;
- struct ipa_rm_create_params mhi_cons_params;
-
- IPA_MHI_FUNC_ENTRY();
-
- if (!params) {
- IPA_MHI_ERR("null args\n");
- return -EINVAL;
- }
-
- if (!params->notify) {
- IPA_MHI_ERR("null notify function\n");
- return -EINVAL;
- }
-
- if (ipa3_mhi_ctx) {
- IPA_MHI_ERR("already initialized\n");
- return -EPERM;
- }
-
- IPA_MHI_DBG("msi: addr_lo = 0x%x addr_hi = 0x%x\n",
- params->msi.addr_low, params->msi.addr_hi);
- IPA_MHI_DBG("msi: data = 0x%x mask = 0x%x\n",
- params->msi.data, params->msi.mask);
- IPA_MHI_DBG("mmio_addr = 0x%x\n", params->mmio_addr);
- IPA_MHI_DBG("first_ch_idx = 0x%x\n", params->first_ch_idx);
- IPA_MHI_DBG("first_er_idx = 0x%x\n", params->first_er_idx);
- IPA_MHI_DBG("notify = %pF priv = %p\n", params->notify, params->priv);
- IPA_MHI_DBG("assert_bit40=%d\n", params->assert_bit40);
- IPA_MHI_DBG("test_mode=%d\n", params->test_mode);
-
- /* Initialize context */
- ipa3_mhi_ctx = kzalloc(sizeof(*ipa3_mhi_ctx), GFP_KERNEL);
- if (!ipa3_mhi_ctx) {
- IPA_MHI_ERR("no memory\n");
- res = -EFAULT;
- goto fail_alloc_ctx;
- }
-
- ipa3_mhi_ctx->state = IPA_MHI_STATE_INITIALIZED;
- ipa3_mhi_ctx->msi = params->msi;
- ipa3_mhi_ctx->mmio_addr = params->mmio_addr;
- ipa3_mhi_ctx->first_ch_idx = params->first_ch_idx;
- ipa3_mhi_ctx->first_er_idx = params->first_er_idx;
- ipa3_mhi_ctx->cb_notify = params->notify;
- ipa3_mhi_ctx->cb_priv = params->priv;
- ipa3_mhi_ctx->rm_cons_state = IPA_MHI_RM_STATE_RELEASED;
- ipa3_mhi_ctx->qmi_req_id = 0;
- ipa3_mhi_ctx->use_ipadma = true;
- ipa3_mhi_ctx->assert_bit40 = !!params->assert_bit40;
- ipa3_mhi_ctx->test_mode = params->test_mode;
- init_completion(&ipa3_mhi_ctx->rm_prod_granted_comp);
- spin_lock_init(&ipa3_mhi_ctx->state_lock);
- init_completion(&ipa3_mhi_ctx->rm_cons_comp);
-
- ipa3_mhi_ctx->wq = create_singlethread_workqueue("ipa_mhi_wq");
- if (!ipa3_mhi_ctx->wq) {
- IPA_MHI_ERR("failed to create workqueue\n");
- res = -EFAULT;
- goto fail_create_wq;
- }
-
- /* Create PROD in IPA RM */
- memset(&mhi_prod_params, 0, sizeof(mhi_prod_params));
- mhi_prod_params.name = IPA_RM_RESOURCE_MHI_PROD;
- mhi_prod_params.floor_voltage = IPA_VOLTAGE_SVS;
- mhi_prod_params.reg_params.notify_cb = ipa3_mhi_rm_prod_notify;
- res = ipa_rm_create_resource(&mhi_prod_params);
- if (res) {
- IPA_MHI_ERR("fail to create IPA_RM_RESOURCE_MHI_PROD\n");
- goto fail_create_rm_prod;
- }
-
- /* Create CONS in IPA RM */
- memset(&mhi_cons_params, 0, sizeof(mhi_cons_params));
- mhi_cons_params.name = IPA_RM_RESOURCE_MHI_CONS;
- mhi_cons_params.floor_voltage = IPA_VOLTAGE_SVS;
- mhi_cons_params.request_resource = ipa3_mhi_rm_cons_request;
- mhi_cons_params.release_resource = ipa3_mhi_rm_cons_release;
- res = ipa_rm_create_resource(&mhi_cons_params);
- if (res) {
- IPA_MHI_ERR("fail to create IPA_RM_RESOURCE_MHI_CONS\n");
- goto fail_create_rm_cons;
- }
-
- /* (ipa3_ctx->transport_prototype == IPA_TRANSPORT_TYPE_GSI)
- * we need to move to READY state only after
- * HPS/DPS/GSI firmware are loaded.
- */
-
- /* Initialize uC interface */
- ipa3_uc_mhi_init(ipa3_mhi_uc_ready_cb,
- ipa3_mhi_uc_wakeup_request_cb);
- if (ipa3_uc_state_check() == 0)
- ipa3_mhi_set_state(IPA_MHI_STATE_READY);
-
- /* Initialize debugfs */
- ipa3_mhi_debugfs_init();
-
- IPA_MHI_FUNC_EXIT();
- return 0;
-
-fail_create_rm_cons:
- ipa_rm_delete_resource(IPA_RM_RESOURCE_MHI_PROD);
-fail_create_rm_prod:
- destroy_workqueue(ipa3_mhi_ctx->wq);
-fail_create_wq:
- kfree(ipa3_mhi_ctx);
- ipa3_mhi_ctx = NULL;
-fail_alloc_ctx:
- return res;
-}
-
-/**
- * ipa3_mhi_start() - Start IPA MHI engine
- * @params: pcie addresses for MHI
- *
- * This function is called by MHI client driver on MHI engine start for
- * handling MHI accelerated channels. This function is called after
- * ipa3_mhi_init() was called and can be called after MHI reset to restart MHI
- * engine. When this function returns device can move to M0 state.
- *
- * Return codes: 0 : success
- * negative : error
- */
-int ipa3_mhi_start(struct ipa_mhi_start_params *params)
-{
- int res;
- struct gsi_device_scratch gsi_scratch;
- struct ipa_gsi_ep_config *gsi_ep_info;
-
- IPA_MHI_FUNC_ENTRY();
-
- if (!params) {
- IPA_MHI_ERR("null args\n");
- return -EINVAL;
- }
-
- if (!ipa3_mhi_ctx) {
- IPA_MHI_ERR("not initialized\n");
- return -EPERM;
- }
-
- if (ipa3_ctx->transport_prototype == IPA_TRANSPORT_TYPE_SPS &&
- ipa3_uc_state_check()) {
- IPA_MHI_ERR("IPA uc is not loaded\n");
- return -EAGAIN;
- }
-
- res = ipa3_mhi_set_state(IPA_MHI_STATE_STARTED);
- if (res) {
- IPA_MHI_ERR("ipa3_mhi_set_state %d\n", res);
- return res;
- }
-
- ipa3_mhi_ctx->host_ctrl_addr = params->host_ctrl_addr;
- ipa3_mhi_ctx->host_data_addr = params->host_data_addr;
- ipa3_mhi_ctx->channel_context_array_addr =
- params->channel_context_array_addr;
- ipa3_mhi_ctx->event_context_array_addr =
- params->event_context_array_addr;
- IPADBG("host_ctrl_addr 0x%x\n", ipa3_mhi_ctx->host_ctrl_addr);
- IPADBG("host_data_addr 0x%x\n", ipa3_mhi_ctx->host_data_addr);
- IPADBG("channel_context_array_addr 0x%llx\n",
- ipa3_mhi_ctx->channel_context_array_addr);
- IPADBG("event_context_array_addr 0x%llx\n",
- ipa3_mhi_ctx->event_context_array_addr);
-
- /* Add MHI <-> Q6 dependencies to IPA RM */
- res = ipa_rm_add_dependency(IPA_RM_RESOURCE_MHI_PROD,
- IPA_RM_RESOURCE_Q6_CONS);
- if (res && res != -EINPROGRESS) {
- IPA_MHI_ERR("failed to add dependency %d\n", res);
- goto fail_add_mhi_q6_dep;
- }
-
- res = ipa_rm_add_dependency(IPA_RM_RESOURCE_Q6_PROD,
- IPA_RM_RESOURCE_MHI_CONS);
- if (res && res != -EINPROGRESS) {
- IPA_MHI_ERR("failed to add dependency %d\n", res);
- goto fail_add_q6_mhi_dep;
- }
-
- res = ipa3_mhi_request_prod();
- if (res) {
- IPA_MHI_ERR("failed request prod %d\n", res);
- goto fail_request_prod;
- }
-
- /* Initialize IPA MHI engine */
- if (ipa3_ctx->transport_prototype == IPA_TRANSPORT_TYPE_GSI) {
- gsi_ep_info = ipa_get_gsi_ep_info(
- ipa_get_ep_mapping(IPA_CLIENT_MHI_PROD));
- if (!gsi_ep_info) {
- IPAERR("MHI PROD has no ep allocated\n");
- BUG();
- }
- memset(&gsi_scratch, 0, sizeof(gsi_scratch));
- gsi_scratch.mhi_base_chan_idx_valid = true;
- gsi_scratch.mhi_base_chan_idx = gsi_ep_info->ipa_gsi_chan_num +
- ipa3_mhi_ctx->first_ch_idx;
- res = gsi_write_device_scratch(ipa3_ctx->gsi_dev_hdl,
- &gsi_scratch);
- if (res) {
- IPA_MHI_ERR("failed to write device scratch %d\n", res);
- goto fail_init_engine;
- }
- } else {
- res = ipa3_uc_mhi_init_engine(&ipa3_mhi_ctx->msi,
- ipa3_mhi_ctx->mmio_addr,
- ipa3_mhi_ctx->host_ctrl_addr,
- ipa3_mhi_ctx->host_data_addr,
- ipa3_mhi_ctx->first_ch_idx,
- ipa3_mhi_ctx->first_er_idx);
- if (res) {
- IPA_MHI_ERR("failed to start MHI engine %d\n", res);
- goto fail_init_engine;
- }
-
- /* Update UL/DL sync if valid */
- res = ipa3_uc_mhi_send_dl_ul_sync_info(
- ipa3_cached_dl_ul_sync_info);
- if (res) {
- IPA_MHI_ERR("failed to update ul/dl sync %d\n", res);
- goto fail_init_engine;
- }
- }
-
- IPA_MHI_FUNC_EXIT();
- return 0;
-
-fail_init_engine:
- ipa3_mhi_release_prod();
-fail_request_prod:
- ipa_rm_delete_dependency(IPA_RM_RESOURCE_Q6_PROD,
- IPA_RM_RESOURCE_MHI_CONS);
-fail_add_q6_mhi_dep:
- ipa_rm_delete_dependency(IPA_RM_RESOURCE_MHI_PROD,
- IPA_RM_RESOURCE_Q6_CONS);
-fail_add_mhi_q6_dep:
- ipa3_mhi_set_state(IPA_MHI_STATE_INITIALIZED);
- return res;
-}
-
-/**
- * ipa3_mhi_connect_pipe() - Connect pipe to IPA and start corresponding
- * MHI channel
- * @in: connect parameters
- * @clnt_hdl: [out] client handle for this pipe
- *
- * This function is called by MHI client driver on MHI channel start.
- * This function is called after MHI engine was started.
- *
- * Return codes: 0 : success
- * negative : error
- */
-int ipa3_mhi_connect_pipe(struct ipa_mhi_connect_params *in, u32 *clnt_hdl)
-{
- struct ipa3_ep_context *ep;
- int ipa_ep_idx;
- int res;
- struct ipa3_mhi_channel_ctx *channel = NULL;
- unsigned long flags;
-
- IPA_MHI_FUNC_ENTRY();
-
- if (!in || !clnt_hdl) {
- IPA_MHI_ERR("NULL args\n");
- return -EINVAL;
- }
-
- if (in->sys.client >= IPA_CLIENT_MAX) {
- IPA_MHI_ERR("bad param client:%d\n", in->sys.client);
- return -EINVAL;
- }
-
- if (!IPA_CLIENT_IS_MHI(in->sys.client)) {
- IPA_MHI_ERR("Invalid MHI client, client: %d\n", in->sys.client);
- return -EINVAL;
- }
-
- IPA_MHI_DBG("channel=%d\n", in->channel_id);
-
- spin_lock_irqsave(&ipa3_mhi_ctx->state_lock, flags);
- if (!ipa3_mhi_ctx || ipa3_mhi_ctx->state != IPA_MHI_STATE_STARTED) {
- IPA_MHI_ERR("IPA MHI was not started\n");
- spin_unlock_irqrestore(&ipa3_mhi_ctx->state_lock, flags);
- return -EINVAL;
- }
- spin_unlock_irqrestore(&ipa3_mhi_ctx->state_lock, flags);
-
- ipa_ep_idx = ipa3_get_ep_mapping(in->sys.client);
- if (ipa_ep_idx == -1) {
- IPA_MHI_ERR("Invalid client.\n");
- return -EINVAL;
- }
-
- ep = &ipa3_ctx->ep[ipa_ep_idx];
-
- if (ep->valid == 1) {
- IPA_MHI_ERR("EP already allocated.\n");
- return -EPERM;
- }
-
- memset(ep, 0, offsetof(struct ipa3_ep_context, sys));
- ep->valid = 1;
- ep->skip_ep_cfg = in->sys.skip_ep_cfg;
- ep->client = in->sys.client;
- ep->client_notify = in->sys.notify;
- ep->priv = in->sys.priv;
- ep->keep_ipa_awake = in->sys.keep_ipa_awake;
-
- channel = ipa3_mhi_get_channel_context(ep,
- in->channel_id);
- if (!channel) {
- IPA_MHI_ERR("ipa3_mhi_get_channel_context failed\n");
- res = -EINVAL;
- goto fail_init_channel;
- }
-
- channel->channel_context_addr =
- ipa3_mhi_ctx->channel_context_array_addr +
- channel->id * sizeof(struct ipa3_mhi_ch_ctx);
-
- /* for event context address index needs to read from host */
-
- IPA_MHI_DBG("client %d channelHandle %d channelIndex %d\n",
- channel->ep->client, channel->index, channel->id);
- IPA_MHI_DBG("channel_context_addr 0x%llx\n",
- channel->channel_context_addr);
-
- IPA_ACTIVE_CLIENTS_INC_EP(in->sys.client);
-
- if (ipa3_ctx->transport_prototype == IPA_TRANSPORT_TYPE_GSI) {
- res = ipa_mhi_start_gsi_channel(channel, ipa_ep_idx);
- if (res) {
- IPA_MHI_ERR("ipa_mhi_start_gsi_channel failed %d\n",
- res);
- goto fail_start_channel;
- }
- channel->state = IPA_HW_MHI_CHANNEL_STATE_RUN;
-
- res = ipa_mhi_read_write_host(IPA_MHI_DMA_TO_HOST,
- &channel->state, channel->channel_context_addr +
- offsetof(struct ipa3_mhi_ch_ctx, chstate),
- sizeof(((struct ipa3_mhi_ch_ctx *)0)->chstate));
- if (res) {
- IPAERR("ipa_mhi_read_write_host failed\n");
- return res;
-
- }
- } else {
- res = ipa_mhi_start_uc_channel(channel, ipa_ep_idx);
- if (res) {
- IPA_MHI_ERR("ipa_mhi_start_uc_channel failed %d\n",
- res);
- goto fail_start_channel;
- }
- channel->state = IPA_HW_MHI_CHANNEL_STATE_RUN;
- }
-
- res = ipa3_enable_data_path(ipa_ep_idx);
- if (res) {
- IPA_MHI_ERR("enable data path failed res=%d clnt=%d.\n", res,
- ipa_ep_idx);
- goto fail_enable_dp;
- }
-
- if (!ep->skip_ep_cfg) {
- if (ipa3_cfg_ep(ipa_ep_idx, &in->sys.ipa_ep_cfg)) {
- IPAERR("fail to configure EP.\n");
- goto fail_ep_cfg;
- }
- if (ipa3_cfg_ep_status(ipa_ep_idx, &ep->status)) {
- IPAERR("fail to configure status of EP.\n");
- goto fail_ep_cfg;
- }
- IPA_MHI_DBG("ep configuration successful\n");
- } else {
- IPA_MHI_DBG("skipping ep configuration\n");
- }
-
- *clnt_hdl = ipa_ep_idx;
-
- if (!ep->skip_ep_cfg && IPA_CLIENT_IS_PROD(in->sys.client))
- ipa3_install_dflt_flt_rules(ipa_ep_idx);
-
- if (!ep->keep_ipa_awake)
- IPA_ACTIVE_CLIENTS_DEC_EP(in->sys.client);
-
- ipa3_ctx->skip_ep_cfg_shadow[ipa_ep_idx] = ep->skip_ep_cfg;
- IPA_MHI_DBG("client %d (ep: %d) connected\n", in->sys.client,
- ipa_ep_idx);
-
- IPA_MHI_FUNC_EXIT();
-
- return 0;
-
-fail_ep_cfg:
- ipa3_disable_data_path(ipa_ep_idx);
-fail_enable_dp:
- ipa3_mhi_reset_channel(channel);
-fail_start_channel:
- IPA_ACTIVE_CLIENTS_DEC_EP(in->sys.client);
-fail_init_channel:
- memset(ep, 0, offsetof(struct ipa3_ep_context, sys));
- return -EPERM;
-}
-
-/**
- * ipa3_mhi_disconnect_pipe() - Disconnect pipe from IPA and reset corresponding
- * MHI channel
- * @clnt_hdl: client handle for this pipe
- *
- * This function is called by MHI client driver on MHI channel reset.
- * This function is called after MHI channel was started.
- * This function is doing the following:
- * - Send command to uC/GSI to reset corresponding MHI channel
- * - Configure IPA EP control
- *
- * Return codes: 0 : success
- * negative : error
- */
-int ipa3_mhi_disconnect_pipe(u32 clnt_hdl)
-{
- struct ipa3_ep_context *ep;
- static struct ipa3_mhi_channel_ctx *channel;
- int res;
-
- IPA_MHI_FUNC_ENTRY();
-
- if (clnt_hdl >= ipa3_ctx->ipa_num_pipes) {
- IPAERR("invalid handle %d\n", clnt_hdl);
- return -EINVAL;
- }
-
- if (ipa3_ctx->ep[clnt_hdl].valid == 0) {
- IPAERR("pipe was not connected %d\n", clnt_hdl);
- return -EINVAL;
- }
-
- if (!ipa3_mhi_ctx) {
- IPA_MHI_ERR("IPA MHI was not initialized\n");
- return -EINVAL;
- }
-
- if (!IPA_CLIENT_IS_MHI(ipa3_ctx->ep[clnt_hdl].client)) {
- IPAERR("invalid IPA MHI client, client: %d\n",
- ipa3_ctx->ep[clnt_hdl].client);
- return -EINVAL;
- }
-
- channel = ipa3_mhi_get_channel_context_by_clnt_hdl(clnt_hdl);
- if (!channel) {
- IPAERR("invalid clnt index\n");
- return -EINVAL;
- }
- ep = &ipa3_ctx->ep[clnt_hdl];
-
- if (!ep->keep_ipa_awake)
- IPA_ACTIVE_CLIENTS_INC_EP(ipa3_get_client_mapping(clnt_hdl));
- res = ipa3_mhi_reset_channel(channel);
- if (res) {
- IPA_MHI_ERR("ipa3_mhi_reset_channel failed %d\n", res);
- goto fail_reset_channel;
- }
-
- if (ipa3_ctx->transport_prototype == IPA_TRANSPORT_TYPE_GSI) {
- res = gsi_dealloc_channel(channel->ep->gsi_chan_hdl);
- if (res) {
- IPAERR("gsi_dealloc_channel failed %d\n", res);
- goto fail_reset_channel;
- }
- }
-
- ep->valid = 0;
- ipa3_delete_dflt_flt_rules(clnt_hdl);
- IPA_ACTIVE_CLIENTS_DEC_EP(ipa3_get_client_mapping(clnt_hdl));
-
- IPA_MHI_DBG("client (ep: %d) disconnected\n", clnt_hdl);
- IPA_MHI_FUNC_EXIT();
- return 0;
-
-fail_reset_channel:
- if (!ep->keep_ipa_awake)
- IPA_ACTIVE_CLIENTS_DEC_EP(ipa3_get_client_mapping(clnt_hdl));
- return res;
-}
-
-
-static int ipa3_mhi_suspend_ul_channels(void)
-{
- int i;
- int res;
-
- IPA_MHI_FUNC_ENTRY();
- for (i = 0; i < IPA_MHI_MAX_UL_CHANNELS; i++) {
- if (!ipa3_mhi_ctx->ul_channels[i].valid)
- continue;
- if (ipa3_mhi_ctx->ul_channels[i].state !=
- IPA_HW_MHI_CHANNEL_STATE_RUN)
- continue;
- IPA_MHI_DBG_LOW("suspending channel %d\n",
- ipa3_mhi_ctx->ul_channels[i].id);
-
- if (ipa3_ctx->transport_prototype == IPA_TRANSPORT_TYPE_GSI)
- res = ipa3_mhi_suspend_gsi_channel(
- &ipa3_mhi_ctx->ul_channels[i]);
- else
- res = ipa3_uc_mhi_suspend_channel(
- ipa3_mhi_ctx->ul_channels[i].index);
-
- if (res) {
- IPA_MHI_ERR("failed to suspend channel %d error %d\n",
- i, res);
- return res;
- }
- ipa3_mhi_ctx->ul_channels[i].state =
- IPA_HW_MHI_CHANNEL_STATE_SUSPEND;
- }
-
- IPA_MHI_FUNC_EXIT();
- return 0;
-}
-static int ipa3_mhi_resume_ul_channels(bool LPTransitionRejected)
-{
- int i;
- int res;
- struct ipa3_mhi_channel_ctx *channel;
-
- IPA_MHI_FUNC_ENTRY();
- for (i = 0; i < IPA_MHI_MAX_UL_CHANNELS; i++) {
- if (!ipa3_mhi_ctx->ul_channels[i].valid)
- continue;
- if (ipa3_mhi_ctx->ul_channels[i].state !=
- IPA_HW_MHI_CHANNEL_STATE_SUSPEND)
- continue;
- channel = &ipa3_mhi_ctx->ul_channels[i];
- IPA_MHI_DBG_LOW("resuming channel %d\n", channel->id);
-
- if (ipa3_ctx->transport_prototype == IPA_TRANSPORT_TYPE_GSI) {
- if (channel->brstmode_enabled &&
- !LPTransitionRejected) {
- /*
- * set polling mode bit to DB mode before
- * resuming the channel
- */
- res = gsi_write_channel_scratch(
- channel->ep->gsi_chan_hdl,
- channel->ch_scratch);
- if (res) {
- IPA_MHI_ERR("write ch scratch fail %d\n"
- , res);
- return res;
- }
- }
- res = gsi_start_channel(channel->ep->gsi_chan_hdl);
- } else {
- res = ipa3_uc_mhi_resume_channel(channel->index,
- LPTransitionRejected);
- }
-
- if (res) {
- IPA_MHI_ERR("failed to resume channel %d error %d\n",
- i, res);
- return res;
- }
-
- channel->stop_in_proc = false;
- channel->state = IPA_HW_MHI_CHANNEL_STATE_RUN;
- }
-
- IPA_MHI_FUNC_EXIT();
- return 0;
-}
-
-static int ipa3_mhi_stop_event_update_ul_channels(void)
-{
- int i;
- int res;
-
- if (ipa3_ctx->transport_prototype == IPA_TRANSPORT_TYPE_GSI)
- return 0;
-
- IPA_MHI_FUNC_ENTRY();
- for (i = 0; i < IPA_MHI_MAX_UL_CHANNELS; i++) {
- if (!ipa3_mhi_ctx->ul_channels[i].valid)
- continue;
- if (ipa3_mhi_ctx->ul_channels[i].state !=
- IPA_HW_MHI_CHANNEL_STATE_SUSPEND)
- continue;
- IPA_MHI_DBG_LOW("stop update event channel %d\n",
- ipa3_mhi_ctx->ul_channels[i].id);
- res = ipa3_uc_mhi_stop_event_update_channel(
- ipa3_mhi_ctx->ul_channels[i].index);
- if (res) {
- IPA_MHI_ERR("failed stop event channel %d error %d\n",
- i, res);
- return res;
- }
- }
-
- IPA_MHI_FUNC_EXIT();
- return 0;
-}
-
-static int ipa3_mhi_suspend_dl_channels(void)
-{
- int i;
- int res;
-
- IPA_MHI_FUNC_ENTRY();
- for (i = 0; i < IPA_MHI_MAX_DL_CHANNELS; i++) {
- if (!ipa3_mhi_ctx->dl_channels[i].valid)
- continue;
- if (ipa3_mhi_ctx->dl_channels[i].state !=
- IPA_HW_MHI_CHANNEL_STATE_RUN)
- continue;
- IPA_MHI_DBG_LOW("suspending channel %d\n",
- ipa3_mhi_ctx->dl_channels[i].id);
- if (ipa3_ctx->transport_prototype == IPA_TRANSPORT_TYPE_GSI)
- res = ipa3_mhi_suspend_gsi_channel(
- &ipa3_mhi_ctx->dl_channels[i]);
- else
- res = ipa3_uc_mhi_suspend_channel(
- ipa3_mhi_ctx->dl_channels[i].index);
- if (res) {
- IPA_MHI_ERR("failed to suspend channel %d error %d\n",
- i, res);
- return res;
- }
- ipa3_mhi_ctx->dl_channels[i].state =
- IPA_HW_MHI_CHANNEL_STATE_SUSPEND;
- }
-
- IPA_MHI_FUNC_EXIT();
- return 0;
-}
-
-static int ipa3_mhi_resume_dl_channels(bool LPTransitionRejected)
-{
- int i;
- int res;
- struct ipa3_mhi_channel_ctx *channel;
-
- IPA_MHI_FUNC_ENTRY();
- for (i = 0; i < IPA_MHI_MAX_DL_CHANNELS; i++) {
- if (!ipa3_mhi_ctx->dl_channels[i].valid)
- continue;
- if (ipa3_mhi_ctx->dl_channels[i].state !=
- IPA_HW_MHI_CHANNEL_STATE_SUSPEND)
- continue;
- channel = &ipa3_mhi_ctx->dl_channels[i];
- IPA_MHI_DBG_LOW("resuming channel %d\n", channel->id);
-
- if (ipa3_ctx->transport_prototype == IPA_TRANSPORT_TYPE_GSI) {
- if (channel->brstmode_enabled &&
- !LPTransitionRejected) {
- /*
- * set polling mode bit to DB mode before
- * resuming the channel
- */
- res = gsi_write_channel_scratch(
- channel->ep->gsi_chan_hdl,
- channel->ch_scratch);
- if (res) {
- IPA_MHI_ERR("write ch scratch fail %d\n"
- , res);
- return res;
- }
- }
- res = gsi_start_channel(channel->ep->gsi_chan_hdl);
- } else {
- res = ipa3_uc_mhi_resume_channel(channel->index,
- LPTransitionRejected);
- }
- if (res) {
- IPA_MHI_ERR("failed to resume channel %d error %d\n",
- i, res);
- return res;
- }
- channel->stop_in_proc = false;
- channel->state = IPA_HW_MHI_CHANNEL_STATE_RUN;
- }
-
- IPA_MHI_FUNC_EXIT();
- return 0;
-}
-
-static int ipa3_mhi_stop_event_update_dl_channels(void)
-{
- int i;
- int res;
+ goto fail_ch_scratch;
+ }
- if (ipa3_ctx->transport_prototype == IPA_TRANSPORT_TYPE_GSI)
- return 0;
+ *params->mhi = ch_scratch.mhi;
- IPA_MHI_FUNC_ENTRY();
- for (i = 0; i < IPA_MHI_MAX_DL_CHANNELS; i++) {
- if (!ipa3_mhi_ctx->dl_channels[i].valid)
- continue;
- if (ipa3_mhi_ctx->dl_channels[i].state !=
- IPA_HW_MHI_CHANNEL_STATE_SUSPEND)
- continue;
- IPA_MHI_DBG_LOW("stop update event channel %d\n",
- ipa3_mhi_ctx->dl_channels[i].id);
- res = ipa3_uc_mhi_stop_event_update_channel(
- ipa3_mhi_ctx->dl_channels[i].index);
- if (res) {
- IPA_MHI_ERR("failed stop event channel %d error %d\n",
- i, res);
- return res;
- }
+ IPA_MHI_DBG("Starting channel\n");
+ res = gsi_start_channel(ep->gsi_chan_hdl);
+ if (res) {
+ IPA_MHI_ERR("gsi_start_channel failed %d\n", res);
+ goto fail_ch_start;
}
IPA_MHI_FUNC_EXIT();
return 0;
+
+fail_ch_start:
+fail_ch_scratch:
+ gsi_dealloc_channel(ep->gsi_chan_hdl);
+fail_alloc_ch:
+ gsi_dealloc_evt_ring(ep->gsi_evt_ring_hdl);
+ ep->gsi_evt_ring_hdl = ~0;
+fail_alloc_evt:
+ return res;
}
-static bool ipa3_mhi_check_pending_packets_from_host(void)
+int ipa3_mhi_init_engine(struct ipa_mhi_init_engine *params)
{
- int i;
int res;
- struct ipa3_mhi_channel_ctx *channel;
+ struct gsi_device_scratch gsi_scratch;
+ struct ipa_gsi_ep_config *gsi_ep_info;
IPA_MHI_FUNC_ENTRY();
- for (i = 0; i < IPA_MHI_MAX_UL_CHANNELS; i++) {
- channel = &ipa3_mhi_ctx->ul_channels[i];
- if (!channel->valid)
- continue;
-
- res = gsi_query_channel_info(channel->ep->gsi_chan_hdl,
- &channel->ch_info);
- if (res) {
- IPAERR("gsi_query_channel_info failed\n");
- return true;
- }
- res = ipa_mhi_read_ch_ctx(channel);
- if (res) {
- IPA_MHI_ERR("ipa_mhi_read_ch_ctx failed %d\n", res);
- return true;
- }
-
- if (channel->ch_info.rp != channel->ch_ctx_host.wp) {
- IPA_MHI_DBG("There are pending packets from host\n");
- IPA_MHI_DBG("device rp 0x%llx host 0x%llx\n",
- channel->ch_info.rp, channel->ch_ctx_host.wp);
- return true;
- }
+ if (!params) {
+ IPA_MHI_ERR("null args\n");
+ return -EINVAL;
}
- IPA_MHI_FUNC_EXIT();
- return false;
-}
-
-static void ipa3_mhi_update_host_ch_state(bool update_rp)
-{
- int i;
- int res;
- struct ipa3_mhi_channel_ctx *channel;
-
- for (i = 0; i < IPA_MHI_MAX_UL_CHANNELS; i++) {
- channel = &ipa3_mhi_ctx->ul_channels[i];
- if (!channel->valid)
- continue;
-
- if (update_rp) {
- res = gsi_query_channel_info(channel->ep->gsi_chan_hdl,
- &channel->ch_info);
- if (res) {
- IPAERR("gsi_query_channel_info failed\n");
- BUG();
- return;
- }
-
- res = ipa_mhi_read_write_host(IPA_MHI_DMA_TO_HOST,
- &channel->ch_info.rp,
- channel->channel_context_addr +
- offsetof(struct ipa3_mhi_ch_ctx, rp),
- sizeof(channel->ch_info.rp));
- if (res) {
- IPAERR("ipa_mhi_read_write_host failed\n");
- BUG();
- return;
- }
- }
-
- res = ipa_mhi_read_write_host(IPA_MHI_DMA_TO_HOST,
- &channel->state, channel->channel_context_addr +
- offsetof(struct ipa3_mhi_ch_ctx, chstate),
- sizeof(((struct ipa3_mhi_ch_ctx *)0)->chstate));
- if (res) {
- IPAERR("ipa_mhi_read_write_host failed\n");
- BUG();
- return;
- }
+ /* Initialize IPA MHI engine */
+ gsi_ep_info = ipa_get_gsi_ep_info(
+ ipa_get_ep_mapping(IPA_CLIENT_MHI_PROD));
+ if (!gsi_ep_info) {
+ IPAERR("MHI PROD has no ep allocated\n");
+ ipa_assert();
}
-
- for (i = 0; i < IPA_MHI_MAX_DL_CHANNELS; i++) {
- channel = &ipa3_mhi_ctx->dl_channels[i];
- if (!channel->valid)
- continue;
-
- if (update_rp) {
- res = gsi_query_channel_info(channel->ep->gsi_chan_hdl,
- &channel->ch_info);
- if (res) {
- IPAERR("gsi_query_channel_info failed\n");
- BUG();
- return;
- }
-
- res = ipa_mhi_read_write_host(IPA_MHI_DMA_TO_HOST,
- &channel->ch_info.rp,
- channel->channel_context_addr +
- offsetof(struct ipa3_mhi_ch_ctx, rp),
- sizeof(channel->ch_info.rp));
- if (res) {
- IPAERR("ipa_mhi_read_write_host failed\n");
- BUG();
- return;
- }
- }
-
- res = ipa_mhi_read_write_host(IPA_MHI_DMA_TO_HOST,
- &channel->state, channel->channel_context_addr +
- offsetof(struct ipa3_mhi_ch_ctx, chstate),
- sizeof(((struct ipa3_mhi_ch_ctx *)0)->chstate));
- if (res) {
- IPAERR("ipa_mhi_read_write_host failed\n");
- BUG();
- }
+ memset(&gsi_scratch, 0, sizeof(gsi_scratch));
+ gsi_scratch.mhi_base_chan_idx_valid = true;
+ gsi_scratch.mhi_base_chan_idx = gsi_ep_info->ipa_gsi_chan_num +
+ params->gsi.first_ch_idx;
+ res = gsi_write_device_scratch(ipa3_ctx->gsi_dev_hdl,
+ &gsi_scratch);
+ if (res) {
+ IPA_MHI_ERR("failed to write device scratch %d\n", res);
+ goto fail_init_engine;
}
-}
-
-static bool ipa3_mhi_has_open_aggr_frame(void)
-{
- struct ipa3_mhi_channel_ctx *channel;
- u32 aggr_state_active;
- int i;
- int ipa_ep_idx;
-
- aggr_state_active = ipahal_read_reg(IPA_STATE_AGGR_ACTIVE);
- IPA_MHI_DBG_LOW("IPA_STATE_AGGR_ACTIVE_OFST 0x%x\n", aggr_state_active);
-
- for (i = 0; i < IPA_MHI_MAX_DL_CHANNELS; i++) {
- channel = &ipa3_mhi_ctx->dl_channels[i];
-
- if (!channel->valid)
- continue;
-
- ipa_ep_idx = ipa_get_ep_mapping(channel->ep->client);
- if (ipa_ep_idx == -1) {
- BUG();
- return false;
- }
- if ((1 << ipa_ep_idx) & aggr_state_active)
- return true;
- }
+ IPA_MHI_FUNC_EXIT();
+ return 0;
- return false;
+fail_init_engine:
+ return res;
}
-
/**
- * ipa3_mhi_suspend() - Suspend MHI accelerated channels
- * @force:
- * false: in case of data pending in IPA, MHI channels will not be
- * suspended and function will fail.
- * true: in case of data pending in IPA, make sure no further access from
- * IPA to PCIe is possible. In this case suspend cannot fail.
+ * ipa3_connect_mhi_pipe() - Connect pipe to IPA and start corresponding
+ * MHI channel
+ * @in: connect parameters
+ * @clnt_hdl: [out] client handle for this pipe
*
- * This function is called by MHI client driver on MHI suspend.
- * This function is called after MHI channel was started.
- * When this function returns device can move to M1/M2/M3/D3cold state.
+ * This function is called by IPA MHI client driver on MHI channel start.
+ * This function is called after MHI engine was started.
*
* Return codes: 0 : success
* negative : error
*/
-int ipa3_mhi_suspend(bool force)
+int ipa3_connect_mhi_pipe(struct ipa_mhi_connect_params_internal *in,
+ u32 *clnt_hdl)
{
+ struct ipa3_ep_context *ep;
+ int ipa_ep_idx;
int res;
- bool empty;
- bool force_clear = false;
+ enum ipa_client_type client;
IPA_MHI_FUNC_ENTRY();
- res = ipa3_mhi_set_state(IPA_MHI_STATE_SUSPEND_IN_PROGRESS);
- if (res) {
- IPA_MHI_ERR("ipa3_mhi_set_state failed %d\n", res);
- return res;
+ if (!in || !clnt_hdl) {
+ IPA_MHI_ERR("NULL args\n");
+ return -EINVAL;
}
- res = ipa3_mhi_suspend_ul_channels();
- if (res) {
- IPA_MHI_ERR("ipa3_mhi_suspend_ul_channels failed %d\n", res);
- goto fail_suspend_ul_channel;
+ client = in->sys->client;
+ ipa_ep_idx = ipa3_get_ep_mapping(client);
+ if (ipa_ep_idx == -1) {
+ IPA_MHI_ERR("Invalid client.\n");
+ return -EINVAL;
}
- empty = ipa3_mhi_wait_for_ul_empty_timeout(
- IPA_MHI_CH_EMPTY_TIMEOUT_MSEC);
-
- if (!empty) {
- if (force) {
- res = ipa3_mhi_enable_force_clear(
- ipa3_mhi_ctx->qmi_req_id, false);
- if (res) {
- IPA_MHI_ERR("failed to enable force clear\n");
- BUG();
- return res;
- }
- force_clear = true;
- IPA_MHI_DBG_LOW("force clear datapath enabled\n");
-
- empty = ipa3_mhi_wait_for_ul_empty_timeout(
- IPA_MHI_CH_EMPTY_TIMEOUT_MSEC);
- IPADBG("empty=%d\n", empty);
- if (!empty && ipa3_ctx->transport_prototype
- == IPA_TRANSPORT_TYPE_GSI) {
- IPA_MHI_ERR("Failed to suspend UL channels\n");
- if (ipa3_mhi_ctx->test_mode) {
- res = -EAGAIN;
- goto fail_suspend_ul_channel;
- }
-
- BUG();
- }
- } else {
- IPA_MHI_DBG("IPA not empty\n");
- res = -EAGAIN;
- goto fail_suspend_ul_channel;
- }
- }
+ ep = &ipa3_ctx->ep[ipa_ep_idx];
- if (force_clear) {
- res = ipa3_mhi_disable_force_clear(ipa3_mhi_ctx->qmi_req_id);
- if (res) {
- IPA_MHI_ERR("failed to disable force clear\n");
- BUG();
- return res;
- }
- IPA_MHI_DBG_LOW("force clear datapath disabled\n");
- ipa3_mhi_ctx->qmi_req_id++;
+ if (ep->valid == 1) {
+ IPA_MHI_ERR("EP already allocated.\n");
+ return -EPERM;
}
- if (!force && ipa3_ctx->transport_prototype == IPA_TRANSPORT_TYPE_GSI) {
- if (ipa3_mhi_check_pending_packets_from_host()) {
- res = -EAGAIN;
- goto fail_suspend_ul_channel;
- }
- }
+ memset(ep, 0, offsetof(struct ipa3_ep_context, sys));
+ ep->valid = 1;
+ ep->skip_ep_cfg = in->sys->skip_ep_cfg;
+ ep->client = client;
+ ep->client_notify = in->sys->notify;
+ ep->priv = in->sys->priv;
+ ep->keep_ipa_awake = in->sys->keep_ipa_awake;
- res = ipa3_mhi_stop_event_update_ul_channels();
+ res = ipa_mhi_start_gsi_channel(client,
+ ipa_ep_idx, &in->start.gsi);
if (res) {
- IPA_MHI_ERR(
- "ipa3_mhi_stop_event_update_ul_channels failed %d\n",
+ IPA_MHI_ERR("ipa_mhi_start_gsi_channel failed %d\n",
res);
- goto fail_suspend_ul_channel;
- }
-
- /*
- * hold IPA clocks and release them after all
- * IPA RM resource are released to make sure tag process will not start
- */
- IPA_ACTIVE_CLIENTS_INC_SIMPLE();
-
- IPA_MHI_DBG_LOW("release prod\n");
- res = ipa3_mhi_release_prod();
- if (res) {
- IPA_MHI_ERR("ipa3_mhi_release_prod failed %d\n", res);
- goto fail_release_prod;
- }
-
- IPA_MHI_DBG_LOW("wait for cons release\n");
- res = ipa3_mhi_wait_for_cons_release();
- if (res) {
- IPA_MHI_ERR("ipa3_mhi_wait_for_cons_release failed %d\n", res);
- goto fail_release_cons;
- }
-
- usleep_range(IPA_MHI_SUSPEND_SLEEP_MIN, IPA_MHI_SUSPEND_SLEEP_MAX);
-
- res = ipa3_mhi_suspend_dl_channels();
- if (res) {
- IPA_MHI_ERR("ipa3_mhi_suspend_dl_channels failed %d\n", res);
- goto fail_suspend_dl_channel;
+ goto fail_start_channel;
}
- res = ipa3_mhi_stop_event_update_dl_channels();
+ res = ipa3_enable_data_path(ipa_ep_idx);
if (res) {
- IPA_MHI_ERR("failed to stop event update on DL %d\n", res);
- goto fail_stop_event_update_dl_channel;
+ IPA_MHI_ERR("enable data path failed res=%d clnt=%d.\n", res,
+ ipa_ep_idx);
+ goto fail_ep_cfg;
}
- if (ipa3_ctx->transport_prototype == IPA_TRANSPORT_TYPE_GSI) {
- if (ipa3_mhi_has_open_aggr_frame()) {
- IPA_MHI_DBG("There is an open aggr frame\n");
- if (force) {
- ipa3_mhi_ctx->trigger_wakeup = true;
- } else {
- res = -EAGAIN;
- goto fail_stop_event_update_dl_channel;
- }
+ if (!ep->skip_ep_cfg) {
+ if (ipa3_cfg_ep(ipa_ep_idx, &in->sys->ipa_ep_cfg)) {
+ IPAERR("fail to configure EP.\n");
+ goto fail_ep_cfg;
+ }
+ if (ipa3_cfg_ep_status(ipa_ep_idx, &ep->status)) {
+ IPAERR("fail to configure status of EP.\n");
+ goto fail_ep_cfg;
}
+ IPA_MHI_DBG("ep configuration successful\n");
+ } else {
+ IPA_MHI_DBG("skipping ep configuration\n");
}
- if (ipa3_ctx->transport_prototype == IPA_TRANSPORT_TYPE_GSI)
- ipa3_mhi_update_host_ch_state(true);
+ *clnt_hdl = ipa_ep_idx;
- if (!empty)
- ipa3_ctx->tag_process_before_gating = false;
+ if (!ep->skip_ep_cfg && IPA_CLIENT_IS_PROD(client))
+ ipa3_install_dflt_flt_rules(ipa_ep_idx);
- res = ipa3_mhi_set_state(IPA_MHI_STATE_SUSPENDED);
- if (res) {
- IPA_MHI_ERR("ipa3_mhi_set_state failed %d\n", res);
- goto fail_release_cons;
- }
+ ipa3_ctx->skip_ep_cfg_shadow[ipa_ep_idx] = ep->skip_ep_cfg;
+ IPA_MHI_DBG("client %d (ep: %d) connected\n", client,
+ ipa_ep_idx);
- IPA_ACTIVE_CLIENTS_DEC_SIMPLE();
IPA_MHI_FUNC_EXIT();
+
return 0;
-fail_stop_event_update_dl_channel:
- ipa3_mhi_resume_dl_channels(true);
-fail_suspend_dl_channel:
-fail_release_cons:
- ipa3_mhi_request_prod();
-fail_release_prod:
- IPA_ACTIVE_CLIENTS_DEC_SIMPLE();
-fail_suspend_ul_channel:
- ipa3_mhi_resume_ul_channels(true);
- ipa3_mhi_set_state(IPA_MHI_STATE_STARTED);
- if (force_clear) {
- if (ipa3_mhi_disable_force_clear(ipa3_mhi_ctx->qmi_req_id)) {
- IPA_MHI_ERR("failed to disable force clear\n");
- BUG();
- }
- IPA_MHI_DBG_LOW("force clear datapath disabled\n");
- ipa3_mhi_ctx->qmi_req_id++;
- }
- return res;
+fail_ep_cfg:
+ ipa3_disable_data_path(ipa_ep_idx);
+fail_start_channel:
+ memset(ep, 0, offsetof(struct ipa3_ep_context, sys));
+ return -EPERM;
}
/**
- * ipa3_mhi_resume() - Resume MHI accelerated channels
+ * ipa3_disconnect_mhi_pipe() - Disconnect pipe from IPA and reset corresponding
+ * MHI channel
+ * @clnt_hdl: client handle for this pipe
*
- * This function is called by MHI client driver on MHI resume.
- * This function is called after MHI channel was suspended.
- * When this function returns device can move to M0 state.
+ * This function is called by IPA MHI client driver on MHI channel reset.
+ * This function is called after MHI channel was started.
* This function is doing the following:
- * - Send command to uC/GSI to resume corresponding MHI channel
- * - Request MHI_PROD in IPA RM
- * - Resume data to IPA
+ * - Send command to uC/GSI to reset corresponding MHI channel
+ * - Configure IPA EP control
*
* Return codes: 0 : success
* negative : error
*/
-int ipa3_mhi_resume(void)
+int ipa3_disconnect_mhi_pipe(u32 clnt_hdl)
{
+ struct ipa3_ep_context *ep;
int res;
- bool dl_channel_resumed = false;
IPA_MHI_FUNC_ENTRY();
- res = ipa3_mhi_set_state(IPA_MHI_STATE_RESUME_IN_PROGRESS);
- if (res) {
- IPA_MHI_ERR("ipa3_mhi_set_state failed %d\n", res);
- return res;
- }
-
- if (ipa3_mhi_ctx->rm_cons_state == IPA_MHI_RM_STATE_REQUESTED) {
- /* resume all DL channels */
- res = ipa3_mhi_resume_dl_channels(false);
- if (res) {
- IPA_MHI_ERR("ipa3_mhi_resume_dl_channels failed %d\n",
- res);
- goto fail_resume_dl_channels;
- }
- dl_channel_resumed = true;
-
- ipa_rm_notify_completion(IPA_RM_RESOURCE_GRANTED,
- IPA_RM_RESOURCE_MHI_CONS);
- ipa3_mhi_ctx->rm_cons_state = IPA_MHI_RM_STATE_GRANTED;
+ if (clnt_hdl >= ipa3_ctx->ipa_num_pipes) {
+ IPAERR("invalid handle %d\n", clnt_hdl);
+ return -EINVAL;
}
- res = ipa3_mhi_request_prod();
- if (res) {
- IPA_MHI_ERR("ipa3_mhi_request_prod failed %d\n", res);
- goto fail_request_prod;
+ if (ipa3_ctx->ep[clnt_hdl].valid == 0) {
+ IPAERR("pipe was not connected %d\n", clnt_hdl);
+ return -EINVAL;
}
- /* resume all UL channels */
- res = ipa3_mhi_resume_ul_channels(false);
- if (res) {
- IPA_MHI_ERR("ipa3_mhi_resume_ul_channels failed %d\n", res);
- goto fail_resume_ul_channels;
- }
+ ep = &ipa3_ctx->ep[clnt_hdl];
- if (!dl_channel_resumed) {
- res = ipa3_mhi_resume_dl_channels(false);
+ if (ipa3_ctx->transport_prototype == IPA_TRANSPORT_TYPE_GSI) {
+ res = gsi_dealloc_channel(ep->gsi_chan_hdl);
if (res) {
- IPA_MHI_ERR("ipa3_mhi_resume_dl_channels failed %d\n",
- res);
- goto fail_resume_dl_channels2;
+ IPAERR("gsi_dealloc_channel failed %d\n", res);
+ goto fail_reset_channel;
}
}
- if (ipa3_ctx->transport_prototype == IPA_TRANSPORT_TYPE_GSI)
- ipa3_mhi_update_host_ch_state(false);
-
- res = ipa3_mhi_set_state(IPA_MHI_STATE_STARTED);
- if (res) {
- IPA_MHI_ERR("ipa3_mhi_set_state failed %d\n", res);
- goto fail_set_state;
- }
+ ep->valid = 0;
+ ipa3_delete_dflt_flt_rules(clnt_hdl);
+ IPA_MHI_DBG("client (ep: %d) disconnected\n", clnt_hdl);
IPA_MHI_FUNC_EXIT();
return 0;
-fail_set_state:
- ipa3_mhi_suspend_dl_channels();
-fail_resume_dl_channels2:
- ipa3_mhi_suspend_ul_channels();
-fail_resume_ul_channels:
- ipa3_mhi_release_prod();
-fail_request_prod:
- ipa3_mhi_suspend_dl_channels();
-fail_resume_dl_channels:
- ipa3_mhi_set_state(IPA_MHI_STATE_SUSPENDED);
+fail_reset_channel:
return res;
}
-static int ipa3_mhi_destroy_channels(struct ipa3_mhi_channel_ctx *channels,
- int num_of_channels)
+int ipa3_mhi_resume_channels_internal(enum ipa_client_type client,
+ bool LPTransitionRejected, bool brstmode_enabled,
+ union __packed gsi_channel_scratch ch_scratch, u8 index)
{
- struct ipa3_mhi_channel_ctx *channel;
- int i, res;
- u32 clnt_hdl;
-
- for (i = 0; i < num_of_channels; i++) {
- channel = &channels[i];
- if (!channel->valid)
- continue;
- if (channel->state == IPA_HW_MHI_CHANNEL_STATE_INVALID)
- continue;
- if (channel->state != IPA_HW_MHI_CHANNEL_STATE_DISABLE) {
- clnt_hdl = ipa3_get_ep_mapping(channel->ep->client);
- IPA_MHI_DBG("disconnect pipe (ep: %d)\n", clnt_hdl);
- res = ipa3_mhi_disconnect_pipe(clnt_hdl);
- if (res) {
- IPAERR("failed to disconnect pipe %d, err %d\n",
- clnt_hdl, res);
- goto fail;
- }
- }
+ int res;
+ int ipa_ep_idx;
+ struct ipa3_ep_context *ep;
+
+ IPA_MHI_FUNC_ENTRY();
- if (ipa3_ctx->transport_prototype == IPA_TRANSPORT_TYPE_GSI) {
- IPA_MHI_DBG("reset event ring (hdl: %lu, ep: %d)\n",
- channel->ep->gsi_evt_ring_hdl, clnt_hdl);
- res = gsi_reset_evt_ring(channel->ep->gsi_evt_ring_hdl);
- if (res) {
- IPAERR(" failed to reset evt ring %lu, err %d\n"
- , channel->ep->gsi_evt_ring_hdl, res);
- goto fail;
- }
- res = gsi_dealloc_evt_ring(
- channel->ep->gsi_evt_ring_hdl);
- if (res) {
- IPAERR("dealloc evt ring %lu failed, err %d\n"
- , channel->ep->gsi_evt_ring_hdl, res);
- goto fail;
- }
+ ipa_ep_idx = ipa3_get_ep_mapping(client);
+ ep = &ipa3_ctx->ep[ipa_ep_idx];
+
+ if (brstmode_enabled && !LPTransitionRejected) {
+ /*
+ * set polling mode bit to DB mode before
+ * resuming the channel
+ */
+ res = gsi_write_channel_scratch(
+ ep->gsi_chan_hdl, ch_scratch);
+ if (res) {
+ IPA_MHI_ERR("write ch scratch fail %d\n"
+ , res);
+ return res;
}
}
+ if (res) {
+ IPA_MHI_ERR("failed to resume channel error %d\n",
+ res);
+ return res;
+ }
+
+ res = gsi_start_channel(ep->gsi_chan_hdl);
+ if (res) {
+ IPA_MHI_ERR("failed to resume channel error %d\n", res);
+ return res;
+ }
+ IPA_MHI_FUNC_EXIT();
return 0;
-fail:
- return res;
}
-/**
- * ipa3_mhi_destroy() - Destroy MHI IPA
- *
- * This function is called by MHI client driver on MHI reset to destroy all IPA
- * MHI resources.
- * When this function returns ipa_mhi can re-initialize.
- */
-void ipa3_mhi_destroy(void)
+int ipa3_mhi_query_ch_info(enum ipa_client_type client,
+ struct gsi_chan_info *ch_info)
{
+ int ipa_ep_idx;
int res;
+ struct ipa3_ep_context *ep;
IPA_MHI_FUNC_ENTRY();
- if (!ipa3_mhi_ctx) {
- IPA_MHI_DBG("IPA MHI was not initialized, already destroyed\n");
- return;
- }
- /* reset all UL and DL acc channels and its accociated event rings */
- res = ipa3_mhi_destroy_channels(ipa3_mhi_ctx->ul_channels,
- IPA_MHI_MAX_UL_CHANNELS);
- if (res) {
- IPAERR("ipa3_mhi_destroy_channels(ul_channels) failed %d\n",
- res);
- goto fail;
- }
- IPA_MHI_DBG("All UL channels are disconnected\n");
- res = ipa3_mhi_destroy_channels(ipa3_mhi_ctx->dl_channels,
- IPA_MHI_MAX_DL_CHANNELS);
- if (res) {
- IPAERR("ipa3_mhi_destroy_channels(dl_channels) failed %d\n",
- res);
- goto fail;
- }
- IPA_MHI_DBG("All DL channels are disconnected\n");
+ ipa_ep_idx = ipa3_get_ep_mapping(client);
- if (ipa3_ctx->transport_prototype == IPA_TRANSPORT_TYPE_SPS) {
- IPA_MHI_DBG("cleanup uC MHI\n");
- ipa3_uc_mhi_cleanup();
+ ep = &ipa3_ctx->ep[ipa_ep_idx];
+ res = gsi_query_channel_info(ep->gsi_chan_hdl, ch_info);
+ if (res) {
+ IPAERR("gsi_query_channel_info failed\n");
+ return res;
}
- if (ipa3_mhi_ctx->state != IPA_MHI_STATE_INITIALIZED &&
- ipa3_mhi_ctx->state != IPA_MHI_STATE_READY) {
- IPA_MHI_DBG("release prod\n");
- res = ipa3_mhi_release_prod();
- if (res) {
- IPA_MHI_ERR("ipa3_mhi_release_prod failed %d\n", res);
- goto fail;
- }
- IPA_MHI_DBG("wait for cons release\n");
- res = ipa3_mhi_wait_for_cons_release();
- if (res) {
- IPAERR("ipa3_mhi_wait_for_cons_release failed %d\n",
- res);
- goto fail;
- }
- usleep_range(IPA_MHI_SUSPEND_SLEEP_MIN,
- IPA_MHI_SUSPEND_SLEEP_MAX);
+ IPA_MHI_FUNC_EXIT();
+ return 0;
+}
- IPA_MHI_DBG("deleate dependency Q6_PROD->MHI_CONS\n");
- res = ipa_rm_delete_dependency(IPA_RM_RESOURCE_Q6_PROD,
- IPA_RM_RESOURCE_MHI_CONS);
- if (res) {
- IPAERR("Error deleting dependency %d->%d, res=%d\n",
- IPA_RM_RESOURCE_Q6_PROD, IPA_RM_RESOURCE_MHI_CONS, res);
- goto fail;
- }
- IPA_MHI_DBG("deleate dependency MHI_PROD->Q6_CONS\n");
- res = ipa_rm_delete_dependency(IPA_RM_RESOURCE_MHI_PROD,
- IPA_RM_RESOURCE_Q6_CONS);
- if (res) {
- IPAERR("Error deleting dependency %d->%d, res=%d\n",
- IPA_RM_RESOURCE_MHI_PROD, IPA_RM_RESOURCE_Q6_CONS, res);
- goto fail;
- }
- }
+bool ipa3_has_open_aggr_frame(enum ipa_client_type client)
+{
+ u32 aggr_state_active;
+ int ipa_ep_idx;
- res = ipa_rm_delete_resource(IPA_RM_RESOURCE_MHI_PROD);
- if (res) {
- IPAERR("Error deleting resource %d, res=%d\n",
- IPA_RM_RESOURCE_MHI_PROD, res);
- goto fail;
- }
+ aggr_state_active = ipahal_read_reg(IPA_STATE_AGGR_ACTIVE);
+ IPA_MHI_DBG_LOW("IPA_STATE_AGGR_ACTIVE_OFST 0x%x\n", aggr_state_active);
- res = ipa_rm_delete_resource(IPA_RM_RESOURCE_MHI_CONS);
- if (res) {
- IPAERR("Error deleting resource %d, res=%d\n",
- IPA_RM_RESOURCE_MHI_CONS, res);
- goto fail;
+ ipa_ep_idx = ipa_get_ep_mapping(client);
+ if (ipa_ep_idx == -1) {
+ ipa_assert();
+ return false;
}
- ipa3_mhi_debugfs_destroy();
- destroy_workqueue(ipa3_mhi_ctx->wq);
- kfree(ipa3_mhi_ctx);
- ipa3_mhi_ctx = NULL;
- IPA_MHI_DBG("IPA MHI was reset, ready for re-init\n");
+ if ((1 << ipa_ep_idx) & aggr_state_active)
+ return true;
- IPA_MHI_FUNC_EXIT();
- return;
-fail:
- BUG();
- return;
+ return false;
}
-/**
- * ipa3_mhi_handle_ipa_config_req() - hanle IPA CONFIG QMI message
- *
- * This function is called by by IPA QMI service to indicate that IPA CONFIG
- * message was sent from modem. IPA MHI will update this information to IPA uC
- * or will cache it until IPA MHI will be initialized.
- *
- * Return codes: 0 : success
- * negative : error
- */
-int ipa3_mhi_handle_ipa_config_req(struct ipa_config_req_msg_v01 *config_req)
+int ipa3_mhi_destroy_channel(enum ipa_client_type client)
{
- IPA_MHI_FUNC_ENTRY();
+ int res;
+ int ipa_ep_idx;
+ struct ipa3_ep_context *ep;
+
+ ipa_ep_idx = ipa3_get_ep_mapping(client);
+
+ ep = &ipa3_ctx->ep[ipa_ep_idx];
+
+ IPA_MHI_DBG("reset event ring (hdl: %lu, ep: %d)\n",
+ ep->gsi_evt_ring_hdl, ipa_ep_idx);
+
+ res = gsi_reset_evt_ring(ep->gsi_evt_ring_hdl);
+ if (res) {
+ IPAERR(" failed to reset evt ring %lu, err %d\n"
+ , ep->gsi_evt_ring_hdl, res);
+ goto fail;
+ }
- if (ipa3_ctx->transport_prototype != IPA_TRANSPORT_TYPE_GSI) {
- ipa3_mhi_cache_dl_ul_sync_info(config_req);
- if (ipa3_mhi_ctx &&
- ipa3_mhi_ctx->state != IPA_MHI_STATE_INITIALIZED)
- ipa3_uc_mhi_send_dl_ul_sync_info(
- ipa3_cached_dl_ul_sync_info);
+ IPA_MHI_DBG("dealloc event ring (hdl: %lu, ep: %d)\n",
+ ep->gsi_evt_ring_hdl, ipa_ep_idx);
+
+ res = gsi_dealloc_evt_ring(
+ ep->gsi_evt_ring_hdl);
+ if (res) {
+ IPAERR("dealloc evt ring %lu failed, err %d\n"
+ , ep->gsi_evt_ring_hdl, res);
+ goto fail;
}
- IPA_MHI_FUNC_EXIT();
return 0;
+fail:
+ return res;
}
MODULE_LICENSE("GPL v2");
memset(&resp, 0, sizeof(struct ipa_config_resp_msg_v01));
resp.resp.result = IPA_QMI_RESULT_SUCCESS_V01;
IPAWANDBG("Received IPA CONFIG Request\n");
- rc = ipa3_mhi_handle_ipa_config_req(
+ rc = ipa_mhi_handle_ipa_config_req(
(struct ipa_config_req_msg_v01 *)req);
if (rc) {
IPAERR("ipa3_mhi_handle_ipa_config_req failed %d\n", rc);
{
struct ipa3_rt_tbl_set *set;
struct ipa3_rt_tbl *tbl;
- struct ipa3_mem_buffer tbl_mem;
+ struct ipa_mem_buffer tbl_mem;
u8 *tbl_mem_buf;
struct ipa3_rt_entry *entry;
int res;
* Return: 0 on success, negative on failure
*/
static int ipa_alloc_init_rt_tbl_hdr(enum ipa_ip_type ip,
- struct ipa3_mem_buffer *hash_hdr, struct ipa3_mem_buffer *nhash_hdr)
+ struct ipa_mem_buffer *hash_hdr, struct ipa_mem_buffer *nhash_hdr)
{
int num_index;
u64 *hash_entr;
* Return: 0 on success, negative on failure
*/
static int ipa_generate_rt_hw_tbl_img(enum ipa_ip_type ip,
- struct ipa3_mem_buffer *hash_hdr, struct ipa3_mem_buffer *nhash_hdr,
- struct ipa3_mem_buffer *hash_bdy, struct ipa3_mem_buffer *nhash_bdy)
+ struct ipa_mem_buffer *hash_hdr, struct ipa_mem_buffer *nhash_hdr,
+ struct ipa_mem_buffer *hash_bdy, struct ipa_mem_buffer *nhash_bdy)
{
u32 hash_bdy_start_ofst, nhash_bdy_start_ofst;
u32 apps_start_idx;
* Return: true if enough space available or false in other cases
*/
static bool ipa_rt_valid_lcl_tbl_size(enum ipa_ip_type ipt,
- enum ipa_rule_type rlt, struct ipa3_mem_buffer *bdy)
+ enum ipa_rule_type rlt, struct ipa_mem_buffer *bdy)
{
u16 avail;
struct ipahal_imm_cmd_dma_shared_mem mem_cmd = {0};
struct ipahal_imm_cmd_pyld *cmd_pyld[5];
int num_cmd = 0;
- struct ipa3_mem_buffer hash_bdy, nhash_bdy;
- struct ipa3_mem_buffer hash_hdr, nhash_hdr;
+ struct ipa_mem_buffer hash_bdy, nhash_bdy;
+ struct ipa_mem_buffer hash_hdr, nhash_hdr;
u32 num_modem_rt_index;
int rc = 0;
u32 lcl_hash_hdr, lcl_nhash_hdr;
int ipa3_uc_memcpy(phys_addr_t dest, phys_addr_t src, int len)
{
int res;
- struct ipa3_mem_buffer mem;
+ struct ipa_mem_buffer mem;
struct IpaHwMemCopyData_t *cmd;
IPADBG("dest 0x%pa src 0x%pa len %d\n", &dest, &src, len);
u32 first_evt_idx)
{
int res;
- struct ipa3_mem_buffer mem;
+ struct ipa_mem_buffer mem;
struct IpaHwMhiInitCmdData_t *init_cmd_data;
struct IpaHwMhiMsiCmdData_t *msi_cmd;
return res;
}
-int ipa3_uc_mhi_send_dl_ul_sync_info(union IpaHwMhiDlUlSyncCmdData_t cmd)
+int ipa3_uc_mhi_send_dl_ul_sync_info(union IpaHwMhiDlUlSyncCmdData_t *cmd)
{
int res;
}
IPADBG("isDlUlSyncEnabled=0x%x UlAccmVal=0x%x\n",
- cmd.params.isDlUlSyncEnabled, cmd.params.UlAccmVal);
+ cmd->params.isDlUlSyncEnabled, cmd->params.UlAccmVal);
IPADBG("ulMsiEventThreshold=0x%x dlMsiEventThreshold=0x%x\n",
- cmd.params.ulMsiEventThreshold, cmd.params.dlMsiEventThreshold);
+ cmd->params.ulMsiEventThreshold,
+ cmd->params.dlMsiEventThreshold);
IPA_ACTIVE_CLIENTS_INC_SIMPLE();
- res = ipa3_uc_send_cmd(cmd.raw32b,
+ res = ipa3_uc_send_cmd(cmd->raw32b,
IPA_CPU_2_HW_CMD_MHI_DL_UL_SYNC_INFO, 0, false, HZ);
if (res) {
IPAERR("ipa3_uc_send_cmd failed %d\n", res);
int ipa_ep_idx;
int result = -EFAULT;
struct ipa3_ep_context *ep;
- struct ipa3_mem_buffer cmd;
+ struct ipa_mem_buffer cmd;
struct IpaHwWdiTxSetUpCmdData_t *tx;
struct IpaHwWdiRxSetUpCmdData_t *rx;
struct IpaHwWdi2TxSetUpCmdData_t *tx_2;
return NULL;
}
+static void ipa3_get_holb(int ep_idx, struct ipa_ep_cfg_holb *holb)
+{
+ *holb = ipa3_ctx->ep[ep_idx].holb;
+}
+
+static void ipa3_set_tag_process_before_gating(bool val)
+{
+ ipa3_ctx->tag_process_before_gating = val;
+}
+
int ipa3_bind_api_controller(enum ipa_hw_type ipa_hw_type,
struct ipa_api_controller *api_ctrl)
{
api_ctrl->ipa_cfg_ep_deaggr = ipa3_cfg_ep_deaggr;
api_ctrl->ipa_cfg_ep_route = ipa3_cfg_ep_route;
api_ctrl->ipa_cfg_ep_holb = ipa3_cfg_ep_holb;
+ api_ctrl->ipa_get_holb = ipa3_get_holb;
+ api_ctrl->ipa_set_tag_process_before_gating =
+ ipa3_set_tag_process_before_gating;
api_ctrl->ipa_cfg_ep_cfg = ipa3_cfg_ep_cfg;
api_ctrl->ipa_cfg_ep_metadata_mask = ipa3_cfg_ep_metadata_mask;
api_ctrl->ipa_cfg_ep_holb_by_client = ipa3_cfg_ep_holb_by_client;
api_ctrl->ipa_dma_async_memcpy = ipa3_dma_async_memcpy;
api_ctrl->ipa_dma_uc_memcpy = ipa3_dma_uc_memcpy;
api_ctrl->ipa_dma_destroy = ipa3_dma_destroy;
- api_ctrl->ipa_mhi_init = ipa3_mhi_init;
- api_ctrl->ipa_mhi_start = ipa3_mhi_start;
- api_ctrl->ipa_mhi_connect_pipe = ipa3_mhi_connect_pipe;
- api_ctrl->ipa_mhi_disconnect_pipe = ipa3_mhi_disconnect_pipe;
- api_ctrl->ipa_mhi_suspend = ipa3_mhi_suspend;
- api_ctrl->ipa_mhi_resume = ipa3_mhi_resume;
- api_ctrl->ipa_mhi_destroy = ipa3_mhi_destroy;
+ api_ctrl->ipa_mhi_init_engine = ipa3_mhi_init_engine;
+ api_ctrl->ipa_connect_mhi_pipe = ipa3_connect_mhi_pipe;
+ api_ctrl->ipa_disconnect_mhi_pipe = ipa3_disconnect_mhi_pipe;
+ api_ctrl->ipa_mhi_stop_gsi_channel = ipa3_mhi_stop_gsi_channel;
+ api_ctrl->ipa_uc_mhi_reset_channel = ipa3_uc_mhi_reset_channel;
+ api_ctrl->ipa_qmi_enable_force_clear_datapath_send =
+ ipa3_qmi_enable_force_clear_datapath_send;
+ api_ctrl->ipa_qmi_disable_force_clear_datapath_send =
+ ipa3_qmi_disable_force_clear_datapath_send;
+ api_ctrl->ipa_mhi_reset_channel_internal =
+ ipa3_mhi_reset_channel_internal;
+ api_ctrl->ipa_mhi_start_channel_internal =
+ ipa3_mhi_start_channel_internal;
+ api_ctrl->ipa_mhi_query_ch_info = ipa3_mhi_query_ch_info;
+ api_ctrl->ipa_mhi_resume_channels_internal =
+ ipa3_mhi_resume_channels_internal;
+ api_ctrl->ipa_has_open_aggr_frame = ipa3_has_open_aggr_frame;
+ api_ctrl->ipa_mhi_destroy_channel = ipa3_mhi_destroy_channel;
+ api_ctrl->ipa_uc_mhi_send_dl_ul_sync_info =
+ ipa3_uc_mhi_send_dl_ul_sync_info;
+ api_ctrl->ipa_uc_mhi_init = ipa3_uc_mhi_init;
+ api_ctrl->ipa_uc_mhi_suspend_channel = ipa3_uc_mhi_suspend_channel;
+ api_ctrl->ipa_uc_mhi_stop_event_update_channel =
+ ipa3_uc_mhi_stop_event_update_channel;
+ api_ctrl->ipa_uc_mhi_cleanup = ipa3_uc_mhi_cleanup;
+ api_ctrl->ipa_uc_state_check = ipa3_uc_state_check;
api_ctrl->ipa_write_qmap_id = ipa3_write_qmap_id;
api_ctrl->ipa_add_interrupt_handler = ipa3_add_interrupt_handler;
api_ctrl->ipa_remove_interrupt_handler = ipa3_remove_interrupt_handler;
*/
int ipa3_inject_dma_task_for_gsi(void)
{
- static struct ipa3_mem_buffer mem = {0};
+ static struct ipa_mem_buffer mem = {0};
struct ipahal_imm_cmd_dma_task_32b_addr cmd = {0};
static struct ipahal_imm_cmd_pyld *cmd_pyld;
struct ipa3_desc desc = {0};
*/
int ipa3_stop_gsi_channel(u32 clnt_hdl)
{
- struct ipa3_mem_buffer mem;
+ struct ipa_mem_buffer mem;
int res = 0;
int i;
struct ipa3_ep_context *ep;
#include <linux/completion.h>
#include <linux/platform_device.h>
#include <linux/msm_ep_pcie.h>
-#include <linux/ipa.h>
+#include <linux/ipa_mhi.h>
#include <linux/vmalloc.h>
#include "mhi.h"
#include <linux/device.h>
#include <linux/errno.h>
#include <linux/debugfs.h>
-#include <linux/ipa.h>
+#include <linux/ipa_mhi.h>
#include "mhi_hwio.h"
#include "mhi_sm.h"
};
/**
- * enum ipa_mhi_event_type - event type for mhi callback
- *
- * @IPA_MHI_EVENT_READY: IPA MHI is ready and IPA uC is loaded. After getting
- * this event MHI client is expected to call to ipa_mhi_start() API
- * @IPA_MHI_EVENT_DATA_AVAILABLE: downlink data available on MHI channel
- */
-enum ipa_mhi_event_type {
- IPA_MHI_EVENT_READY,
- IPA_MHI_EVENT_DATA_AVAILABLE,
- IPA_MHI_EVENT_MAX,
-};
-
-typedef void (*mhi_client_cb)(void *priv, enum ipa_mhi_event_type event,
- unsigned long data);
-
-/**
- * struct ipa_mhi_msi_info - parameters for MSI (Message Signaled Interrupts)
- * @addr_low: MSI lower base physical address
- * @addr_hi: MSI higher base physical address
- * @data: Data Pattern to use when generating the MSI
- * @mask: Mask indicating number of messages assigned by the host to device
- *
- * msi value is written according to this formula:
- * ((data & ~mask) | (mmio.msiVec & mask))
- */
-struct ipa_mhi_msi_info {
- u32 addr_low;
- u32 addr_hi;
- u32 data;
- u32 mask;
-};
-
-/**
- * struct ipa_mhi_init_params - parameters for IPA MHI initialization API
- *
- * @msi: MSI (Message Signaled Interrupts) parameters
- * @mmio_addr: MHI MMIO physical address
- * @first_ch_idx: First channel ID for hardware accelerated channels.
- * @first_er_idx: First event ring ID for hardware accelerated channels.
- * @assert_bit40: should assert bit 40 in order to access hots space.
- * if PCIe iATU is configured then not need to assert bit40
- * @notify: client callback
- * @priv: client private data to be provided in client callback
- * @test_mode: flag to indicate if IPA MHI is in unit test mode
- */
-struct ipa_mhi_init_params {
- struct ipa_mhi_msi_info msi;
- u32 mmio_addr;
- u32 first_ch_idx;
- u32 first_er_idx;
- bool assert_bit40;
- mhi_client_cb notify;
- void *priv;
- bool test_mode;
-};
-
-/**
- * struct ipa_mhi_start_params - parameters for IPA MHI start API
- *
- * @host_ctrl_addr: Base address of MHI control data structures
- * @host_data_addr: Base address of MHI data buffers
- * @channel_context_addr: channel context array address in host address space
- * @event_context_addr: event context array address in host address space
- */
-struct ipa_mhi_start_params {
- u32 host_ctrl_addr;
- u32 host_data_addr;
- u64 channel_context_array_addr;
- u64 event_context_array_addr;
-};
-
-/**
- * struct ipa_mhi_connect_params - parameters for IPA MHI channel connect API
- *
- * @sys: IPA EP configuration info
- * @channel_id: MHI channel id
- */
-struct ipa_mhi_connect_params {
- struct ipa_sys_connect_params sys;
- u8 channel_id;
-};
-
-/* bit #40 in address should be asserted for MHI transfers over pcie */
-#define IPA_MHI_HOST_ADDR(addr) ((addr) | BIT_ULL(40))
-
-/**
* struct ipa_gsi_ep_config - IPA GSI endpoint configurations
*
* @ipa_ep_num: IPA EP pipe number
void ipa_dma_destroy(void);
/*
- * MHI
- */
-int ipa_mhi_init(struct ipa_mhi_init_params *params);
-
-int ipa_mhi_start(struct ipa_mhi_start_params *params);
-
-int ipa_mhi_connect_pipe(struct ipa_mhi_connect_params *in, u32 *clnt_hdl);
-
-int ipa_mhi_disconnect_pipe(u32 clnt_hdl);
-
-int ipa_mhi_suspend(bool force);
-
-int ipa_mhi_resume(void);
-
-void ipa_mhi_destroy(void);
-
-/*
* mux id
*/
int ipa_write_qmap_id(struct ipa_ioc_write_qmapid *param_in);
}
/*
- * MHI
- */
-static inline int ipa_mhi_init(struct ipa_mhi_init_params *params)
-{
- return -EPERM;
-}
-
-static inline int ipa_mhi_start(struct ipa_mhi_start_params *params)
-{
- return -EPERM;
-}
-
-static inline int ipa_mhi_connect_pipe(struct ipa_mhi_connect_params *in,
- u32 *clnt_hdl)
-{
- return -EPERM;
-}
-
-static inline int ipa_mhi_disconnect_pipe(u32 clnt_hdl)
-{
- return -EPERM;
-}
-
-static inline int ipa_mhi_suspend(bool force)
-{
- return -EPERM;
-}
-
-static inline int ipa_mhi_resume(void)
-{
- return -EPERM;
-}
-
-static inline void ipa_mhi_destroy(void)
-{
- return;
-}
-
-/*
* mux id
*/
static inline int ipa_write_qmap_id(struct ipa_ioc_write_qmapid *param_in)
--- /dev/null
+/* Copyright (c) 2016, 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
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef IPA_MHI_H_
+#define IPA_MHI_H_
+
+#include <linux/ipa.h>
+#include <linux/types.h>
+
+/**
+ * enum ipa_mhi_event_type - event type for mhi callback
+ *
+ * @IPA_MHI_EVENT_READY: IPA MHI is ready and IPA uC is loaded. After getting
+ * this event MHI client is expected to call to ipa_mhi_start() API
+ * @IPA_MHI_EVENT_DATA_AVAILABLE: downlink data available on MHI channel
+ */
+enum ipa_mhi_event_type {
+ IPA_MHI_EVENT_READY,
+ IPA_MHI_EVENT_DATA_AVAILABLE,
+ IPA_MHI_EVENT_MAX,
+};
+
+typedef void (*mhi_client_cb)(void *priv, enum ipa_mhi_event_type event,
+ unsigned long data);
+
+/**
+ * struct ipa_mhi_msi_info - parameters for MSI (Message Signaled Interrupts)
+ * @addr_low: MSI lower base physical address
+ * @addr_hi: MSI higher base physical address
+ * @data: Data Pattern to use when generating the MSI
+ * @mask: Mask indicating number of messages assigned by the host to device
+ *
+ * msi value is written according to this formula:
+ * ((data & ~mask) | (mmio.msiVec & mask))
+ */
+struct ipa_mhi_msi_info {
+ u32 addr_low;
+ u32 addr_hi;
+ u32 data;
+ u32 mask;
+};
+
+/**
+ * struct ipa_mhi_init_params - parameters for IPA MHI initialization API
+ *
+ * @msi: MSI (Message Signaled Interrupts) parameters
+ * @mmio_addr: MHI MMIO physical address
+ * @first_ch_idx: First channel ID for hardware accelerated channels.
+ * @first_er_idx: First event ring ID for hardware accelerated channels.
+ * @assert_bit40: should assert bit 40 in order to access host space.
+ * if PCIe iATU is configured then not need to assert bit40
+ * @notify: client callback
+ * @priv: client private data to be provided in client callback
+ * @test_mode: flag to indicate if IPA MHI is in unit test mode
+ */
+struct ipa_mhi_init_params {
+ struct ipa_mhi_msi_info msi;
+ u32 mmio_addr;
+ u32 first_ch_idx;
+ u32 first_er_idx;
+ bool assert_bit40;
+ mhi_client_cb notify;
+ void *priv;
+ bool test_mode;
+};
+
+/**
+ * struct ipa_mhi_start_params - parameters for IPA MHI start API
+ *
+ * @host_ctrl_addr: Base address of MHI control data structures
+ * @host_data_addr: Base address of MHI data buffers
+ * @channel_context_addr: channel context array address in host address space
+ * @event_context_addr: event context array address in host address space
+ */
+struct ipa_mhi_start_params {
+ u32 host_ctrl_addr;
+ u32 host_data_addr;
+ u64 channel_context_array_addr;
+ u64 event_context_array_addr;
+};
+
+/**
+ * struct ipa_mhi_connect_params - parameters for IPA MHI channel connect API
+ *
+ * @sys: IPA EP configuration info
+ * @channel_id: MHI channel id
+ */
+struct ipa_mhi_connect_params {
+ struct ipa_sys_connect_params sys;
+ u8 channel_id;
+};
+
+/* bit #40 in address should be asserted for MHI transfers over pcie */
+#define IPA_MHI_HOST_ADDR(addr) ((addr) | BIT_ULL(40))
+
+#if defined CONFIG_IPA || defined CONFIG_IPA3
+
+int ipa_mhi_init(struct ipa_mhi_init_params *params);
+
+int ipa_mhi_start(struct ipa_mhi_start_params *params);
+
+int ipa_mhi_connect_pipe(struct ipa_mhi_connect_params *in, u32 *clnt_hdl);
+
+int ipa_mhi_disconnect_pipe(u32 clnt_hdl);
+
+int ipa_mhi_suspend(bool force);
+
+int ipa_mhi_resume(void);
+
+void ipa_mhi_destroy(void);
+
+#else /* (CONFIG_IPA || CONFIG_IPA3) */
+
+static inline int ipa_mhi_init(struct ipa_mhi_init_params *params)
+{
+ return -EPERM;
+}
+
+static inline int ipa_mhi_start(struct ipa_mhi_start_params *params)
+{
+ return -EPERM;
+}
+
+static inline int ipa_mhi_connect_pipe(struct ipa_mhi_connect_params *in,
+ u32 *clnt_hdl)
+{
+ return -EPERM;
+}
+
+static inline int ipa_mhi_disconnect_pipe(u32 clnt_hdl)
+{
+ return -EPERM;
+}
+
+static inline int ipa_mhi_suspend(bool force)
+{
+ return -EPERM;
+}
+
+static inline int ipa_mhi_resume(void)
+{
+ return -EPERM;
+}
+
+static inline void ipa_mhi_destroy(void)
+{
+
+}
+
+#endif /* (CONFIG_IPA || CONFIG_IPA3) */
+
+#endif /* IPA_MHI_H_ */