diff --git a/drivers/platform/msm/ipa/ipa_api.c b/drivers/platform/msm/ipa/ipa_api.c index c5e8532ba90e..2dab4cecae31 100644 --- a/drivers/platform/msm/ipa/ipa_api.c +++ b/drivers/platform/msm/ipa/ipa_api.c @@ -14,7 +14,6 @@ #include #include #include "ipa_api.h" -#include "ipa_v3/ipa_i.h" /* * The following for adding code (ie. for EMULATION) not found on x86. @@ -2883,19 +2882,24 @@ enum ipa_client_type ipa_get_client_mapping(int pipe_idx) EXPORT_SYMBOL(ipa_get_client_mapping); /** - * ipa_get_rm_resource_from_ep() - this function is part of the deprecated - * RM mechanism but is still used by some drivers so we kept the definition. + * ipa_get_rm_resource_from_ep() - get the IPA_RM resource which is related to + * the supplied pipe index. + * + * @pipe_idx: + * + * Return value: IPA_RM resource related to the pipe, -1 if a resource was not + * found. */ - enum ipa_rm_resource_name ipa_get_rm_resource_from_ep(int pipe_idx) { - IPAERR("IPA RM is not supported idx=%d\n", pipe_idx); - return -EFAULT; + int ret; + + IPA_API_DISPATCH_RETURN(ipa_get_rm_resource_from_ep, pipe_idx); + + return ret; } EXPORT_SYMBOL(ipa_get_rm_resource_from_ep); - - /** * ipa_get_modem_cfg_emb_pipe_flt()- Return ipa_ctx->modem_cfg_emb_pipe_flt * @@ -3792,6 +3796,18 @@ int ipa_get_prot_id(enum ipa_client_type client) } EXPORT_SYMBOL(ipa_get_prot_id); +/** + * ipa_pm_is_used() - Returns if IPA PM framework is used + */ +bool ipa_pm_is_used(void) +{ + bool ret; + + IPA_API_DISPATCH_RETURN(ipa_pm_is_used); + + return ret; +} + static const struct dev_pm_ops ipa_pm_ops = { .suspend_late = ipa_ap_suspend, .resume_early = ipa_ap_resume, diff --git a/drivers/platform/msm/ipa/ipa_clients/rndis_ipa.c b/drivers/platform/msm/ipa/ipa_clients/rndis_ipa.c index 2d54862b430c..0faf2b46e7ed 100644 --- a/drivers/platform/msm/ipa/ipa_clients/rndis_ipa.c +++ b/drivers/platform/msm/ipa/ipa_clients/rndis_ipa.c @@ -31,6 +31,7 @@ #define DEBUGFS_DIR_NAME "rndis_ipa" #define DEBUGFS_AGGR_DIR_NAME "rndis_ipa_aggregation" #define NETDEV_NAME "rndis" +#define DRV_RESOURCE_ID IPA_RM_RESOURCE_RNDIS_PROD #define IPV4_HDR_NAME "rndis_eth_ipv4" #define IPV6_HDR_NAME "rndis_eth_ipv6" #define IPA_TO_USB_CLIENT IPA_CLIENT_USB_CONS @@ -159,6 +160,7 @@ enum rndis_ipa_operation { * @rx_dropped: number of filtered out Rx packets * @rx_dump_enable: dump all Rx packets * @icmp_filter: allow all ICMP packet to pass through the filters + * @rm_enable: flag that enable/disable Resource manager request prior to Tx * @deaggregation_enable: enable/disable IPA HW deaggregation logic * @during_xmit_error: flags that indicate that the driver is in a middle * of error handling in Tx path @@ -194,6 +196,7 @@ struct rndis_ipa_dev { u32 rx_dropped; bool rx_dump_enable; bool icmp_filter; + bool rm_enable; bool deaggregation_enable; bool during_xmit_error; struct dentry *directory; @@ -255,10 +258,18 @@ static int rndis_ipa_hdrs_destroy(struct rndis_ipa_dev *rndis_ipa_ctx); static struct net_device_stats *rndis_ipa_get_stats(struct net_device *net); static int rndis_ipa_register_properties(char *netdev_name, bool is_vlan_mode); static int rndis_ipa_deregister_properties(char *netdev_name); +static void rndis_ipa_rm_notify + (void *user_data, enum ipa_rm_event event, + unsigned long data); +static int rndis_ipa_create_rm_resource(struct rndis_ipa_dev *rndis_ipa_ctx); +static int rndis_ipa_destroy_rm_resource(struct rndis_ipa_dev *rndis_ipa_ctx); static int rndis_ipa_register_pm_client(struct rndis_ipa_dev *rndis_ipa_ctx); static int rndis_ipa_deregister_pm_client(struct rndis_ipa_dev *rndis_ipa_ctx); static bool rx_filter(struct sk_buff *skb); static bool tx_filter(struct sk_buff *skb); +static bool rm_enabled(struct rndis_ipa_dev *rndis_ipa_ctx); +static int resource_request(struct rndis_ipa_dev *rndis_ipa_ctx); +static void resource_release(struct rndis_ipa_dev *rndis_ipa_ctx); static netdev_tx_t rndis_ipa_start_xmit (struct sk_buff *skb, struct net_device *net); static int rndis_ipa_debugfs_atomic_open @@ -541,6 +552,7 @@ int rndis_ipa_init(struct ipa_usb_init_params *params) rndis_ipa_ctx->tx_filter = false; rndis_ipa_ctx->rx_filter = false; rndis_ipa_ctx->icmp_filter = true; + rndis_ipa_ctx->rm_enable = true; rndis_ipa_ctx->tx_dropped = 0; rndis_ipa_ctx->rx_dropped = 0; rndis_ipa_ctx->tx_dump_enable = false; @@ -750,12 +762,15 @@ int rndis_ipa_pipe_connect_notify( return -EINVAL; } - result = rndis_ipa_register_pm_client(rndis_ipa_ctx); + if (ipa_pm_is_used()) + result = rndis_ipa_register_pm_client(rndis_ipa_ctx); + else + result = rndis_ipa_create_rm_resource(rndis_ipa_ctx); if (result) { - RNDIS_IPA_ERROR("fail on PM register\n"); - goto fail_register_pm; + RNDIS_IPA_ERROR("fail on RM create\n"); + goto fail_create_rm; } - RNDIS_IPA_DEBUG("PM client was registered\n"); + RNDIS_IPA_DEBUG("RM resource was created\n"); rndis_ipa_ctx->ipa_to_usb_hdl = ipa_to_usb_hdl; rndis_ipa_ctx->usb_to_ipa_hdl = usb_to_ipa_hdl; @@ -833,8 +848,11 @@ int rndis_ipa_pipe_connect_notify( return 0; fail: - rndis_ipa_deregister_pm_client(rndis_ipa_ctx); -fail_register_pm: + if (ipa_pm_is_used()) + rndis_ipa_deregister_pm_client(rndis_ipa_ctx); + else + rndis_ipa_destroy_rm_resource(rndis_ipa_ctx); +fail_create_rm: return result; } EXPORT_SYMBOL(rndis_ipa_pipe_connect_notify); @@ -952,11 +970,11 @@ static netdev_tx_t rndis_ipa_start_xmit(struct sk_buff *skb, goto out; } - ret = ipa_pm_activate(rndis_ipa_ctx->pm_hdl); - if (unlikely(ret)) { - RNDIS_IPA_DEBUG("Failed activate PM client\n"); + ret = resource_request(rndis_ipa_ctx); + if (ret) { + RNDIS_IPA_DEBUG("Waiting to resource\n"); netif_stop_queue(net); - goto fail_pm_activate; + goto resource_busy; } if (atomic_read(&rndis_ipa_ctx->outstanding_pkts) >= @@ -985,8 +1003,8 @@ static netdev_tx_t rndis_ipa_start_xmit(struct sk_buff *skb, fail_tx_packet: rndis_ipa_xmit_error(skb); out: - ipa_pm_deferred_deactivate(rndis_ipa_ctx->pm_hdl); -fail_pm_activate: + resource_release(rndis_ipa_ctx); +resource_busy: RNDIS_IPA_DEBUG ("packet Tx done - %s\n", (status == NETDEV_TX_OK) ? "OK" : "FAIL"); @@ -1074,6 +1092,50 @@ static void rndis_ipa_tx_timeout(struct net_device *net) net->stats.tx_errors++; } +/** + * rndis_ipa_rm_notify() - callback supplied to IPA resource manager + * for grant/release events + * user_data: the driver context supplied to IPA resource manager during call + * to ipa_rm_create_resource(). + * event: the event notified to us by IPA resource manager (Release/Grant) + * data: reserved field supplied by IPA resource manager + * + * This callback shall be called based on resource request/release sent + * to the IPA resource manager. + * In case the queue was stopped during EINPROGRESS for Tx path and the + * event received is Grant then the queue shall be restarted. + * In case the event notified is a release notification the netdev discard it. + */ +static void rndis_ipa_rm_notify( + void *user_data, enum ipa_rm_event event, + unsigned long data) +{ + struct rndis_ipa_dev *rndis_ipa_ctx = user_data; + + RNDIS_IPA_LOG_ENTRY(); + + if (event == IPA_RM_RESOURCE_RELEASED) { + RNDIS_IPA_DEBUG("Resource Released\n"); + return; + } + + if (event != IPA_RM_RESOURCE_GRANTED) { + RNDIS_IPA_ERROR + ("Unexceoted event receieved from RM (%d\n)", event); + return; + } + RNDIS_IPA_DEBUG("Resource Granted\n"); + + if (netif_queue_stopped(rndis_ipa_ctx->net)) { + RNDIS_IPA_DEBUG("starting queue\n"); + netif_start_queue(rndis_ipa_ctx->net); + } else { + RNDIS_IPA_DEBUG("queue already awake\n"); + } + + RNDIS_IPA_LOG_EXIT(); +} + /** * rndis_ipa_packet_receive_notify() - Rx notify for packet sent from * tethered PC (USB->IPA). @@ -1293,12 +1355,15 @@ int rndis_ipa_pipe_disconnect_notify(void *private) rndis_ipa_ctx->net->stats.tx_dropped += outstanding_dropped_pkts; atomic_set(&rndis_ipa_ctx->outstanding_pkts, 0); - retval = rndis_ipa_deregister_pm_client(rndis_ipa_ctx); + if (ipa_pm_is_used()) + retval = rndis_ipa_deregister_pm_client(rndis_ipa_ctx); + else + retval = rndis_ipa_destroy_rm_resource(rndis_ipa_ctx); if (retval) { - RNDIS_IPA_ERROR("Fail to deregister PM\n"); + RNDIS_IPA_ERROR("Fail to clean RM\n"); return retval; } - RNDIS_IPA_DEBUG("PM was successfully deregistered\n"); + RNDIS_IPA_DEBUG("RM was successfully destroyed\n"); spin_lock_irqsave(&rndis_ipa_ctx->state_lock, flags); next_state = rndis_ipa_next_state(rndis_ipa_ctx->state, @@ -1766,7 +1831,86 @@ static int rndis_ipa_deregister_properties(char *netdev_name) return 0; } +/** + * rndis_ipa_create_rm_resource() -creates the resource representing + * this Netdev and supply notification callback for resource event + * such as Grant/Release + * @rndis_ipa_ctx: this driver context + * + * In order make sure all needed resources are available during packet + * transmit this Netdev shall use Request/Release mechanism of + * the IPA resource manager. + * This mechanism shall iterate over a dependency graph and make sure + * all dependent entities are ready to for packet Tx + * transfer (Apps->IPA->USB). + * In this function the resource representing the Netdev is created + * in addition to the basic dependency between the Netdev and the USB client. + * Hence, USB client, is a dependency for the Netdev and may be notified in + * case of packet transmit from this Netdev to tethered Host. + * As implied from the "may" in the above sentence there is a scenario where + * the USB is not notified. This is done thanks to the IPA resource manager + * inactivity timer. + * The inactivity timer allow the Release requests to be delayed in order + * prevent ping-pong with the USB and other dependencies. + */ +static int rndis_ipa_create_rm_resource(struct rndis_ipa_dev *rndis_ipa_ctx) +{ + struct ipa_rm_create_params create_params = {0}; + struct ipa_rm_perf_profile profile; + int result; + RNDIS_IPA_LOG_ENTRY(); + + create_params.name = DRV_RESOURCE_ID; + create_params.reg_params.user_data = rndis_ipa_ctx; + create_params.reg_params.notify_cb = rndis_ipa_rm_notify; + result = ipa_rm_create_resource(&create_params); + if (result) { + RNDIS_IPA_ERROR("Fail on ipa_rm_create_resource\n"); + goto fail_rm_create; + } + RNDIS_IPA_DEBUG("RM client was created\n"); + + profile.max_supported_bandwidth_mbps = IPA_APPS_MAX_BW_IN_MBPS; + ipa_rm_set_perf_profile(DRV_RESOURCE_ID, &profile); + + result = ipa_rm_inactivity_timer_init + (DRV_RESOURCE_ID, + INACTIVITY_MSEC_DELAY); + if (result) { + RNDIS_IPA_ERROR("Fail on ipa_rm_inactivity_timer_init\n"); + goto fail_inactivity_timer; + } + + RNDIS_IPA_DEBUG("rm_it client was created\n"); + + result = ipa_rm_add_dependency_sync + (DRV_RESOURCE_ID, + IPA_RM_RESOURCE_USB_CONS); + + if (result && result != -EINPROGRESS) + RNDIS_IPA_ERROR("unable to add RNDIS/USB dependency (%d)\n", + result); + else + RNDIS_IPA_DEBUG("RNDIS/USB dependency was set\n"); + + result = ipa_rm_add_dependency_sync + (IPA_RM_RESOURCE_USB_PROD, + IPA_RM_RESOURCE_APPS_CONS); + if (result && result != -EINPROGRESS) + RNDIS_IPA_ERROR("unable to add USB/APPS dependency (%d)\n", + result); + else + RNDIS_IPA_DEBUG("USB/APPS dependency was set\n"); + + RNDIS_IPA_LOG_EXIT(); + + return 0; + +fail_inactivity_timer: +fail_rm_create: + return result; +} static void rndis_ipa_pm_cb(void *p, enum ipa_pm_cb_event event) { @@ -1791,6 +1935,64 @@ static void rndis_ipa_pm_cb(void *p, enum ipa_pm_cb_event event) RNDIS_IPA_LOG_EXIT(); } +/** + * rndis_ipa_destroy_rm_resource() - delete the dependency and destroy + * the resource done on rndis_ipa_create_rm_resource() + * @rndis_ipa_ctx: this driver context + * + * This function shall delete the dependency create between + * the Netdev to the USB. + * In addition the inactivity time shall be destroy and the resource shall + * be deleted. + */ +static int rndis_ipa_destroy_rm_resource(struct rndis_ipa_dev *rndis_ipa_ctx) +{ + int result; + + RNDIS_IPA_LOG_ENTRY(); + + result = ipa_rm_delete_dependency + (DRV_RESOURCE_ID, + IPA_RM_RESOURCE_USB_CONS); + if (result && result != -EINPROGRESS) { + RNDIS_IPA_ERROR("Fail to delete RNDIS/USB dependency\n"); + goto bail; + } + RNDIS_IPA_DEBUG("RNDIS/USB dependency was successfully deleted\n"); + + result = ipa_rm_delete_dependency + (IPA_RM_RESOURCE_USB_PROD, + IPA_RM_RESOURCE_APPS_CONS); + if (result == -EINPROGRESS) { + RNDIS_IPA_DEBUG("RM dependency deletion is in progress"); + } else if (result) { + RNDIS_IPA_ERROR("Fail to delete USB/APPS dependency\n"); + goto bail; + } else { + RNDIS_IPA_DEBUG("USB/APPS dependency was deleted\n"); + } + + result = ipa_rm_inactivity_timer_destroy(DRV_RESOURCE_ID); + if (result) { + RNDIS_IPA_ERROR("Fail to destroy inactivity timern"); + goto bail; + } + RNDIS_IPA_DEBUG("RM inactivity timer was successfully destroy\n"); + + result = ipa_rm_delete_resource(DRV_RESOURCE_ID); + if (result) { + RNDIS_IPA_ERROR("resource deletion failed\n"); + goto bail; + } + RNDIS_IPA_DEBUG + ("Netdev RM resource was deleted (resid:%d)\n", + DRV_RESOURCE_ID); + + RNDIS_IPA_LOG_EXIT(); + +bail: + return result; +} static int rndis_ipa_register_pm_client(struct rndis_ipa_dev *rndis_ipa_ctx) { @@ -1819,6 +2021,52 @@ static int rndis_ipa_deregister_pm_client(struct rndis_ipa_dev *rndis_ipa_ctx) return 0; } +/** + * resource_request() - request for the Netdev resource + * @rndis_ipa_ctx: main driver context + * + * This function shall send the IPA resource manager inactivity time a request + * to Grant the Netdev producer. + * In case the resource is already Granted the function shall return immediately + * and "pet" the inactivity timer. + * In case the resource was not already Granted this function shall + * return EINPROGRESS and the Netdev shall stop the send queue until + * the IPA resource manager notify it that the resource is + * granted (done in a differ context) + */ +static int resource_request(struct rndis_ipa_dev *rndis_ipa_ctx) +{ + int result = 0; + + if (!rm_enabled(rndis_ipa_ctx)) + return result; + + if (ipa_pm_is_used()) + return ipa_pm_activate(rndis_ipa_ctx->pm_hdl); + + return ipa_rm_inactivity_timer_request_resource( + DRV_RESOURCE_ID); + +} + +/** + * resource_release() - release the Netdev resource + * @rndis_ipa_ctx: main driver context + * + * start the inactivity timer count down.by using the IPA resource + * manager inactivity time. + * The actual resource release shall occur only if no request shall be done + * during the INACTIVITY_MSEC_DELAY. + */ +static void resource_release(struct rndis_ipa_dev *rndis_ipa_ctx) +{ + if (!rm_enabled(rndis_ipa_ctx)) + return; + if (ipa_pm_is_used()) + ipa_pm_deferred_deactivate(rndis_ipa_ctx->pm_hdl); + else + ipa_rm_inactivity_timer_release_resource(DRV_RESOURCE_ID); +} /** * rndis_encapsulate_skb() - encapsulate the given Ethernet skb with @@ -1906,6 +2154,19 @@ static bool tx_filter(struct sk_buff *skb) return true; } +/** + * rm_enabled() - allow the use of resource manager Request/Release to + * be bypassed + * @rndis_ipa_ctx: main driver context + * + * By disabling the resource manager flag the Request for the Netdev resource + * shall be bypassed and the packet shall be sent. + * accordingly, Release request shall be bypass as well. + */ +static bool rm_enabled(struct rndis_ipa_dev *rndis_ipa_ctx) +{ + return rndis_ipa_ctx->rm_enable; +} /** * rndis_ipa_ep_registers_cfg() - configure the USB endpoints @@ -2181,6 +2442,14 @@ static void rndis_ipa_debugfs_init(struct rndis_ipa_dev *rndis_ipa_ctx) goto fail_file; } + file = debugfs_create_bool + ("rm_enable", flags_read_write, + rndis_ipa_ctx->directory, &rndis_ipa_ctx->rm_enable); + if (!file) { + RNDIS_IPA_ERROR("could not create debugfs rm file\n"); + goto fail_file; + } + file = debugfs_create_u32 ("outstanding_high", flags_read_write, rndis_ipa_ctx->directory, diff --git a/drivers/platform/msm/ipa/ipa_common_i.h b/drivers/platform/msm/ipa/ipa_common_i.h index f765bda3a83e..4a313ad450c4 100644 --- a/drivers/platform/msm/ipa/ipa_common_i.h +++ b/drivers/platform/msm/ipa/ipa_common_i.h @@ -428,6 +428,8 @@ int ipa_disable_wdi_pipes(int ipa_ep_idx_tx, int ipa_ep_idx_rx); const char *ipa_get_version_string(enum ipa_hw_type ver); int ipa_start_gsi_channel(u32 clnt_hdl); +bool ipa_pm_is_used(void); + int ipa_smmu_store_sgt(struct sg_table **out_ch_ptr, struct sg_table *in_sgt_ptr); int ipa_smmu_free_sgt(struct sg_table **out_sgt_ptr);