diff --git a/drivers/platform/msm/ipa/ipa_v3/ethernet/ipa_eth.c b/drivers/platform/msm/ipa/ipa_v3/ethernet/ipa_eth.c index 67eafb233d93..0e987a776be0 100644 --- a/drivers/platform/msm/ipa/ipa_v3/ethernet/ipa_eth.c +++ b/drivers/platform/msm/ipa/ipa_v3/ethernet/ipa_eth.c @@ -27,6 +27,11 @@ enum ipa_eth_states { IPA_ETH_ST_MAX, }; +enum ipa_eth_dev_states { + IPA_ETH_DEV_ST_UNPAIRING, + IPA_ETH_DEV_ST_MAX, +}; + static unsigned long ipa_eth_state; static struct dentry *ipa_eth_debugfs; @@ -56,12 +61,15 @@ static inline bool ipa_eth_ready(void) static inline bool initable(struct ipa_eth_device *eth_dev) { - return eth_dev->init; + return !test_bit(IPA_ETH_DEV_ST_UNPAIRING, ð_dev->state) && + eth_dev->init; } static inline bool startable(struct ipa_eth_device *eth_dev) { - return eth_dev->init && eth_dev->start && + return !test_bit(IPA_ETH_DEV_ST_UNPAIRING, ð_dev->state) && + eth_dev->init && + eth_dev->start && test_bit(IPA_ETH_IF_ST_LOWER_UP, ð_dev->if_state); } @@ -148,9 +156,13 @@ static int ipa_eth_deinit_device(struct ipa_eth_device *eth_dev) return 0; } +static void ipa_eth_free_msg(void *buff, u32 len, u32 type) {} + static int ipa_eth_start_device(struct ipa_eth_device *eth_dev) { int rc; + struct ipa_msg_meta msg_meta; + struct ipa_ecm_msg ecm_msg; if (eth_dev->of_state == IPA_ETH_OF_ST_STARTED) return 0; @@ -180,6 +192,16 @@ static int ipa_eth_start_device(struct ipa_eth_device *eth_dev) return rc; } + memset(&msg_meta, 0, sizeof(msg_meta)); + memset(&ecm_msg, 0, sizeof(ecm_msg)); + + ecm_msg.ifindex = eth_dev->net_dev->ifindex; + strlcpy(ecm_msg.name, eth_dev->net_dev->name, IPA_RESOURCE_NAME_MAX); + + msg_meta.msg_type = ECM_CONNECT; + msg_meta.msg_len = sizeof(struct ipa_ecm_msg); + (void) ipa_send_msg(&msg_meta, &ecm_msg, ipa_eth_free_msg); + ipa_eth_dev_log(eth_dev, "Started device"); eth_dev->of_state = IPA_ETH_OF_ST_STARTED; @@ -190,6 +212,18 @@ static int ipa_eth_start_device(struct ipa_eth_device *eth_dev) static int ipa_eth_stop_device(struct ipa_eth_device *eth_dev) { int rc; + struct ipa_msg_meta msg_meta; + struct ipa_ecm_msg ecm_msg; + + memset(&msg_meta, 0, sizeof(msg_meta)); + memset(&ecm_msg, 0, sizeof(ecm_msg)); + + ecm_msg.ifindex = eth_dev->net_dev->ifindex; + strlcpy(ecm_msg.name, eth_dev->net_dev->name, IPA_RESOURCE_NAME_MAX); + + msg_meta.msg_type = ECM_DISCONNECT; + msg_meta.msg_len = sizeof(struct ipa_ecm_msg); + (void) ipa_send_msg(&msg_meta, &ecm_msg, ipa_eth_free_msg); if (eth_dev->of_state == IPA_ETH_OF_ST_DEINITED) return 0; @@ -342,7 +376,7 @@ static void ipa_eth_dev_start_timer_cb(unsigned long data) ipa_eth_refresh_device(eth_dev); } -static int ipa_eth_netdev_event_change(struct ipa_eth_device *eth_dev) +static int __ipa_eth_netdev_event(struct ipa_eth_device *eth_dev) { bool refresh_needed = netif_carrier_ok(eth_dev->net_dev) ? !test_and_set_bit(IPA_ETH_IF_ST_LOWER_UP, ð_dev->if_state) : @@ -365,17 +399,9 @@ static int ipa_eth_netdev_event(struct notifier_block *nb, if (net_dev != eth_dev->net_dev) return NOTIFY_DONE; - ipa_eth_dev_log(eth_dev, "Received netdev event %lu", event); - - switch (event) { - case NETDEV_CHANGE: - return ipa_eth_netdev_event_change(eth_dev); - default: - /* Ignore other events */ - break; - } + ipa_eth_dev_log(eth_dev, "Received netdev event 0x%04lx", event); - return NOTIFY_DONE; + return __ipa_eth_netdev_event(eth_dev); } static int ipa_eth_uc_ready_cb(struct notifier_block *nb, @@ -620,7 +646,7 @@ static void __ipa_eth_unpair_device(struct ipa_eth_device *eth_dev) flush_work(ð_dev->refresh); - eth_dev->init = eth_dev->start = false; + set_bit(IPA_ETH_DEV_ST_UNPAIRING, ð_dev->state); ipa_eth_refresh_device(eth_dev); flush_work(ð_dev->refresh); @@ -628,6 +654,8 @@ static void __ipa_eth_unpair_device(struct ipa_eth_device *eth_dev) unregister_netdevice_notifier(ð_dev->netdevice_nb); ipa_eth_offload_unpair_device(eth_dev); + + clear_bit(IPA_ETH_DEV_ST_UNPAIRING, ð_dev->state); } static void ipa_eth_pair_devices(void) @@ -904,6 +932,7 @@ int ipa_eth_register_offload_driver(struct ipa_eth_offload_driver *od) ipa_eth_log("Registered offload driver %s", od->name); ipa_eth_pair_devices(); + ipa_eth_refresh_devices(); return 0; } diff --git a/drivers/platform/msm/ipa/ipa_v3/ethernet/ipa_eth_gsi.c b/drivers/platform/msm/ipa/ipa_v3/ethernet/ipa_eth_gsi.c index 8f3983dd7c7d..006953a825ce 100644 --- a/drivers/platform/msm/ipa/ipa_v3/ethernet/ipa_eth_gsi.c +++ b/drivers/platform/msm/ipa/ipa_v3/ethernet/ipa_eth_gsi.c @@ -242,6 +242,14 @@ int ipa_eth_gsi_dealloc(struct ipa_eth_channel *ch) } if (ep_ctx->gsi_evt_ring_hdl != ~0) { + gsi_rc = gsi_reset_evt_ring(ep_ctx->gsi_evt_ring_hdl); + if (gsi_rc != GSI_STATUS_SUCCESS) { + ipa_eth_dev_err(ch->eth_dev, + "Failed to reset event ring %lu", + ep_ctx->gsi_evt_ring_hdl); + return gsi_rc; + } + gsi_rc = gsi_dealloc_evt_ring(ep_ctx->gsi_evt_ring_hdl); if (gsi_rc != GSI_STATUS_SUCCESS) { ipa_eth_dev_err(ch->eth_dev, diff --git a/drivers/platform/msm/ipa/ipa_v3/ethernet/ipa_eth_offload.c b/drivers/platform/msm/ipa/ipa_v3/ethernet/ipa_eth_offload.c index 89acc788609c..77d92d9ae82f 100644 --- a/drivers/platform/msm/ipa/ipa_v3/ethernet/ipa_eth_offload.c +++ b/drivers/platform/msm/ipa/ipa_v3/ethernet/ipa_eth_offload.c @@ -207,6 +207,8 @@ int ipa_eth_offload_register_driver(struct ipa_eth_offload_driver *od) void ipa_eth_offload_unregister_driver(struct ipa_eth_offload_driver *od) { + debugfs_remove_recursive(od->debugfs); + mutex_lock(&ipa_eth_offload_drivers_lock); list_del(&od->driver_list); mutex_unlock(&ipa_eth_offload_drivers_lock); diff --git a/drivers/platform/msm/ipa/ipa_v3/ethernet/ipa_eth_pm.c b/drivers/platform/msm/ipa/ipa_v3/ethernet/ipa_eth_pm.c index d969bb56b834..a8d4c7b7b50b 100644 --- a/drivers/platform/msm/ipa/ipa_v3/ethernet/ipa_eth_pm.c +++ b/drivers/platform/msm/ipa/ipa_v3/ethernet/ipa_eth_pm.c @@ -11,6 +11,7 @@ */ #include +#include #include "ipa_eth_i.h" @@ -100,7 +101,10 @@ static u32 __fetch_ethtool_link_speed(struct ipa_eth_device *eth_dev) int rc; struct ethtool_link_ksettings link_ksettings; + rtnl_lock(); rc = __ethtool_get_link_ksettings(eth_dev->net_dev, &link_ksettings); + rtnl_unlock(); + if (rc) { ipa_eth_dev_err(eth_dev, "Failed to obtain link settings via ethtool"); diff --git a/include/linux/ipa_eth.h b/include/linux/ipa_eth.h index 2e09ac2b3916..732cc199f5cc 100644 --- a/include/linux/ipa_eth.h +++ b/include/linux/ipa_eth.h @@ -440,6 +440,7 @@ struct ipa_eth_channel { * @start_on_resume: Allow start upon driver resume * @start_on_timeout: Timeout in milliseconds after which @start is enabled * @start_timer: Timer associated with @start_on_timer + * @state: Device state * @if_state: Interface state - one or more bit numbers IPA_ETH_IF_ST_* * @pm_handle: IPA PM client handle for the device * @bus_priv: Private field for use by offload subsystem bus layer @@ -478,6 +479,7 @@ struct ipa_eth_device { u32 start_on_timeout; struct timer_list start_timer; + unsigned long state; unsigned long if_state; u32 pm_handle;