diff --git a/drivers/platform/msm/Kconfig b/drivers/platform/msm/Kconfig index 5bb96011b7f8..ecd74be324fe 100644 --- a/drivers/platform/msm/Kconfig +++ b/drivers/platform/msm/Kconfig @@ -195,32 +195,4 @@ config AQFWD_IOSS aqc_ioss. If unsure, say N. -config IPA_ETH - bool "IPA Ethernet Offload Sub-system support" - depends on IPA3 - help - Enables IPA Ethernet Offload Subsystem for offloading PCI based - ethernet devices to IPA. The offload subsystem still require a - compatible network driver to register with it and a corresponding - offload driver to manage one of more offload data paths that uses - the network device. - -config IPA_ETH_NOAUTO - bool "Disable automatic offload initialization of interfaces" - depends on IPA_ETH - help - Enabling this option prevents automatic initialization of offload on - ethernet interfaces. Debugfs control interface will instead be used - to enable offloading. This feature is meant only for debugging. - If unsure, say N. - -config IPA_ETH_DEBUG - bool "Enable addtional debug logging in the offload sub-system" - depends on IPA_ETH - help - Enabling this option increases the number of messages logged by the offload - sub-system for aiding debugging. Additional debug messages are logged into - IPC log as well as kernel log using dynamic debug. - If unsure, say N. - endmenu diff --git a/drivers/platform/msm/Makefile b/drivers/platform/msm/Makefile index 8c8a9c6d8ad9..593d90687fa0 100644 --- a/drivers/platform/msm/Makefile +++ b/drivers/platform/msm/Makefile @@ -13,5 +13,4 @@ obj-$(CONFIG_SPS) += sps/ obj-$(CONFIG_MSM_11AD) += msm_11ad/ obj-$(CONFIG_EP_PCIE) += ep_pcie/ obj-$(CONFIG_MSM_MHI_DEV) += mhi_dev/ -obj-$(CONFIG_IPA_ETH) += ethernet/ obj-$(CONFIG_VETH_IPA) += veth_ipa/ diff --git a/drivers/platform/msm/ethernet/Makefile b/drivers/platform/msm/ethernet/Makefile deleted file mode 100644 index 8f345485d2ab..000000000000 --- a/drivers/platform/msm/ethernet/Makefile +++ /dev/null @@ -1,16 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0-only - -# -# Makefile for the MSM specific device drivers. -# - -obj-$(CONFIG_IPA_ETH) += ipa-eth.o - -ipa-eth-y := \ - ipa_eth_bus.o \ - ipa_eth.o \ - ipa_eth_debugfs.o \ - ipa_eth_net.o \ - ipa_eth_offload.o \ - ipa_eth_pci.o \ - ipa_eth_utils.o diff --git a/drivers/platform/msm/ethernet/ipa_eth.c b/drivers/platform/msm/ethernet/ipa_eth.c deleted file mode 100644 index 9e92e1f95cd1..000000000000 --- a/drivers/platform/msm/ethernet/ipa_eth.c +++ /dev/null @@ -1,840 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Copyright (c) 2019-2020, The Linux Foundation. All rights reserved - */ - -#include -#include - -#include "ipa_eth_i.h" - -unsigned long ipa_eth_state; - -static LIST_HEAD(ipa_eth_devices); -static DEFINE_MUTEX(ipa_eth_devices_lock); - -#ifdef CONFIG_IPA_ETH_NOAUTO -bool ipa_eth_noauto = true; -#else -bool ipa_eth_noauto; -#endif - -module_param(ipa_eth_noauto, bool, 0444); -MODULE_PARM_DESC(ipa_eth_noauto, - "Disable automatic offload initialization of interfaces"); - -static struct workqueue_struct *ipa_eth_wq; - -bool ipa_eth_is_ready(void) -{ - return test_bit(IPA_ETH_ST_READY, &ipa_eth_state); -} - -bool ipa_eth_all_ready(void) -{ - return ipa_eth_is_ready() && - test_bit(IPA_ETH_ST_API_READY, &ipa_eth_state); -} - -static inline bool present(struct ipa_eth_device *eth_dev) -{ - return - !test_bit(IPA_ETH_DEV_F_REMOVING, ð_dev->flags); -} - -static inline bool reachable(struct ipa_eth_device *eth_dev) -{ - return - present(eth_dev) && - !test_bit(IPA_ETH_DEV_F_RESETTING, ð_dev->flags); -} - -static inline bool offloadable(struct ipa_eth_device *eth_dev) -{ - return - ipa_eth_all_ready() && - reachable(eth_dev) && - !test_bit(IPA_ETH_DEV_F_UNPAIRING, ð_dev->flags); -} - -static inline bool initable(struct ipa_eth_device *eth_dev) -{ - return - offloadable(eth_dev) && - test_bit(IPA_ETH_IF_ST_UP, ð_dev->if_state) && - eth_dev->init; -} - -static inline bool startable(struct ipa_eth_device *eth_dev) -{ - return - initable(eth_dev) && - test_bit(IPA_ETH_IF_ST_LOWER_UP, ð_dev->if_state) && - eth_dev->start; -} - -static int ipa_eth_init_device(struct ipa_eth_device *eth_dev) -{ - int rc; - - if (eth_dev->of_state == IPA_ETH_OF_ST_INITED) - return 0; - - if (eth_dev->of_state != IPA_ETH_OF_ST_DEINITED) - return -EFAULT; - - rc = ipa_eth_offload_init(eth_dev); - if (rc) { - ipa_eth_dev_err(eth_dev, "Failed to init offload"); - eth_dev->of_state = IPA_ETH_OF_ST_ERROR; - return rc; - } - - ipa_eth_dev_log(eth_dev, "Initialized device"); - - eth_dev->of_state = IPA_ETH_OF_ST_INITED; - - return 0; -} - -static int ipa_eth_deinit_device(struct ipa_eth_device *eth_dev) -{ - int rc; - - if (eth_dev->of_state == IPA_ETH_OF_ST_DEINITED) - return 0; - - if (eth_dev->of_state != IPA_ETH_OF_ST_INITED) - return -EFAULT; - - rc = ipa_eth_offload_deinit(eth_dev); - if (rc) { - ipa_eth_dev_err(eth_dev, "Failed to deinit offload"); - eth_dev->of_state = IPA_ETH_OF_ST_ERROR; - return rc; - } - - ipa_eth_dev_log(eth_dev, "Deinitialized device"); - - eth_dev->of_state = IPA_ETH_OF_ST_DEINITED; - - return 0; -} - -static int ipa_eth_start_device(struct ipa_eth_device *eth_dev) -{ - int rc; - - if (eth_dev->of_state == IPA_ETH_OF_ST_STARTED) - return 0; - - if (eth_dev->of_state != IPA_ETH_OF_ST_INITED) - return -EFAULT; - - rc = ipa_eth_offload_start(eth_dev); - if (rc) { - ipa_eth_dev_err(eth_dev, "Failed to start offload"); - eth_dev->of_state = IPA_ETH_OF_ST_ERROR; - return rc; - } - - ipa_eth_dev_log(eth_dev, "Started device"); - - eth_dev->of_state = IPA_ETH_OF_ST_STARTED; - - return 0; -} - -static int ipa_eth_stop_device(struct ipa_eth_device *eth_dev) -{ - int rc; - - if (eth_dev->of_state == IPA_ETH_OF_ST_DEINITED) - return 0; - - if (eth_dev->of_state != IPA_ETH_OF_ST_STARTED) - return -EFAULT; - - rc = ipa_eth_offload_stop(eth_dev); - if (rc) { - ipa_eth_dev_err(eth_dev, "Failed to stop offload"); - eth_dev->of_state = IPA_ETH_OF_ST_ERROR; - return rc; - } - - ipa_eth_dev_log(eth_dev, "Stopped device"); - - eth_dev->of_state = IPA_ETH_OF_ST_INITED; - - return 0; -} - -static void ipa_eth_device_refresh(struct ipa_eth_device *eth_dev) -{ - ipa_eth_dev_log(eth_dev, "Refreshing offload state for device"); - - if (!ipa_eth_offload_device_paired(eth_dev)) { - ipa_eth_dev_log(eth_dev, "Device is not paired. Skipping."); - return; - } - - if (eth_dev->of_state == IPA_ETH_OF_ST_ERROR) { - ipa_eth_dev_err(eth_dev, - "Device in ERROR state, skipping refresh"); - return; - } - - if (initable(eth_dev)) { - if (eth_dev->of_state == IPA_ETH_OF_ST_DEINITED) { - (void) ipa_eth_init_device(eth_dev); - - if (eth_dev->of_state != IPA_ETH_OF_ST_INITED) { - ipa_eth_dev_err(eth_dev, - "Failed to init device"); - return; - } - } - } - - if (startable(eth_dev)) { - (void) ipa_eth_start_device(eth_dev); - - if (eth_dev->of_state != IPA_ETH_OF_ST_STARTED) { - ipa_eth_dev_err(eth_dev, "Failed to start device"); - return; - } - } else { - ipa_eth_dev_log(eth_dev, "Start is disallowed for the device"); - - if (eth_dev->of_state == IPA_ETH_OF_ST_STARTED) { - ipa_eth_stop_device(eth_dev); - - if (eth_dev->of_state != IPA_ETH_OF_ST_INITED) { - ipa_eth_dev_err(eth_dev, - "Failed to stop device"); - return; - } - } - } - - if (!initable(eth_dev)) { - ipa_eth_dev_log(eth_dev, "Init is disallowed for the device"); - - ipa_eth_deinit_device(eth_dev); - - if (eth_dev->of_state != IPA_ETH_OF_ST_DEINITED) { - ipa_eth_dev_err(eth_dev, "Failed to deinit device"); - return; - } - } -} - -static void ipa_eth_device_refresh_work(struct work_struct *work) -{ - struct ipa_eth_device *eth_dev = container_of(work, - struct ipa_eth_device, refresh); - - ipa_eth_device_refresh(eth_dev); -} - -void ipa_eth_device_refresh_sched(struct ipa_eth_device *eth_dev) -{ - queue_work(ipa_eth_wq, ð_dev->refresh); -} - -void ipa_eth_device_refresh_sync(struct ipa_eth_device *eth_dev) -{ - ipa_eth_device_refresh_sched(eth_dev); - flush_work(ð_dev->refresh); -} - -static void ipa_eth_global_refresh_work(struct work_struct *work) -{ - struct ipa_eth_device *eth_dev; - - ipa_eth_log("Performing global refresh"); - - mutex_lock(&ipa_eth_devices_lock); - - if (ipa_eth_all_ready()) { - list_for_each_entry(eth_dev, &ipa_eth_devices, device_list) { - ipa_eth_device_refresh_sched(eth_dev); - } - } - - mutex_unlock(&ipa_eth_devices_lock); -} - -static DECLARE_WORK(ipa_eth_global_refresh, ipa_eth_global_refresh_work); - -void ipa_eth_global_refresh_sched(void) -{ - queue_work(ipa_eth_wq, &ipa_eth_global_refresh); -} - -static void ipa_eth_global_refresh_sync(void) -{ - ipa_eth_global_refresh_sched(); - flush_workqueue(ipa_eth_wq); -} - -static int ipa_eth_device_prepare_reset( - struct ipa_eth_device *eth_dev, void *data) -{ - int rc = 0; - - /* Set the bit so that any in-progress operation can possibly - * return early. - */ - set_bit(IPA_ETH_DEV_F_RESETTING, ð_dev->flags); - - rc = ipa_eth_offload_prepare_reset(eth_dev, data); - - return rc; -} - -static int ipa_eth_device_complete_reset( - struct ipa_eth_device *eth_dev, void *data) -{ - int rc = 0; - - rc = ipa_eth_offload_complete_reset(eth_dev, data); - - /* Clear the flag before unlocking the mutex so that blocked threads - * can resume with the updated value. - */ - clear_bit(IPA_ETH_DEV_F_RESETTING, ð_dev->flags); - - return rc; -} - -static int ipa_eth_device_event_add_macsec( - struct ipa_eth_device *eth_dev, void *data) -{ - struct net_device *net_dev = data; - - ipa_eth_dev_log(eth_dev, - "Network device added MACSec interface %s", net_dev->name); - - return 0; -} - -static int ipa_eth_device_event_del_macsec( - struct ipa_eth_device *eth_dev, void *data) -{ - struct net_device *net_dev = data; - - ipa_eth_dev_log(eth_dev, - "Network device removed MACSec interface %s", net_dev->name); - - return 0; -} - -/** - * ipa_eth_device_notify() - Notifies a device event to the offload sub-system - * @eth_dev: Device for which the event is generated - * @event: Device event - * @data: Event specific data, if required - * - * Return: 0 on success, non-zero otherwise - */ -int ipa_eth_device_notify(struct ipa_eth_device *eth_dev, - enum ipa_eth_device_event event, void *data) -{ - int rc = -EINVAL; - - ipa_eth_dev_log(eth_dev, - "Received notificaiton %s", ipa_eth_device_event_name(event)); - - switch (event) { - case IPA_ETH_DEV_RESET_PREPARE: - rc = ipa_eth_device_prepare_reset(eth_dev, data); - break; - case IPA_ETH_DEV_RESET_COMPLETE: - rc = ipa_eth_device_complete_reset(eth_dev, data); - break; - case IPA_ETH_DEV_ADD_MACSEC_IF: - rc = ipa_eth_device_event_add_macsec(eth_dev, data); - break; - case IPA_ETH_DEV_DEL_MACSEC_IF: - rc = ipa_eth_device_event_del_macsec(eth_dev, data); - break; - default: - ipa_eth_dev_log(eth_dev, "Unknown event %d", event); - break; - } - - if (rc) - ipa_eth_dev_err(eth_dev, "Failed to handle notification"); - - return rc; -} -EXPORT_SYMBOL(ipa_eth_device_notify); - -static void ipa_eth_dev_start_timer_cb(struct timer_list *t) -{ - struct ipa_eth_device *eth_dev = from_timer(eth_dev, t, start_timer); - - ipa_eth_dev_log(eth_dev, "Start timer has fired"); - - /* Do not start offload if user disabled timer in between */ - if (present(eth_dev) && eth_dev->start_on_timeout) { - eth_dev->start = true; - - ipa_eth_device_refresh_sched(eth_dev); - } -} - -static int ipa_eth_panic_notifier(struct notifier_block *nb, - unsigned long event, void *ptr) -{ - struct ipa_eth_device_private *ipa_priv = container_of(nb, - struct ipa_eth_device_private, panic_nb); - struct ipa_eth_device *eth_dev = ipa_priv->eth_dev; - - if (!reachable(eth_dev)) - return -ENODEV; - - ipa_eth_net_save_regs(eth_dev); - ipa_eth_offload_save_regs(eth_dev); - - return NOTIFY_DONE; -} - -/* During a system suspend, suspend-prepare callbacks are called first by the - * PM framework before freezing processes. This gives us an early opportunity - * to abort the suspend and reduces the chances for device resumes at a later - * stage. - */ -static int ipa_eth_pm_notifier_event_suspend_prepare( - struct ipa_eth_device *eth_dev) -{ - /* We look for any ethernet rx activity since previous attempt to - * suspend, and if such an activity is found, we hold a wake lock - * for WAKE_TIME_MS time. Any Rx packets received beyond this point - * should cause a wake up due to the Rx interrupt. In rare cases - * where Rx interrupt was already processed and NAPI poll is yet to - * complete, we perform a second check in the suspend late handler - * and reverts the device suspension by aborting the system suspend. - */ - if (ipa_eth_net_check_active(eth_dev)) { - pr_info("%s: %s is active, preventing suspend for %u ms\n", - IPA_ETH_SUBSYS, eth_dev->net_dev->name, - IPA_ETH_WAKE_TIME_MS); - pm_wakeup_dev_event(eth_dev->dev, IPA_ETH_WAKE_TIME_MS, false); - return NOTIFY_BAD; - } - - return NOTIFY_OK; -} - -static int ipa_eth_pm_notifier_cb(struct notifier_block *nb, - unsigned long pm_event, void *unused) -{ - struct ipa_eth_device_private *ipa_priv = container_of(nb, - struct ipa_eth_device_private, pm_nb); - struct ipa_eth_device *eth_dev = ipa_priv->eth_dev; - - ipa_eth_dbg("PM notifier called for event %s (0x%04lx)", - ipa_eth_pm_notifier_event_name(pm_event), pm_event); - - switch (pm_event) { - case PM_SUSPEND_PREPARE: - return ipa_eth_pm_notifier_event_suspend_prepare(eth_dev); - default: - break; - } - - return NOTIFY_DONE; -} - -/* - * ipa_eth_alloc_device() - Allocate an ipa_eth_device structure and initialize - * all common fields - * @dev: struct device pointer of the net device - * @nd: Offload sub-system net driver structure - * - * This API is meant to be called by the bus layer (ex. ipa_eth_pci.c) to - * allocate an ipa_eth_device object, initialize it with bus specific values - * and register the discovered device using ipa_eth_register_device() API. - * - * Return: 0 on success, non-zero otherwise - */ -struct ipa_eth_device *ipa_eth_alloc_device( - struct device *dev, - struct ipa_eth_net_driver *nd) -{ - struct ipa_eth_device *eth_dev; - struct ipa_eth_device_private *ipa_priv; - - if (!dev || !nd) { - ipa_eth_err("Invalid device or net driver"); - return NULL; - } - - eth_dev = kzalloc(sizeof(*eth_dev), GFP_KERNEL); - if (!eth_dev) - return NULL; - - ipa_priv = kzalloc(sizeof(*ipa_priv), GFP_KERNEL); - if (!ipa_priv) { - kfree(eth_dev); - return NULL; - } - - eth_dev->dev = dev; - eth_dev->nd = nd; - - eth_dev->of_state = IPA_ETH_OF_ST_DEINITED; - INIT_WORK(ð_dev->refresh, ipa_eth_device_refresh_work); - - INIT_LIST_HEAD(ð_dev->rx_channels); - INIT_LIST_HEAD(ð_dev->tx_channels); - - timer_setup(ð_dev->start_timer, ipa_eth_dev_start_timer_cb, 0); - - eth_dev->init = eth_dev->start = !ipa_eth_noauto; - - /* Initialize private data */ - - ipa_priv->eth_dev = eth_dev; - - INIT_LIST_HEAD(&ipa_priv->upper_devices); - - ipa_priv->pm_nb.notifier_call = ipa_eth_pm_notifier_cb; - ipa_priv->panic_nb.notifier_call = ipa_eth_panic_notifier; - - eth_dev->ipa_priv = ipa_priv; - - return eth_dev; -} - -/* - * ipa_eth_free_device() - Free an ipa_eth_device object previously allocated - * using ipa_eth_alloc_device() - * @eth_dev: ipa_eth_device object to be freed - */ -void ipa_eth_free_device(struct ipa_eth_device *eth_dev) -{ - kzfree(eth_dev->ipa_priv); - kzfree(eth_dev); -} - -/* - * ipa_eth_register_device() - Register a new device with offload sub-system - * @eth_dev: Eth device to register - * - * This API is meant to be called by the bus layer (ex. ipa_eth_pci.c) to - * register an ipa_eth_device object linked to a newly discovered device. The - * @eth_dev object is expected to be allocated using ipa_eth_alloc_device() API. - * - * Return: 0 on success, non-zero otherwise - */ -int ipa_eth_register_device(struct ipa_eth_device *eth_dev) -{ - int rc; - struct ipa_eth_device_private *ipa_priv = eth_dev->ipa_priv; - - ipa_eth_dev_log(eth_dev, "Registering new device"); - - (void) atomic_notifier_chain_register( - &panic_notifier_list, &ipa_priv->panic_nb); - - rc = ipa_eth_net_open_device(eth_dev); - if (rc) { - ipa_eth_dev_err(eth_dev, "Failed to open network device"); - return rc; - } - - /* Add new device to the devices list before pairing with offload - * driver in order to synchronize with offload driver deregistration. - */ - mutex_lock(&ipa_eth_devices_lock); - list_add(ð_dev->device_list, &ipa_eth_devices); - mutex_unlock(&ipa_eth_devices_lock); - - (void) register_pm_notifier(&ipa_priv->pm_nb); - - ipa_eth_dev_log(eth_dev, "Registered new device"); - - rc = ipa_eth_offload_pair_device(eth_dev); - if (rc) - ipa_eth_dev_log(eth_dev, "Failed to pair device. Deferring."); - - ipa_eth_debugfs_add_device(eth_dev); - - return 0; -} - -/* - * ipa_eth_unpair_device() - Unpair a device from its offload driver - * @eth_dev: Device to unpair - * - * It is safe to call this function in parallel from ipa_eth_unregister_device() - * and ipa_eth_unregister_offload_driver() as the necessary locking is placed - * in ipa_eth_offload_unpair_device(). - * - * Return: 0 on success, non-zero otherwise - */ - -static void ipa_eth_unpair_device(struct ipa_eth_device *eth_dev) -{ - ipa_eth_dev_log(eth_dev, "Unpairing device"); - - /* Set UNPAIRING flag to prevent offload init or start */ - set_bit(IPA_ETH_DEV_F_UNPAIRING, ð_dev->flags); - - /* Wait for next refresh to tear down offload path */ - ipa_eth_device_refresh_sync(eth_dev); - - ipa_eth_offload_unpair_device(eth_dev); - - clear_bit(IPA_ETH_DEV_F_UNPAIRING, ð_dev->flags); - - ipa_eth_dev_log(eth_dev, "Unpaired device"); -} - -/* - * ipa_eth_unregister_device() - Unregister a device from the offload sub-system - * - * This API is meant to be called by the bus layer when a device is removed from - * the system. The @eth_dev object itself need to be freed separately by calling - * the ipa_eth_free_device() API. - */ -void ipa_eth_unregister_device(struct ipa_eth_device *eth_dev) -{ - struct ipa_eth_device_private *ipa_priv = eth_dev->ipa_priv; - - ipa_eth_dev_log(eth_dev, "Unregistering device"); - - /* Set REMOVING flag so that device refreshes do not happen */ - set_bit(IPA_ETH_DEV_F_REMOVING, ð_dev->flags); - - /* Remove debugfs node to prevent any new 'start_timer' to - * be started. - */ - ipa_eth_debugfs_remove_device(eth_dev); - - del_timer_sync(ð_dev->start_timer); - - /* Unpair the device before removing from devices list so that - * unregister_offload_driver() does not skip this device. - */ - ipa_eth_unpair_device(eth_dev); - - (void) unregister_pm_notifier(&ipa_priv->pm_nb); - - /* Remove from devices list so that no new global refreshes are - * scheduled. - */ - mutex_lock(&ipa_eth_devices_lock); - list_del(ð_dev->device_list); - mutex_unlock(&ipa_eth_devices_lock); - - /* Closing device from ipa_eth_net should prevent further events to be - * received from either network driver via ipa_eth_device_notify() or - * the network sub-system via netdevice notifier. - */ - ipa_eth_net_close_device(eth_dev); - - /* No more refresh work would be queued beyond this point. Flush out - * any pending refresh work items. - */ - cancel_work_sync(ð_dev->refresh); - - /* By this time we would have processed all pending events for the - * device and removed/disabled all event sources. - */ - clear_bit(IPA_ETH_DEV_F_REMOVING, ð_dev->flags); - - (void) atomic_notifier_chain_unregister( - &panic_notifier_list, &ipa_priv->panic_nb); - - ipa_eth_dev_log(eth_dev, "Device unregistered"); -} - -/** - * ipa_eth_register_net_driver() - Register a network driver with the offload - * subsystem - * @nd: Network driver to register - * - * Return: 0 on success, negative errno otherwise - */ -int ipa_eth_register_net_driver(struct ipa_eth_net_driver *nd) -{ - return ipa_eth_net_register_driver(nd); -} -EXPORT_SYMBOL(ipa_eth_register_net_driver); - -/** - * ipa_eth_unregister_net_driver() - Unregister a network driver - * @nd: Network driver to unregister - */ -void ipa_eth_unregister_net_driver(struct ipa_eth_net_driver *nd) -{ - ipa_eth_net_unregister_driver(nd); -} -EXPORT_SYMBOL(ipa_eth_unregister_net_driver); - - -/** - * ipa_eth_register_offload_driver - Register an offload driver with the offload - * subsystem - * @nd: Offload driver to register - * - * Return: 0 on success, negative errno otherwise - */ -int ipa_eth_register_offload_driver(struct ipa_eth_offload_driver *od) -{ - int rc; - struct ipa_eth_device *eth_dev; - - rc = ipa_eth_offload_register_driver(od); - if (rc) { - ipa_eth_err("Failed to register offload driver %s", od->name); - return rc; - } - - ipa_eth_log("Registered offload driver %s", od->name); - - mutex_lock(&ipa_eth_devices_lock); - - list_for_each_entry(eth_dev, &ipa_eth_devices, device_list) { - if (!ipa_eth_offload_pair_device(eth_dev)) - ipa_eth_device_refresh_sched(eth_dev); - } - - mutex_unlock(&ipa_eth_devices_lock); - - return 0; -} -EXPORT_SYMBOL(ipa_eth_register_offload_driver); - -/** - * ipa_eth_unregister_offload_driver() - Unregister an offload driver - * @nd: Offload driver to unregister - */ -void ipa_eth_unregister_offload_driver(struct ipa_eth_offload_driver *od) -{ - struct ipa_eth_device *eth_dev; - - /* Unregister offload driver first from ipa_eth_offload so that any - * new device registration will not be able to pair with the driver. - */ - ipa_eth_offload_unregister_driver(od); - - mutex_lock(&ipa_eth_devices_lock); - - list_for_each_entry(eth_dev, &ipa_eth_devices, device_list) - if (eth_dev->od == od) - ipa_eth_unpair_device(eth_dev); - - mutex_unlock(&ipa_eth_devices_lock); -} -EXPORT_SYMBOL(ipa_eth_unregister_offload_driver); - -static void ipa_eth_api_ready_cb(void *user_data) -{ - ipa_eth_log("IPA API is ready"); - - set_bit(IPA_ETH_ST_API_READY, &ipa_eth_state); - ipa_eth_global_refresh_sched(); -} - -static struct ipa_eth_ready eth_api_rdy = { - .notify = ipa_eth_api_ready_cb, -}; - -int ipa_eth_init(void) -{ - int rc; - unsigned int wq_flags = WQ_UNBOUND | WQ_MEM_RECLAIM; - - /* Freeze the workqueue so that a refresh will not happen while the - * device is suspended as the suspend operation itself can generate - * Netlink events. - */ - wq_flags |= WQ_FREEZABLE; - - rc = ipa_eth_ipc_log_init(); - if (rc) { - ipa_eth_err("Failed to initialize IPC logging"); - goto err_ipclog; - } - - ipa_eth_dbg("Initializing IPA Ethernet Offload Sub-System"); - - ipa_eth_wq = alloc_workqueue("ipa_eth", wq_flags, 0); - if (!ipa_eth_wq) { - ipa_eth_err("Failed to alloc workqueue"); - goto err_wq; - } - - rc = ipa_eth_bus_modinit(); - if (rc) { - ipa_eth_err("Failed to initialize bus"); - goto err_bus; - } - - rc = ipa_eth_debugfs_init(); - if (rc) { - ipa_eth_err("Failed to initialize debugfs"); - goto err_dbgfs; - } - - rc = ipa_eth_register_ready_cb(ð_api_rdy); - if (rc) { - ipa_eth_err("Failed to register ready cb with IPA driver"); - goto err_reg_rdy; - } - - set_bit(IPA_ETH_ST_READY, &ipa_eth_state); - - ipa_eth_log("Offload sub-system init is complete"); - - ipa_eth_global_refresh_sched(); - - return 0; - -err_reg_rdy: - ipa_eth_debugfs_cleanup(); -err_dbgfs: - ipa_eth_bus_modexit(); -err_bus: - destroy_workqueue(ipa_eth_wq); - ipa_eth_wq = NULL; -err_wq: - ipa_eth_ipc_log_cleanup(); -err_ipclog: - return rc; -} -module_init(ipa_eth_init); - -void ipa_eth_exit(void) -{ - ipa_eth_dbg("De-initializing IPA Ethernet Offload Sub-System"); - - /* Clear READY bit so that all active offload paths can start - * deinitializing. - */ - clear_bit(IPA_ETH_ST_READY, &ipa_eth_state); - - ipa_eth_unregister_ready_cb(ð_api_rdy); - - ipa_eth_debugfs_cleanup(); - - /* Wait for all offload paths to deinit. Although the chances for any - * such path to exist is quite low when IPA is a platform device, we - * still need to make sure we wait here before destroying the work - * queue. - */ - ipa_eth_global_refresh_sync(); - - ipa_eth_bus_modexit(); - - destroy_workqueue(ipa_eth_wq); - ipa_eth_wq = NULL; - - ipa_eth_ipc_log_cleanup(); -} -module_exit(ipa_eth_exit); diff --git a/drivers/platform/msm/ethernet/ipa_eth_bus.c b/drivers/platform/msm/ethernet/ipa_eth_bus.c deleted file mode 100644 index fa95f8f21d88..000000000000 --- a/drivers/platform/msm/ethernet/ipa_eth_bus.c +++ /dev/null @@ -1,136 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Copyright (c) 2019-2020, The Linux Foundation. All rights reserved - */ - -#include - -#include "ipa_eth_i.h" - -static bool ipa_eth_bus_is_ready; - -struct ipa_eth_bus_map { - struct bus_type *bus; - struct ipa_eth_bus *eth_bus; - int (*modinit)(void); - void (*modexit)(void); -} static const bus_map[] = { - { - &pci_bus_type, - &ipa_eth_pci_bus, - &ipa_eth_pci_modinit, - &ipa_eth_pci_modexit - }, - {}, -}; - -static struct ipa_eth_bus *lookup_eth_bus(struct bus_type *bus) -{ - const struct ipa_eth_bus_map *map; - - for (map = bus_map; map->bus != NULL; map++) { - if (map->bus == bus) - return map->eth_bus; - } - - return NULL; -} - -int ipa_eth_bus_register_driver(struct ipa_eth_net_driver *nd) -{ - struct ipa_eth_bus *eth_bus; - - eth_bus = lookup_eth_bus(nd->bus); - if (!eth_bus) { - ipa_eth_err("Unsupported bus %s", nd->bus->name); - return -EOPNOTSUPP; - } - - return eth_bus->register_driver(nd); -} - -void ipa_eth_bus_unregister_driver(struct ipa_eth_net_driver *nd) -{ - struct ipa_eth_bus *eth_bus = lookup_eth_bus(nd->bus); - - if (!eth_bus) { - ipa_eth_bug("Failed to lookup eth_bus for %s", nd->bus->name); - return; - } - - eth_bus->unregister_driver(nd); -} - -int ipa_eth_bus_enable_pc(struct ipa_eth_device *eth_dev) -{ - struct ipa_eth_net_driver *nd = eth_dev->nd; - struct ipa_eth_bus *eth_bus = lookup_eth_bus(nd->bus); - - if (!eth_bus) { - ipa_eth_dev_bug(eth_dev, "Failed to lookup eth_bus"); - return -EFAULT; - } - - if (eth_bus->enable_pc) - return eth_bus->enable_pc(eth_dev); - - return -EFAULT; -} - -int ipa_eth_bus_disable_pc(struct ipa_eth_device *eth_dev) -{ - struct ipa_eth_net_driver *nd = eth_dev->nd; - struct ipa_eth_bus *eth_bus = lookup_eth_bus(nd->bus); - - if (!eth_bus) { - ipa_eth_dev_bug(eth_dev, "Failed to lookup eth_bus"); - return -EFAULT; - } - - if (eth_bus->disable_pc) - return eth_bus->disable_pc(eth_dev); - - return -EFAULT; -} - -int ipa_eth_bus_modinit(void) -{ - int rc; - const struct ipa_eth_bus_map *map; - - /* initialize all registered busses */ - for (rc = 0, map = bus_map; map->bus != NULL; map++) - rc |= map->modinit(); - - if (rc) { - ipa_eth_err("Failed to initialize one or more busses"); - goto err_init; - } - - ipa_eth_bus_is_ready = true; - - ipa_eth_log("Offload sub-system bus module init is complete"); - - return 0; - -err_init: - for (map = bus_map; map->bus != NULL; map++) - map->modexit(); - - return rc; -} - -void ipa_eth_bus_modexit(void) -{ - const struct ipa_eth_bus_map *map; - - ipa_eth_log("De-initing offload sub-system bus module"); - - if (!ipa_eth_bus_is_ready) - return; - - ipa_eth_bus_is_ready = false; - - for (map = bus_map; map->bus != NULL; map++) - map->modexit(); -} diff --git a/drivers/platform/msm/ethernet/ipa_eth_debugfs.c b/drivers/platform/msm/ethernet/ipa_eth_debugfs.c deleted file mode 100644 index 6a445bb4668a..000000000000 --- a/drivers/platform/msm/ethernet/ipa_eth_debugfs.c +++ /dev/null @@ -1,302 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Copyright (c) 2019-2020, The Linux Foundation. All rights reserved - */ - -#include - -#include "ipa_eth_debugfs.h" - -static struct dentry *ipa_eth_debugfs; - -static struct dentry *ipa_eth_devices_debugfs; -static struct dentry *ipa_eth_drivers_debugfs; - -static struct dentry *ipa_eth_bus_debugfs; -static struct dentry *ipa_eth_pci_debugfs; -static struct dentry *ipa_eth_offload_debugfs; - -static ssize_t ipa_eth_dev_write_init(struct file *file, - const char __user *user_buf, - size_t count, loff_t *ppos) -{ - ssize_t ret = debugfs_write_file_bool(file, user_buf, count, ppos); - struct ipa_eth_device *eth_dev = container_of(file->private_data, - struct ipa_eth_device, - init); - - ipa_eth_device_refresh_sync(eth_dev); - - return ret; -} - -static const struct file_operations fops_eth_dev_init = { - .read = debugfs_read_file_bool, - .write = ipa_eth_dev_write_init, - .open = simple_open, - .llseek = default_llseek, -}; - -static ssize_t ipa_eth_dev_write_start(struct file *file, - const char __user *user_buf, - size_t count, loff_t *ppos) -{ - ssize_t ret = debugfs_write_file_bool(file, user_buf, count, ppos); - struct ipa_eth_device *eth_dev = container_of(file->private_data, - struct ipa_eth_device, - start); - - /* Set/reset timer to automatically start offload after the timeout - * specified in eth_dev->start_on_timeout (milliseconds) expires. - */ - if (!eth_dev->start && eth_dev->start_on_timeout) - mod_timer(ð_dev->start_timer, - jiffies + msecs_to_jiffies(eth_dev->start_on_timeout)); - - ipa_eth_device_refresh_sync(eth_dev); - - return ret; -} - -static const struct file_operations fops_eth_dev_start = { - .read = debugfs_read_file_bool, - .write = ipa_eth_dev_write_start, - .open = simple_open, - .llseek = default_llseek, -}; - -static ssize_t eth_dev_stats_print_one(char *buf, const size_t size, - const char *dir, const char *link, - struct ipa_eth_offload_link_stats *stats) -{ - return scnprintf(buf, size, - "%10s%10s%10s%10llu%10llu%10llu%10llu\n", - dir, link, (stats->valid ? "yes" : "no"), - stats->events, stats->frames, - stats->packets, stats->octets); -} - -static ssize_t eth_dev_stats_print(char *buf, const size_t size, - struct ipa_eth_device *eth_dev) -{ - ssize_t n = 0; - struct ipa_eth_offload_stats stats; - - if (!eth_dev->od->ops->get_stats) - return scnprintf(buf, size - n, "Not supported\n"); - - memset(&stats, 0, sizeof(stats)); - - if (eth_dev->od->ops->get_stats(eth_dev, &stats)) - return scnprintf(buf, size - n, "Operation failed\n"); - - n += scnprintf(&buf[n], size - n, - "%10s%10s%10s%10s%10s%10s%10s\n", - "Dir", "Link", "Valid", - "Events", "Frames", "Packets", "Octets"); - - n += eth_dev_stats_print_one(&buf[n], size - n, - "rx", "ndev", &stats.rx.ndev); - n += eth_dev_stats_print_one(&buf[n], size - n, - "rx", "host", &stats.rx.host); - n += eth_dev_stats_print_one(&buf[n], size - n, - "rx", "uc", &stats.rx.uc); - n += eth_dev_stats_print_one(&buf[n], size - n, - "rx", "gsi", &stats.rx.gsi); - n += eth_dev_stats_print_one(&buf[n], size - n, - "rx", "ipa", &stats.rx.ipa); - - n += scnprintf(&buf[n], size - n, "\n"); - - n += eth_dev_stats_print_one(&buf[n], size - n, - "tx", "ndev", &stats.tx.ndev); - n += eth_dev_stats_print_one(&buf[n], size - n, - "tx", "host", &stats.tx.host); - n += eth_dev_stats_print_one(&buf[n], size - n, - "tx", "uc", &stats.tx.uc); - n += eth_dev_stats_print_one(&buf[n], size - n, - "tx", "gsi", &stats.tx.gsi); - n += eth_dev_stats_print_one(&buf[n], size - n, - "tx", "ipa", &stats.tx.ipa); - - return n; -} - -static ssize_t eth_dev_stats_read(struct file *file, char __user *user_buf, - size_t count, loff_t *ppos) -{ - ssize_t n = 0, size = 2048; - char *buf = NULL; - struct ipa_eth_device *eth_dev = file->private_data; - - buf = kzalloc(size, GFP_KERNEL); - if (buf == NULL) - return 0; - - n = eth_dev_stats_print(buf, size, eth_dev); - n = simple_read_from_buffer(user_buf, count, ppos, buf, n); - - kfree(buf); - - return n; -} - -static const struct file_operations fops_eth_dev_stats = { - .read = eth_dev_stats_read, - .open = simple_open, - .llseek = default_llseek, -}; - -int ipa_eth_debugfs_add_device(struct ipa_eth_device *eth_dev) -{ - eth_dev->debugfs = debugfs_create_dir(eth_dev->net_dev->name, - ipa_eth_devices_debugfs); - if (IS_ERR_OR_NULL(eth_dev->debugfs)) { - ipa_eth_dev_err(eth_dev, "Failed to create debugfs root"); - return -EFAULT; - } - - debugfs_create_file("init", 0644, eth_dev->debugfs, ð_dev->init, - &fops_eth_dev_init); - - debugfs_create_file("start", 0644, eth_dev->debugfs, ð_dev->start, - &fops_eth_dev_start); - - debugfs_create_bool("start_on_wakeup", 0644, - eth_dev->debugfs, ð_dev->start_on_wakeup); - - debugfs_create_bool("start_on_resume", 0644, - eth_dev->debugfs, ð_dev->start_on_resume); - - debugfs_create_u32("start_on_timeout", 0644, eth_dev->debugfs, - ð_dev->start_on_timeout); - - debugfs_create_file("stats", 0644, eth_dev->debugfs, eth_dev, - &fops_eth_dev_stats); - - return 0; -} - -void ipa_eth_debugfs_remove_device(struct ipa_eth_device *eth_dev) -{ - debugfs_remove_recursive(eth_dev->debugfs); - eth_dev->debugfs = NULL; -} - -int ipa_eth_debugfs_add_offload_driver(struct ipa_eth_offload_driver *od) -{ - if (!od->debugfs && ipa_eth_offload_debugfs) { - od->debugfs = - debugfs_create_dir(od->name, ipa_eth_offload_debugfs); - if (!IS_ERR_OR_NULL(od->debugfs)) - return 0; - } - - return -EFAULT; -} - -void ipa_eth_debugfs_remove_offload_driver(struct ipa_eth_offload_driver *od) -{ - debugfs_remove_recursive(od->debugfs); - od->debugfs = NULL; -} - -static ssize_t eth_dev_ready_read(struct file *file, char __user *user_buf, - size_t count, loff_t *ppos) -{ - char *buf; - ssize_t n = 0, size = 128; - - buf = kzalloc(size, GFP_KERNEL); - if (buf == NULL) - return 0; - - n += scnprintf(&buf[n], size - n, "Offload Sub-system: %s\n", - test_bit(IPA_ETH_ST_READY, &ipa_eth_state) ? - "Ready" : "Not Ready"); - - n += scnprintf(&buf[n], size - n, "IPA API: %s\n", - test_bit(IPA_ETH_ST_API_READY, &ipa_eth_state) ? - "Ready" : "Not Ready"); - - n += scnprintf(&buf[n], size - n, "ALL: %s\n", - ipa_eth_all_ready() ? "Ready" : "Not Ready"); - - n = simple_read_from_buffer(user_buf, count, ppos, buf, n); - - kfree(buf); - - return n; -} - -static const struct file_operations fops_eth_dev_ready = { - .read = eth_dev_ready_read, - .open = simple_open, - .llseek = default_llseek, -}; - -int ipa_eth_debugfs_init(void) -{ - ipa_eth_debugfs = - debugfs_create_dir("ethernet", NULL); - if (IS_ERR_OR_NULL(ipa_eth_debugfs)) { - ipa_eth_log("Unable to create debugfs root"); - goto err_exit; - } - - (void) debugfs_create_file("ready", 0644, ipa_eth_debugfs, NULL, - &fops_eth_dev_ready); - - (void) debugfs_create_bool("no_auto", 0644, - ipa_eth_debugfs, &ipa_eth_noauto); - - (void) debugfs_create_bool("ipc_logdbg", 0644, - ipa_eth_debugfs, &ipa_eth_ipc_logdbg); - - ipa_eth_devices_debugfs = - debugfs_create_dir("devices", ipa_eth_debugfs); - if (IS_ERR_OR_NULL(ipa_eth_devices_debugfs)) { - ipa_eth_log("Unable to create debugfs root for devices"); - goto err_exit; - } - - ipa_eth_drivers_debugfs = - debugfs_create_dir("drivers", ipa_eth_debugfs); - if (IS_ERR_OR_NULL(ipa_eth_drivers_debugfs)) { - ipa_eth_log("Unable to create debugfs root for drivers"); - goto err_exit; - } - - ipa_eth_bus_debugfs = - debugfs_create_dir("bus", ipa_eth_drivers_debugfs); - if (IS_ERR_OR_NULL(ipa_eth_bus_debugfs)) { - ipa_eth_log("Unable to create debugfs root for bus"); - goto err_exit; - } - - ipa_eth_pci_debugfs = debugfs_create_dir("pci", ipa_eth_bus_debugfs); - if (IS_ERR_OR_NULL(ipa_eth_pci_debugfs)) { - ipa_eth_log("Unable to create debugfs root for pci"); - goto err_exit; - } - - ipa_eth_offload_debugfs = - debugfs_create_dir("offload", ipa_eth_drivers_debugfs); - if (IS_ERR_OR_NULL(ipa_eth_offload_debugfs)) { - ipa_eth_log("Unable to create debugfs root for offload"); - goto err_exit; - } - - ipa_eth_log("Debugfs root is initialized"); - - return 0; - -err_exit: - ipa_eth_debugfs_cleanup(); - return -EFAULT; -} - -void ipa_eth_debugfs_cleanup(void) -{ - debugfs_remove_recursive(ipa_eth_debugfs); -} diff --git a/drivers/platform/msm/ethernet/ipa_eth_debugfs.h b/drivers/platform/msm/ethernet/ipa_eth_debugfs.h deleted file mode 100644 index b7d65efc7005..000000000000 --- a/drivers/platform/msm/ethernet/ipa_eth_debugfs.h +++ /dev/null @@ -1,20 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -/* - * Copyright (c) 2019-2020, The Linux Foundation. All rights reserved - */ - -#ifndef _IPA_ETH_DEBUGFS_H_ -#define _IPA_ETH_DEBUGFS_H_ - -#include "ipa_eth_i.h" - -int ipa_eth_debugfs_add_device(struct ipa_eth_device *eth_dev); -void ipa_eth_debugfs_remove_device(struct ipa_eth_device *eth_dev); - -int ipa_eth_debugfs_add_offload_driver(struct ipa_eth_offload_driver *od); -void ipa_eth_debugfs_remove_offload_driver(struct ipa_eth_offload_driver *od); - -int ipa_eth_debugfs_init(void); -void ipa_eth_debugfs_cleanup(void); - -#endif /* _IPA_ETH_DEBUGFS_H_ */ diff --git a/drivers/platform/msm/ethernet/ipa_eth_i.h b/drivers/platform/msm/ethernet/ipa_eth_i.h deleted file mode 100644 index 6381b71e1553..000000000000 --- a/drivers/platform/msm/ethernet/ipa_eth_i.h +++ /dev/null @@ -1,230 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -/* - * Copyright (c) 2019-2020, The Linux Foundation. All rights reserved - */ - -#ifndef _IPA_ETH_I_H_ -#define _IPA_ETH_I_H_ - -#include - -#define IPA_ETH_NET_DRIVER -#define IPA_ETH_OFFLOAD_DRIVER -#include - -#include "ipa_eth_debugfs.h" - -/* Time to remain awake after a suspend abort due to NIC activity */ -#define IPA_ETH_WAKE_TIME_MS 500 - -/* Time for NIC HW to settle down (ex. receive link interrupt) after a resume */ -#define IPA_ETH_RESUME_SETTLE_MS 2000 - -#define IPA_ETH_PFDEV NULL -#define IPA_ETH_SUBSYS "IOSS" - -#define IPA_ETH_NET_DEVICE_MAX_EVENTS (NETDEV_CHANGE_TX_QUEUE_LEN + 1) -#define IPA_ETH_PM_NOTIFIER_MAX_EVENTS (PM_POST_RESTORE + 1) - -enum ipa_eth_states { - IPA_ETH_ST_READY, - IPA_ETH_ST_API_READY, - IPA_ETH_ST_MAX, -}; - -enum ipa_eth_dev_flags { - IPA_ETH_DEV_F_REMOVING, - IPA_ETH_DEV_F_UNPAIRING, - IPA_ETH_DEV_F_RESETTING, - IPA_ETH_DEV_F_COUNT, -}; - -#define ipa_eth_err(fmt, args...) \ - do { \ - dev_err(IPA_ETH_PFDEV, \ - IPA_ETH_SUBSYS " %s:%d ERROR: " fmt "\n", \ - __func__, __LINE__, ## args); \ - ipa_eth_ipc_log("ERROR: " fmt, ## args); \ - } while (0) - -#define ipa_eth_bug(fmt, args...) \ - do { \ - ipa_eth_err("BUG: " fmt, ## args); \ - dump_stack(); \ - } while (0) - -#define ipa_eth_log(fmt, args...) \ - do { \ - dev_dbg(IPA_ETH_PFDEV, \ - IPA_ETH_SUBSYS " %s:%d " fmt "\n", \ - __func__, __LINE__, ## args); \ - ipa_eth_ipc_log(fmt, ## args); \ - } while (0) - -#define ipa_eth_dbg(fmt, args...) \ - do { \ - dev_dbg(IPA_ETH_PFDEV, \ - IPA_ETH_SUBSYS " %s:%d " fmt "\n", \ - __func__, __LINE__, ## args); \ - ipa_eth_ipc_dbg("DEBUG: " fmt, ## args); \ - } while (0) - -#define ipa_eth_dev_err(edev, fmt, args...) \ - ipa_eth_err("(%s) " fmt, ((edev && edev->net_dev) ? \ - edev->net_dev->name : ""), \ - ## args) - -#define ipa_eth_dev_bug(edev, fmt, args...) \ - ipa_eth_bug("(%s) " fmt, ((edev && edev->net_dev) ? \ - edev->net_dev->name : ""), \ - ## args) - -#define ipa_eth_dev_dbg(edev, fmt, args...) \ - ipa_eth_dbg("(%s) " fmt, ((edev && edev->net_dev) ? \ - edev->net_dev->name : ""), \ - ## args) - -#define ipa_eth_dev_log(edev, fmt, args...) \ - ipa_eth_log("(%s) " fmt, ((edev && edev->net_dev) ? \ - edev->net_dev->name : ""), \ - ## args) - -struct ipa_eth_upper_device { - struct list_head upper_list; - - struct net_device *net_dev; - struct ipa_eth_device *eth_dev; - - bool up; /* interface is up */ - bool registered; /* registered with IPA */ -}; - -struct ipa_eth_device_private { - struct ipa_eth_device *eth_dev; - - struct list_head upper_devices; - - unsigned long assume_active; - struct rtnl_link_stats64 last_rtnl_stats; - - struct notifier_block pm_nb; - struct notifier_block panic_nb; -}; - -#define eth_dev_priv(eth_dev) \ - ((struct ipa_eth_device_private *)((eth_dev)->ipa_priv)) - -struct ipa_eth_bus { - struct list_head bus_list; - - struct bus_type *bus; - - int (*register_driver)(struct ipa_eth_net_driver *nd); - void (*unregister_driver)(struct ipa_eth_net_driver *nd); - - int (*enable_pc)(struct ipa_eth_device *eth_dev); - int (*disable_pc)(struct ipa_eth_device *eth_dev); -}; - -extern struct ipa_eth_bus ipa_eth_pci_bus; - -extern unsigned long ipa_eth_state; -extern bool ipa_eth_noauto; -extern bool ipa_eth_ipc_logdbg; - -bool ipa_eth_is_ready(void); -bool ipa_eth_all_ready(void); - -static inline void ipa_eth_dev_assume_active_ms( - struct ipa_eth_device *eth_dev, - unsigned int msec) -{ - eth_dev_priv(eth_dev)->assume_active += - DIV_ROUND_UP(msec, IPA_ETH_WAKE_TIME_MS); - pm_system_wakeup(); -} - -static inline void ipa_eth_dev_assume_active_inc( - struct ipa_eth_device *eth_dev, - unsigned int count) -{ - eth_dev_priv(eth_dev)->assume_active += count; - pm_system_wakeup(); -} - -static inline void ipa_eth_dev_assume_active_dec( - struct ipa_eth_device *eth_dev, - unsigned int count) -{ - if (eth_dev_priv(eth_dev)->assume_active > count) - eth_dev_priv(eth_dev)->assume_active -= count; - else - eth_dev_priv(eth_dev)->assume_active = 0; -} - -struct ipa_eth_device *ipa_eth_alloc_device( - struct device *dev, - struct ipa_eth_net_driver *nd); -void ipa_eth_free_device(struct ipa_eth_device *eth_dev); -int ipa_eth_register_device(struct ipa_eth_device *eth_dev); -void ipa_eth_unregister_device(struct ipa_eth_device *eth_dev); - -void ipa_eth_device_refresh_sync(struct ipa_eth_device *eth_dev); -void ipa_eth_device_refresh_sched(struct ipa_eth_device *eth_dev); -void ipa_eth_global_refresh_sched(void); - -int ipa_eth_pci_modinit(void); -void ipa_eth_pci_modexit(void); - -int ipa_eth_bus_modinit(void); -void ipa_eth_bus_modexit(void); - -int ipa_eth_bus_register_driver(struct ipa_eth_net_driver *nd); -void ipa_eth_bus_unregister_driver(struct ipa_eth_net_driver *nd); - -int ipa_eth_bus_enable_pc(struct ipa_eth_device *eth_dev); -int ipa_eth_bus_disable_pc(struct ipa_eth_device *eth_dev); - -int ipa_eth_offload_register_driver(struct ipa_eth_offload_driver *od); -void ipa_eth_offload_unregister_driver(struct ipa_eth_offload_driver *od); - -static inline bool ipa_eth_offload_device_paired(struct ipa_eth_device *eth_dev) -{ - return eth_dev->od != NULL; -} - -int ipa_eth_offload_pair_device(struct ipa_eth_device *eth_dev); -void ipa_eth_offload_unpair_device(struct ipa_eth_device *eth_dev); - -int ipa_eth_offload_init(struct ipa_eth_device *eth_dev); -int ipa_eth_offload_deinit(struct ipa_eth_device *eth_dev); -int ipa_eth_offload_start(struct ipa_eth_device *eth_dev); -int ipa_eth_offload_stop(struct ipa_eth_device *eth_dev); - -int ipa_eth_offload_save_regs(struct ipa_eth_device *eth_dev); -int ipa_eth_offload_prepare_reset(struct ipa_eth_device *eth_dev, void *data); -int ipa_eth_offload_complete_reset(struct ipa_eth_device *eth_dev, void *data); - -bool ipa_eth_net_check_active(struct ipa_eth_device *eth_dev); - -int ipa_eth_net_register_driver(struct ipa_eth_net_driver *nd); -void ipa_eth_net_unregister_driver(struct ipa_eth_net_driver *nd); - -int ipa_eth_net_open_device(struct ipa_eth_device *eth_dev); -void ipa_eth_net_close_device(struct ipa_eth_device *eth_dev); - -int ipa_eth_net_save_regs(struct ipa_eth_device *eth_dev); - -/* ipa_eth_utils.c APIs */ - -const char *ipa_eth_device_event_name(enum ipa_eth_device_event event); -const char *ipa_eth_net_device_event_name(unsigned long event); -const char *ipa_eth_pm_notifier_event_name(unsigned long event); - -void *ipa_eth_get_ipc_logbuf(void); -void *ipa_eth_get_ipc_logbuf_dbg(void); - -int ipa_eth_ipc_log_init(void); -void ipa_eth_ipc_log_cleanup(void); - -#endif // _IPA_ETH_I_H_ diff --git a/drivers/platform/msm/ethernet/ipa_eth_net.c b/drivers/platform/msm/ethernet/ipa_eth_net.c deleted file mode 100644 index 07a05a334ede..000000000000 --- a/drivers/platform/msm/ethernet/ipa_eth_net.c +++ /dev/null @@ -1,232 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Copyright (c) 2019-2020, The Linux Foundation. All rights reserved - */ - -#include - -#include "ipa_eth_i.h" - -#define ipa_eth_nd_op(eth_dev, op, args...) (eth_dev->nd->ops->op(args)) - -static LIST_HEAD(ipa_eth_net_drivers); -static DEFINE_MUTEX(ipa_eth_net_drivers_mutex); - -static bool ipa_eth_net_check_ops(struct ipa_eth_net_ops *ops) -{ - /* Following call backs are optional: - * - save_regs() - */ - return - ops && - ops->open_device && - ops->close_device; -} - -static bool ipa_eth_net_check_driver(struct ipa_eth_net_driver *nd) -{ - return - nd && - nd->bus && - nd->name && - nd->driver && - ipa_eth_net_check_ops(nd->ops); -} - -static int __ipa_eth_net_register_driver(struct ipa_eth_net_driver *nd) -{ - int rc; - - rc = ipa_eth_bus_register_driver(nd); - if (rc) { - ipa_eth_err("Failed to register network driver %s", nd->name); - return rc; - } - - mutex_lock(&ipa_eth_net_drivers_mutex); - list_add(&nd->driver_list, &ipa_eth_net_drivers); - mutex_unlock(&ipa_eth_net_drivers_mutex); - - ipa_eth_log("Registered network driver %s", nd->name); - - return 0; -} - -int ipa_eth_net_register_driver(struct ipa_eth_net_driver *nd) -{ - if (!ipa_eth_net_check_driver(nd)) { - ipa_eth_err("Net driver validation failed"); - return -EINVAL; - } - - return __ipa_eth_net_register_driver(nd); -} - -void ipa_eth_net_unregister_driver(struct ipa_eth_net_driver *nd) -{ - mutex_lock(&ipa_eth_net_drivers_mutex); - list_del(&nd->driver_list); - mutex_unlock(&ipa_eth_net_drivers_mutex); - - ipa_eth_bus_unregister_driver(nd); -} - -static inline bool __is_netdev_link_up(struct ipa_eth_device *eth_dev) -{ - return netif_carrier_ok(eth_dev->net_dev); -} - -static inline bool __is_netdev_iface_up(struct ipa_eth_device *eth_dev) -{ - return !!(eth_dev->net_dev->flags & IFF_UP); -} - -bool ipa_eth_net_check_active(struct ipa_eth_device *eth_dev) -{ - bool active = false; - struct rtnl_link_stats64 curr_rtnl_stats; - struct ipa_eth_device_private *ipa_priv = eth_dev_priv(eth_dev); - struct rtnl_link_stats64 *last_rtnl_stats = &ipa_priv->last_rtnl_stats; - - dev_get_stats(eth_dev->net_dev, &curr_rtnl_stats); - - if (ipa_priv->assume_active) { - ipa_priv->assume_active--; - active = true; - } - - if (curr_rtnl_stats.rx_packets != last_rtnl_stats->rx_packets) - active = true; - - *last_rtnl_stats = curr_rtnl_stats; - - return active; -} - -static bool ipa_eth_net_update_link(struct ipa_eth_device *eth_dev) -{ - return __is_netdev_link_up(eth_dev) ? - !test_and_set_bit(IPA_ETH_IF_ST_LOWER_UP, ð_dev->if_state) : - test_and_clear_bit(IPA_ETH_IF_ST_LOWER_UP, ð_dev->if_state); - -} - -static bool ipa_eth_net_event_up(struct ipa_eth_device *eth_dev, - unsigned long event, void *ptr) -{ - return !test_and_set_bit(IPA_ETH_IF_ST_UP, ð_dev->if_state); -} - -static bool ipa_eth_net_event_down(struct ipa_eth_device *eth_dev, - unsigned long event, void *ptr) -{ - return test_and_clear_bit(IPA_ETH_IF_ST_UP, ð_dev->if_state); -} - -static bool ipa_eth_net_event_change(struct ipa_eth_device *eth_dev, - unsigned long event, void *ptr) -{ - return ipa_eth_net_update_link(eth_dev); -} - -typedef bool (*ipa_eth_net_event_handler)(struct ipa_eth_device *eth_dev, - unsigned long event, void *ptr); - -/* Event handlers for netdevice events from real interface */ -static ipa_eth_net_event_handler - ipa_eth_net_event_handlers[IPA_ETH_NET_DEVICE_MAX_EVENTS] = { - [NETDEV_UP] = ipa_eth_net_event_up, - [NETDEV_DOWN] = ipa_eth_net_event_down, - [NETDEV_CHANGE] = ipa_eth_net_event_change, - [NETDEV_CHANGELOWERSTATE] = ipa_eth_net_event_change, -}; - -static int ipa_eth_net_device_event(struct notifier_block *nb, - unsigned long event, void *ptr) -{ - struct net_device *net_dev = netdev_notifier_info_to_dev(ptr); - struct ipa_eth_device *eth_dev = container_of(nb, - struct ipa_eth_device, netdevice_nb); - - if (net_dev != eth_dev->net_dev) - return NOTIFY_DONE; - - ipa_eth_dev_log(eth_dev, "Received netdev event %s (0x%04lx)", - ipa_eth_net_device_event_name(event), event); - - if (event < IPA_ETH_NET_DEVICE_MAX_EVENTS) { - ipa_eth_net_event_handler handler = - ipa_eth_net_event_handlers[event]; - bool refresh_needed = handler && handler(eth_dev, event, ptr); - - /* We can not wait for refresh to complete as we are holding - * the rtnl mutex. - */ - if (refresh_needed) - ipa_eth_device_refresh_sched(eth_dev); - } else { - ipa_eth_dev_err(eth_dev, "Event number out of bounds"); - return NOTIFY_DONE; - } - - return NOTIFY_OK; -} - -int ipa_eth_net_open_device(struct ipa_eth_device *eth_dev) -{ - int rc; - - rc = ipa_eth_nd_op(eth_dev, open_device, eth_dev); - if (rc) { - ipa_eth_dev_err(eth_dev, "Failed to open net device"); - goto err_open; - } - - if (!eth_dev->net_dev) { - rc = -EFAULT; - ipa_eth_dev_err(eth_dev, - "Network driver failed to fill net_dev"); - goto err_net_dev; - } - - eth_dev->netdevice_nb.notifier_call = ipa_eth_net_device_event; - rc = register_netdevice_notifier(ð_dev->netdevice_nb); - if (rc) { - ipa_eth_dev_err(eth_dev, "Failed to register netdev notifier"); - goto err_register; - } - - /* When registering with netdevice notifier, only REGISTER and UP events - * are replayed. Fetch current link state; future link state updates - * will be processed through CHANGE event. - */ - if (ipa_eth_net_update_link(eth_dev)) - ipa_eth_device_refresh_sched(eth_dev); - - return 0; - -err_register: - eth_dev->netdevice_nb.notifier_call = NULL; -err_net_dev: - ipa_eth_nd_op(eth_dev, close_device, eth_dev); -err_open: - return rc; -} - -void ipa_eth_net_close_device(struct ipa_eth_device *eth_dev) -{ - unregister_netdevice_notifier(ð_dev->netdevice_nb); - eth_dev->netdevice_nb.notifier_call = NULL; - - ipa_eth_nd_op(eth_dev, close_device, eth_dev); -} - -int ipa_eth_net_save_regs(struct ipa_eth_device *eth_dev) -{ - struct ipa_eth_net_driver *nd = eth_dev->nd; - - if (nd && nd->ops->save_regs) - return ipa_eth_nd_op(eth_dev, save_regs, eth_dev, NULL, NULL); - - return 0; -} diff --git a/drivers/platform/msm/ethernet/ipa_eth_offload.c b/drivers/platform/msm/ethernet/ipa_eth_offload.c deleted file mode 100644 index 75492b9dde7f..000000000000 --- a/drivers/platform/msm/ethernet/ipa_eth_offload.c +++ /dev/null @@ -1,260 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Copyright (c) 2019-2020, The Linux Foundation. All rights reserved - */ - -#include "ipa_eth_i.h" - -static LIST_HEAD(ipa_eth_offload_drivers); -static DEFINE_MUTEX(ipa_eth_offload_drivers_lock); - -int ipa_eth_offload_init(struct ipa_eth_device *eth_dev) -{ - int rc; - - rc = eth_dev->od->ops->init_tx(eth_dev); - if (rc) { - ipa_eth_dev_err(eth_dev, "Failed to init offload for tx"); - return rc; - } - - rc = eth_dev->od->ops->init_rx(eth_dev); - if (rc) { - ipa_eth_dev_err(eth_dev, "Failed to init offload for rx"); - (void) eth_dev->od->ops->deinit_tx(eth_dev); - return rc; - } - - return 0; -} - -int ipa_eth_offload_deinit(struct ipa_eth_device *eth_dev) -{ - int rc_rx, rc_tx; - - rc_rx = eth_dev->od->ops->deinit_rx(eth_dev); - if (rc_rx) - ipa_eth_dev_err(eth_dev, "Failed to deinit offload for rx"); - - rc_tx = eth_dev->od->ops->deinit_tx(eth_dev); - if (rc_tx) - ipa_eth_dev_err(eth_dev, "Failed to deinit offload for tx"); - - return rc_rx || rc_tx; -} - -int ipa_eth_offload_start(struct ipa_eth_device *eth_dev) -{ - int rc; - - rc = eth_dev->od->ops->start_tx(eth_dev); - if (rc) { - ipa_eth_dev_err(eth_dev, "Failed to start offload for tx"); - return rc; - } - - rc = eth_dev->od->ops->start_rx(eth_dev); - if (rc) { - ipa_eth_dev_err(eth_dev, "Failed to start offload for rx"); - (void) eth_dev->od->ops->stop_tx(eth_dev); - return rc; - } - - return 0; -} - -int ipa_eth_offload_stop(struct ipa_eth_device *eth_dev) -{ - int rc_rx, rc_tx; - - rc_rx = eth_dev->od->ops->stop_rx(eth_dev); - if (rc_rx) - ipa_eth_dev_err(eth_dev, "Failed to stop offload for rx"); - - rc_tx = eth_dev->od->ops->stop_tx(eth_dev); - if (rc_tx) - ipa_eth_dev_err(eth_dev, "Failed to stop offload for tx"); - - return rc_rx || rc_tx; -} - -static int __try_pair_device(struct ipa_eth_device *eth_dev, - struct ipa_eth_offload_driver *od) -{ - int rc = od->ops->pair(eth_dev); - - if (rc) { - ipa_eth_dev_dbg(eth_dev, - "Offload driver %s passed up paring with %s", - od->name, eth_dev->nd->name); - return rc; - } - - ipa_eth_dev_log(eth_dev, - "Offload driver %s is paired with device from %s", - od->name, eth_dev->nd->name); - - eth_dev->od = od; - - ipa_eth_dev_log(eth_dev, - "Offload driver %s successfully paired with device from %s", - od->name, eth_dev->nd->name); - - ipa_eth_device_refresh_sched(eth_dev); - - return 0; -} - -static int try_pair_device(struct ipa_eth_device *eth_dev, - struct ipa_eth_offload_driver *od) -{ - int rc; - - mutex_lock(&od->mutex); - - if (od->bus != eth_dev->dev->bus) { - rc = -EOPNOTSUPP; - ipa_eth_dev_dbg(eth_dev, - "Offload driver %s is not a bus match for %s", - od->name, eth_dev->nd->name); - } else { - rc = __try_pair_device(eth_dev, od); - } - - mutex_unlock(&od->mutex); - - return rc; -} - -int ipa_eth_offload_pair_device(struct ipa_eth_device *eth_dev) -{ - int rc = -ENODEV; - - mutex_lock(&ipa_eth_offload_drivers_lock); - - /* Check if device is already paired while holding the offload_drivers - * mutex to ensure this API is re-entrant safe. - */ - if (!eth_dev->od) { - struct ipa_eth_offload_driver *od; - - list_for_each_entry(od, &ipa_eth_offload_drivers, driver_list) { - if (!try_pair_device(eth_dev, od)) { - rc = 0; - break; - } - } - } - - mutex_unlock(&ipa_eth_offload_drivers_lock); - - return rc; -} - -void ipa_eth_offload_unpair_device(struct ipa_eth_device *eth_dev) -{ - mutex_lock(&ipa_eth_offload_drivers_lock); - - /* Check if device is already unpaired while holding the offload_drivers - * mutex to ensure this API is re-entrant safe. - */ - if (eth_dev->od) { - struct ipa_eth_offload_driver *od = eth_dev->od; - - mutex_lock(&od->mutex); - od->ops->unpair(eth_dev); - eth_dev->od = NULL; - mutex_unlock(&od->mutex); - } - - mutex_unlock(&ipa_eth_offload_drivers_lock); -} - -static bool ipa_eth_offload_check_ops(struct ipa_eth_offload_ops *ops) -{ - /* Following call backs are optional: - * - get_stats() - * - clear_stats() - * - save_regs() - * - prepare_reset() - * - complete_reset() - */ - return - ops && - ops->pair && - ops->unpair && - ops->init_tx && - ops->start_tx && - ops->stop_tx && - ops->deinit_tx && - ops->init_rx && - ops->start_rx && - ops->stop_rx && - ops->deinit_rx; -} - -static bool ipa_eth_offload_check_driver(struct ipa_eth_offload_driver *od) -{ - return - od && - od->bus && - od->name && - ipa_eth_offload_check_ops(od->ops); -} - -int ipa_eth_offload_register_driver(struct ipa_eth_offload_driver *od) -{ - if (!ipa_eth_offload_check_driver(od)) { - ipa_eth_err("Offload driver validation failed"); - return -EINVAL; - } - - mutex_init(&od->mutex); - - mutex_lock(&ipa_eth_offload_drivers_lock); - list_add(&od->driver_list, &ipa_eth_offload_drivers); - mutex_unlock(&ipa_eth_offload_drivers_lock); - - (void) ipa_eth_debugfs_add_offload_driver(od); - - return 0; -} - -void ipa_eth_offload_unregister_driver(struct ipa_eth_offload_driver *od) -{ - ipa_eth_debugfs_remove_offload_driver(od); - - mutex_lock(&ipa_eth_offload_drivers_lock); - list_del(&od->driver_list); - mutex_unlock(&ipa_eth_offload_drivers_lock); -} - -int ipa_eth_offload_save_regs(struct ipa_eth_device *eth_dev) -{ - struct ipa_eth_offload_driver *od = eth_dev->od; - - if (od && od->ops->save_regs) - return eth_dev->od->ops->save_regs(eth_dev, NULL, NULL); - - return 0; -} - -int ipa_eth_offload_prepare_reset(struct ipa_eth_device *eth_dev, void *data) -{ - struct ipa_eth_offload_driver *od = eth_dev->od; - - if (od && od->ops->prepare_reset) - return eth_dev->od->ops->prepare_reset(eth_dev, data); - - return 0; -} - -int ipa_eth_offload_complete_reset(struct ipa_eth_device *eth_dev, void *data) -{ - struct ipa_eth_offload_driver *od = eth_dev->od; - - if (od && od->ops->complete_reset) - return eth_dev->od->ops->complete_reset(eth_dev, data); - - return 0; -} diff --git a/drivers/platform/msm/ethernet/ipa_eth_pci.c b/drivers/platform/msm/ethernet/ipa_eth_pci.c deleted file mode 100644 index 9c6a862bc878..000000000000 --- a/drivers/platform/msm/ethernet/ipa_eth_pci.c +++ /dev/null @@ -1,611 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Copyright (c) 2019-2020, The Linux Foundation. All rights reserved - */ - -#include -#include - -#include -#include - -#include "ipa_eth_i.h" - -struct ipa_eth_pci_driver { - struct ipa_eth_net_driver *nd; - - int (*probe_real)(struct pci_dev *dev, const struct pci_device_id *id); - void (*remove_real)(struct pci_dev *dev); - - const struct dev_pm_ops *pm_ops_real; -}; - -struct ipa_eth_pci_device { - struct ipa_eth_device *eth_dev; - struct ipa_eth_pci_driver *eth_pdrv; - struct msm_pcie_register_event pcie_event; -}; - -#define eth_dev_to_pdev(e) \ - ((struct ipa_eth_pci_device *)e->bus_priv) - -#define eth_dev_pm_ops(e) \ - (eth_dev_to_pdev(e)->eth_pdrv->pm_ops_real) - -static LIST_HEAD(ipa_eth_pci_drivers); -static DEFINE_MUTEX(ipa_eth_pci_drivers_mutex); - -static LIST_HEAD(ipa_eth_pci_devices); -static DEFINE_MUTEX(ipa_eth_pci_devices_mutex); - -static bool ipa_eth_pci_is_ready; - -static struct ipa_eth_pci_driver *__lookup_eth_pdrv( - struct pci_driver *pdrv) -{ - struct ipa_eth_net_driver *nd; - - list_for_each_entry(nd, &ipa_eth_pci_drivers, bus_driver_list) { - if (nd->driver == &pdrv->driver) - return nd->bus_priv; - } - - return NULL; -} - -static struct ipa_eth_pci_driver *lookup_eth_pdrv(struct pci_driver *pdrv) -{ - struct ipa_eth_pci_driver *eth_pdrv; - - mutex_lock(&ipa_eth_pci_drivers_mutex); - eth_pdrv = __lookup_eth_pdrv(pdrv); - mutex_unlock(&ipa_eth_pci_drivers_mutex); - - return eth_pdrv; -} - -static struct ipa_eth_device *__lookup_eth_dev(struct pci_dev *pdev) -{ - struct device *dev = &pdev->dev; - struct ipa_eth_device *eth_dev; - - list_for_each_entry(eth_dev, &ipa_eth_pci_devices, bus_device_list) { - if (eth_dev->dev == dev) - return eth_dev; - } - - return NULL; -} - -static struct ipa_eth_device *lookup_eth_dev(struct pci_dev *pdev) -{ - struct ipa_eth_device *eth_dev; - - mutex_lock(&ipa_eth_pci_devices_mutex); - eth_dev = __lookup_eth_dev(pdev); - mutex_unlock(&ipa_eth_pci_devices_mutex); - - return eth_dev; -} - -static struct ipa_eth_device *dev_to_eth_dev(struct device *dev) -{ - return lookup_eth_dev(to_pci_dev(dev)); -} - -static bool is_driver_used(struct ipa_eth_pci_driver *eth_pdrv) -{ - bool in_use = false; - struct ipa_eth_device *eth_dev; - - list_for_each_entry(eth_dev, &ipa_eth_pci_devices, bus_device_list) { - if (eth_dev_to_pdev(eth_dev)->eth_pdrv == eth_pdrv) { - in_use = true; - break; - } - } - - return in_use; -} - -static void ipa_eth_pcie_event_wakeup(struct pci_dev *pdev) -{ - struct ipa_eth_device *eth_dev; - - /* We are not expected to be re-entrant while processing wake up - * event. - */ - eth_dev = __lookup_eth_dev(pdev); - if (!eth_dev) { - ipa_eth_bug("Failed to lookup eth device"); - return; - } - - if (eth_dev->start_on_wakeup && !eth_dev->start) { - eth_dev->start = true; - ipa_eth_device_refresh_sched(eth_dev); - } -} - -static void ipa_eth_pcie_event_cb(struct msm_pcie_notify *notify) -{ - struct pci_dev *pdev = notify->user; - - ipa_eth_log("Received PCIe event %d", notify->event); - - switch (notify->event) { - case MSM_PCIE_EVENT_WAKEUP: - ipa_eth_pcie_event_wakeup(pdev); - break; - default: - break; - } -} - -static struct ipa_eth_device *ipa_eth_pci_alloc_device( - struct pci_dev *pdev, - struct ipa_eth_pci_driver *eth_pdrv) -{ - struct device *dev = &pdev->dev; - struct ipa_eth_device *eth_dev; - struct ipa_eth_pci_device *eth_pdev; - - eth_pdev = kzalloc(sizeof(*eth_pdev), GFP_KERNEL); - if (!eth_pdev) - return NULL; - - eth_dev = ipa_eth_alloc_device(dev, eth_pdrv->nd); - if (!eth_dev) { - ipa_eth_err("Failed to alloc eth device"); - kfree(eth_pdev); - return NULL; - } - - eth_pdev->eth_dev = eth_dev; - eth_pdev->eth_pdrv = eth_pdrv; - - eth_pdev->pcie_event.events = MSM_PCIE_EVENT_WAKEUP; - eth_pdev->pcie_event.user = pdev; - eth_pdev->pcie_event.mode = MSM_PCIE_TRIGGER_CALLBACK; - eth_pdev->pcie_event.callback = ipa_eth_pcie_event_cb; - - eth_dev->bus_priv = eth_pdev; - - return eth_dev; -} - -static void ipa_eth_pci_free_device(struct ipa_eth_device *eth_dev) -{ - struct ipa_eth_pci_device *eth_pdev = eth_dev->bus_priv; - - if (eth_pdev) { - memset(eth_pdev, 0, sizeof(*eth_pdev)); - kfree(eth_pdev); - } - - ipa_eth_free_device(eth_dev); -} - -static int ipa_eth_pci_register_device( - struct pci_dev *pdev, - struct ipa_eth_pci_driver *eth_pdrv) -{ - int rc; - struct ipa_eth_device *eth_dev; - - eth_dev = ipa_eth_pci_alloc_device(pdev, eth_pdrv); - if (!eth_dev) { - rc = -ENOMEM; - ipa_eth_err("Failed to alloc eth pci device"); - goto err_alloc; - } - - rc = ipa_eth_register_device(eth_dev); - if (rc) { - ipa_eth_dev_err(eth_dev, "Failed to register eth device"); - goto err_register; - } - - mutex_lock(&ipa_eth_pci_devices_mutex); - list_add(ð_dev->bus_device_list, &ipa_eth_pci_devices); - mutex_unlock(&ipa_eth_pci_devices_mutex); - - rc = msm_pcie_register_event(ð_dev_to_pdev(eth_dev)->pcie_event); - if (rc) { - ipa_eth_dev_err(eth_dev, "Failed to register for PCIe event"); - goto err_pcie_register; - } - - return 0; - -err_pcie_register: - ipa_eth_unregister_device(eth_dev); -err_register: - ipa_eth_pci_free_device(eth_dev); -err_alloc: - return rc; -} - -static void ipa_eth_pci_unregister_device(struct ipa_eth_device *eth_dev) -{ - /* Deregister event handler first to prevent possible race with - * __lookup_eth_dev() while remove the device from devices list. - */ - msm_pcie_deregister_event(ð_dev_to_pdev(eth_dev)->pcie_event); - - mutex_lock(&ipa_eth_pci_devices_mutex); - list_del(ð_dev->bus_device_list); - mutex_unlock(&ipa_eth_pci_devices_mutex); - - ipa_eth_unregister_device(eth_dev); - ipa_eth_pci_free_device(eth_dev); -} - -static int ipa_eth_pci_probe_handler(struct pci_dev *pdev, - const struct pci_device_id *id) -{ - int rc = 0; - struct ipa_eth_pci_driver *eth_pdrv; - - ipa_eth_dbg("PCI probe called for %s driver with devfn %u", - pdev->driver->name, pdev->devfn); - - if (!ipa_eth_is_ready()) { - ipa_eth_log( - "Offload sub-system not initialized, deferring probe"); - return -EPROBE_DEFER; - } - - eth_pdrv = lookup_eth_pdrv(pdev->driver); - if (!eth_pdrv) { - ipa_eth_bug("Failed to lookup epci driver"); - return -EFAULT; - } - - rc = eth_pdrv->probe_real(pdev, id); - if (rc) { - ipa_eth_err("Failed real PCI probe of devfn=%u"); - goto err_probe; - } - - rc = ipa_eth_pci_register_device(pdev, eth_pdrv); - if (rc) { - ipa_eth_err("Failed to register PCI eth device"); - goto err_register; - } - - return 0; - -err_register: - eth_pdrv->remove_real(pdev); -err_probe: - return rc; -} - -static void ipa_eth_pci_remove_handler(struct pci_dev *pdev) -{ - struct ipa_eth_device *eth_dev = NULL; - struct ipa_eth_pci_driver *eth_pdrv = NULL; - - ipa_eth_dbg("PCI remove called for %s driver with devfn %u", - pdev->driver->name, pdev->devfn); - - eth_dev = lookup_eth_dev(pdev); - if (!eth_dev) { - ipa_eth_bug("Failed to lookup pci_dev -> eth_dev"); - return; - } - - eth_pdrv = eth_dev_to_pdev(eth_dev)->eth_pdrv; - - ipa_eth_pci_unregister_device(eth_dev); - - eth_pdrv->remove_real(pdev); -} - -static int ipa_eth_pci_suspend_handler(struct device *dev) -{ - int rc = 0; - struct ipa_eth_device *eth_dev; - - eth_dev = dev_to_eth_dev(dev); - if (!eth_dev) { - ipa_eth_bug("Failed to lookup pci_dev -> eth_dev"); - return -EFAULT; - } - - if (work_pending(ð_dev->refresh)) { - ipa_eth_dev_log(eth_dev, - "Refresh work is pending, aborting suspend"); - - /* Just abort suspend. Since the wq is freezable, the work item - * would get flushed before we get called again. - */ - return -EAGAIN; - } - - /* When offload is started, PCI power collapse is already disabled by - * the ipa_eth_pci_disable_pc() api. Nonetheless, we still need to do - * a dummy PCI config space save so that the PCIe framework will not by - * itself perform a config space save-restore. - */ - if (eth_dev->of_state == IPA_ETH_OF_ST_STARTED) { - ipa_eth_dev_dbg(eth_dev, - "Device suspend performing dummy config space save"); - rc = pci_save_state(to_pci_dev(dev)); - } else { - ipa_eth_dev_log(eth_dev, - "Device suspend delegated to net driver"); - rc = eth_dev_pm_ops(eth_dev)->suspend(dev); - } - - if (rc) - ipa_eth_dev_err(eth_dev, "Device suspend failed"); - else - ipa_eth_dev_dbg(eth_dev, "Device suspend complete"); - - return rc; -} - -static int ipa_eth_pci_suspend_late_handler(struct device *dev) -{ - struct ipa_eth_device *eth_dev; - - eth_dev = dev_to_eth_dev(dev); - if (!eth_dev) { - ipa_eth_bug("Failed to lookup pci_dev -> eth_dev"); - return -EFAULT; - } - - /* In rare case where we detect some interface activity between the - * time PM_SUSPEND_PREPARE event was processed and the device was - * actually frozen, abort the suspend operation. - */ - if (ipa_eth_net_check_active(eth_dev)) { - pr_info("%s: %s shows late stage activity, preventing suspend\n", - IPA_ETH_SUBSYS, eth_dev->net_dev->name); - - /* Have PM_SUSPEND_PREPARE give us one wakeup time quanta */ - ipa_eth_dev_assume_active_inc(eth_dev, 1); - - return -EAGAIN; - } - - return 0; -} - -static int ipa_eth_pci_resume_early_handler(struct device *dev) -{ - struct ipa_eth_device *eth_dev; - - eth_dev = dev_to_eth_dev(dev); - if (!eth_dev) { - ipa_eth_bug("Failed to lookup pci_dev -> eth_dev"); - return -EFAULT; - } - - /* We cannot check start_on_resume in the resume handler as it can get - * invoked also if .suspend_late() aborts due to interface activity. - */ - if (eth_dev->start_on_resume && !eth_dev->start) { - eth_dev->start = true; - ipa_eth_device_refresh_sched(eth_dev); - } - - return 0; -} - -static int ipa_eth_pci_resume_handler(struct device *dev) -{ - int rc = 0; - struct ipa_eth_device *eth_dev; - struct pci_dev *pci_dev = to_pci_dev(dev); - - eth_dev = lookup_eth_dev(pci_dev); - if (!eth_dev) { - ipa_eth_bug("Failed to lookup pci_dev -> eth_dev"); - return -EFAULT; - } - - /* During suspend, RC power collapse would not have happened if offload - * was started. Ignore resume callback since the device does not need - * to be re-initialized. - */ - if (eth_dev->of_state == IPA_ETH_OF_ST_STARTED) { - ipa_eth_dev_dbg(eth_dev, - "Device resume performing nop"); - rc = 0; - } else { - ipa_eth_dev_log(eth_dev, - "Device resume delegated to net driver"); - rc = eth_dev_pm_ops(eth_dev)->resume(dev); - - /* Give some time for device to settle after a resume */ - ipa_eth_dev_assume_active_ms(eth_dev, IPA_ETH_RESUME_SETTLE_MS); - } - - if (rc) - ipa_eth_dev_err(eth_dev, "Device resume failed"); - else - ipa_eth_dev_dbg(eth_dev, "Device resume complete"); - - return 0; -} - -/* MSM PCIe driver invokes only suspend and resume callbacks, other operations - * can be ignored unless we see a client requiring the feature. - */ -static const struct dev_pm_ops ipa_eth_pci_pm_ops = { - .suspend = ipa_eth_pci_suspend_handler, - .suspend_late = ipa_eth_pci_suspend_late_handler, - .resume_early = ipa_eth_pci_resume_early_handler, - .resume = ipa_eth_pci_resume_handler, -}; - -static bool ipa_eth_pci_check_driver(struct pci_driver *pdrv) -{ - return - pdrv->name && - pdrv->probe && - pdrv->remove && - pdrv->driver.pm && - pdrv->driver.pm->suspend && - pdrv->driver.pm->resume; -} - -static struct ipa_eth_pci_driver *ipa_eth_pci_setup_driver( - struct pci_driver *pdrv, - struct ipa_eth_net_driver *nd) -{ - struct ipa_eth_pci_driver *eth_pdrv; - - eth_pdrv = kzalloc(sizeof(*eth_pdrv), GFP_KERNEL); - if (!eth_pdrv) - return NULL; - - eth_pdrv->probe_real = pdrv->probe; - pdrv->probe = ipa_eth_pci_probe_handler; - - eth_pdrv->remove_real = pdrv->remove; - pdrv->remove = ipa_eth_pci_remove_handler; - - eth_pdrv->pm_ops_real = pdrv->driver.pm; - pdrv->driver.pm = &ipa_eth_pci_pm_ops; - - eth_pdrv->nd = nd; - nd->bus_priv = eth_pdrv; - - return eth_pdrv; -} - -static void ipa_eth_pci_reset_driver(struct ipa_eth_net_driver *nd) -{ - struct pci_driver *pdrv = to_pci_driver(nd->driver); - struct ipa_eth_pci_driver *eth_pdrv = nd->bus_priv; - - if (is_driver_used(eth_pdrv)) - ipa_eth_bug("Driver is still being used by a device"); - - nd->bus_priv = NULL; - - pdrv->probe = eth_pdrv->probe_real; - pdrv->remove = eth_pdrv->remove_real; - pdrv->driver.pm = eth_pdrv->pm_ops_real; - - memset(eth_pdrv, 0, sizeof(*eth_pdrv)); - kfree(eth_pdrv); -} - -static int ipa_eth_pci_register_driver(struct ipa_eth_net_driver *nd) -{ - struct pci_driver *pdrv; - struct ipa_eth_pci_driver *eth_pdrv; - - pdrv = to_pci_driver(nd->driver); - - if (!ipa_eth_pci_check_driver(pdrv)) { - ipa_eth_err("PCI driver validation failed for %s", nd->name); - return -EINVAL; - } - - eth_pdrv = ipa_eth_pci_setup_driver(pdrv, nd); - if (!eth_pdrv) - return -ENOMEM; - - mutex_lock(&ipa_eth_pci_drivers_mutex); - list_add(&nd->bus_driver_list, &ipa_eth_pci_drivers); - mutex_unlock(&ipa_eth_pci_drivers_mutex); - - return 0; -} - -static void ipa_eth_pci_unregister_driver(struct ipa_eth_net_driver *nd) -{ - - mutex_lock(&ipa_eth_pci_drivers_mutex); - list_del(&nd->bus_driver_list); - mutex_unlock(&ipa_eth_pci_drivers_mutex); - - ipa_eth_pci_reset_driver(nd); -} - -/** - * ipa_eth_pci_enable_pc() - Permit power collapse of the PCI root port - * @eth_dev: Device attached to the PCI bus - * - * This function instructs the MSM PCIe bus driver to permit power collapse - * of the root complex when Linux goes to suspend state. - * - * Return: 0 on success, non-zero otherwise - */ -static int ipa_eth_pci_enable_pc(struct ipa_eth_device *eth_dev) -{ - int rc; - struct pci_dev *pci_dev = to_pci_dev(eth_dev->dev); - - rc = msm_pcie_pm_control(MSM_PCIE_ENABLE_PC, - pci_dev->bus->number, pci_dev, NULL, MSM_PCIE_CONFIG_INVALID); - if (rc) { - ipa_eth_dev_err(eth_dev, - "Failed to enable MSM PCIe power collapse"); - } else { - ipa_eth_dev_log(eth_dev, - "Enabled MSM PCIe power collapse"); - } - - return rc; -} - -/** - * ipa_eth_pci_disable_pc() - Prevent power collapse of the PCI root port - * @eth_dev: Device attached to the PCI bus - * - * This function instructs the MSM PCIe bus driver to prevent power collapse - * of the root complex and connected EPs when Linux goes to suspend state. - * - * Return: 0 on success, non-zero otherwise - */ -static int ipa_eth_pci_disable_pc(struct ipa_eth_device *eth_dev) -{ - int rc; - struct pci_dev *pci_dev = to_pci_dev(eth_dev->dev); - - rc = msm_pcie_pm_control(MSM_PCIE_DISABLE_PC, - pci_dev->bus->number, pci_dev, NULL, MSM_PCIE_CONFIG_INVALID); - if (rc) { - ipa_eth_dev_err(eth_dev, - "Failed to disable MSM PCIe power collapse"); - } else { - ipa_eth_dev_log(eth_dev, - "Disabled MSM PCIe power collapse"); - } - - return rc; -} - -struct ipa_eth_bus ipa_eth_pci_bus = { - .bus = &pci_bus_type, - .register_driver = ipa_eth_pci_register_driver, - .unregister_driver = ipa_eth_pci_unregister_driver, - .enable_pc = ipa_eth_pci_enable_pc, - .disable_pc = ipa_eth_pci_disable_pc, -}; - -int ipa_eth_pci_modinit(void) -{ - ipa_eth_pci_is_ready = true; - - ipa_eth_log("Offload sub-system pci bus module init is complete"); - - return 0; -} - -void ipa_eth_pci_modexit(void) -{ - ipa_eth_log("De-initing offload sub-system pci bus module"); - - if (!ipa_eth_pci_is_ready) - return; - - ipa_eth_pci_is_ready = false; -} diff --git a/drivers/platform/msm/ethernet/ipa_eth_utils.c b/drivers/platform/msm/ethernet/ipa_eth_utils.c deleted file mode 100644 index 788c09e4461d..000000000000 --- a/drivers/platform/msm/ethernet/ipa_eth_utils.c +++ /dev/null @@ -1,119 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Copyright (c) 2019-2020, The Linux Foundation. All rights reserved - */ - -#include -#include - -#include "ipa_eth_i.h" - -#ifdef CONFIG_IPA_ETH_DEBUG -bool ipa_eth_ipc_logdbg = true; -#else -bool ipa_eth_ipc_logdbg; -#endif - -static const char * const ipa_eth_device_events[IPA_ETH_DEV_EVENT_COUNT] = { - [IPA_ETH_DEV_RESET_PREPARE] = "RESET_PREPARE", - [IPA_ETH_DEV_RESET_COMPLETE] = "RESET_COMPLETE", - [IPA_ETH_DEV_ADD_MACSEC_IF] = "ADD_MACSEC_IF", - [IPA_ETH_DEV_DEL_MACSEC_IF] = "DEL_MACSEC_IF", -}; - -const char *ipa_eth_device_event_name(enum ipa_eth_device_event event) -{ - const char *name = ""; - - if (event < IPA_ETH_DEV_EVENT_COUNT && ipa_eth_device_events[event]) - name = ipa_eth_device_events[event]; - - return name; -} - -static const char * const - ipa_eth_net_device_events[IPA_ETH_NET_DEVICE_MAX_EVENTS] = { - [NETDEV_REGISTER] = "REGISTER", - [NETDEV_UNREGISTER] = "UNREGISTER", - [NETDEV_CHANGENAME] = "CHANGE_NAME", - [NETDEV_PRE_UP] = "PRE_UP", - [NETDEV_UP] = "UP", - [NETDEV_GOING_DOWN] = "GOING_DOWN", - [NETDEV_DOWN] = "DOWN", - [NETDEV_CHANGE] = "CHANGE", - [NETDEV_CHANGELOWERSTATE] = "CHANGE_LOWER_STATE", - [NETDEV_PRECHANGEUPPER] = "PRE_CHANGE_UPPER", - [NETDEV_CHANGEUPPER] = "CHANGE_UPPER", - [NETDEV_JOIN] = "JOIN", -}; - -const char *ipa_eth_net_device_event_name(unsigned long event) -{ - const char *name = ""; - - if (event < IPA_ETH_NET_DEVICE_MAX_EVENTS && - ipa_eth_net_device_events[event]) - name = ipa_eth_net_device_events[event]; - - return name; -} - -static const char * const - ipa_eth_pm_notifier_events[IPA_ETH_PM_NOTIFIER_MAX_EVENTS] = { - [PM_HIBERNATION_PREPARE] = "HIBERNATION_PREPARE", - [PM_POST_HIBERNATION] = "POST_HIBERNATION", - [PM_SUSPEND_PREPARE] = "SUSPEND_PREPARE", - [PM_POST_SUSPEND] = "POST_SUSPEND", - [PM_RESTORE_PREPARE] = "RESTORE_PREPARE", - [PM_POST_RESTORE] = "POST_RESTORE", -}; - -const char *ipa_eth_pm_notifier_event_name(unsigned long event) -{ - const char *name = ""; - - if (event < IPA_ETH_PM_NOTIFIER_MAX_EVENTS && - ipa_eth_pm_notifier_events[event]) - name = ipa_eth_pm_notifier_events[event]; - - return name; -} - -/* IPC Logging */ -module_param(ipa_eth_ipc_logdbg, bool, 0444); -MODULE_PARM_DESC(ipa_eth_ipc_logdbg, "Log debug IPC messages"); - -static void *ipa_eth_ipc_logbuf; - -void *ipa_eth_get_ipc_logbuf(void) -{ - return ipa_eth_ipc_logbuf; -} -EXPORT_SYMBOL(ipa_eth_get_ipc_logbuf); - -void *ipa_eth_get_ipc_logbuf_dbg(void) -{ - return ipa_eth_ipc_logdbg ? ipa_eth_ipc_logbuf : NULL; -} -EXPORT_SYMBOL(ipa_eth_get_ipc_logbuf_dbg); - -#define IPA_ETH_IPC_LOG_PAGES 128 - -int ipa_eth_ipc_log_init(void) -{ - if (ipa_eth_ipc_logbuf) - return 0; - - ipa_eth_ipc_logbuf = ipc_log_context_create( - IPA_ETH_IPC_LOG_PAGES, IPA_ETH_SUBSYS, 0); - - return ipa_eth_ipc_logbuf ? 0 : -EFAULT; -} - -void ipa_eth_ipc_log_cleanup(void) -{ - if (ipa_eth_ipc_logbuf) { - ipc_log_context_destroy(ipa_eth_ipc_logbuf); - ipa_eth_ipc_logbuf = NULL; - } -} diff --git a/include/linux/ipa_eth.h b/include/linux/ipa_eth.h index ff230cbb466f..3a2ba3031a0d 100644 --- a/include/linux/ipa_eth.h +++ b/include/linux/ipa_eth.h @@ -6,838 +6,10 @@ #ifndef _IPA_ETH_H_ #define _IPA_ETH_H_ -#include -#include -#include -#include -#include -#include - -#include -#include - #include #include #include -/** - * API Version Changes - * --------------------------------------------------------------------------- - * 1 - Initial version - * 2 - API separation for use by network and offload drivers - * - New ipa_eth_net_*() APIs offer safer interface for offload - * drivers to call into ipa_eth_net_ops - * - ipa_eth_net_ops.request_channel() to accept additional - * memory allocation params, including custom memory allocator - * defined via struct ipa_eth_dma_allocator interface - * - probe() and remove() offload bus ops are replaced by pair() - * and unpair() callbacks respectively - * 3 - Added .save_regs() callback for network and offload drivers - * 4 - Added ipa_eth_device_notify() interface for client drivers - * to notify of various device events. - * 5 - Removed ipa_eth_{gsi,uc}_iommu_*{} APIs that were used for - * mapping memory to GSI and IPA uC IOMMU CBs. - * 6 - Added ipa_eth_ep_deinit() - * 7 - ipa_eth_net_ops.receive_skb() now accepts in_napi parameter - * 8 - Added IPA rx/tx intf properties to ipa_eth_device - */ - -#define IPA_ETH_API_VER 8 - -/** - * enum ipa_eth_dev_features - Features supported by an ethernet device or - * driver that may be requested via offload APIs - * @IPA_ETH_DEV_F_L2_CSUM_BIT: Ethernet L2 checksum offload - * @IPA_ETH_DEV_F_L3_CSUM_BIT: Ethernet L2 checksum offload - * @IPA_ETH_DEV_F_TCP_CSUM_BIT: TCP checksum offload - * @IPA_ETH_DEV_F_UDP_CSUM_BIT: UDP checksum offload - * @IPA_ETH_DEV_F_LSO_BIT: Large Send Offload / TCP Segmentation Offload - * @IPA_ETH_DEV_F_LRO_BIT: Large Receive Offload / Receive Side Coalescing - * @IPA_ETH_DEV_F_VLAN_BIT: VLAN Offload - * @IPA_ETH_DEV_F_MODC_BIT: Counter based event moderation - * @IPA_ETH_DEV_F_MODT_BIT: Timer based event moderation - * - * Ethernet hardware features represented as bit numbers below are used in - * IPA_ETH_DEV_F_* feature flags that are to be used in offload APIs. - */ -enum ipa_eth_dev_features { - IPA_ETH_DEV_F_L2_CSUM_BIT, - IPA_ETH_DEV_F_L3_CSUM_BIT, - IPA_ETH_DEV_F_TCP_CSUM_BIT, - IPA_ETH_DEV_F_UDP_CSUM_BIT, - IPA_ETH_DEV_F_LSO_BIT, - IPA_ETH_DEV_F_LRO_BIT, - IPA_ETH_DEV_F_VLAN_BIT, - IPA_ETH_DEV_F_MODC_BIT, - IPA_ETH_DEV_F_MODT_BIT, -}; - -#define ipa_eth_dev_f(f) BIT(IPA_ETH_DEV_F_##f##_BIT) - -#define IPA_ETH_DEV_F_L2_CSUM ipa_eth_dev_f(L2_CSUM) -#define IPA_ETH_DEV_F_L3_CSUM ipa_eth_dev_f(L3_CSUM) -#define IPA_ETH_DEV_F_TCP_CSUM ipa_eth_dev_f(TCP_CSUM) -#define IPA_ETH_DEV_F_UDP_CSUM ipa_eth_dev_f(UDP_CSUM) -#define IPA_ETH_DEV_F_LSO ipa_eth_dev_f(LSO) -#define IPA_ETH_DEV_F_LRO ipa_eth_dev_f(LRO) -#define IPA_ETH_DEV_F_VLAN ipa_eth_dev_f(VLAN) -#define IPA_ETH_DEV_F_MODC ipa_eth_dev_f(MODC) -#define IPA_ETH_DEV_F_MODT ipa_eth_dev_f(MODT) - -/** - * enum ipa_eth_dev_events - Events supported by an ethernet device that may be - * requested via offload APIs - * @IPA_ETH_DEV_EV_RX_INT_BIT: Rx interrupt - * @IPA_ETH_DEV_EV_TX_INT_BIT: Tx interrupt - * @IPA_ETH_DEV_EV_RX_PTR_BIT: Rx (head) pointer write-back - * @IPA_ETH_DEV_EV_TX_PTR_BIT: Tx (head) pointer write-back - */ -enum ipa_eth_dev_events { - IPA_ETH_DEV_EV_RX_INT_BIT, - IPA_ETH_DEV_EV_TX_INT_BIT, - IPA_ETH_DEV_EV_RX_PTR_BIT, - IPA_ETH_DEV_EV_TX_PTR_BIT -}; - -#define ipa_eth_dev_ev(ev) BIT(IPA_ETH_DEV_EV_##ev##_BIT) - -#define IPA_ETH_DEV_EV_RX_INT ipa_eth_dev_ev(RX_INT) -#define IPA_ETH_DEV_EV_TX_INT ipa_eth_dev_ev(TX_INT) -#define IPA_ETH_DEV_EV_RX_PTR ipa_eth_dev_ev(RX_PTR) -#define IPA_ETH_DEV_EV_TX_PTR ipa_eth_dev_ev(TX_PTR) - -/** - * enum ipa_eth_channel_dir - Direction of a ring / channel - * @IPA_ETH_CH_DIR_RX: Traffic flowing from device to IPA - * @IPA_ETH_CH_DIR_TX: Traffic flowing from IPA to device - */ -enum ipa_eth_channel_dir { - IPA_ETH_CH_DIR_RX, - IPA_ETH_CH_DIR_TX, -}; - -#define IPA_ETH_DIR_RX IPA_ETH_CH_DIR_RX -#define IPA_ETH_DIR_TX IPA_ETH_CH_DIR_TX - -/** - * enum ipa_eth_offload_state - Offload state of an ethernet device - * @IPA_ETH_OF_ST_DEINITED: No offload path resources are allocated - * @IPA_ETH_OF_ST_INITED: Offload path resources are allocated, but not started - * @IPA_ETH_OF_ST_STARTED: Offload path is started and ready to handle traffic - * @IPA_ETH_OF_ST_ERROR: One or more offload path components are in error state - * @IPA_ETH_OF_ST_RECOVERY: Offload path is attempting to recover from error - */ -enum ipa_eth_offload_state { - IPA_ETH_OF_ST_DEINITED = 0, - IPA_ETH_OF_ST_INITED, - IPA_ETH_OF_ST_STARTED, - IPA_ETH_OF_ST_ERROR, - IPA_ETH_OF_ST_RECOVERY, - IPA_ETH_OF_ST_MAX, -}; - -/** - * enum ipa_eth_offload_state - States of the network interface - * @IPA_ETH_IF_ST_UP: Network interface is up in software/Linux - * @IPA_ETH_IF_ST_LOWER_UP: Network interface PHY link is up / cable connected - */ -enum ipa_eth_interface_states { - IPA_ETH_IF_ST_UP, - IPA_ETH_IF_ST_LOWER_UP, - IPA_ETH_IF_ST_MAX, -}; - -struct ipa_eth_device; -struct ipa_eth_net_driver; - -/** - * struct ipa_eth_resource - Memory based resource info - * @size: Size of the memory accessible - * @vaddr: Kernel mapped address of the resource - * @daddr: DMA address of the resource - * @paddr: Physical address of the resource - */ -struct ipa_eth_resource { - size_t size; - - void *vaddr; - dma_addr_t daddr; - phys_addr_t paddr; -}; - -/** - * ipa_eth_mem_it_t() - Callback used by the ipa_eth_dma_allocator.walk() as it - * iterates through each contiguous chunk of memory - * @eth_dev: Device to which the memory belong - * @cmem: Contigous mapping. If cmem->paddr is NULL, it could be determined from - * ipa_eth_dma_allocator.paddr(cmem->vaddr) - * @arg: Private argument passed to ipa_eth_dma_allocator.walk() that can hold - * necessary context required by the iterator - * - * See &struct ipa_eth_dma_allocator for more details. - */ -typedef int (*ipa_eth_mem_it_t)(struct ipa_eth_device *eth_dev, - const struct ipa_eth_resource *cmem, void *arg); - -/** - * struct ipa_eth_dma_allocator - Custom DMA memory allocator interface - * @name: Name of the allocator - */ -struct ipa_eth_dma_allocator { - const char *name; - - /** - * .paddr() - Convert a virtual address previously allocated by this - * ipa_eth_dma_allocator, to physical address - * @eth_dev: Device which owns the memory - * @vaddr: Kernel mapped address of the resource - * - * Return: Physical address of @vaddr - */ - phys_addr_t (*paddr)(struct ipa_eth_device *eth_dev, - const void *vaddr); - - /** - * .alloc() - Allocates DMA memory for a given device - * @eth_dev: Device which will own the memory. Note that @eth_dev->dev - * should be a valid struct device pointer. - * @size: Minimum number of bytes to allocate - * @gfp: Kernel GFP_* flags to use, if the allocator supports it - * @mem: Memory info if the allocation was successful. @mem->paddr may - * be NUll or not valid, use ipa_eth_dma_allocator.paddr() to get - * the correct physical address for a page of virtual address. - * - * This API callback is typically called by the network driver to - * allocate memory for descriptor rings, buffers, etc. Any memory - * allocated by this callback should be traversable by the .remap() - * API callback implementation. - * - * Return: 0 on success, non-zero otherwise - */ - int (*alloc)(struct ipa_eth_device *eth_dev, size_t size, gfp_t gfp, - struct ipa_eth_resource *mem); - - /** - * .free() - Free a memory previously allocated using .alloc() API - * @eth_dev: Device which owns the memory - * @mem: Memory info - * - * This API callback is typically called by the network driver to free - * memory resources associated with descriptor rings, buffers, etc. once - * their associated hardware queues are stopped. - */ - void (*free)(struct ipa_eth_device *eth_dev, - struct ipa_eth_resource *mem); - - /** - * .walk() - Iterates over memory previously allocated by .alloc() - * @eth_dev: Device which owns the memory - * @mem: Allocated memory that need to be iterated over - * @it: Iterator that is invoked for each chunk (typically PAGE_SIZE) - * of memory - * @arg: Private argument passed to @it for each invocation - * - * This API is expected to iterate over pieces of an allocated memory - * for typical purposes remapping to additional peripherals, and later - * unmapping from them. .walk() is responsible to invoke the iterator - * @it for each mappable region within an allocated space, which is - * typically page sized. - * - * Iteration is expected to stop either when the page walk completes or - * when any of the @it() invocations return failure. Iterator can add - * additional checks for memory alignments, addressability, etc. that - * can also fail. In any case, the API is expected to return the number - * of bytes (<= mem->size) from the given memory that was successfully - * iterated. Although the API may re-align memory regions while invoking - * the iterator, it should still return only the number of bytes of the - * original memory that was successfully iterated. - * - * .walk() API could be used for any purpose of iteration, not limited - * to memory mapping and unmapping. Implementation of the API should be - * flexible for generalized use-cases. Caller of this API should always - * check for the return value against @mem->size to make sure if the - * intended operation was successful, and explicitly revert any partial - * operation. Ex, - * - * mapped_bytes = alctr->walk(dev, mem_region, iommu_mapper, map_ctx); - * if (mapped_bytes != mem_region->size) { - * struct ipa_eth_resource unmap_region = *mem_region; - * unmap_region.size = mapped_bytes; - * alctr->walk(dev, unmap_region, smmu_map, ctx); - * return -ENOMEM; - * } - * - * Return: the number of bytes in @mem that were successfully iterated - */ - size_t (*walk)(struct ipa_eth_device *eth_dev, - const struct ipa_eth_resource *mem, - ipa_eth_mem_it_t it, void *arg); -}; - -/** - * enum ipa_eth_hw_type - Types of hardware used in an offload path - * @IPA_ETH_HW_UC: IPA uC - * @IPA_ETH_HW_GSI: GSI - * @IPA_ETH_HW_IPA: IPA hardware/endpoint - */ -enum ipa_eth_hw_type { - IPA_ETH_HW_UC, - IPA_ETH_HW_GSI, - IPA_ETH_HW_IPA, - IPA_ETH_HW_MAX, -}; - -/** - * struct ipa_eth_hw_map_param - Params for mapping memory to IPA hardware - * @map: If true, perform mapping of memory to the given hardware - * @sym: If true, performs symmetric mapping where IO virtual address (IOVA) - * used is the same as the one used on original device. - * @read: Memory should be readable by hardware - * @write: Memory should be writable by hardware - */ -struct ipa_eth_hw_map_param { - bool map; - bool sym; - bool read; - bool write; -}; - -/** - * struct ipa_eth_desc_params - Params for allocating descriptor memory - * @size: Size of each descriptor. This field is usually filled in by the - * network driver. - * @count: Number of descriptors to be allocated - * @allocator: DMA allocator to be used for IO memory allocation and mapping - * @hw_map_params: Info on memory mapping requirements to IPA CBs - */ -struct ipa_eth_desc_params { - size_t size; - size_t count; - struct ipa_eth_dma_allocator *allocator; - struct ipa_eth_hw_map_param hw_map_params[IPA_ETH_HW_MAX]; -}; - -/** - * struct ipa_eth_buff_params - Params for allocating buffer memory - * @size: Size of data buffer associated with a descriptor - * @count: Number of buffers. This ield is usually filled in by the network - * driver and is typically the same value as descriptor count. - * @allocator: DMA allocator to be used for IO memory allocation and mapping - * @maps: Info on memory mapping requirements to IPA CBs - */ -struct ipa_eth_buff_params { - size_t size; - size_t count; - struct ipa_eth_dma_allocator *allocator; - struct ipa_eth_hw_map_param hw_map_params[IPA_ETH_HW_MAX]; -}; - -/** - * struct ipa_eth_channel_mem_params - Params for various channel memory - * @desc: Descriptor memory parameters - * @buff: Buffer memory parameters - */ -struct ipa_eth_channel_mem_params { - struct ipa_eth_desc_params desc; - struct ipa_eth_buff_params buff; -}; - -/** - * struct ipa_eth_channel_mem - Represents a piece of memory used by the - * network device channel for descriptrors, buffers, etc. - * @mem_list_entry: list entry in either of ipa_eth_channel.desc_mem or - * ipa_eth_channel.buff_mem - * @mem: Memory mapping info as used by the network device/driver - * @cb_mem: Memory mapping info as used by each of IPA context banks. When a - * mapping is present, cb_mem[cb_type].size would be non-zero. - */ -struct ipa_eth_channel_mem { - struct list_head mem_list_entry; - - struct ipa_eth_resource mem; - -/* private: for internal use by offload sub-system */ - struct ipa_eth_resource *cb_mem; -}; - -/** - * struct ipa_eth_channel - Represents a network device channel - * @channel_list: list entry in either of ipa_eth_device.rx_channels or - * ipa_eth_device.tx_channels - * @nd_priv: Private field for use by network driver - * @events: Events supported by the channel - * @features: Features enabled in the channel - * @direction: Channel direction - * @mem_params: Channel memory params filled in collectively by Offload driver, - * Network driver and Offload sub-system - * @queue: Network device queue/ring number - * @desc_mem: Descriptor ring memory list - * @buff_mem: Data buffer memory list - * @od_priv: Private field for use by offload driver - * @eth_dev: Associated ipa_eth_device - * @ipa_client: IPA client type enum to be used for the channel - * @ipa_ep_num: IPA endpoint number configured for the client type - * @process_skb: Callback to be called for processing IPA exception - * packets received from the IPA endpoint - * @ipa_priv: Private field to be used by offload subsystem - * @exception_total: Total number of exception packets received - * @exception_drops: Total number of dropped exception packets - * @exception_loopback: Total number of exception packets looped back - * into IPA - */ -struct ipa_eth_channel { - struct list_head channel_list; - - /* fields managed by network driver */ - void *nd_priv; - - unsigned long events; - unsigned long features; - enum ipa_eth_channel_dir direction; - struct ipa_eth_channel_mem_params mem_params; - - int queue; - struct list_head desc_mem; - struct list_head buff_mem; - - /* fields managed by offload driver */ - void *od_priv; - struct ipa_eth_device *eth_dev; - - enum ipa_client_type ipa_client; - int ipa_ep_num; - - int (*process_skb)(struct ipa_eth_channel *ch, struct sk_buff *skb); - - /* fields managed by offload subsystem */ - void *ipa_priv; - - u64 exception_total; - u64 exception_drops; - u64 exception_loopback; -}; - -#define IPA_ETH_CH_IS_RX(ch) ((ch)->direction == IPA_ETH_CH_DIR_RX) -#define IPA_ETH_CH_IS_TX(ch) ((ch)->direction == IPA_ETH_CH_DIR_TX) - -/** - * struct ipa_eth_device - Represents an ethernet device - * @device_list: Entry in the global offload device list - * @bus_device_list: Entry in the per-bus offload device list - * @net_dev: Netdev registered by the network driver - * @nd_priv: Private field for use by network driver - * @od_priv: Private field for use by offload driver - * @rx_channels: Rx channels allocated for the offload path - * @tx_channels: Tx channels allocated for the offload path - * @ipa_rx_intf: IPA properties Rx interface - * @ipa_tx_intf: IPA properties Tx interface - * @of_state: Offload state of the device - * @dev: Pointer to struct device - * @nd: IPA offload net driver associated with the device - * @od: IPA offload driver that is managing the device - * @netdevice_nb: Notifier block for receiving netdev events from the - * network device (to monitor link state changes) - * @init: Allowed to initialize offload path for the device - * @start: Allowed to start offload data path for the device - * @start_on_wakeup: Allow start upon wake up by device - * @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 - * @flags: Device flags - * @if_state: Interface state - one or more bit numbers IPA_ETH_IF_ST_* - * @pm_handle: IPA PM client handle for the device - * @phdr_v4_handle: Partial header handle for IPv4 - * @phdr_v6_handle: Partial header handle for IPv6 - * @bus_priv: Private field for use by offload subsystem bus layer - * @ipa_priv: Private field for use by offload subsystem - * @debugfs: Debugfs root for the device - * @refresh: Work struct used to perform device refresh - */ -struct ipa_eth_device { - struct list_head device_list; - struct list_head bus_device_list; - - /* fields managed by the network driver */ - struct net_device *net_dev; - void *nd_priv; - - /* fields managed by offload driver */ - void *od_priv; - - /* fields managed by offload subsystem */ - struct list_head rx_channels; - struct list_head tx_channels; - - struct ipa_rx_intf ipa_rx_intf; - struct ipa_tx_intf ipa_tx_intf; - - enum ipa_eth_offload_state of_state; - - struct device *dev; - struct ipa_eth_net_driver *nd; - struct ipa_eth_offload_driver *od; - - struct notifier_block netdevice_nb; - - bool init; - bool start; - - bool start_on_wakeup; - bool start_on_resume; - u32 start_on_timeout; - struct timer_list start_timer; - - unsigned long flags; - unsigned long if_state; - - u32 pm_handle; - u32 phdr_v4_handle; - u32 phdr_v6_handle; - - void *bus_priv; - void *ipa_priv; - struct dentry *debugfs; - - struct work_struct refresh; -}; - -/** - * enum ipa_eth_device_event - Events related to device state - * @IPA_ETH_DEV_RESET_PREPARE: Device is entering reset and is requesting - * offload path to stop using the device - * @IPA_ETH_DEV_RESET_COMPLETE: Device has completed resetting and is - * requesting offload path to resume its operations - * @IPA_ETH_DEV_ADD_MACSEC_IF: A MACSec interface is coming up - * @IPA_ETH_DEV_DEL_MACSEC_IF: A MACSec interface is going down - */ -enum ipa_eth_device_event { - IPA_ETH_DEV_RESET_PREPARE, - IPA_ETH_DEV_RESET_COMPLETE, - IPA_ETH_DEV_ADD_MACSEC_IF, - IPA_ETH_DEV_DEL_MACSEC_IF, - IPA_ETH_DEV_EVENT_COUNT, -}; - -int ipa_eth_device_notify(struct ipa_eth_device *eth_dev, - enum ipa_eth_device_event event, void *data); - -/** - * struct ipa_eth_net_ops - Network device operations required for IPA offload - */ -struct ipa_eth_net_ops { - /** - * .open_device() - Initialize the network device if needed and fill in - * @eth_dev->net_dev field - * @eth_dev: Device to initialize - * - * Some network perform complete device initialization only in driver - * .ndo_open(). Offload sub-system may try to use the network hardware - * for offload path even before .ndo_open() is called. .open_device() is - * expected to perform all device initialization that may be required - * to make the hardware functional for offload path, irrespective of - * whether .ndo_open() gets called. - * - * Once the device is initialized, .open_device() should initialize the - * @eth_dev->net_dev field with the driver struct net_device pointer - * before returning. - * - * Return: 0 on success, negative errno otherwise - */ - int (*open_device)(struct ipa_eth_device *eth_dev); - - /** - * .close_device() Deinitialize the device if required - * @eth_dev: Device to deinitialize - * - * This is called when the offload data path is deinitialized and the - * offload subsystem do not see any offload drivers registered to - * handle the device anymore. Typically the device is maintained in - * the initialized state until both Linux and IPA data paths are - * deinitialized. - * - * Return: 0 on success, negative errno otherwise - */ - void (*close_device)(struct ipa_eth_device *eth_dev); - - /** - * .save_regs() - Save registers for debugging - * @eth_dev: Offloaded device - * @regs: if not NULL, write saved data address to the given pointer - * @size: if not NULL, write the size of saved data to the given pointer - * - * Return: 0 on success, errno otherwise. - */ - int (*save_regs)(struct ipa_eth_device *eth_dev, - void **regs, size_t *size); -}; - -/** - * struct ipa_eth_net_driver - Network driver to be registered with IPA offload - * subsystem - * @driver_list: Entry in the offload sub-system network driver list - * @bus_driver_list: Entry in the bus specific driver list - * @ops: Network device operations - * @bus_priv: Bus private data - * @name: Name of the network driver - * @bus: Pointer to the bus object. Must use &pci_bus_type for PCI and - * &platform_bus_type for platform drivers. This property is used by the - * offload subsystem to deduce the network driver's original pci_driver - * or plaform_driver object from the @driver argument during driver - * registration. - * Beyond registration, the bus type is also used to deduce a pci_dev or - * platform_device object from a `struct device` pointer. - * @driver: Pointer to the device_driver object embedded within a PCI/plaform - * driver object used by the network driver - * @events: Events supported by the network device - * @features: Capabilities of the network device - * @debugfs: Debugfs directory for the device - */ -struct ipa_eth_net_driver { - struct list_head driver_list; - struct list_head bus_driver_list; - - struct ipa_eth_net_ops *ops; - void *bus_priv; - - const char *name; - struct bus_type *bus; - struct device_driver *driver; - - unsigned long events; - unsigned long features; - - struct dentry *debugfs; -}; - -int ipa_eth_register_net_driver(struct ipa_eth_net_driver *nd); -void ipa_eth_unregister_net_driver(struct ipa_eth_net_driver *nd); - -/** - * struct ipa_eth_offload_link_stats - Stats for each link within an - * offload data path - * @valid: Stats are not available for the link - * @events: Number of events reported to the link - * @frames: Number of frames received by the link - * @packets: Number of packets received by the link - * @octets: Number of bytes received by the link - */ -struct ipa_eth_offload_link_stats { - bool valid; - u64 events; - u64 frames; - u64 packets; - u64 octets; -}; - -struct ipa_eth_offload_ch_stats { - struct ipa_eth_offload_link_stats ndev; - struct ipa_eth_offload_link_stats host; - struct ipa_eth_offload_link_stats uc; - struct ipa_eth_offload_link_stats gsi; - struct ipa_eth_offload_link_stats ipa; -}; - -struct ipa_eth_offload_stats { - struct ipa_eth_offload_ch_stats rx; - struct ipa_eth_offload_ch_stats tx; -}; - -/** - * struct ipa_eth_offload_ops - Offload operations provided by an offload driver - */ -struct ipa_eth_offload_ops { - /** - * .pair() - Pair a new device, if compatible, with the offload driver - * @eth_dev: Device to pair with the offload driver - * - * This API is called by offload sub-system in order to pair an ethernet - * device with the offload driver. Typically this API is called soon - * after the device probe is completed and registered with the offload - * sub-system. When the API is called, offload driver is expected to - * check if the device is compatible with the driver and that the driver - * has enough offload resources for offloading the device to IPA. - * - * The API implementation can expect the eth_dev->dev to have been - * already initialized by the offload subsystem, from which a bus - * specific device pointer (pci_dev, platform_device, etc.) can be - * derived. The API is expected to perform at least the following: - * - * 1. Ensure the device is compatible with the offload driver. For - * plaform drivers, the .compatible DT property could be used while - * PCI IDs could be used for matching with a PCI device. - * 2. If multiple network devices of the same type can be managed by the - * offload driver, it may attempt to pair @eth_dev with an available - * resource set for a single instance. - * - * Return: 0 on success, negative errno otherwise - */ - int (*pair)(struct ipa_eth_device *eth_dev); - - /** - * .unpair() - Unpair a device from the offload driver - * @eth_dev: Device to unpair - * - * Unpairing the device from offload driver should make sure that all - * resources allocated for the device are freed and the data path is - * stopped and deinitialized, and device is readied for removal. The - * implementation should be able to handle plug-n-play devices by not - * assuming the device to be connected anymore to the bus/system when - * this API is called. - */ - void (*unpair)(struct ipa_eth_device *eth_dev); - - /** - * .init_tx() - Initialize offload path in Tx direction - * - * Return: 0 on success, negative errno otherwise - */ - int (*init_tx)(struct ipa_eth_device *eth_dev); - - /** - * .start_tx() - Start offload path in Tx direction - * - * Return: 0 on success, negative errno otherwise - */ - int (*start_tx)(struct ipa_eth_device *eth_dev); - - /** - * .stop_tx() - Stop offload path in Tx direction - * - * Return: 0 on success, negative errno otherwise - */ - int (*stop_tx)(struct ipa_eth_device *eth_dev); - - /** - * .deinit_tx() - Deinitialize offload path in Tx direction - * - * Return: 0 on success, negative errno otherwise - */ - int (*deinit_tx)(struct ipa_eth_device *eth_dev); - - /** - * .init_rx() - Initialize offload path in Rx direction - * - * Return: 0 on success, negative errno otherwise - */ - int (*init_rx)(struct ipa_eth_device *eth_dev); - - /** - * .start_rx() - Start offload path in Rx direction - * - * Return: 0 on success, negative errno otherwise - */ - int (*start_rx)(struct ipa_eth_device *eth_dev); - - /** - * .stop_rx() - Stop offload path in Rx direction - * - * Return: 0 on success, negative errno otherwise - */ - int (*stop_rx)(struct ipa_eth_device *eth_dev); - - /** - * .deinit_rx() - Deinitialize offload path in Rx direction - * - * Return: 0 on success, negative errno otherwise - */ - int (*deinit_rx)(struct ipa_eth_device *eth_dev); - - /** - * .get_stats() - Get offload statistics - * - * Return: 0 on success, negative errno otherwise - */ - int (*get_stats)(struct ipa_eth_device *eth_dev, - struct ipa_eth_offload_stats *stats); - - /** - * .clear_stats() - Clear offload statistics - * - * Return: 0 on success, negative errno otherwise - */ - int (*clear_stats)(struct ipa_eth_device *eth_dev); - - /** - * .save_regs() - Save registers for debugging - * @eth_dev: Offloaded device - * @regs: if not NULL, write saved data address to the given pointer - * @size: if not NULL, write the size of saved data to the given pointer - * - * Return: 0 on success, errno otherwise. - */ - int (*save_regs)(struct ipa_eth_device *eth_dev, - void **regs, size_t *size); - - /** - * .prepare_reset() - Prepare offload path for netdev reset - * @eth_dev: Offloaded device - * @data: Private data the network driver has provided - * - * Return: 0 on success, errno otherwise. - */ - int (*prepare_reset)(struct ipa_eth_device *eth_dev, void *data); - - /** - * .complete_reset() - Netdev reset completed, offload path can resume - * @eth_dev: Offloaded device - * @data: Private data the network driver has provided - * - * Return: 0 on success, errno otherwise. - */ - int (*complete_reset)(struct ipa_eth_device *eth_dev, void *data); - -}; - -/** - * struct ipa_eth_offload_driver - Offload driver to be registered with the - * offload sub-system - * @driver_list: Entry in the global offload driver list - * @mutex: Mutex to protect offload driver struct - * @name: Name of the offload driver - * @bus: Supported network device bus type - * @ops: Offload operations - * @bus_ops: Bus level callbacks and operations for the offload driver - * @debugfs: Debugfs directory for the offload driver - */ -struct ipa_eth_offload_driver { - struct list_head driver_list; - struct mutex mutex; - - const char *name; - struct bus_type *bus; - - struct ipa_eth_offload_ops *ops; - - struct dentry *debugfs; -}; - -int ipa_eth_register_offload_driver(struct ipa_eth_offload_driver *od); -void ipa_eth_unregister_offload_driver(struct ipa_eth_offload_driver *od); - -/* IPC logging interface */ - -#define ipa_eth_ipc_do_log(ipcbuf, fmt, args...) \ - do { \ - void *__buf = (ipcbuf); \ - ipc_log_string(__buf, " %s:%d " fmt "\n", \ - __func__, __LINE__, ## args); \ - } while (0) - -#define ipa_eth_ipc_log(fmt, args...) \ - do { \ - void *ipa_eth_get_ipc_logbuf(void); \ - ipa_eth_ipc_do_log(ipa_eth_get_ipc_logbuf(), \ - fmt, ## args); \ - } while (0) - -#define ipa_eth_ipc_dbg(fmt, args...) \ - do { \ - void *ipa_eth_get_ipc_logbuf_dbg(void); \ - ipa_eth_ipc_do_log(ipa_eth_get_ipc_logbuf_dbg(), \ - fmt, ## args); \ - } while (0) - - /* New architecture prototypes */ typedef void (*ipa_eth_ready_cb)(void *user_data);