diff options
author | Linux Build Service Account <lnxbuild@localhost> | 2018-07-30 23:59:59 -0700 |
---|---|---|
committer | Gerrit - the friendly Code Review server <code-review@localhost> | 2018-07-30 23:59:59 -0700 |
commit | 6dc8f1cb011466bb3ad53157f3bdef25bc527c4f (patch) | |
tree | 07193a18874a9b681fd8930d123441c6a72fd699 /drivers/platform | |
parent | fb5e9276e4eceeda7c605a2aee5eb5947c20ea68 (diff) | |
parent | ac27bafd6ea23c9982528f1ba707aba100e9fd6c (diff) |
Merge "msm: ipa: Add support to IPA platform shutdown cleanup"
Diffstat (limited to 'drivers/platform')
-rw-r--r-- | drivers/platform/msm/ipa/ipa_api.c | 39 | ||||
-rw-r--r-- | drivers/platform/msm/ipa/ipa_api.h | 17 | ||||
-rw-r--r-- | drivers/platform/msm/ipa/ipa_v2/ipa.c | 155 | ||||
-rw-r--r-- | drivers/platform/msm/ipa/ipa_v2/ipa_client.c | 78 | ||||
-rw-r--r-- | drivers/platform/msm/ipa/ipa_v2/ipa_i.h | 11 | ||||
-rw-r--r-- | drivers/platform/msm/ipa/ipa_v2/rmnet_ipa.c | 54 |
6 files changed, 337 insertions, 17 deletions
diff --git a/drivers/platform/msm/ipa/ipa_api.c b/drivers/platform/msm/ipa/ipa_api.c index bc0263c371a1..719a1ec5360f 100644 --- a/drivers/platform/msm/ipa/ipa_api.c +++ b/drivers/platform/msm/ipa/ipa_api.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved. +/* Copyright (c) 2015-2018, 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 @@ -2672,6 +2672,42 @@ static int ipa_generic_plat_drv_probe(struct platform_device *pdev_p) return result; } +static void ipa_generic_plat_drv_shutdown(struct platform_device *pdev_p) +{ + int result; + + pr_info("ipa: IPA driver shutdown started for %s\n", + pdev_p->dev.of_node->name); + + if (!ipa_api_ctrl) { + pr_err("ipa: invalid ipa_api_ctrl\n"); + return; + } + + /* call probe based on IPA HW version */ + switch (ipa_api_hw_type) { + case IPA_HW_v2_0: + case IPA_HW_v2_1: + case IPA_HW_v2_5: + case IPA_HW_v2_6L: + result = ipa_plat_drv_shutdown(pdev_p, ipa_api_ctrl, + ipa_plat_drv_match); + break; + case IPA_HW_v3_0: + case IPA_HW_v3_1: + case IPA_HW_v3_5: + case IPA_HW_v3_5_1: + default: + pr_err("ipa: ipa_generic_plat_drv_shutdown, unsupported version %d\n", + ipa_api_hw_type); + return; + } + + if (result) + pr_err("ipa: ipa_generic_plat_drv_shutdown failed\n"); +} + + static int ipa_ap_suspend(struct device *dev) { int ret; @@ -2975,6 +3011,7 @@ static struct platform_driver ipa_plat_drv = { .pm = &ipa_pm_ops, .of_match_table = ipa_plat_drv_match, }, + .shutdown = ipa_generic_plat_drv_shutdown, }; static int __init ipa_module_init(void) diff --git a/drivers/platform/msm/ipa/ipa_api.h b/drivers/platform/msm/ipa/ipa_api.h index 1fb0e7122042..33e0f469d0d2 100644 --- a/drivers/platform/msm/ipa/ipa_api.h +++ b/drivers/platform/msm/ipa/ipa_api.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved. +/* Copyright (c) 2015-2018, 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 @@ -384,12 +384,27 @@ struct ipa_api_controller { #ifdef CONFIG_IPA int ipa_plat_drv_probe(struct platform_device *pdev_p, struct ipa_api_controller *api_ctrl, struct of_device_id *pdrv_match); +int ipa_plat_drv_shutdown(struct platform_device *pdev_p, + struct ipa_api_controller *api_ctrl, + const struct of_device_id *pdrv_match); +void ipa_platform_shutdown(void); #else static inline int ipa_plat_drv_probe(struct platform_device *pdev_p, struct ipa_api_controller *api_ctrl, struct of_device_id *pdrv_match) { return -ENODEV; } +static inline int ipa_plat_drv_shutdown(struct platform_device *pdev_p, + struct ipa_api_controller *api_ctrl, + const struct of_device_id *pdrv_match) +{ + return -ENODEV; +} +static inline int ipa_platform_shutdown(void) +{ + return -ENODEV; +} + #endif /* (CONFIG_IPA) */ #ifdef CONFIG_IPA3 diff --git a/drivers/platform/msm/ipa/ipa_v2/ipa.c b/drivers/platform/msm/ipa/ipa_v2/ipa.c index ad7d2d6175bd..bf46ea860fef 100644 --- a/drivers/platform/msm/ipa/ipa_v2/ipa.c +++ b/drivers/platform/msm/ipa/ipa_v2/ipa.c @@ -1752,6 +1752,31 @@ int ipa_q6_pipe_delay(bool zip_pipes) return 0; } +/* Remove delay only for IPA consumer pipes */ +static void ipa_pipe_delay(bool set_reset) +{ + u32 reg_val = 0; + int client_idx; + int ep_idx; + + for (client_idx = 0; client_idx < IPA_CLIENT_MAX; client_idx++) { + /* Break the processing for IPA PROD pipes and avoid looping. */ + if (IPA_CLIENT_IS_CONS(client_idx)) + break; + + ep_idx = ipa2_get_ep_mapping(client_idx); + if (ep_idx == -1) + continue; + + IPA_SETFIELD_IN_REG(reg_val, set_reset, + IPA_ENDP_INIT_CTRL_N_ENDP_DELAY_SHFT, + IPA_ENDP_INIT_CTRL_N_ENDP_DELAY_BMSK); + + ipa_write_reg(ipa_ctx->mmio, + IPA_ENDP_INIT_CTRL_N_OFST(ep_idx), reg_val); + } +} + int ipa_q6_monitor_holb_mitigation(bool enable) { int ep_idx; @@ -1830,6 +1855,51 @@ static int ipa_q6_avoid_holb(bool zip_pipes) return 0; } +/* + * Set HOLB drop on all IPA producer/client cons pipes, + * do not set suspend + */ +static void ipa_avoid_holb(void) +{ + u32 reg_val; + int ep_idx; + int client_idx = IPA_CLIENT_MAX - 1; + + for (; client_idx >= 0; client_idx--) { + /* Break the processing for IPA CONS pipes and avoid looping. */ + if (IPA_CLIENT_IS_PROD(client_idx)) + break; + + ep_idx = ipa2_get_ep_mapping(client_idx); + if (ep_idx == -1) + continue; + + /* + * ipa2_cfg_ep_holb is not used here because we are + * also setting HOLB on Q6 pipes, and from APPS perspective + * they are not valid, therefore, the above function + * will fail. + */ + reg_val = 0; + IPA_SETFIELD_IN_REG(reg_val, 0, + IPA_ENDP_INIT_HOL_BLOCK_TIMER_N_TIMER_SHFT, + IPA_ENDP_INIT_HOL_BLOCK_TIMER_N_TIMER_BMSK); + + ipa_write_reg(ipa_ctx->mmio, + IPA_ENDP_INIT_HOL_BLOCK_TIMER_N_OFST_v2_0(ep_idx), + reg_val); + + reg_val = 0; + IPA_SETFIELD_IN_REG(reg_val, 1, + IPA_ENDP_INIT_HOL_BLOCK_EN_N_EN_SHFT, + IPA_ENDP_INIT_HOL_BLOCK_EN_N_EN_BMSK); + + ipa_write_reg(ipa_ctx->mmio, + IPA_ENDP_INIT_HOL_BLOCK_EN_N_OFST_v2_0(ep_idx), + reg_val); + } +} + static u32 ipa_get_max_flt_rt_cmds(u32 num_pipes) { u32 max_cmds = 0; @@ -2089,6 +2159,49 @@ static int ipa_q6_set_ex_path_dis_agg(void) return retval; } +int register_ipa_platform_cb(int (*q6_cleanup_cb)(void)) +{ + IPAERR("In register_ipa_platform_cb\n"); + if (ipa_ctx) { + if (ipa_ctx->q6_cleanup_cb == NULL) { + IPAERR("reg q6_cleanup_cb\n"); + ipa_ctx->q6_cleanup_cb = q6_cleanup_cb; + } else + IPAERR("Already registered\n"); + } else { + IPAERR("IPA driver not initialized, retry\n"); + return -EAGAIN; + } + return 0; +} + +/** +* ipa_apps_shutdown_cleanup() - Take care Apps ep's cleanup +* 1) Set HOLB drop on all IPA producer pipes. +* 2) Remove delay for all IPA consumer pipes. +* 3) Wait for all IPA consumer pipes to go empty and +* reset it. +* 4) Do aggregation force close for all pipes. +* 5) Reset all IPA producer pipes + +* 0: success +*/ + +int ipa_apps_shutdown_cleanup(void) +{ + IPA_ACTIVE_CLIENTS_INC_SPECIAL("APPS_SHUTDOWN"); + + ipa_avoid_holb(); + + ipa_pipe_delay(false); + + ipa2_apps_shutdown_apps_ep_reset(); + + IPA_ACTIVE_CLIENTS_DEC_SPECIAL("APPS_SHUTDOWN"); + + return 0; +} + /** * ipa_q6_pre_shutdown_cleanup() - A cleanup for all Q6 related configuration * in IPA HW before modem shutdown. This is performed in @@ -3907,6 +4020,8 @@ static int ipa_init(const struct ipa_plat_drv_res *resource_p, ipa_ctx->skip_uc_pipe_reset = resource_p->skip_uc_pipe_reset; ipa_ctx->use_dma_zone = resource_p->use_dma_zone; ipa_ctx->tethered_flow_control = resource_p->tethered_flow_control; + ipa_ctx->is_apps_shutdown_support = + resource_p->is_apps_shutdown_support; /* Setting up IPA RX Polling Timeout Seconds */ ipa_rx_timeout_min_max_calc(&ipa_ctx->ipa_rx_min_timeout_usec, @@ -4447,6 +4562,7 @@ static int get_ipa_dts_configuration(struct platform_device *pdev, ipa_drv_res->ipa_wdi2 = false; ipa_drv_res->wan_rx_ring_size = IPA_GENERIC_RX_POOL_SZ; ipa_drv_res->lan_rx_ring_size = IPA_GENERIC_RX_POOL_SZ; + ipa_drv_res->is_apps_shutdown_support = false; /* Get IPA HW Version */ result = of_property_read_u32(pdev->dev.of_node, "qcom,ipa-hw-ver", @@ -4474,6 +4590,14 @@ static int get_ipa_dts_configuration(struct platform_device *pdev, ipa_drv_res->ipa_uc_monitor_holb ? "Enabled" : "Disabled"); + /* Check apps_shutdown_support enabled or disabled */ + ipa_drv_res->is_apps_shutdown_support = + of_property_read_bool(pdev->dev.of_node, + "qcom,apps-shutdown-support"); + IPAERR(": apps shutdown support = %s\n", + ipa_drv_res->is_apps_shutdown_support + ? "Enabled" : "Disabled"); + /* Get IPA WAN / LAN RX pool sizes */ result = of_property_read_u32(pdev->dev.of_node, "qcom,wan-rx-ring-size", @@ -4918,6 +5042,37 @@ static int ipa_smmu_ap_cb_probe(struct device *dev) return result; } +/** +* ipa_platform_shutdown() - Ensure Q6 ep cleanup is done and +* followed by APPS ep's cleanup. +*/ +void ipa_platform_shutdown(void) +{ + IPADBG("****************ipa_platform_shutdown****************\n"); + if (ipa_ctx->q6_cleanup_cb) + ipa_ctx->q6_cleanup_cb(); + else + IPADBG("No Q6 cleanup callback registered\n"); + ipa_apps_shutdown_cleanup(); +} + +int ipa_plat_drv_shutdown(struct platform_device *pdev_p, + struct ipa_api_controller *api_ctrl, + const struct of_device_id *pdrv_match) +{ + if (!ipa_ctx) { + pr_err("IPA driver not initialized\n"); + return -EOPNOTSUPP; + } + if (ipa_ctx->is_apps_shutdown_support) + ipa_platform_shutdown(); + else { + pr_err("There is no apps IPA driver shutdown support\n"); + return -EOPNOTSUPP; + } + return 0; +} + int ipa_plat_drv_probe(struct platform_device *pdev_p, struct ipa_api_controller *api_ctrl, struct of_device_id *pdrv_match) { diff --git a/drivers/platform/msm/ipa/ipa_v2/ipa_client.c b/drivers/platform/msm/ipa/ipa_v2/ipa_client.c index 74e7394a80b1..b1eb67dbf02c 100644 --- a/drivers/platform/msm/ipa/ipa_v2/ipa_client.c +++ b/drivers/platform/msm/ipa/ipa_v2/ipa_client.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved. +/* Copyright (c) 2012-2018, 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 @@ -84,11 +84,20 @@ int ipa_disable_data_path(u32 clnt_hdl) IPA_ENDP_INIT_AGGR_N_OFST_v2_0(clnt_hdl)); if (((aggr_init & IPA_ENDP_INIT_AGGR_N_AGGR_EN_BMSK) >> IPA_ENDP_INIT_AGGR_N_AGGR_EN_SHFT) == IPA_ENABLE_AGGR) { - res = ipa_tag_aggr_force_close(clnt_hdl); - if (res) { - IPAERR("tag process timeout, client:%d err:%d\n", - clnt_hdl, res); - BUG(); + /* + * Tag process will not work for, + * APPS CMD PROD --> Uses the same for IMM cmd over tag + * APPS LAN CONS --> Already suspend is set + */ + if (!(ep->client == IPA_CLIENT_APPS_CMD_PROD || + ep->client == IPA_CLIENT_APPS_LAN_CONS)) { + res = ipa_tag_aggr_force_close(clnt_hdl); + if (res) { + IPAERR("tag process timeout"); + IPAERR("client:%d err:%d\n", + clnt_hdl, res); + ipa_assert(); + } } } @@ -765,6 +774,63 @@ bail: } /** +* ipa2_apps_shutdown_apps_ep_reset() - +* reset an endpoints from BAM perspective. +* +* Q6 ep reset is not handled here +*/ +void ipa2_apps_shutdown_apps_ep_reset(void) +{ + struct ipa_ep_context *ep; + int ep_idx, client_idx; + + if (unlikely(!ipa_ctx)) { + IPAERR("IPA driver was not initialized\n"); + return; + } + + for (client_idx = 0; client_idx < IPA_CLIENT_MAX; client_idx++) { + + ep_idx = ipa2_get_ep_mapping(client_idx); + if (ep_idx == -1) + continue; + + ep = &ipa_ctx->ep[ep_idx]; + if (ep->valid && (IPA_CLIENT_IS_APPS_PROD(client_idx) || + IPA_CLIENT_IS_APPS_CONS(client_idx))) { + /* + * we shouldn't reset APPS CMD PROD + * and LAN CONS in for loop + * these 2 ep's should be resetted at last, + * since it is used in Tag Process + */ + if (!(client_idx == IPA_CLIENT_APPS_CMD_PROD || + client_idx == IPA_CLIENT_APPS_LAN_CONS)) { + IPADBG("teardown ep (%d)\n", ep_idx); + ipa2_teardown_sys_pipe(ep_idx); + } + } + } + ep_idx = ipa2_get_ep_mapping(IPA_CLIENT_APPS_LAN_CONS); + if (ep_idx != -1) { + ep = &ipa_ctx->ep[ep_idx]; + if (ep->valid) { + IPADBG("teardown ep (%d)\n", ep_idx); + ipa2_teardown_sys_pipe(ep_idx); + } + } + + ep_idx = ipa2_get_ep_mapping(IPA_CLIENT_APPS_CMD_PROD); + if (ep_idx != -1) { + ep = &ipa_ctx->ep[ep_idx]; + if (ep->valid) { + IPADBG("teardown ep (%d)\n", ep_idx); + ipa2_teardown_sys_pipe(ep_idx); + } + } +} + +/** * ipa2_clear_endpoint_delay() - Remove ep delay set on the IPA pipe before * client disconnect. * @clnt_hdl: [in] opaque client handle assigned by IPA to client diff --git a/drivers/platform/msm/ipa/ipa_v2/ipa_i.h b/drivers/platform/msm/ipa/ipa_v2/ipa_i.h index 28689eb83d4e..1986dc2af097 100644 --- a/drivers/platform/msm/ipa/ipa_v2/ipa_i.h +++ b/drivers/platform/msm/ipa/ipa_v2/ipa_i.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved. +/* Copyright (c) 2012-2018, 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 @@ -1207,6 +1207,8 @@ struct ipa_context { struct ipa_cne_evt ipa_cne_evt_req_cache[IPA_MAX_NUM_REQ_CACHE]; int num_ipa_cne_evt_req; struct mutex ipa_cne_evt_lock; + int (*q6_cleanup_cb)(void); + bool is_apps_shutdown_support; }; /** @@ -1263,6 +1265,7 @@ struct ipa_plat_drv_res { u32 ipa_rx_polling_sleep_msec; u32 ipa_polling_iteration; bool ipa_uc_monitor_holb; + bool is_apps_shutdown_support; }; struct ipa_mem_partition { @@ -1388,6 +1391,8 @@ int ipa2_disconnect(u32 clnt_hdl); */ int ipa2_reset_endpoint(u32 clnt_hdl); +void ipa2_apps_shutdown_apps_ep_reset(void); + /* * Remove ep delay */ @@ -1781,7 +1786,7 @@ static inline u32 ipa_read_reg_field(void *base, u32 offset, return (ipa_read_reg(base, offset) & mask) >> shift; } -static inline void ipa_write_reg(void *base, u32 offset, u32 val) +static inline void ipa_write_reg(void __iomem *base, u32 offset, u32 val) { iowrite32(val, base + offset); } @@ -1868,6 +1873,8 @@ int ipa_tag_process(struct ipa_desc *desc, int num_descs, unsigned long timeout); int ipa_q6_pre_shutdown_cleanup(void); +int ipa_apps_shutdown_cleanup(void); +int register_ipa_platform_cb(int (*cb)(void)); int ipa_q6_post_shutdown_cleanup(void); int ipa_init_q6_smem(void); int ipa_q6_monitor_holb_mitigation(bool enable); diff --git a/drivers/platform/msm/ipa/ipa_v2/rmnet_ipa.c b/drivers/platform/msm/ipa/ipa_v2/rmnet_ipa.c index 3defc03c2571..c79978038668 100644 --- a/drivers/platform/msm/ipa/ipa_v2/rmnet_ipa.c +++ b/drivers/platform/msm/ipa/ipa_v2/rmnet_ipa.c @@ -76,6 +76,8 @@ static bool egress_set, a7_ul_flt_set; static struct workqueue_struct *ipa_rm_q6_workqueue; /* IPA_RM workqueue*/ static atomic_t is_initialized; static atomic_t is_ssr; +static atomic_t is_after_powerup_cmpltd; +static struct completion is_after_shutdown_cmpltd; static void *subsys_notify_handle; u32 apps_to_ipa_hdl, ipa_to_apps_hdl; /* get handler from ipa */ @@ -135,6 +137,14 @@ struct wwan_private { struct napi_struct napi; }; +static int ssr_notifier_cb(struct notifier_block *this, + unsigned long code, + void *data); + +static struct notifier_block ssr_notifier = { + .notifier_call = ssr_notifier_cb, +}; + /** * ipa_setup_a7_qmap_hdr() - Setup default a7 qmap hdr * @@ -1958,15 +1968,34 @@ static void ipa_rm_notify(void *dev, enum ipa_rm_event event, } } -/* IPA_RM related functions end*/ +/** +* q6_cleanup_cb() - IPA q6 cleanup +* +* This function is called in the sequence +* of ipa platform shutdown +*/ -static int ssr_notifier_cb(struct notifier_block *this, - unsigned long code, - void *data); +static int q6_cleanup_cb(void) +{ + int ret = 0; -static struct notifier_block ssr_notifier = { - .notifier_call = ssr_notifier_cb, -}; + IPAWANERR("Start\n"); + if (atomic_read(&is_initialized) && + atomic_read(&is_after_powerup_cmpltd)) { + pr_info("Wait for q6 cleanup\n"); + wait_for_completion(&is_after_shutdown_cmpltd); + pr_info("q6_cleanup_cb: Q6 SSR cleanup is taken care\n"); + } else { + if (!atomic_read(&is_initialized)) + pr_info("RmNET IPA driver is not inited\n"); + if (!atomic_read(&is_after_powerup_cmpltd)) + pr_info("Modem is not up\n"); + } + IPAWANERR("END\n"); + return ret; +} + +/* IPA_RM related functions end*/ static int get_ipa_rmnet_dts_configuration(struct platform_device *pdev, struct ipa_rmnet_plat_drv_res *ipa_rmnet_drv_res) @@ -2179,6 +2208,8 @@ static int ipa_wwan_probe(struct platform_device *pdev) ipa2_proxy_clk_unvote(); } atomic_set(&is_ssr, 0); + atomic_set(&is_after_powerup_cmpltd, 0); + init_completion(&is_after_shutdown_cmpltd); pr_info("rmnet_ipa completed initialization\n"); return 0; @@ -2221,6 +2252,7 @@ setup_dflt_wan_rt_tables_err: setup_a7_qmap_hdr_err: ipa_qmi_service_exit(); atomic_set(&is_ssr, 0); + atomic_set(&is_after_powerup_cmpltd, 0); return ret; } @@ -2409,6 +2441,7 @@ static int ssr_notifier_cb(struct notifier_block *this, if (atomic_read(&is_ssr)) ipa_q6_post_shutdown_cleanup(); pr_info("IPA AFTER_SHUTDOWN handling is complete\n"); + complete(&is_after_shutdown_cmpltd); return NOTIFY_DONE; } if (SUBSYS_AFTER_POWERUP == code) { @@ -2416,6 +2449,7 @@ static int ssr_notifier_cb(struct notifier_block *this, if (!atomic_read(&is_initialized) && atomic_read(&is_ssr)) platform_driver_register(&rmnet_ipa_driver); + atomic_set(&is_after_powerup_cmpltd, 1); pr_info("IPA AFTER_POWERUP handling is complete\n"); return NOTIFY_DONE; } @@ -3222,6 +3256,7 @@ void ipa_q6_handshake_complete(bool ssr_bootup) static int __init ipa_wwan_init(void) { + int ret = 0; atomic_set(&is_initialized, 0); atomic_set(&is_ssr, 0); @@ -3231,6 +3266,11 @@ static int __init ipa_wwan_init(void) ipa_qmi_init(); + IPAWANERR("Registering for q6_cleanup_cb\n"); + ret = register_ipa_platform_cb(&q6_cleanup_cb); + if (ret == -EAGAIN) + IPAWANERR("Register for q6_cleanup_cb is un-successful\n"); + /* Register for Modem SSR */ subsys_notify_handle = subsys_notif_register_notifier(SUBSYS_MODEM, &ssr_notifier); |