18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci *  FUJITSU Extended Socket Network Device driver
48c2ecf20Sopenharmony_ci *  Copyright (c) 2015 FUJITSU LIMITED
58c2ecf20Sopenharmony_ci */
68c2ecf20Sopenharmony_ci
78c2ecf20Sopenharmony_ci#include <linux/module.h>
88c2ecf20Sopenharmony_ci#include <linux/types.h>
98c2ecf20Sopenharmony_ci#include <linux/nls.h>
108c2ecf20Sopenharmony_ci#include <linux/platform_device.h>
118c2ecf20Sopenharmony_ci#include <linux/netdevice.h>
128c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
138c2ecf20Sopenharmony_ci
148c2ecf20Sopenharmony_ci#include "fjes.h"
158c2ecf20Sopenharmony_ci#include "fjes_trace.h"
168c2ecf20Sopenharmony_ci
178c2ecf20Sopenharmony_ci#define MAJ 1
188c2ecf20Sopenharmony_ci#define MIN 2
198c2ecf20Sopenharmony_ci#define DRV_VERSION __stringify(MAJ) "." __stringify(MIN)
208c2ecf20Sopenharmony_ci#define DRV_NAME	"fjes"
218c2ecf20Sopenharmony_cichar fjes_driver_name[] = DRV_NAME;
228c2ecf20Sopenharmony_cichar fjes_driver_version[] = DRV_VERSION;
238c2ecf20Sopenharmony_cistatic const char fjes_driver_string[] =
248c2ecf20Sopenharmony_ci		"FUJITSU Extended Socket Network Device Driver";
258c2ecf20Sopenharmony_cistatic const char fjes_copyright[] =
268c2ecf20Sopenharmony_ci		"Copyright (c) 2015 FUJITSU LIMITED";
278c2ecf20Sopenharmony_ci
288c2ecf20Sopenharmony_ciMODULE_AUTHOR("Taku Izumi <izumi.taku@jp.fujitsu.com>");
298c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("FUJITSU Extended Socket Network Device Driver");
308c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
318c2ecf20Sopenharmony_ciMODULE_VERSION(DRV_VERSION);
328c2ecf20Sopenharmony_ci
338c2ecf20Sopenharmony_ci#define ACPI_MOTHERBOARD_RESOURCE_HID "PNP0C02"
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_cistatic int fjes_request_irq(struct fjes_adapter *);
368c2ecf20Sopenharmony_cistatic void fjes_free_irq(struct fjes_adapter *);
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_cistatic int fjes_open(struct net_device *);
398c2ecf20Sopenharmony_cistatic int fjes_close(struct net_device *);
408c2ecf20Sopenharmony_cistatic int fjes_setup_resources(struct fjes_adapter *);
418c2ecf20Sopenharmony_cistatic void fjes_free_resources(struct fjes_adapter *);
428c2ecf20Sopenharmony_cistatic netdev_tx_t fjes_xmit_frame(struct sk_buff *, struct net_device *);
438c2ecf20Sopenharmony_cistatic void fjes_raise_intr_rxdata_task(struct work_struct *);
448c2ecf20Sopenharmony_cistatic void fjes_tx_stall_task(struct work_struct *);
458c2ecf20Sopenharmony_cistatic void fjes_force_close_task(struct work_struct *);
468c2ecf20Sopenharmony_cistatic irqreturn_t fjes_intr(int, void*);
478c2ecf20Sopenharmony_cistatic void fjes_get_stats64(struct net_device *, struct rtnl_link_stats64 *);
488c2ecf20Sopenharmony_cistatic int fjes_change_mtu(struct net_device *, int);
498c2ecf20Sopenharmony_cistatic int fjes_vlan_rx_add_vid(struct net_device *, __be16 proto, u16);
508c2ecf20Sopenharmony_cistatic int fjes_vlan_rx_kill_vid(struct net_device *, __be16 proto, u16);
518c2ecf20Sopenharmony_cistatic void fjes_tx_retry(struct net_device *, unsigned int txqueue);
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_cistatic int fjes_acpi_add(struct acpi_device *);
548c2ecf20Sopenharmony_cistatic int fjes_acpi_remove(struct acpi_device *);
558c2ecf20Sopenharmony_cistatic acpi_status fjes_get_acpi_resource(struct acpi_resource *, void*);
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_cistatic int fjes_probe(struct platform_device *);
588c2ecf20Sopenharmony_cistatic int fjes_remove(struct platform_device *);
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_cistatic int fjes_sw_init(struct fjes_adapter *);
618c2ecf20Sopenharmony_cistatic void fjes_netdev_setup(struct net_device *);
628c2ecf20Sopenharmony_cistatic void fjes_irq_watch_task(struct work_struct *);
638c2ecf20Sopenharmony_cistatic void fjes_watch_unshare_task(struct work_struct *);
648c2ecf20Sopenharmony_cistatic void fjes_rx_irq(struct fjes_adapter *, int);
658c2ecf20Sopenharmony_cistatic int fjes_poll(struct napi_struct *, int);
668c2ecf20Sopenharmony_ci
678c2ecf20Sopenharmony_cistatic const struct acpi_device_id fjes_acpi_ids[] = {
688c2ecf20Sopenharmony_ci	{ACPI_MOTHERBOARD_RESOURCE_HID, 0},
698c2ecf20Sopenharmony_ci	{"", 0},
708c2ecf20Sopenharmony_ci};
718c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(acpi, fjes_acpi_ids);
728c2ecf20Sopenharmony_ci
738c2ecf20Sopenharmony_cistatic struct acpi_driver fjes_acpi_driver = {
748c2ecf20Sopenharmony_ci	.name = DRV_NAME,
758c2ecf20Sopenharmony_ci	.class = DRV_NAME,
768c2ecf20Sopenharmony_ci	.owner = THIS_MODULE,
778c2ecf20Sopenharmony_ci	.ids = fjes_acpi_ids,
788c2ecf20Sopenharmony_ci	.ops = {
798c2ecf20Sopenharmony_ci		.add = fjes_acpi_add,
808c2ecf20Sopenharmony_ci		.remove = fjes_acpi_remove,
818c2ecf20Sopenharmony_ci	},
828c2ecf20Sopenharmony_ci};
838c2ecf20Sopenharmony_ci
848c2ecf20Sopenharmony_cistatic struct platform_driver fjes_driver = {
858c2ecf20Sopenharmony_ci	.driver = {
868c2ecf20Sopenharmony_ci		.name = DRV_NAME,
878c2ecf20Sopenharmony_ci	},
888c2ecf20Sopenharmony_ci	.probe = fjes_probe,
898c2ecf20Sopenharmony_ci	.remove = fjes_remove,
908c2ecf20Sopenharmony_ci};
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_cistatic struct resource fjes_resource[] = {
938c2ecf20Sopenharmony_ci	{
948c2ecf20Sopenharmony_ci		.flags = IORESOURCE_MEM,
958c2ecf20Sopenharmony_ci		.start = 0,
968c2ecf20Sopenharmony_ci		.end = 0,
978c2ecf20Sopenharmony_ci	},
988c2ecf20Sopenharmony_ci	{
998c2ecf20Sopenharmony_ci		.flags = IORESOURCE_IRQ,
1008c2ecf20Sopenharmony_ci		.start = 0,
1018c2ecf20Sopenharmony_ci		.end = 0,
1028c2ecf20Sopenharmony_ci	},
1038c2ecf20Sopenharmony_ci};
1048c2ecf20Sopenharmony_ci
1058c2ecf20Sopenharmony_cistatic bool is_extended_socket_device(struct acpi_device *device)
1068c2ecf20Sopenharmony_ci{
1078c2ecf20Sopenharmony_ci	struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL};
1088c2ecf20Sopenharmony_ci	char str_buf[sizeof(FJES_ACPI_SYMBOL) + 1];
1098c2ecf20Sopenharmony_ci	union acpi_object *str;
1108c2ecf20Sopenharmony_ci	acpi_status status;
1118c2ecf20Sopenharmony_ci	int result;
1128c2ecf20Sopenharmony_ci
1138c2ecf20Sopenharmony_ci	status = acpi_evaluate_object(device->handle, "_STR", NULL, &buffer);
1148c2ecf20Sopenharmony_ci	if (ACPI_FAILURE(status))
1158c2ecf20Sopenharmony_ci		return false;
1168c2ecf20Sopenharmony_ci
1178c2ecf20Sopenharmony_ci	str = buffer.pointer;
1188c2ecf20Sopenharmony_ci	result = utf16s_to_utf8s((wchar_t *)str->string.pointer,
1198c2ecf20Sopenharmony_ci				 str->string.length, UTF16_LITTLE_ENDIAN,
1208c2ecf20Sopenharmony_ci				 str_buf, sizeof(str_buf) - 1);
1218c2ecf20Sopenharmony_ci	str_buf[result] = 0;
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_ci	if (strncmp(FJES_ACPI_SYMBOL, str_buf, strlen(FJES_ACPI_SYMBOL)) != 0) {
1248c2ecf20Sopenharmony_ci		kfree(buffer.pointer);
1258c2ecf20Sopenharmony_ci		return false;
1268c2ecf20Sopenharmony_ci	}
1278c2ecf20Sopenharmony_ci	kfree(buffer.pointer);
1288c2ecf20Sopenharmony_ci
1298c2ecf20Sopenharmony_ci	return true;
1308c2ecf20Sopenharmony_ci}
1318c2ecf20Sopenharmony_ci
1328c2ecf20Sopenharmony_cistatic int acpi_check_extended_socket_status(struct acpi_device *device)
1338c2ecf20Sopenharmony_ci{
1348c2ecf20Sopenharmony_ci	unsigned long long sta;
1358c2ecf20Sopenharmony_ci	acpi_status status;
1368c2ecf20Sopenharmony_ci
1378c2ecf20Sopenharmony_ci	status = acpi_evaluate_integer(device->handle, "_STA", NULL, &sta);
1388c2ecf20Sopenharmony_ci	if (ACPI_FAILURE(status))
1398c2ecf20Sopenharmony_ci		return -ENODEV;
1408c2ecf20Sopenharmony_ci
1418c2ecf20Sopenharmony_ci	if (!((sta & ACPI_STA_DEVICE_PRESENT) &&
1428c2ecf20Sopenharmony_ci	      (sta & ACPI_STA_DEVICE_ENABLED) &&
1438c2ecf20Sopenharmony_ci	      (sta & ACPI_STA_DEVICE_UI) &&
1448c2ecf20Sopenharmony_ci	      (sta & ACPI_STA_DEVICE_FUNCTIONING)))
1458c2ecf20Sopenharmony_ci		return -ENODEV;
1468c2ecf20Sopenharmony_ci
1478c2ecf20Sopenharmony_ci	return 0;
1488c2ecf20Sopenharmony_ci}
1498c2ecf20Sopenharmony_ci
1508c2ecf20Sopenharmony_cistatic int fjes_acpi_add(struct acpi_device *device)
1518c2ecf20Sopenharmony_ci{
1528c2ecf20Sopenharmony_ci	struct platform_device *plat_dev;
1538c2ecf20Sopenharmony_ci	acpi_status status;
1548c2ecf20Sopenharmony_ci
1558c2ecf20Sopenharmony_ci	if (!is_extended_socket_device(device))
1568c2ecf20Sopenharmony_ci		return -ENODEV;
1578c2ecf20Sopenharmony_ci
1588c2ecf20Sopenharmony_ci	if (acpi_check_extended_socket_status(device))
1598c2ecf20Sopenharmony_ci		return -ENODEV;
1608c2ecf20Sopenharmony_ci
1618c2ecf20Sopenharmony_ci	status = acpi_walk_resources(device->handle, METHOD_NAME__CRS,
1628c2ecf20Sopenharmony_ci				     fjes_get_acpi_resource, fjes_resource);
1638c2ecf20Sopenharmony_ci	if (ACPI_FAILURE(status))
1648c2ecf20Sopenharmony_ci		return -ENODEV;
1658c2ecf20Sopenharmony_ci
1668c2ecf20Sopenharmony_ci	/* create platform_device */
1678c2ecf20Sopenharmony_ci	plat_dev = platform_device_register_simple(DRV_NAME, 0, fjes_resource,
1688c2ecf20Sopenharmony_ci						   ARRAY_SIZE(fjes_resource));
1698c2ecf20Sopenharmony_ci	if (IS_ERR(plat_dev))
1708c2ecf20Sopenharmony_ci		return PTR_ERR(plat_dev);
1718c2ecf20Sopenharmony_ci
1728c2ecf20Sopenharmony_ci	device->driver_data = plat_dev;
1738c2ecf20Sopenharmony_ci
1748c2ecf20Sopenharmony_ci	return 0;
1758c2ecf20Sopenharmony_ci}
1768c2ecf20Sopenharmony_ci
1778c2ecf20Sopenharmony_cistatic int fjes_acpi_remove(struct acpi_device *device)
1788c2ecf20Sopenharmony_ci{
1798c2ecf20Sopenharmony_ci	struct platform_device *plat_dev;
1808c2ecf20Sopenharmony_ci
1818c2ecf20Sopenharmony_ci	plat_dev = (struct platform_device *)acpi_driver_data(device);
1828c2ecf20Sopenharmony_ci	platform_device_unregister(plat_dev);
1838c2ecf20Sopenharmony_ci
1848c2ecf20Sopenharmony_ci	return 0;
1858c2ecf20Sopenharmony_ci}
1868c2ecf20Sopenharmony_ci
1878c2ecf20Sopenharmony_cistatic acpi_status
1888c2ecf20Sopenharmony_cifjes_get_acpi_resource(struct acpi_resource *acpi_res, void *data)
1898c2ecf20Sopenharmony_ci{
1908c2ecf20Sopenharmony_ci	struct acpi_resource_address32 *addr;
1918c2ecf20Sopenharmony_ci	struct acpi_resource_irq *irq;
1928c2ecf20Sopenharmony_ci	struct resource *res = data;
1938c2ecf20Sopenharmony_ci
1948c2ecf20Sopenharmony_ci	switch (acpi_res->type) {
1958c2ecf20Sopenharmony_ci	case ACPI_RESOURCE_TYPE_ADDRESS32:
1968c2ecf20Sopenharmony_ci		addr = &acpi_res->data.address32;
1978c2ecf20Sopenharmony_ci		res[0].start = addr->address.minimum;
1988c2ecf20Sopenharmony_ci		res[0].end = addr->address.minimum +
1998c2ecf20Sopenharmony_ci			addr->address.address_length - 1;
2008c2ecf20Sopenharmony_ci		break;
2018c2ecf20Sopenharmony_ci
2028c2ecf20Sopenharmony_ci	case ACPI_RESOURCE_TYPE_IRQ:
2038c2ecf20Sopenharmony_ci		irq = &acpi_res->data.irq;
2048c2ecf20Sopenharmony_ci		if (irq->interrupt_count != 1)
2058c2ecf20Sopenharmony_ci			return AE_ERROR;
2068c2ecf20Sopenharmony_ci		res[1].start = irq->interrupts[0];
2078c2ecf20Sopenharmony_ci		res[1].end = irq->interrupts[0];
2088c2ecf20Sopenharmony_ci		break;
2098c2ecf20Sopenharmony_ci
2108c2ecf20Sopenharmony_ci	default:
2118c2ecf20Sopenharmony_ci		break;
2128c2ecf20Sopenharmony_ci	}
2138c2ecf20Sopenharmony_ci
2148c2ecf20Sopenharmony_ci	return AE_OK;
2158c2ecf20Sopenharmony_ci}
2168c2ecf20Sopenharmony_ci
2178c2ecf20Sopenharmony_cistatic int fjes_request_irq(struct fjes_adapter *adapter)
2188c2ecf20Sopenharmony_ci{
2198c2ecf20Sopenharmony_ci	struct net_device *netdev = adapter->netdev;
2208c2ecf20Sopenharmony_ci	int result = -1;
2218c2ecf20Sopenharmony_ci
2228c2ecf20Sopenharmony_ci	adapter->interrupt_watch_enable = true;
2238c2ecf20Sopenharmony_ci	if (!delayed_work_pending(&adapter->interrupt_watch_task)) {
2248c2ecf20Sopenharmony_ci		queue_delayed_work(adapter->control_wq,
2258c2ecf20Sopenharmony_ci				   &adapter->interrupt_watch_task,
2268c2ecf20Sopenharmony_ci				   FJES_IRQ_WATCH_DELAY);
2278c2ecf20Sopenharmony_ci	}
2288c2ecf20Sopenharmony_ci
2298c2ecf20Sopenharmony_ci	if (!adapter->irq_registered) {
2308c2ecf20Sopenharmony_ci		result = request_irq(adapter->hw.hw_res.irq, fjes_intr,
2318c2ecf20Sopenharmony_ci				     IRQF_SHARED, netdev->name, adapter);
2328c2ecf20Sopenharmony_ci		if (result)
2338c2ecf20Sopenharmony_ci			adapter->irq_registered = false;
2348c2ecf20Sopenharmony_ci		else
2358c2ecf20Sopenharmony_ci			adapter->irq_registered = true;
2368c2ecf20Sopenharmony_ci	}
2378c2ecf20Sopenharmony_ci
2388c2ecf20Sopenharmony_ci	return result;
2398c2ecf20Sopenharmony_ci}
2408c2ecf20Sopenharmony_ci
2418c2ecf20Sopenharmony_cistatic void fjes_free_irq(struct fjes_adapter *adapter)
2428c2ecf20Sopenharmony_ci{
2438c2ecf20Sopenharmony_ci	struct fjes_hw *hw = &adapter->hw;
2448c2ecf20Sopenharmony_ci
2458c2ecf20Sopenharmony_ci	adapter->interrupt_watch_enable = false;
2468c2ecf20Sopenharmony_ci	cancel_delayed_work_sync(&adapter->interrupt_watch_task);
2478c2ecf20Sopenharmony_ci
2488c2ecf20Sopenharmony_ci	fjes_hw_set_irqmask(hw, REG_ICTL_MASK_ALL, true);
2498c2ecf20Sopenharmony_ci
2508c2ecf20Sopenharmony_ci	if (adapter->irq_registered) {
2518c2ecf20Sopenharmony_ci		free_irq(adapter->hw.hw_res.irq, adapter);
2528c2ecf20Sopenharmony_ci		adapter->irq_registered = false;
2538c2ecf20Sopenharmony_ci	}
2548c2ecf20Sopenharmony_ci}
2558c2ecf20Sopenharmony_ci
2568c2ecf20Sopenharmony_cistatic const struct net_device_ops fjes_netdev_ops = {
2578c2ecf20Sopenharmony_ci	.ndo_open		= fjes_open,
2588c2ecf20Sopenharmony_ci	.ndo_stop		= fjes_close,
2598c2ecf20Sopenharmony_ci	.ndo_start_xmit		= fjes_xmit_frame,
2608c2ecf20Sopenharmony_ci	.ndo_get_stats64	= fjes_get_stats64,
2618c2ecf20Sopenharmony_ci	.ndo_change_mtu		= fjes_change_mtu,
2628c2ecf20Sopenharmony_ci	.ndo_tx_timeout		= fjes_tx_retry,
2638c2ecf20Sopenharmony_ci	.ndo_vlan_rx_add_vid	= fjes_vlan_rx_add_vid,
2648c2ecf20Sopenharmony_ci	.ndo_vlan_rx_kill_vid = fjes_vlan_rx_kill_vid,
2658c2ecf20Sopenharmony_ci};
2668c2ecf20Sopenharmony_ci
2678c2ecf20Sopenharmony_ci/* fjes_open - Called when a network interface is made active */
2688c2ecf20Sopenharmony_cistatic int fjes_open(struct net_device *netdev)
2698c2ecf20Sopenharmony_ci{
2708c2ecf20Sopenharmony_ci	struct fjes_adapter *adapter = netdev_priv(netdev);
2718c2ecf20Sopenharmony_ci	struct fjes_hw *hw = &adapter->hw;
2728c2ecf20Sopenharmony_ci	int result;
2738c2ecf20Sopenharmony_ci
2748c2ecf20Sopenharmony_ci	if (adapter->open_guard)
2758c2ecf20Sopenharmony_ci		return -ENXIO;
2768c2ecf20Sopenharmony_ci
2778c2ecf20Sopenharmony_ci	result = fjes_setup_resources(adapter);
2788c2ecf20Sopenharmony_ci	if (result)
2798c2ecf20Sopenharmony_ci		goto err_setup_res;
2808c2ecf20Sopenharmony_ci
2818c2ecf20Sopenharmony_ci	hw->txrx_stop_req_bit = 0;
2828c2ecf20Sopenharmony_ci	hw->epstop_req_bit = 0;
2838c2ecf20Sopenharmony_ci
2848c2ecf20Sopenharmony_ci	napi_enable(&adapter->napi);
2858c2ecf20Sopenharmony_ci
2868c2ecf20Sopenharmony_ci	fjes_hw_capture_interrupt_status(hw);
2878c2ecf20Sopenharmony_ci
2888c2ecf20Sopenharmony_ci	result = fjes_request_irq(adapter);
2898c2ecf20Sopenharmony_ci	if (result)
2908c2ecf20Sopenharmony_ci		goto err_req_irq;
2918c2ecf20Sopenharmony_ci
2928c2ecf20Sopenharmony_ci	fjes_hw_set_irqmask(hw, REG_ICTL_MASK_ALL, false);
2938c2ecf20Sopenharmony_ci
2948c2ecf20Sopenharmony_ci	netif_tx_start_all_queues(netdev);
2958c2ecf20Sopenharmony_ci	netif_carrier_on(netdev);
2968c2ecf20Sopenharmony_ci
2978c2ecf20Sopenharmony_ci	return 0;
2988c2ecf20Sopenharmony_ci
2998c2ecf20Sopenharmony_cierr_req_irq:
3008c2ecf20Sopenharmony_ci	fjes_free_irq(adapter);
3018c2ecf20Sopenharmony_ci	napi_disable(&adapter->napi);
3028c2ecf20Sopenharmony_ci
3038c2ecf20Sopenharmony_cierr_setup_res:
3048c2ecf20Sopenharmony_ci	fjes_free_resources(adapter);
3058c2ecf20Sopenharmony_ci	return result;
3068c2ecf20Sopenharmony_ci}
3078c2ecf20Sopenharmony_ci
3088c2ecf20Sopenharmony_ci/* fjes_close - Disables a network interface */
3098c2ecf20Sopenharmony_cistatic int fjes_close(struct net_device *netdev)
3108c2ecf20Sopenharmony_ci{
3118c2ecf20Sopenharmony_ci	struct fjes_adapter *adapter = netdev_priv(netdev);
3128c2ecf20Sopenharmony_ci	struct fjes_hw *hw = &adapter->hw;
3138c2ecf20Sopenharmony_ci	unsigned long flags;
3148c2ecf20Sopenharmony_ci	int epidx;
3158c2ecf20Sopenharmony_ci
3168c2ecf20Sopenharmony_ci	netif_tx_stop_all_queues(netdev);
3178c2ecf20Sopenharmony_ci	netif_carrier_off(netdev);
3188c2ecf20Sopenharmony_ci
3198c2ecf20Sopenharmony_ci	fjes_hw_raise_epstop(hw);
3208c2ecf20Sopenharmony_ci
3218c2ecf20Sopenharmony_ci	napi_disable(&adapter->napi);
3228c2ecf20Sopenharmony_ci
3238c2ecf20Sopenharmony_ci	spin_lock_irqsave(&hw->rx_status_lock, flags);
3248c2ecf20Sopenharmony_ci	for (epidx = 0; epidx < hw->max_epid; epidx++) {
3258c2ecf20Sopenharmony_ci		if (epidx == hw->my_epid)
3268c2ecf20Sopenharmony_ci			continue;
3278c2ecf20Sopenharmony_ci
3288c2ecf20Sopenharmony_ci		if (fjes_hw_get_partner_ep_status(hw, epidx) ==
3298c2ecf20Sopenharmony_ci		    EP_PARTNER_SHARED)
3308c2ecf20Sopenharmony_ci			adapter->hw.ep_shm_info[epidx]
3318c2ecf20Sopenharmony_ci				   .tx.info->v1i.rx_status &=
3328c2ecf20Sopenharmony_ci				~FJES_RX_POLL_WORK;
3338c2ecf20Sopenharmony_ci	}
3348c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&hw->rx_status_lock, flags);
3358c2ecf20Sopenharmony_ci
3368c2ecf20Sopenharmony_ci	fjes_free_irq(adapter);
3378c2ecf20Sopenharmony_ci
3388c2ecf20Sopenharmony_ci	cancel_delayed_work_sync(&adapter->interrupt_watch_task);
3398c2ecf20Sopenharmony_ci	cancel_work_sync(&adapter->unshare_watch_task);
3408c2ecf20Sopenharmony_ci	adapter->unshare_watch_bitmask = 0;
3418c2ecf20Sopenharmony_ci	cancel_work_sync(&adapter->raise_intr_rxdata_task);
3428c2ecf20Sopenharmony_ci	cancel_work_sync(&adapter->tx_stall_task);
3438c2ecf20Sopenharmony_ci
3448c2ecf20Sopenharmony_ci	cancel_work_sync(&hw->update_zone_task);
3458c2ecf20Sopenharmony_ci	cancel_work_sync(&hw->epstop_task);
3468c2ecf20Sopenharmony_ci
3478c2ecf20Sopenharmony_ci	fjes_hw_wait_epstop(hw);
3488c2ecf20Sopenharmony_ci
3498c2ecf20Sopenharmony_ci	fjes_free_resources(adapter);
3508c2ecf20Sopenharmony_ci
3518c2ecf20Sopenharmony_ci	return 0;
3528c2ecf20Sopenharmony_ci}
3538c2ecf20Sopenharmony_ci
3548c2ecf20Sopenharmony_cistatic int fjes_setup_resources(struct fjes_adapter *adapter)
3558c2ecf20Sopenharmony_ci{
3568c2ecf20Sopenharmony_ci	struct net_device *netdev = adapter->netdev;
3578c2ecf20Sopenharmony_ci	struct ep_share_mem_info *buf_pair;
3588c2ecf20Sopenharmony_ci	struct fjes_hw *hw = &adapter->hw;
3598c2ecf20Sopenharmony_ci	unsigned long flags;
3608c2ecf20Sopenharmony_ci	int result;
3618c2ecf20Sopenharmony_ci	int epidx;
3628c2ecf20Sopenharmony_ci
3638c2ecf20Sopenharmony_ci	mutex_lock(&hw->hw_info.lock);
3648c2ecf20Sopenharmony_ci	result = fjes_hw_request_info(hw);
3658c2ecf20Sopenharmony_ci	switch (result) {
3668c2ecf20Sopenharmony_ci	case 0:
3678c2ecf20Sopenharmony_ci		for (epidx = 0; epidx < hw->max_epid; epidx++) {
3688c2ecf20Sopenharmony_ci			hw->ep_shm_info[epidx].es_status =
3698c2ecf20Sopenharmony_ci			    hw->hw_info.res_buf->info.info[epidx].es_status;
3708c2ecf20Sopenharmony_ci			hw->ep_shm_info[epidx].zone =
3718c2ecf20Sopenharmony_ci			    hw->hw_info.res_buf->info.info[epidx].zone;
3728c2ecf20Sopenharmony_ci		}
3738c2ecf20Sopenharmony_ci		break;
3748c2ecf20Sopenharmony_ci	default:
3758c2ecf20Sopenharmony_ci	case -ENOMSG:
3768c2ecf20Sopenharmony_ci	case -EBUSY:
3778c2ecf20Sopenharmony_ci		adapter->force_reset = true;
3788c2ecf20Sopenharmony_ci
3798c2ecf20Sopenharmony_ci		mutex_unlock(&hw->hw_info.lock);
3808c2ecf20Sopenharmony_ci		return result;
3818c2ecf20Sopenharmony_ci	}
3828c2ecf20Sopenharmony_ci	mutex_unlock(&hw->hw_info.lock);
3838c2ecf20Sopenharmony_ci
3848c2ecf20Sopenharmony_ci	for (epidx = 0; epidx < (hw->max_epid); epidx++) {
3858c2ecf20Sopenharmony_ci		if ((epidx != hw->my_epid) &&
3868c2ecf20Sopenharmony_ci		    (hw->ep_shm_info[epidx].es_status ==
3878c2ecf20Sopenharmony_ci		     FJES_ZONING_STATUS_ENABLE)) {
3888c2ecf20Sopenharmony_ci			fjes_hw_raise_interrupt(hw, epidx,
3898c2ecf20Sopenharmony_ci						REG_ICTL_MASK_INFO_UPDATE);
3908c2ecf20Sopenharmony_ci			hw->ep_shm_info[epidx].ep_stats
3918c2ecf20Sopenharmony_ci				.send_intr_zoneupdate += 1;
3928c2ecf20Sopenharmony_ci		}
3938c2ecf20Sopenharmony_ci	}
3948c2ecf20Sopenharmony_ci
3958c2ecf20Sopenharmony_ci	msleep(FJES_OPEN_ZONE_UPDATE_WAIT * hw->max_epid);
3968c2ecf20Sopenharmony_ci
3978c2ecf20Sopenharmony_ci	for (epidx = 0; epidx < (hw->max_epid); epidx++) {
3988c2ecf20Sopenharmony_ci		if (epidx == hw->my_epid)
3998c2ecf20Sopenharmony_ci			continue;
4008c2ecf20Sopenharmony_ci
4018c2ecf20Sopenharmony_ci		buf_pair = &hw->ep_shm_info[epidx];
4028c2ecf20Sopenharmony_ci
4038c2ecf20Sopenharmony_ci		spin_lock_irqsave(&hw->rx_status_lock, flags);
4048c2ecf20Sopenharmony_ci		fjes_hw_setup_epbuf(&buf_pair->tx, netdev->dev_addr,
4058c2ecf20Sopenharmony_ci				    netdev->mtu);
4068c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&hw->rx_status_lock, flags);
4078c2ecf20Sopenharmony_ci
4088c2ecf20Sopenharmony_ci		if (fjes_hw_epid_is_same_zone(hw, epidx)) {
4098c2ecf20Sopenharmony_ci			mutex_lock(&hw->hw_info.lock);
4108c2ecf20Sopenharmony_ci			result =
4118c2ecf20Sopenharmony_ci			fjes_hw_register_buff_addr(hw, epidx, buf_pair);
4128c2ecf20Sopenharmony_ci			mutex_unlock(&hw->hw_info.lock);
4138c2ecf20Sopenharmony_ci
4148c2ecf20Sopenharmony_ci			switch (result) {
4158c2ecf20Sopenharmony_ci			case 0:
4168c2ecf20Sopenharmony_ci				break;
4178c2ecf20Sopenharmony_ci			case -ENOMSG:
4188c2ecf20Sopenharmony_ci			case -EBUSY:
4198c2ecf20Sopenharmony_ci			default:
4208c2ecf20Sopenharmony_ci				adapter->force_reset = true;
4218c2ecf20Sopenharmony_ci				return result;
4228c2ecf20Sopenharmony_ci			}
4238c2ecf20Sopenharmony_ci
4248c2ecf20Sopenharmony_ci			hw->ep_shm_info[epidx].ep_stats
4258c2ecf20Sopenharmony_ci				.com_regist_buf_exec += 1;
4268c2ecf20Sopenharmony_ci		}
4278c2ecf20Sopenharmony_ci	}
4288c2ecf20Sopenharmony_ci
4298c2ecf20Sopenharmony_ci	return 0;
4308c2ecf20Sopenharmony_ci}
4318c2ecf20Sopenharmony_ci
4328c2ecf20Sopenharmony_cistatic void fjes_free_resources(struct fjes_adapter *adapter)
4338c2ecf20Sopenharmony_ci{
4348c2ecf20Sopenharmony_ci	struct net_device *netdev = adapter->netdev;
4358c2ecf20Sopenharmony_ci	struct fjes_device_command_param param;
4368c2ecf20Sopenharmony_ci	struct ep_share_mem_info *buf_pair;
4378c2ecf20Sopenharmony_ci	struct fjes_hw *hw = &adapter->hw;
4388c2ecf20Sopenharmony_ci	bool reset_flag = false;
4398c2ecf20Sopenharmony_ci	unsigned long flags;
4408c2ecf20Sopenharmony_ci	int result;
4418c2ecf20Sopenharmony_ci	int epidx;
4428c2ecf20Sopenharmony_ci
4438c2ecf20Sopenharmony_ci	for (epidx = 0; epidx < hw->max_epid; epidx++) {
4448c2ecf20Sopenharmony_ci		if (epidx == hw->my_epid)
4458c2ecf20Sopenharmony_ci			continue;
4468c2ecf20Sopenharmony_ci
4478c2ecf20Sopenharmony_ci		mutex_lock(&hw->hw_info.lock);
4488c2ecf20Sopenharmony_ci		result = fjes_hw_unregister_buff_addr(hw, epidx);
4498c2ecf20Sopenharmony_ci		mutex_unlock(&hw->hw_info.lock);
4508c2ecf20Sopenharmony_ci
4518c2ecf20Sopenharmony_ci		hw->ep_shm_info[epidx].ep_stats.com_unregist_buf_exec += 1;
4528c2ecf20Sopenharmony_ci
4538c2ecf20Sopenharmony_ci		if (result)
4548c2ecf20Sopenharmony_ci			reset_flag = true;
4558c2ecf20Sopenharmony_ci
4568c2ecf20Sopenharmony_ci		buf_pair = &hw->ep_shm_info[epidx];
4578c2ecf20Sopenharmony_ci
4588c2ecf20Sopenharmony_ci		spin_lock_irqsave(&hw->rx_status_lock, flags);
4598c2ecf20Sopenharmony_ci		fjes_hw_setup_epbuf(&buf_pair->tx,
4608c2ecf20Sopenharmony_ci				    netdev->dev_addr, netdev->mtu);
4618c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&hw->rx_status_lock, flags);
4628c2ecf20Sopenharmony_ci
4638c2ecf20Sopenharmony_ci		clear_bit(epidx, &hw->txrx_stop_req_bit);
4648c2ecf20Sopenharmony_ci	}
4658c2ecf20Sopenharmony_ci
4668c2ecf20Sopenharmony_ci	if (reset_flag || adapter->force_reset) {
4678c2ecf20Sopenharmony_ci		result = fjes_hw_reset(hw);
4688c2ecf20Sopenharmony_ci
4698c2ecf20Sopenharmony_ci		adapter->force_reset = false;
4708c2ecf20Sopenharmony_ci
4718c2ecf20Sopenharmony_ci		if (result)
4728c2ecf20Sopenharmony_ci			adapter->open_guard = true;
4738c2ecf20Sopenharmony_ci
4748c2ecf20Sopenharmony_ci		hw->hw_info.buffer_share_bit = 0;
4758c2ecf20Sopenharmony_ci
4768c2ecf20Sopenharmony_ci		memset((void *)&param, 0, sizeof(param));
4778c2ecf20Sopenharmony_ci
4788c2ecf20Sopenharmony_ci		param.req_len = hw->hw_info.req_buf_size;
4798c2ecf20Sopenharmony_ci		param.req_start = __pa(hw->hw_info.req_buf);
4808c2ecf20Sopenharmony_ci		param.res_len = hw->hw_info.res_buf_size;
4818c2ecf20Sopenharmony_ci		param.res_start = __pa(hw->hw_info.res_buf);
4828c2ecf20Sopenharmony_ci		param.share_start = __pa(hw->hw_info.share->ep_status);
4838c2ecf20Sopenharmony_ci
4848c2ecf20Sopenharmony_ci		fjes_hw_init_command_registers(hw, &param);
4858c2ecf20Sopenharmony_ci	}
4868c2ecf20Sopenharmony_ci}
4878c2ecf20Sopenharmony_ci
4888c2ecf20Sopenharmony_cistatic void fjes_tx_stall_task(struct work_struct *work)
4898c2ecf20Sopenharmony_ci{
4908c2ecf20Sopenharmony_ci	struct fjes_adapter *adapter = container_of(work,
4918c2ecf20Sopenharmony_ci			struct fjes_adapter, tx_stall_task);
4928c2ecf20Sopenharmony_ci	struct net_device *netdev = adapter->netdev;
4938c2ecf20Sopenharmony_ci	struct fjes_hw *hw = &adapter->hw;
4948c2ecf20Sopenharmony_ci	int all_queue_available, sendable;
4958c2ecf20Sopenharmony_ci	enum ep_partner_status pstatus;
4968c2ecf20Sopenharmony_ci	int max_epid, my_epid, epid;
4978c2ecf20Sopenharmony_ci	union ep_buffer_info *info;
4988c2ecf20Sopenharmony_ci	int i;
4998c2ecf20Sopenharmony_ci
5008c2ecf20Sopenharmony_ci	if (((long)jiffies -
5018c2ecf20Sopenharmony_ci		dev_trans_start(netdev)) > FJES_TX_TX_STALL_TIMEOUT) {
5028c2ecf20Sopenharmony_ci		netif_wake_queue(netdev);
5038c2ecf20Sopenharmony_ci		return;
5048c2ecf20Sopenharmony_ci	}
5058c2ecf20Sopenharmony_ci
5068c2ecf20Sopenharmony_ci	my_epid = hw->my_epid;
5078c2ecf20Sopenharmony_ci	max_epid = hw->max_epid;
5088c2ecf20Sopenharmony_ci
5098c2ecf20Sopenharmony_ci	for (i = 0; i < 5; i++) {
5108c2ecf20Sopenharmony_ci		all_queue_available = 1;
5118c2ecf20Sopenharmony_ci
5128c2ecf20Sopenharmony_ci		for (epid = 0; epid < max_epid; epid++) {
5138c2ecf20Sopenharmony_ci			if (my_epid == epid)
5148c2ecf20Sopenharmony_ci				continue;
5158c2ecf20Sopenharmony_ci
5168c2ecf20Sopenharmony_ci			pstatus = fjes_hw_get_partner_ep_status(hw, epid);
5178c2ecf20Sopenharmony_ci			sendable = (pstatus == EP_PARTNER_SHARED);
5188c2ecf20Sopenharmony_ci			if (!sendable)
5198c2ecf20Sopenharmony_ci				continue;
5208c2ecf20Sopenharmony_ci
5218c2ecf20Sopenharmony_ci			info = adapter->hw.ep_shm_info[epid].tx.info;
5228c2ecf20Sopenharmony_ci
5238c2ecf20Sopenharmony_ci			if (!(info->v1i.rx_status & FJES_RX_MTU_CHANGING_DONE))
5248c2ecf20Sopenharmony_ci				return;
5258c2ecf20Sopenharmony_ci
5268c2ecf20Sopenharmony_ci			if (EP_RING_FULL(info->v1i.head, info->v1i.tail,
5278c2ecf20Sopenharmony_ci					 info->v1i.count_max)) {
5288c2ecf20Sopenharmony_ci				all_queue_available = 0;
5298c2ecf20Sopenharmony_ci				break;
5308c2ecf20Sopenharmony_ci			}
5318c2ecf20Sopenharmony_ci		}
5328c2ecf20Sopenharmony_ci
5338c2ecf20Sopenharmony_ci		if (all_queue_available) {
5348c2ecf20Sopenharmony_ci			netif_wake_queue(netdev);
5358c2ecf20Sopenharmony_ci			return;
5368c2ecf20Sopenharmony_ci		}
5378c2ecf20Sopenharmony_ci	}
5388c2ecf20Sopenharmony_ci
5398c2ecf20Sopenharmony_ci	usleep_range(50, 100);
5408c2ecf20Sopenharmony_ci
5418c2ecf20Sopenharmony_ci	queue_work(adapter->txrx_wq, &adapter->tx_stall_task);
5428c2ecf20Sopenharmony_ci}
5438c2ecf20Sopenharmony_ci
5448c2ecf20Sopenharmony_cistatic void fjes_force_close_task(struct work_struct *work)
5458c2ecf20Sopenharmony_ci{
5468c2ecf20Sopenharmony_ci	struct fjes_adapter *adapter = container_of(work,
5478c2ecf20Sopenharmony_ci			struct fjes_adapter, force_close_task);
5488c2ecf20Sopenharmony_ci	struct net_device *netdev = adapter->netdev;
5498c2ecf20Sopenharmony_ci
5508c2ecf20Sopenharmony_ci	rtnl_lock();
5518c2ecf20Sopenharmony_ci	dev_close(netdev);
5528c2ecf20Sopenharmony_ci	rtnl_unlock();
5538c2ecf20Sopenharmony_ci}
5548c2ecf20Sopenharmony_ci
5558c2ecf20Sopenharmony_cistatic void fjes_raise_intr_rxdata_task(struct work_struct *work)
5568c2ecf20Sopenharmony_ci{
5578c2ecf20Sopenharmony_ci	struct fjes_adapter *adapter = container_of(work,
5588c2ecf20Sopenharmony_ci			struct fjes_adapter, raise_intr_rxdata_task);
5598c2ecf20Sopenharmony_ci	struct fjes_hw *hw = &adapter->hw;
5608c2ecf20Sopenharmony_ci	enum ep_partner_status pstatus;
5618c2ecf20Sopenharmony_ci	int max_epid, my_epid, epid;
5628c2ecf20Sopenharmony_ci
5638c2ecf20Sopenharmony_ci	my_epid = hw->my_epid;
5648c2ecf20Sopenharmony_ci	max_epid = hw->max_epid;
5658c2ecf20Sopenharmony_ci
5668c2ecf20Sopenharmony_ci	for (epid = 0; epid < max_epid; epid++)
5678c2ecf20Sopenharmony_ci		hw->ep_shm_info[epid].tx_status_work = 0;
5688c2ecf20Sopenharmony_ci
5698c2ecf20Sopenharmony_ci	for (epid = 0; epid < max_epid; epid++) {
5708c2ecf20Sopenharmony_ci		if (epid == my_epid)
5718c2ecf20Sopenharmony_ci			continue;
5728c2ecf20Sopenharmony_ci
5738c2ecf20Sopenharmony_ci		pstatus = fjes_hw_get_partner_ep_status(hw, epid);
5748c2ecf20Sopenharmony_ci		if (pstatus == EP_PARTNER_SHARED) {
5758c2ecf20Sopenharmony_ci			hw->ep_shm_info[epid].tx_status_work =
5768c2ecf20Sopenharmony_ci				hw->ep_shm_info[epid].tx.info->v1i.tx_status;
5778c2ecf20Sopenharmony_ci
5788c2ecf20Sopenharmony_ci			if (hw->ep_shm_info[epid].tx_status_work ==
5798c2ecf20Sopenharmony_ci				FJES_TX_DELAY_SEND_PENDING) {
5808c2ecf20Sopenharmony_ci				hw->ep_shm_info[epid].tx.info->v1i.tx_status =
5818c2ecf20Sopenharmony_ci					FJES_TX_DELAY_SEND_NONE;
5828c2ecf20Sopenharmony_ci			}
5838c2ecf20Sopenharmony_ci		}
5848c2ecf20Sopenharmony_ci	}
5858c2ecf20Sopenharmony_ci
5868c2ecf20Sopenharmony_ci	for (epid = 0; epid < max_epid; epid++) {
5878c2ecf20Sopenharmony_ci		if (epid == my_epid)
5888c2ecf20Sopenharmony_ci			continue;
5898c2ecf20Sopenharmony_ci
5908c2ecf20Sopenharmony_ci		pstatus = fjes_hw_get_partner_ep_status(hw, epid);
5918c2ecf20Sopenharmony_ci		if ((hw->ep_shm_info[epid].tx_status_work ==
5928c2ecf20Sopenharmony_ci		     FJES_TX_DELAY_SEND_PENDING) &&
5938c2ecf20Sopenharmony_ci		    (pstatus == EP_PARTNER_SHARED) &&
5948c2ecf20Sopenharmony_ci		    !(hw->ep_shm_info[epid].rx.info->v1i.rx_status &
5958c2ecf20Sopenharmony_ci		      FJES_RX_POLL_WORK)) {
5968c2ecf20Sopenharmony_ci			fjes_hw_raise_interrupt(hw, epid,
5978c2ecf20Sopenharmony_ci						REG_ICTL_MASK_RX_DATA);
5988c2ecf20Sopenharmony_ci			hw->ep_shm_info[epid].ep_stats.send_intr_rx += 1;
5998c2ecf20Sopenharmony_ci		}
6008c2ecf20Sopenharmony_ci	}
6018c2ecf20Sopenharmony_ci
6028c2ecf20Sopenharmony_ci	usleep_range(500, 1000);
6038c2ecf20Sopenharmony_ci}
6048c2ecf20Sopenharmony_ci
6058c2ecf20Sopenharmony_cistatic int fjes_tx_send(struct fjes_adapter *adapter, int dest,
6068c2ecf20Sopenharmony_ci			void *data, size_t len)
6078c2ecf20Sopenharmony_ci{
6088c2ecf20Sopenharmony_ci	int retval;
6098c2ecf20Sopenharmony_ci
6108c2ecf20Sopenharmony_ci	retval = fjes_hw_epbuf_tx_pkt_send(&adapter->hw.ep_shm_info[dest].tx,
6118c2ecf20Sopenharmony_ci					   data, len);
6128c2ecf20Sopenharmony_ci	if (retval)
6138c2ecf20Sopenharmony_ci		return retval;
6148c2ecf20Sopenharmony_ci
6158c2ecf20Sopenharmony_ci	adapter->hw.ep_shm_info[dest].tx.info->v1i.tx_status =
6168c2ecf20Sopenharmony_ci		FJES_TX_DELAY_SEND_PENDING;
6178c2ecf20Sopenharmony_ci	if (!work_pending(&adapter->raise_intr_rxdata_task))
6188c2ecf20Sopenharmony_ci		queue_work(adapter->txrx_wq,
6198c2ecf20Sopenharmony_ci			   &adapter->raise_intr_rxdata_task);
6208c2ecf20Sopenharmony_ci
6218c2ecf20Sopenharmony_ci	retval = 0;
6228c2ecf20Sopenharmony_ci	return retval;
6238c2ecf20Sopenharmony_ci}
6248c2ecf20Sopenharmony_ci
6258c2ecf20Sopenharmony_cistatic netdev_tx_t
6268c2ecf20Sopenharmony_cifjes_xmit_frame(struct sk_buff *skb, struct net_device *netdev)
6278c2ecf20Sopenharmony_ci{
6288c2ecf20Sopenharmony_ci	struct fjes_adapter *adapter = netdev_priv(netdev);
6298c2ecf20Sopenharmony_ci	struct fjes_hw *hw = &adapter->hw;
6308c2ecf20Sopenharmony_ci
6318c2ecf20Sopenharmony_ci	int max_epid, my_epid, dest_epid;
6328c2ecf20Sopenharmony_ci	enum ep_partner_status pstatus;
6338c2ecf20Sopenharmony_ci	struct netdev_queue *cur_queue;
6348c2ecf20Sopenharmony_ci	char shortpkt[VLAN_ETH_HLEN];
6358c2ecf20Sopenharmony_ci	bool is_multi, vlan;
6368c2ecf20Sopenharmony_ci	struct ethhdr *eth;
6378c2ecf20Sopenharmony_ci	u16 queue_no = 0;
6388c2ecf20Sopenharmony_ci	u16 vlan_id = 0;
6398c2ecf20Sopenharmony_ci	netdev_tx_t ret;
6408c2ecf20Sopenharmony_ci	char *data;
6418c2ecf20Sopenharmony_ci	int len;
6428c2ecf20Sopenharmony_ci
6438c2ecf20Sopenharmony_ci	ret = NETDEV_TX_OK;
6448c2ecf20Sopenharmony_ci	is_multi = false;
6458c2ecf20Sopenharmony_ci	cur_queue = netdev_get_tx_queue(netdev, queue_no);
6468c2ecf20Sopenharmony_ci
6478c2ecf20Sopenharmony_ci	eth = (struct ethhdr *)skb->data;
6488c2ecf20Sopenharmony_ci	my_epid = hw->my_epid;
6498c2ecf20Sopenharmony_ci
6508c2ecf20Sopenharmony_ci	vlan = (vlan_get_tag(skb, &vlan_id) == 0) ? true : false;
6518c2ecf20Sopenharmony_ci
6528c2ecf20Sopenharmony_ci	data = skb->data;
6538c2ecf20Sopenharmony_ci	len = skb->len;
6548c2ecf20Sopenharmony_ci
6558c2ecf20Sopenharmony_ci	if (is_multicast_ether_addr(eth->h_dest)) {
6568c2ecf20Sopenharmony_ci		dest_epid = 0;
6578c2ecf20Sopenharmony_ci		max_epid = hw->max_epid;
6588c2ecf20Sopenharmony_ci		is_multi = true;
6598c2ecf20Sopenharmony_ci	} else if (is_local_ether_addr(eth->h_dest)) {
6608c2ecf20Sopenharmony_ci		dest_epid = eth->h_dest[ETH_ALEN - 1];
6618c2ecf20Sopenharmony_ci		max_epid = dest_epid + 1;
6628c2ecf20Sopenharmony_ci
6638c2ecf20Sopenharmony_ci		if ((eth->h_dest[0] == 0x02) &&
6648c2ecf20Sopenharmony_ci		    (0x00 == (eth->h_dest[1] | eth->h_dest[2] |
6658c2ecf20Sopenharmony_ci			      eth->h_dest[3] | eth->h_dest[4])) &&
6668c2ecf20Sopenharmony_ci		    (dest_epid < hw->max_epid)) {
6678c2ecf20Sopenharmony_ci			;
6688c2ecf20Sopenharmony_ci		} else {
6698c2ecf20Sopenharmony_ci			dest_epid = 0;
6708c2ecf20Sopenharmony_ci			max_epid = 0;
6718c2ecf20Sopenharmony_ci			ret = NETDEV_TX_OK;
6728c2ecf20Sopenharmony_ci
6738c2ecf20Sopenharmony_ci			adapter->stats64.tx_packets += 1;
6748c2ecf20Sopenharmony_ci			hw->ep_shm_info[my_epid].net_stats.tx_packets += 1;
6758c2ecf20Sopenharmony_ci			adapter->stats64.tx_bytes += len;
6768c2ecf20Sopenharmony_ci			hw->ep_shm_info[my_epid].net_stats.tx_bytes += len;
6778c2ecf20Sopenharmony_ci		}
6788c2ecf20Sopenharmony_ci	} else {
6798c2ecf20Sopenharmony_ci		dest_epid = 0;
6808c2ecf20Sopenharmony_ci		max_epid = 0;
6818c2ecf20Sopenharmony_ci		ret = NETDEV_TX_OK;
6828c2ecf20Sopenharmony_ci
6838c2ecf20Sopenharmony_ci		adapter->stats64.tx_packets += 1;
6848c2ecf20Sopenharmony_ci		hw->ep_shm_info[my_epid].net_stats.tx_packets += 1;
6858c2ecf20Sopenharmony_ci		adapter->stats64.tx_bytes += len;
6868c2ecf20Sopenharmony_ci		hw->ep_shm_info[my_epid].net_stats.tx_bytes += len;
6878c2ecf20Sopenharmony_ci	}
6888c2ecf20Sopenharmony_ci
6898c2ecf20Sopenharmony_ci	for (; dest_epid < max_epid; dest_epid++) {
6908c2ecf20Sopenharmony_ci		if (my_epid == dest_epid)
6918c2ecf20Sopenharmony_ci			continue;
6928c2ecf20Sopenharmony_ci
6938c2ecf20Sopenharmony_ci		pstatus = fjes_hw_get_partner_ep_status(hw, dest_epid);
6948c2ecf20Sopenharmony_ci		if (pstatus != EP_PARTNER_SHARED) {
6958c2ecf20Sopenharmony_ci			if (!is_multi)
6968c2ecf20Sopenharmony_ci				hw->ep_shm_info[dest_epid].ep_stats
6978c2ecf20Sopenharmony_ci					.tx_dropped_not_shared += 1;
6988c2ecf20Sopenharmony_ci			ret = NETDEV_TX_OK;
6998c2ecf20Sopenharmony_ci		} else if (!fjes_hw_check_epbuf_version(
7008c2ecf20Sopenharmony_ci				&adapter->hw.ep_shm_info[dest_epid].rx, 0)) {
7018c2ecf20Sopenharmony_ci			/* version is NOT 0 */
7028c2ecf20Sopenharmony_ci			adapter->stats64.tx_carrier_errors += 1;
7038c2ecf20Sopenharmony_ci			hw->ep_shm_info[dest_epid].net_stats
7048c2ecf20Sopenharmony_ci						.tx_carrier_errors += 1;
7058c2ecf20Sopenharmony_ci			hw->ep_shm_info[dest_epid].ep_stats
7068c2ecf20Sopenharmony_ci					.tx_dropped_ver_mismatch += 1;
7078c2ecf20Sopenharmony_ci
7088c2ecf20Sopenharmony_ci			ret = NETDEV_TX_OK;
7098c2ecf20Sopenharmony_ci		} else if (!fjes_hw_check_mtu(
7108c2ecf20Sopenharmony_ci				&adapter->hw.ep_shm_info[dest_epid].rx,
7118c2ecf20Sopenharmony_ci				netdev->mtu)) {
7128c2ecf20Sopenharmony_ci			adapter->stats64.tx_dropped += 1;
7138c2ecf20Sopenharmony_ci			hw->ep_shm_info[dest_epid].net_stats.tx_dropped += 1;
7148c2ecf20Sopenharmony_ci			adapter->stats64.tx_errors += 1;
7158c2ecf20Sopenharmony_ci			hw->ep_shm_info[dest_epid].net_stats.tx_errors += 1;
7168c2ecf20Sopenharmony_ci			hw->ep_shm_info[dest_epid].ep_stats
7178c2ecf20Sopenharmony_ci					.tx_dropped_buf_size_mismatch += 1;
7188c2ecf20Sopenharmony_ci
7198c2ecf20Sopenharmony_ci			ret = NETDEV_TX_OK;
7208c2ecf20Sopenharmony_ci		} else if (vlan &&
7218c2ecf20Sopenharmony_ci			   !fjes_hw_check_vlan_id(
7228c2ecf20Sopenharmony_ci				&adapter->hw.ep_shm_info[dest_epid].rx,
7238c2ecf20Sopenharmony_ci				vlan_id)) {
7248c2ecf20Sopenharmony_ci			hw->ep_shm_info[dest_epid].ep_stats
7258c2ecf20Sopenharmony_ci				.tx_dropped_vlanid_mismatch += 1;
7268c2ecf20Sopenharmony_ci			ret = NETDEV_TX_OK;
7278c2ecf20Sopenharmony_ci		} else {
7288c2ecf20Sopenharmony_ci			if (len < VLAN_ETH_HLEN) {
7298c2ecf20Sopenharmony_ci				memset(shortpkt, 0, VLAN_ETH_HLEN);
7308c2ecf20Sopenharmony_ci				memcpy(shortpkt, skb->data, skb->len);
7318c2ecf20Sopenharmony_ci				len = VLAN_ETH_HLEN;
7328c2ecf20Sopenharmony_ci				data = shortpkt;
7338c2ecf20Sopenharmony_ci			}
7348c2ecf20Sopenharmony_ci
7358c2ecf20Sopenharmony_ci			if (adapter->tx_retry_count == 0) {
7368c2ecf20Sopenharmony_ci				adapter->tx_start_jiffies = jiffies;
7378c2ecf20Sopenharmony_ci				adapter->tx_retry_count = 1;
7388c2ecf20Sopenharmony_ci			} else {
7398c2ecf20Sopenharmony_ci				adapter->tx_retry_count++;
7408c2ecf20Sopenharmony_ci			}
7418c2ecf20Sopenharmony_ci
7428c2ecf20Sopenharmony_ci			if (fjes_tx_send(adapter, dest_epid, data, len)) {
7438c2ecf20Sopenharmony_ci				if (is_multi) {
7448c2ecf20Sopenharmony_ci					ret = NETDEV_TX_OK;
7458c2ecf20Sopenharmony_ci				} else if (
7468c2ecf20Sopenharmony_ci					   ((long)jiffies -
7478c2ecf20Sopenharmony_ci					    (long)adapter->tx_start_jiffies) >=
7488c2ecf20Sopenharmony_ci					    FJES_TX_RETRY_TIMEOUT) {
7498c2ecf20Sopenharmony_ci					adapter->stats64.tx_fifo_errors += 1;
7508c2ecf20Sopenharmony_ci					hw->ep_shm_info[dest_epid].net_stats
7518c2ecf20Sopenharmony_ci								.tx_fifo_errors += 1;
7528c2ecf20Sopenharmony_ci					adapter->stats64.tx_errors += 1;
7538c2ecf20Sopenharmony_ci					hw->ep_shm_info[dest_epid].net_stats
7548c2ecf20Sopenharmony_ci								.tx_errors += 1;
7558c2ecf20Sopenharmony_ci
7568c2ecf20Sopenharmony_ci					ret = NETDEV_TX_OK;
7578c2ecf20Sopenharmony_ci				} else {
7588c2ecf20Sopenharmony_ci					netif_trans_update(netdev);
7598c2ecf20Sopenharmony_ci					hw->ep_shm_info[dest_epid].ep_stats
7608c2ecf20Sopenharmony_ci						.tx_buffer_full += 1;
7618c2ecf20Sopenharmony_ci					netif_tx_stop_queue(cur_queue);
7628c2ecf20Sopenharmony_ci
7638c2ecf20Sopenharmony_ci					if (!work_pending(&adapter->tx_stall_task))
7648c2ecf20Sopenharmony_ci						queue_work(adapter->txrx_wq,
7658c2ecf20Sopenharmony_ci							   &adapter->tx_stall_task);
7668c2ecf20Sopenharmony_ci
7678c2ecf20Sopenharmony_ci					ret = NETDEV_TX_BUSY;
7688c2ecf20Sopenharmony_ci				}
7698c2ecf20Sopenharmony_ci			} else {
7708c2ecf20Sopenharmony_ci				if (!is_multi) {
7718c2ecf20Sopenharmony_ci					adapter->stats64.tx_packets += 1;
7728c2ecf20Sopenharmony_ci					hw->ep_shm_info[dest_epid].net_stats
7738c2ecf20Sopenharmony_ci								.tx_packets += 1;
7748c2ecf20Sopenharmony_ci					adapter->stats64.tx_bytes += len;
7758c2ecf20Sopenharmony_ci					hw->ep_shm_info[dest_epid].net_stats
7768c2ecf20Sopenharmony_ci								.tx_bytes += len;
7778c2ecf20Sopenharmony_ci				}
7788c2ecf20Sopenharmony_ci
7798c2ecf20Sopenharmony_ci				adapter->tx_retry_count = 0;
7808c2ecf20Sopenharmony_ci				ret = NETDEV_TX_OK;
7818c2ecf20Sopenharmony_ci			}
7828c2ecf20Sopenharmony_ci		}
7838c2ecf20Sopenharmony_ci	}
7848c2ecf20Sopenharmony_ci
7858c2ecf20Sopenharmony_ci	if (ret == NETDEV_TX_OK) {
7868c2ecf20Sopenharmony_ci		dev_kfree_skb(skb);
7878c2ecf20Sopenharmony_ci		if (is_multi) {
7888c2ecf20Sopenharmony_ci			adapter->stats64.tx_packets += 1;
7898c2ecf20Sopenharmony_ci			hw->ep_shm_info[my_epid].net_stats.tx_packets += 1;
7908c2ecf20Sopenharmony_ci			adapter->stats64.tx_bytes += 1;
7918c2ecf20Sopenharmony_ci			hw->ep_shm_info[my_epid].net_stats.tx_bytes += len;
7928c2ecf20Sopenharmony_ci		}
7938c2ecf20Sopenharmony_ci	}
7948c2ecf20Sopenharmony_ci
7958c2ecf20Sopenharmony_ci	return ret;
7968c2ecf20Sopenharmony_ci}
7978c2ecf20Sopenharmony_ci
7988c2ecf20Sopenharmony_cistatic void fjes_tx_retry(struct net_device *netdev, unsigned int txqueue)
7998c2ecf20Sopenharmony_ci{
8008c2ecf20Sopenharmony_ci	struct netdev_queue *queue = netdev_get_tx_queue(netdev, 0);
8018c2ecf20Sopenharmony_ci
8028c2ecf20Sopenharmony_ci	netif_tx_wake_queue(queue);
8038c2ecf20Sopenharmony_ci}
8048c2ecf20Sopenharmony_ci
8058c2ecf20Sopenharmony_cistatic void
8068c2ecf20Sopenharmony_cifjes_get_stats64(struct net_device *netdev, struct rtnl_link_stats64 *stats)
8078c2ecf20Sopenharmony_ci{
8088c2ecf20Sopenharmony_ci	struct fjes_adapter *adapter = netdev_priv(netdev);
8098c2ecf20Sopenharmony_ci
8108c2ecf20Sopenharmony_ci	memcpy(stats, &adapter->stats64, sizeof(struct rtnl_link_stats64));
8118c2ecf20Sopenharmony_ci}
8128c2ecf20Sopenharmony_ci
8138c2ecf20Sopenharmony_cistatic int fjes_change_mtu(struct net_device *netdev, int new_mtu)
8148c2ecf20Sopenharmony_ci{
8158c2ecf20Sopenharmony_ci	struct fjes_adapter *adapter = netdev_priv(netdev);
8168c2ecf20Sopenharmony_ci	bool running = netif_running(netdev);
8178c2ecf20Sopenharmony_ci	struct fjes_hw *hw = &adapter->hw;
8188c2ecf20Sopenharmony_ci	unsigned long flags;
8198c2ecf20Sopenharmony_ci	int ret = -EINVAL;
8208c2ecf20Sopenharmony_ci	int idx, epidx;
8218c2ecf20Sopenharmony_ci
8228c2ecf20Sopenharmony_ci	for (idx = 0; fjes_support_mtu[idx] != 0; idx++) {
8238c2ecf20Sopenharmony_ci		if (new_mtu <= fjes_support_mtu[idx]) {
8248c2ecf20Sopenharmony_ci			new_mtu = fjes_support_mtu[idx];
8258c2ecf20Sopenharmony_ci			if (new_mtu == netdev->mtu)
8268c2ecf20Sopenharmony_ci				return 0;
8278c2ecf20Sopenharmony_ci
8288c2ecf20Sopenharmony_ci			ret = 0;
8298c2ecf20Sopenharmony_ci			break;
8308c2ecf20Sopenharmony_ci		}
8318c2ecf20Sopenharmony_ci	}
8328c2ecf20Sopenharmony_ci
8338c2ecf20Sopenharmony_ci	if (ret)
8348c2ecf20Sopenharmony_ci		return ret;
8358c2ecf20Sopenharmony_ci
8368c2ecf20Sopenharmony_ci	if (running) {
8378c2ecf20Sopenharmony_ci		spin_lock_irqsave(&hw->rx_status_lock, flags);
8388c2ecf20Sopenharmony_ci		for (epidx = 0; epidx < hw->max_epid; epidx++) {
8398c2ecf20Sopenharmony_ci			if (epidx == hw->my_epid)
8408c2ecf20Sopenharmony_ci				continue;
8418c2ecf20Sopenharmony_ci			hw->ep_shm_info[epidx].tx.info->v1i.rx_status &=
8428c2ecf20Sopenharmony_ci				~FJES_RX_MTU_CHANGING_DONE;
8438c2ecf20Sopenharmony_ci		}
8448c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&hw->rx_status_lock, flags);
8458c2ecf20Sopenharmony_ci
8468c2ecf20Sopenharmony_ci		netif_tx_stop_all_queues(netdev);
8478c2ecf20Sopenharmony_ci		netif_carrier_off(netdev);
8488c2ecf20Sopenharmony_ci		cancel_work_sync(&adapter->tx_stall_task);
8498c2ecf20Sopenharmony_ci		napi_disable(&adapter->napi);
8508c2ecf20Sopenharmony_ci
8518c2ecf20Sopenharmony_ci		msleep(1000);
8528c2ecf20Sopenharmony_ci
8538c2ecf20Sopenharmony_ci		netif_tx_stop_all_queues(netdev);
8548c2ecf20Sopenharmony_ci	}
8558c2ecf20Sopenharmony_ci
8568c2ecf20Sopenharmony_ci	netdev->mtu = new_mtu;
8578c2ecf20Sopenharmony_ci
8588c2ecf20Sopenharmony_ci	if (running) {
8598c2ecf20Sopenharmony_ci		for (epidx = 0; epidx < hw->max_epid; epidx++) {
8608c2ecf20Sopenharmony_ci			if (epidx == hw->my_epid)
8618c2ecf20Sopenharmony_ci				continue;
8628c2ecf20Sopenharmony_ci
8638c2ecf20Sopenharmony_ci			spin_lock_irqsave(&hw->rx_status_lock, flags);
8648c2ecf20Sopenharmony_ci			fjes_hw_setup_epbuf(&hw->ep_shm_info[epidx].tx,
8658c2ecf20Sopenharmony_ci					    netdev->dev_addr,
8668c2ecf20Sopenharmony_ci					    netdev->mtu);
8678c2ecf20Sopenharmony_ci
8688c2ecf20Sopenharmony_ci			hw->ep_shm_info[epidx].tx.info->v1i.rx_status |=
8698c2ecf20Sopenharmony_ci				FJES_RX_MTU_CHANGING_DONE;
8708c2ecf20Sopenharmony_ci			spin_unlock_irqrestore(&hw->rx_status_lock, flags);
8718c2ecf20Sopenharmony_ci		}
8728c2ecf20Sopenharmony_ci
8738c2ecf20Sopenharmony_ci		netif_tx_wake_all_queues(netdev);
8748c2ecf20Sopenharmony_ci		netif_carrier_on(netdev);
8758c2ecf20Sopenharmony_ci		napi_enable(&adapter->napi);
8768c2ecf20Sopenharmony_ci		napi_schedule(&adapter->napi);
8778c2ecf20Sopenharmony_ci	}
8788c2ecf20Sopenharmony_ci
8798c2ecf20Sopenharmony_ci	return ret;
8808c2ecf20Sopenharmony_ci}
8818c2ecf20Sopenharmony_ci
8828c2ecf20Sopenharmony_cistatic int fjes_vlan_rx_add_vid(struct net_device *netdev,
8838c2ecf20Sopenharmony_ci				__be16 proto, u16 vid)
8848c2ecf20Sopenharmony_ci{
8858c2ecf20Sopenharmony_ci	struct fjes_adapter *adapter = netdev_priv(netdev);
8868c2ecf20Sopenharmony_ci	bool ret = true;
8878c2ecf20Sopenharmony_ci	int epid;
8888c2ecf20Sopenharmony_ci
8898c2ecf20Sopenharmony_ci	for (epid = 0; epid < adapter->hw.max_epid; epid++) {
8908c2ecf20Sopenharmony_ci		if (epid == adapter->hw.my_epid)
8918c2ecf20Sopenharmony_ci			continue;
8928c2ecf20Sopenharmony_ci
8938c2ecf20Sopenharmony_ci		if (!fjes_hw_check_vlan_id(
8948c2ecf20Sopenharmony_ci			&adapter->hw.ep_shm_info[epid].tx, vid))
8958c2ecf20Sopenharmony_ci			ret = fjes_hw_set_vlan_id(
8968c2ecf20Sopenharmony_ci				&adapter->hw.ep_shm_info[epid].tx, vid);
8978c2ecf20Sopenharmony_ci	}
8988c2ecf20Sopenharmony_ci
8998c2ecf20Sopenharmony_ci	return ret ? 0 : -ENOSPC;
9008c2ecf20Sopenharmony_ci}
9018c2ecf20Sopenharmony_ci
9028c2ecf20Sopenharmony_cistatic int fjes_vlan_rx_kill_vid(struct net_device *netdev,
9038c2ecf20Sopenharmony_ci				 __be16 proto, u16 vid)
9048c2ecf20Sopenharmony_ci{
9058c2ecf20Sopenharmony_ci	struct fjes_adapter *adapter = netdev_priv(netdev);
9068c2ecf20Sopenharmony_ci	int epid;
9078c2ecf20Sopenharmony_ci
9088c2ecf20Sopenharmony_ci	for (epid = 0; epid < adapter->hw.max_epid; epid++) {
9098c2ecf20Sopenharmony_ci		if (epid == adapter->hw.my_epid)
9108c2ecf20Sopenharmony_ci			continue;
9118c2ecf20Sopenharmony_ci
9128c2ecf20Sopenharmony_ci		fjes_hw_del_vlan_id(&adapter->hw.ep_shm_info[epid].tx, vid);
9138c2ecf20Sopenharmony_ci	}
9148c2ecf20Sopenharmony_ci
9158c2ecf20Sopenharmony_ci	return 0;
9168c2ecf20Sopenharmony_ci}
9178c2ecf20Sopenharmony_ci
9188c2ecf20Sopenharmony_cistatic void fjes_txrx_stop_req_irq(struct fjes_adapter *adapter,
9198c2ecf20Sopenharmony_ci				   int src_epid)
9208c2ecf20Sopenharmony_ci{
9218c2ecf20Sopenharmony_ci	struct fjes_hw *hw = &adapter->hw;
9228c2ecf20Sopenharmony_ci	enum ep_partner_status status;
9238c2ecf20Sopenharmony_ci	unsigned long flags;
9248c2ecf20Sopenharmony_ci
9258c2ecf20Sopenharmony_ci	status = fjes_hw_get_partner_ep_status(hw, src_epid);
9268c2ecf20Sopenharmony_ci	trace_fjes_txrx_stop_req_irq_pre(hw, src_epid, status);
9278c2ecf20Sopenharmony_ci	switch (status) {
9288c2ecf20Sopenharmony_ci	case EP_PARTNER_UNSHARE:
9298c2ecf20Sopenharmony_ci	case EP_PARTNER_COMPLETE:
9308c2ecf20Sopenharmony_ci	default:
9318c2ecf20Sopenharmony_ci		break;
9328c2ecf20Sopenharmony_ci	case EP_PARTNER_WAITING:
9338c2ecf20Sopenharmony_ci		if (src_epid < hw->my_epid) {
9348c2ecf20Sopenharmony_ci			spin_lock_irqsave(&hw->rx_status_lock, flags);
9358c2ecf20Sopenharmony_ci			hw->ep_shm_info[src_epid].tx.info->v1i.rx_status |=
9368c2ecf20Sopenharmony_ci				FJES_RX_STOP_REQ_DONE;
9378c2ecf20Sopenharmony_ci			spin_unlock_irqrestore(&hw->rx_status_lock, flags);
9388c2ecf20Sopenharmony_ci
9398c2ecf20Sopenharmony_ci			clear_bit(src_epid, &hw->txrx_stop_req_bit);
9408c2ecf20Sopenharmony_ci			set_bit(src_epid, &adapter->unshare_watch_bitmask);
9418c2ecf20Sopenharmony_ci
9428c2ecf20Sopenharmony_ci			if (!work_pending(&adapter->unshare_watch_task))
9438c2ecf20Sopenharmony_ci				queue_work(adapter->control_wq,
9448c2ecf20Sopenharmony_ci					   &adapter->unshare_watch_task);
9458c2ecf20Sopenharmony_ci		}
9468c2ecf20Sopenharmony_ci		break;
9478c2ecf20Sopenharmony_ci	case EP_PARTNER_SHARED:
9488c2ecf20Sopenharmony_ci		if (hw->ep_shm_info[src_epid].rx.info->v1i.rx_status &
9498c2ecf20Sopenharmony_ci		    FJES_RX_STOP_REQ_REQUEST) {
9508c2ecf20Sopenharmony_ci			set_bit(src_epid, &hw->epstop_req_bit);
9518c2ecf20Sopenharmony_ci			if (!work_pending(&hw->epstop_task))
9528c2ecf20Sopenharmony_ci				queue_work(adapter->control_wq,
9538c2ecf20Sopenharmony_ci					   &hw->epstop_task);
9548c2ecf20Sopenharmony_ci		}
9558c2ecf20Sopenharmony_ci		break;
9568c2ecf20Sopenharmony_ci	}
9578c2ecf20Sopenharmony_ci	trace_fjes_txrx_stop_req_irq_post(hw, src_epid);
9588c2ecf20Sopenharmony_ci}
9598c2ecf20Sopenharmony_ci
9608c2ecf20Sopenharmony_cistatic void fjes_stop_req_irq(struct fjes_adapter *adapter, int src_epid)
9618c2ecf20Sopenharmony_ci{
9628c2ecf20Sopenharmony_ci	struct fjes_hw *hw = &adapter->hw;
9638c2ecf20Sopenharmony_ci	enum ep_partner_status status;
9648c2ecf20Sopenharmony_ci	unsigned long flags;
9658c2ecf20Sopenharmony_ci
9668c2ecf20Sopenharmony_ci	set_bit(src_epid, &hw->hw_info.buffer_unshare_reserve_bit);
9678c2ecf20Sopenharmony_ci
9688c2ecf20Sopenharmony_ci	status = fjes_hw_get_partner_ep_status(hw, src_epid);
9698c2ecf20Sopenharmony_ci	trace_fjes_stop_req_irq_pre(hw, src_epid, status);
9708c2ecf20Sopenharmony_ci	switch (status) {
9718c2ecf20Sopenharmony_ci	case EP_PARTNER_WAITING:
9728c2ecf20Sopenharmony_ci		spin_lock_irqsave(&hw->rx_status_lock, flags);
9738c2ecf20Sopenharmony_ci		hw->ep_shm_info[src_epid].tx.info->v1i.rx_status |=
9748c2ecf20Sopenharmony_ci				FJES_RX_STOP_REQ_DONE;
9758c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&hw->rx_status_lock, flags);
9768c2ecf20Sopenharmony_ci		clear_bit(src_epid, &hw->txrx_stop_req_bit);
9778c2ecf20Sopenharmony_ci		fallthrough;
9788c2ecf20Sopenharmony_ci	case EP_PARTNER_UNSHARE:
9798c2ecf20Sopenharmony_ci	case EP_PARTNER_COMPLETE:
9808c2ecf20Sopenharmony_ci	default:
9818c2ecf20Sopenharmony_ci		set_bit(src_epid, &adapter->unshare_watch_bitmask);
9828c2ecf20Sopenharmony_ci		if (!work_pending(&adapter->unshare_watch_task))
9838c2ecf20Sopenharmony_ci			queue_work(adapter->control_wq,
9848c2ecf20Sopenharmony_ci				   &adapter->unshare_watch_task);
9858c2ecf20Sopenharmony_ci		break;
9868c2ecf20Sopenharmony_ci	case EP_PARTNER_SHARED:
9878c2ecf20Sopenharmony_ci		set_bit(src_epid, &hw->epstop_req_bit);
9888c2ecf20Sopenharmony_ci
9898c2ecf20Sopenharmony_ci		if (!work_pending(&hw->epstop_task))
9908c2ecf20Sopenharmony_ci			queue_work(adapter->control_wq, &hw->epstop_task);
9918c2ecf20Sopenharmony_ci		break;
9928c2ecf20Sopenharmony_ci	}
9938c2ecf20Sopenharmony_ci	trace_fjes_stop_req_irq_post(hw, src_epid);
9948c2ecf20Sopenharmony_ci}
9958c2ecf20Sopenharmony_ci
9968c2ecf20Sopenharmony_cistatic void fjes_update_zone_irq(struct fjes_adapter *adapter,
9978c2ecf20Sopenharmony_ci				 int src_epid)
9988c2ecf20Sopenharmony_ci{
9998c2ecf20Sopenharmony_ci	struct fjes_hw *hw = &adapter->hw;
10008c2ecf20Sopenharmony_ci
10018c2ecf20Sopenharmony_ci	if (!work_pending(&hw->update_zone_task))
10028c2ecf20Sopenharmony_ci		queue_work(adapter->control_wq, &hw->update_zone_task);
10038c2ecf20Sopenharmony_ci}
10048c2ecf20Sopenharmony_ci
10058c2ecf20Sopenharmony_cistatic irqreturn_t fjes_intr(int irq, void *data)
10068c2ecf20Sopenharmony_ci{
10078c2ecf20Sopenharmony_ci	struct fjes_adapter *adapter = data;
10088c2ecf20Sopenharmony_ci	struct fjes_hw *hw = &adapter->hw;
10098c2ecf20Sopenharmony_ci	irqreturn_t ret;
10108c2ecf20Sopenharmony_ci	u32 icr;
10118c2ecf20Sopenharmony_ci
10128c2ecf20Sopenharmony_ci	icr = fjes_hw_capture_interrupt_status(hw);
10138c2ecf20Sopenharmony_ci
10148c2ecf20Sopenharmony_ci	if (icr & REG_IS_MASK_IS_ASSERT) {
10158c2ecf20Sopenharmony_ci		if (icr & REG_ICTL_MASK_RX_DATA) {
10168c2ecf20Sopenharmony_ci			fjes_rx_irq(adapter, icr & REG_IS_MASK_EPID);
10178c2ecf20Sopenharmony_ci			hw->ep_shm_info[icr & REG_IS_MASK_EPID].ep_stats
10188c2ecf20Sopenharmony_ci				.recv_intr_rx += 1;
10198c2ecf20Sopenharmony_ci		}
10208c2ecf20Sopenharmony_ci
10218c2ecf20Sopenharmony_ci		if (icr & REG_ICTL_MASK_DEV_STOP_REQ) {
10228c2ecf20Sopenharmony_ci			fjes_stop_req_irq(adapter, icr & REG_IS_MASK_EPID);
10238c2ecf20Sopenharmony_ci			hw->ep_shm_info[icr & REG_IS_MASK_EPID].ep_stats
10248c2ecf20Sopenharmony_ci				.recv_intr_stop += 1;
10258c2ecf20Sopenharmony_ci		}
10268c2ecf20Sopenharmony_ci
10278c2ecf20Sopenharmony_ci		if (icr & REG_ICTL_MASK_TXRX_STOP_REQ) {
10288c2ecf20Sopenharmony_ci			fjes_txrx_stop_req_irq(adapter, icr & REG_IS_MASK_EPID);
10298c2ecf20Sopenharmony_ci			hw->ep_shm_info[icr & REG_IS_MASK_EPID].ep_stats
10308c2ecf20Sopenharmony_ci				.recv_intr_unshare += 1;
10318c2ecf20Sopenharmony_ci		}
10328c2ecf20Sopenharmony_ci
10338c2ecf20Sopenharmony_ci		if (icr & REG_ICTL_MASK_TXRX_STOP_DONE)
10348c2ecf20Sopenharmony_ci			fjes_hw_set_irqmask(hw,
10358c2ecf20Sopenharmony_ci					    REG_ICTL_MASK_TXRX_STOP_DONE, true);
10368c2ecf20Sopenharmony_ci
10378c2ecf20Sopenharmony_ci		if (icr & REG_ICTL_MASK_INFO_UPDATE) {
10388c2ecf20Sopenharmony_ci			fjes_update_zone_irq(adapter, icr & REG_IS_MASK_EPID);
10398c2ecf20Sopenharmony_ci			hw->ep_shm_info[icr & REG_IS_MASK_EPID].ep_stats
10408c2ecf20Sopenharmony_ci				.recv_intr_zoneupdate += 1;
10418c2ecf20Sopenharmony_ci		}
10428c2ecf20Sopenharmony_ci
10438c2ecf20Sopenharmony_ci		ret = IRQ_HANDLED;
10448c2ecf20Sopenharmony_ci	} else {
10458c2ecf20Sopenharmony_ci		ret = IRQ_NONE;
10468c2ecf20Sopenharmony_ci	}
10478c2ecf20Sopenharmony_ci
10488c2ecf20Sopenharmony_ci	return ret;
10498c2ecf20Sopenharmony_ci}
10508c2ecf20Sopenharmony_ci
10518c2ecf20Sopenharmony_cistatic int fjes_rxframe_search_exist(struct fjes_adapter *adapter,
10528c2ecf20Sopenharmony_ci				     int start_epid)
10538c2ecf20Sopenharmony_ci{
10548c2ecf20Sopenharmony_ci	struct fjes_hw *hw = &adapter->hw;
10558c2ecf20Sopenharmony_ci	enum ep_partner_status pstatus;
10568c2ecf20Sopenharmony_ci	int max_epid, cur_epid;
10578c2ecf20Sopenharmony_ci	int i;
10588c2ecf20Sopenharmony_ci
10598c2ecf20Sopenharmony_ci	max_epid = hw->max_epid;
10608c2ecf20Sopenharmony_ci	start_epid = (start_epid + 1 + max_epid) % max_epid;
10618c2ecf20Sopenharmony_ci
10628c2ecf20Sopenharmony_ci	for (i = 0; i < max_epid; i++) {
10638c2ecf20Sopenharmony_ci		cur_epid = (start_epid + i) % max_epid;
10648c2ecf20Sopenharmony_ci		if (cur_epid == hw->my_epid)
10658c2ecf20Sopenharmony_ci			continue;
10668c2ecf20Sopenharmony_ci
10678c2ecf20Sopenharmony_ci		pstatus = fjes_hw_get_partner_ep_status(hw, cur_epid);
10688c2ecf20Sopenharmony_ci		if (pstatus == EP_PARTNER_SHARED) {
10698c2ecf20Sopenharmony_ci			if (!fjes_hw_epbuf_rx_is_empty(
10708c2ecf20Sopenharmony_ci				&hw->ep_shm_info[cur_epid].rx))
10718c2ecf20Sopenharmony_ci				return cur_epid;
10728c2ecf20Sopenharmony_ci		}
10738c2ecf20Sopenharmony_ci	}
10748c2ecf20Sopenharmony_ci	return -1;
10758c2ecf20Sopenharmony_ci}
10768c2ecf20Sopenharmony_ci
10778c2ecf20Sopenharmony_cistatic void *fjes_rxframe_get(struct fjes_adapter *adapter, size_t *psize,
10788c2ecf20Sopenharmony_ci			      int *cur_epid)
10798c2ecf20Sopenharmony_ci{
10808c2ecf20Sopenharmony_ci	void *frame;
10818c2ecf20Sopenharmony_ci
10828c2ecf20Sopenharmony_ci	*cur_epid = fjes_rxframe_search_exist(adapter, *cur_epid);
10838c2ecf20Sopenharmony_ci	if (*cur_epid < 0)
10848c2ecf20Sopenharmony_ci		return NULL;
10858c2ecf20Sopenharmony_ci
10868c2ecf20Sopenharmony_ci	frame =
10878c2ecf20Sopenharmony_ci	fjes_hw_epbuf_rx_curpkt_get_addr(
10888c2ecf20Sopenharmony_ci		&adapter->hw.ep_shm_info[*cur_epid].rx, psize);
10898c2ecf20Sopenharmony_ci
10908c2ecf20Sopenharmony_ci	return frame;
10918c2ecf20Sopenharmony_ci}
10928c2ecf20Sopenharmony_ci
10938c2ecf20Sopenharmony_cistatic void fjes_rxframe_release(struct fjes_adapter *adapter, int cur_epid)
10948c2ecf20Sopenharmony_ci{
10958c2ecf20Sopenharmony_ci	fjes_hw_epbuf_rx_curpkt_drop(&adapter->hw.ep_shm_info[cur_epid].rx);
10968c2ecf20Sopenharmony_ci}
10978c2ecf20Sopenharmony_ci
10988c2ecf20Sopenharmony_cistatic void fjes_rx_irq(struct fjes_adapter *adapter, int src_epid)
10998c2ecf20Sopenharmony_ci{
11008c2ecf20Sopenharmony_ci	struct fjes_hw *hw = &adapter->hw;
11018c2ecf20Sopenharmony_ci
11028c2ecf20Sopenharmony_ci	fjes_hw_set_irqmask(hw, REG_ICTL_MASK_RX_DATA, true);
11038c2ecf20Sopenharmony_ci
11048c2ecf20Sopenharmony_ci	adapter->unset_rx_last = true;
11058c2ecf20Sopenharmony_ci	napi_schedule(&adapter->napi);
11068c2ecf20Sopenharmony_ci}
11078c2ecf20Sopenharmony_ci
11088c2ecf20Sopenharmony_cistatic int fjes_poll(struct napi_struct *napi, int budget)
11098c2ecf20Sopenharmony_ci{
11108c2ecf20Sopenharmony_ci	struct fjes_adapter *adapter =
11118c2ecf20Sopenharmony_ci			container_of(napi, struct fjes_adapter, napi);
11128c2ecf20Sopenharmony_ci	struct net_device *netdev = napi->dev;
11138c2ecf20Sopenharmony_ci	struct fjes_hw *hw = &adapter->hw;
11148c2ecf20Sopenharmony_ci	struct sk_buff *skb;
11158c2ecf20Sopenharmony_ci	int work_done = 0;
11168c2ecf20Sopenharmony_ci	int cur_epid = 0;
11178c2ecf20Sopenharmony_ci	int epidx;
11188c2ecf20Sopenharmony_ci	size_t frame_len;
11198c2ecf20Sopenharmony_ci	void *frame;
11208c2ecf20Sopenharmony_ci
11218c2ecf20Sopenharmony_ci	spin_lock(&hw->rx_status_lock);
11228c2ecf20Sopenharmony_ci	for (epidx = 0; epidx < hw->max_epid; epidx++) {
11238c2ecf20Sopenharmony_ci		if (epidx == hw->my_epid)
11248c2ecf20Sopenharmony_ci			continue;
11258c2ecf20Sopenharmony_ci
11268c2ecf20Sopenharmony_ci		if (fjes_hw_get_partner_ep_status(hw, epidx) ==
11278c2ecf20Sopenharmony_ci		    EP_PARTNER_SHARED)
11288c2ecf20Sopenharmony_ci			adapter->hw.ep_shm_info[epidx]
11298c2ecf20Sopenharmony_ci				   .tx.info->v1i.rx_status |= FJES_RX_POLL_WORK;
11308c2ecf20Sopenharmony_ci	}
11318c2ecf20Sopenharmony_ci	spin_unlock(&hw->rx_status_lock);
11328c2ecf20Sopenharmony_ci
11338c2ecf20Sopenharmony_ci	while (work_done < budget) {
11348c2ecf20Sopenharmony_ci		prefetch(&adapter->hw);
11358c2ecf20Sopenharmony_ci		frame = fjes_rxframe_get(adapter, &frame_len, &cur_epid);
11368c2ecf20Sopenharmony_ci
11378c2ecf20Sopenharmony_ci		if (frame) {
11388c2ecf20Sopenharmony_ci			skb = napi_alloc_skb(napi, frame_len);
11398c2ecf20Sopenharmony_ci			if (!skb) {
11408c2ecf20Sopenharmony_ci				adapter->stats64.rx_dropped += 1;
11418c2ecf20Sopenharmony_ci				hw->ep_shm_info[cur_epid].net_stats
11428c2ecf20Sopenharmony_ci							 .rx_dropped += 1;
11438c2ecf20Sopenharmony_ci				adapter->stats64.rx_errors += 1;
11448c2ecf20Sopenharmony_ci				hw->ep_shm_info[cur_epid].net_stats
11458c2ecf20Sopenharmony_ci							 .rx_errors += 1;
11468c2ecf20Sopenharmony_ci			} else {
11478c2ecf20Sopenharmony_ci				skb_put_data(skb, frame, frame_len);
11488c2ecf20Sopenharmony_ci				skb->protocol = eth_type_trans(skb, netdev);
11498c2ecf20Sopenharmony_ci				skb->ip_summed = CHECKSUM_UNNECESSARY;
11508c2ecf20Sopenharmony_ci
11518c2ecf20Sopenharmony_ci				netif_receive_skb(skb);
11528c2ecf20Sopenharmony_ci
11538c2ecf20Sopenharmony_ci				work_done++;
11548c2ecf20Sopenharmony_ci
11558c2ecf20Sopenharmony_ci				adapter->stats64.rx_packets += 1;
11568c2ecf20Sopenharmony_ci				hw->ep_shm_info[cur_epid].net_stats
11578c2ecf20Sopenharmony_ci							 .rx_packets += 1;
11588c2ecf20Sopenharmony_ci				adapter->stats64.rx_bytes += frame_len;
11598c2ecf20Sopenharmony_ci				hw->ep_shm_info[cur_epid].net_stats
11608c2ecf20Sopenharmony_ci							 .rx_bytes += frame_len;
11618c2ecf20Sopenharmony_ci
11628c2ecf20Sopenharmony_ci				if (is_multicast_ether_addr(
11638c2ecf20Sopenharmony_ci					((struct ethhdr *)frame)->h_dest)) {
11648c2ecf20Sopenharmony_ci					adapter->stats64.multicast += 1;
11658c2ecf20Sopenharmony_ci					hw->ep_shm_info[cur_epid].net_stats
11668c2ecf20Sopenharmony_ci								 .multicast += 1;
11678c2ecf20Sopenharmony_ci				}
11688c2ecf20Sopenharmony_ci			}
11698c2ecf20Sopenharmony_ci
11708c2ecf20Sopenharmony_ci			fjes_rxframe_release(adapter, cur_epid);
11718c2ecf20Sopenharmony_ci			adapter->unset_rx_last = true;
11728c2ecf20Sopenharmony_ci		} else {
11738c2ecf20Sopenharmony_ci			break;
11748c2ecf20Sopenharmony_ci		}
11758c2ecf20Sopenharmony_ci	}
11768c2ecf20Sopenharmony_ci
11778c2ecf20Sopenharmony_ci	if (work_done < budget) {
11788c2ecf20Sopenharmony_ci		napi_complete_done(napi, work_done);
11798c2ecf20Sopenharmony_ci
11808c2ecf20Sopenharmony_ci		if (adapter->unset_rx_last) {
11818c2ecf20Sopenharmony_ci			adapter->rx_last_jiffies = jiffies;
11828c2ecf20Sopenharmony_ci			adapter->unset_rx_last = false;
11838c2ecf20Sopenharmony_ci		}
11848c2ecf20Sopenharmony_ci
11858c2ecf20Sopenharmony_ci		if (((long)jiffies - (long)adapter->rx_last_jiffies) < 3) {
11868c2ecf20Sopenharmony_ci			napi_reschedule(napi);
11878c2ecf20Sopenharmony_ci		} else {
11888c2ecf20Sopenharmony_ci			spin_lock(&hw->rx_status_lock);
11898c2ecf20Sopenharmony_ci			for (epidx = 0; epidx < hw->max_epid; epidx++) {
11908c2ecf20Sopenharmony_ci				if (epidx == hw->my_epid)
11918c2ecf20Sopenharmony_ci					continue;
11928c2ecf20Sopenharmony_ci				if (fjes_hw_get_partner_ep_status(hw, epidx) ==
11938c2ecf20Sopenharmony_ci				    EP_PARTNER_SHARED)
11948c2ecf20Sopenharmony_ci					adapter->hw.ep_shm_info[epidx].tx
11958c2ecf20Sopenharmony_ci						   .info->v1i.rx_status &=
11968c2ecf20Sopenharmony_ci						~FJES_RX_POLL_WORK;
11978c2ecf20Sopenharmony_ci			}
11988c2ecf20Sopenharmony_ci			spin_unlock(&hw->rx_status_lock);
11998c2ecf20Sopenharmony_ci
12008c2ecf20Sopenharmony_ci			fjes_hw_set_irqmask(hw, REG_ICTL_MASK_RX_DATA, false);
12018c2ecf20Sopenharmony_ci		}
12028c2ecf20Sopenharmony_ci	}
12038c2ecf20Sopenharmony_ci
12048c2ecf20Sopenharmony_ci	return work_done;
12058c2ecf20Sopenharmony_ci}
12068c2ecf20Sopenharmony_ci
12078c2ecf20Sopenharmony_ci/* fjes_probe - Device Initialization Routine */
12088c2ecf20Sopenharmony_cistatic int fjes_probe(struct platform_device *plat_dev)
12098c2ecf20Sopenharmony_ci{
12108c2ecf20Sopenharmony_ci	struct fjes_adapter *adapter;
12118c2ecf20Sopenharmony_ci	struct net_device *netdev;
12128c2ecf20Sopenharmony_ci	struct resource *res;
12138c2ecf20Sopenharmony_ci	struct fjes_hw *hw;
12148c2ecf20Sopenharmony_ci	int err;
12158c2ecf20Sopenharmony_ci
12168c2ecf20Sopenharmony_ci	err = -ENOMEM;
12178c2ecf20Sopenharmony_ci	netdev = alloc_netdev_mq(sizeof(struct fjes_adapter), "es%d",
12188c2ecf20Sopenharmony_ci				 NET_NAME_UNKNOWN, fjes_netdev_setup,
12198c2ecf20Sopenharmony_ci				 FJES_MAX_QUEUES);
12208c2ecf20Sopenharmony_ci
12218c2ecf20Sopenharmony_ci	if (!netdev)
12228c2ecf20Sopenharmony_ci		goto err_out;
12238c2ecf20Sopenharmony_ci
12248c2ecf20Sopenharmony_ci	SET_NETDEV_DEV(netdev, &plat_dev->dev);
12258c2ecf20Sopenharmony_ci
12268c2ecf20Sopenharmony_ci	dev_set_drvdata(&plat_dev->dev, netdev);
12278c2ecf20Sopenharmony_ci	adapter = netdev_priv(netdev);
12288c2ecf20Sopenharmony_ci	adapter->netdev = netdev;
12298c2ecf20Sopenharmony_ci	adapter->plat_dev = plat_dev;
12308c2ecf20Sopenharmony_ci	hw = &adapter->hw;
12318c2ecf20Sopenharmony_ci	hw->back = adapter;
12328c2ecf20Sopenharmony_ci
12338c2ecf20Sopenharmony_ci	/* setup the private structure */
12348c2ecf20Sopenharmony_ci	err = fjes_sw_init(adapter);
12358c2ecf20Sopenharmony_ci	if (err)
12368c2ecf20Sopenharmony_ci		goto err_free_netdev;
12378c2ecf20Sopenharmony_ci
12388c2ecf20Sopenharmony_ci	INIT_WORK(&adapter->force_close_task, fjes_force_close_task);
12398c2ecf20Sopenharmony_ci	adapter->force_reset = false;
12408c2ecf20Sopenharmony_ci	adapter->open_guard = false;
12418c2ecf20Sopenharmony_ci
12428c2ecf20Sopenharmony_ci	adapter->txrx_wq = alloc_workqueue(DRV_NAME "/txrx", WQ_MEM_RECLAIM, 0);
12438c2ecf20Sopenharmony_ci	if (unlikely(!adapter->txrx_wq)) {
12448c2ecf20Sopenharmony_ci		err = -ENOMEM;
12458c2ecf20Sopenharmony_ci		goto err_free_netdev;
12468c2ecf20Sopenharmony_ci	}
12478c2ecf20Sopenharmony_ci
12488c2ecf20Sopenharmony_ci	adapter->control_wq = alloc_workqueue(DRV_NAME "/control",
12498c2ecf20Sopenharmony_ci					      WQ_MEM_RECLAIM, 0);
12508c2ecf20Sopenharmony_ci	if (unlikely(!adapter->control_wq)) {
12518c2ecf20Sopenharmony_ci		err = -ENOMEM;
12528c2ecf20Sopenharmony_ci		goto err_free_txrx_wq;
12538c2ecf20Sopenharmony_ci	}
12548c2ecf20Sopenharmony_ci
12558c2ecf20Sopenharmony_ci	INIT_WORK(&adapter->tx_stall_task, fjes_tx_stall_task);
12568c2ecf20Sopenharmony_ci	INIT_WORK(&adapter->raise_intr_rxdata_task,
12578c2ecf20Sopenharmony_ci		  fjes_raise_intr_rxdata_task);
12588c2ecf20Sopenharmony_ci	INIT_WORK(&adapter->unshare_watch_task, fjes_watch_unshare_task);
12598c2ecf20Sopenharmony_ci	adapter->unshare_watch_bitmask = 0;
12608c2ecf20Sopenharmony_ci
12618c2ecf20Sopenharmony_ci	INIT_DELAYED_WORK(&adapter->interrupt_watch_task, fjes_irq_watch_task);
12628c2ecf20Sopenharmony_ci	adapter->interrupt_watch_enable = false;
12638c2ecf20Sopenharmony_ci
12648c2ecf20Sopenharmony_ci	res = platform_get_resource(plat_dev, IORESOURCE_MEM, 0);
12658c2ecf20Sopenharmony_ci	if (!res) {
12668c2ecf20Sopenharmony_ci		err = -EINVAL;
12678c2ecf20Sopenharmony_ci		goto err_free_control_wq;
12688c2ecf20Sopenharmony_ci	}
12698c2ecf20Sopenharmony_ci	hw->hw_res.start = res->start;
12708c2ecf20Sopenharmony_ci	hw->hw_res.size = resource_size(res);
12718c2ecf20Sopenharmony_ci	hw->hw_res.irq = platform_get_irq(plat_dev, 0);
12728c2ecf20Sopenharmony_ci	if (hw->hw_res.irq < 0) {
12738c2ecf20Sopenharmony_ci		err = hw->hw_res.irq;
12748c2ecf20Sopenharmony_ci		goto err_free_control_wq;
12758c2ecf20Sopenharmony_ci	}
12768c2ecf20Sopenharmony_ci
12778c2ecf20Sopenharmony_ci	err = fjes_hw_init(&adapter->hw);
12788c2ecf20Sopenharmony_ci	if (err)
12798c2ecf20Sopenharmony_ci		goto err_free_control_wq;
12808c2ecf20Sopenharmony_ci
12818c2ecf20Sopenharmony_ci	/* setup MAC address (02:00:00:00:00:[epid])*/
12828c2ecf20Sopenharmony_ci	netdev->dev_addr[0] = 2;
12838c2ecf20Sopenharmony_ci	netdev->dev_addr[1] = 0;
12848c2ecf20Sopenharmony_ci	netdev->dev_addr[2] = 0;
12858c2ecf20Sopenharmony_ci	netdev->dev_addr[3] = 0;
12868c2ecf20Sopenharmony_ci	netdev->dev_addr[4] = 0;
12878c2ecf20Sopenharmony_ci	netdev->dev_addr[5] = hw->my_epid; /* EPID */
12888c2ecf20Sopenharmony_ci
12898c2ecf20Sopenharmony_ci	err = register_netdev(netdev);
12908c2ecf20Sopenharmony_ci	if (err)
12918c2ecf20Sopenharmony_ci		goto err_hw_exit;
12928c2ecf20Sopenharmony_ci
12938c2ecf20Sopenharmony_ci	netif_carrier_off(netdev);
12948c2ecf20Sopenharmony_ci
12958c2ecf20Sopenharmony_ci	fjes_dbg_adapter_init(adapter);
12968c2ecf20Sopenharmony_ci
12978c2ecf20Sopenharmony_ci	return 0;
12988c2ecf20Sopenharmony_ci
12998c2ecf20Sopenharmony_cierr_hw_exit:
13008c2ecf20Sopenharmony_ci	fjes_hw_exit(&adapter->hw);
13018c2ecf20Sopenharmony_cierr_free_control_wq:
13028c2ecf20Sopenharmony_ci	destroy_workqueue(adapter->control_wq);
13038c2ecf20Sopenharmony_cierr_free_txrx_wq:
13048c2ecf20Sopenharmony_ci	destroy_workqueue(adapter->txrx_wq);
13058c2ecf20Sopenharmony_cierr_free_netdev:
13068c2ecf20Sopenharmony_ci	free_netdev(netdev);
13078c2ecf20Sopenharmony_cierr_out:
13088c2ecf20Sopenharmony_ci	return err;
13098c2ecf20Sopenharmony_ci}
13108c2ecf20Sopenharmony_ci
13118c2ecf20Sopenharmony_ci/* fjes_remove - Device Removal Routine */
13128c2ecf20Sopenharmony_cistatic int fjes_remove(struct platform_device *plat_dev)
13138c2ecf20Sopenharmony_ci{
13148c2ecf20Sopenharmony_ci	struct net_device *netdev = dev_get_drvdata(&plat_dev->dev);
13158c2ecf20Sopenharmony_ci	struct fjes_adapter *adapter = netdev_priv(netdev);
13168c2ecf20Sopenharmony_ci	struct fjes_hw *hw = &adapter->hw;
13178c2ecf20Sopenharmony_ci
13188c2ecf20Sopenharmony_ci	fjes_dbg_adapter_exit(adapter);
13198c2ecf20Sopenharmony_ci
13208c2ecf20Sopenharmony_ci	cancel_delayed_work_sync(&adapter->interrupt_watch_task);
13218c2ecf20Sopenharmony_ci	cancel_work_sync(&adapter->unshare_watch_task);
13228c2ecf20Sopenharmony_ci	cancel_work_sync(&adapter->raise_intr_rxdata_task);
13238c2ecf20Sopenharmony_ci	cancel_work_sync(&adapter->tx_stall_task);
13248c2ecf20Sopenharmony_ci	if (adapter->control_wq)
13258c2ecf20Sopenharmony_ci		destroy_workqueue(adapter->control_wq);
13268c2ecf20Sopenharmony_ci	if (adapter->txrx_wq)
13278c2ecf20Sopenharmony_ci		destroy_workqueue(adapter->txrx_wq);
13288c2ecf20Sopenharmony_ci
13298c2ecf20Sopenharmony_ci	unregister_netdev(netdev);
13308c2ecf20Sopenharmony_ci
13318c2ecf20Sopenharmony_ci	fjes_hw_exit(hw);
13328c2ecf20Sopenharmony_ci
13338c2ecf20Sopenharmony_ci	netif_napi_del(&adapter->napi);
13348c2ecf20Sopenharmony_ci
13358c2ecf20Sopenharmony_ci	free_netdev(netdev);
13368c2ecf20Sopenharmony_ci
13378c2ecf20Sopenharmony_ci	return 0;
13388c2ecf20Sopenharmony_ci}
13398c2ecf20Sopenharmony_ci
13408c2ecf20Sopenharmony_cistatic int fjes_sw_init(struct fjes_adapter *adapter)
13418c2ecf20Sopenharmony_ci{
13428c2ecf20Sopenharmony_ci	struct net_device *netdev = adapter->netdev;
13438c2ecf20Sopenharmony_ci
13448c2ecf20Sopenharmony_ci	netif_napi_add(netdev, &adapter->napi, fjes_poll, 64);
13458c2ecf20Sopenharmony_ci
13468c2ecf20Sopenharmony_ci	return 0;
13478c2ecf20Sopenharmony_ci}
13488c2ecf20Sopenharmony_ci
13498c2ecf20Sopenharmony_ci/* fjes_netdev_setup - netdevice initialization routine */
13508c2ecf20Sopenharmony_cistatic void fjes_netdev_setup(struct net_device *netdev)
13518c2ecf20Sopenharmony_ci{
13528c2ecf20Sopenharmony_ci	ether_setup(netdev);
13538c2ecf20Sopenharmony_ci
13548c2ecf20Sopenharmony_ci	netdev->watchdog_timeo = FJES_TX_RETRY_INTERVAL;
13558c2ecf20Sopenharmony_ci	netdev->netdev_ops = &fjes_netdev_ops;
13568c2ecf20Sopenharmony_ci	fjes_set_ethtool_ops(netdev);
13578c2ecf20Sopenharmony_ci	netdev->mtu = fjes_support_mtu[3];
13588c2ecf20Sopenharmony_ci	netdev->min_mtu = fjes_support_mtu[0];
13598c2ecf20Sopenharmony_ci	netdev->max_mtu = fjes_support_mtu[3];
13608c2ecf20Sopenharmony_ci	netdev->features |= NETIF_F_HW_VLAN_CTAG_FILTER;
13618c2ecf20Sopenharmony_ci}
13628c2ecf20Sopenharmony_ci
13638c2ecf20Sopenharmony_cistatic void fjes_irq_watch_task(struct work_struct *work)
13648c2ecf20Sopenharmony_ci{
13658c2ecf20Sopenharmony_ci	struct fjes_adapter *adapter = container_of(to_delayed_work(work),
13668c2ecf20Sopenharmony_ci			struct fjes_adapter, interrupt_watch_task);
13678c2ecf20Sopenharmony_ci
13688c2ecf20Sopenharmony_ci	local_irq_disable();
13698c2ecf20Sopenharmony_ci	fjes_intr(adapter->hw.hw_res.irq, adapter);
13708c2ecf20Sopenharmony_ci	local_irq_enable();
13718c2ecf20Sopenharmony_ci
13728c2ecf20Sopenharmony_ci	if (fjes_rxframe_search_exist(adapter, 0) >= 0)
13738c2ecf20Sopenharmony_ci		napi_schedule(&adapter->napi);
13748c2ecf20Sopenharmony_ci
13758c2ecf20Sopenharmony_ci	if (adapter->interrupt_watch_enable) {
13768c2ecf20Sopenharmony_ci		if (!delayed_work_pending(&adapter->interrupt_watch_task))
13778c2ecf20Sopenharmony_ci			queue_delayed_work(adapter->control_wq,
13788c2ecf20Sopenharmony_ci					   &adapter->interrupt_watch_task,
13798c2ecf20Sopenharmony_ci					   FJES_IRQ_WATCH_DELAY);
13808c2ecf20Sopenharmony_ci	}
13818c2ecf20Sopenharmony_ci}
13828c2ecf20Sopenharmony_ci
13838c2ecf20Sopenharmony_cistatic void fjes_watch_unshare_task(struct work_struct *work)
13848c2ecf20Sopenharmony_ci{
13858c2ecf20Sopenharmony_ci	struct fjes_adapter *adapter =
13868c2ecf20Sopenharmony_ci	container_of(work, struct fjes_adapter, unshare_watch_task);
13878c2ecf20Sopenharmony_ci
13888c2ecf20Sopenharmony_ci	struct net_device *netdev = adapter->netdev;
13898c2ecf20Sopenharmony_ci	struct fjes_hw *hw = &adapter->hw;
13908c2ecf20Sopenharmony_ci
13918c2ecf20Sopenharmony_ci	int unshare_watch, unshare_reserve;
13928c2ecf20Sopenharmony_ci	int max_epid, my_epid, epidx;
13938c2ecf20Sopenharmony_ci	int stop_req, stop_req_done;
13948c2ecf20Sopenharmony_ci	ulong unshare_watch_bitmask;
13958c2ecf20Sopenharmony_ci	unsigned long flags;
13968c2ecf20Sopenharmony_ci	int wait_time = 0;
13978c2ecf20Sopenharmony_ci	int is_shared;
13988c2ecf20Sopenharmony_ci	int ret;
13998c2ecf20Sopenharmony_ci
14008c2ecf20Sopenharmony_ci	my_epid = hw->my_epid;
14018c2ecf20Sopenharmony_ci	max_epid = hw->max_epid;
14028c2ecf20Sopenharmony_ci
14038c2ecf20Sopenharmony_ci	unshare_watch_bitmask = adapter->unshare_watch_bitmask;
14048c2ecf20Sopenharmony_ci	adapter->unshare_watch_bitmask = 0;
14058c2ecf20Sopenharmony_ci
14068c2ecf20Sopenharmony_ci	while ((unshare_watch_bitmask || hw->txrx_stop_req_bit) &&
14078c2ecf20Sopenharmony_ci	       (wait_time < 3000)) {
14088c2ecf20Sopenharmony_ci		for (epidx = 0; epidx < max_epid; epidx++) {
14098c2ecf20Sopenharmony_ci			if (epidx == my_epid)
14108c2ecf20Sopenharmony_ci				continue;
14118c2ecf20Sopenharmony_ci
14128c2ecf20Sopenharmony_ci			is_shared = fjes_hw_epid_is_shared(hw->hw_info.share,
14138c2ecf20Sopenharmony_ci							   epidx);
14148c2ecf20Sopenharmony_ci
14158c2ecf20Sopenharmony_ci			stop_req = test_bit(epidx, &hw->txrx_stop_req_bit);
14168c2ecf20Sopenharmony_ci
14178c2ecf20Sopenharmony_ci			stop_req_done = hw->ep_shm_info[epidx].rx.info->v1i.rx_status &
14188c2ecf20Sopenharmony_ci					FJES_RX_STOP_REQ_DONE;
14198c2ecf20Sopenharmony_ci
14208c2ecf20Sopenharmony_ci			unshare_watch = test_bit(epidx, &unshare_watch_bitmask);
14218c2ecf20Sopenharmony_ci
14228c2ecf20Sopenharmony_ci			unshare_reserve = test_bit(epidx,
14238c2ecf20Sopenharmony_ci						   &hw->hw_info.buffer_unshare_reserve_bit);
14248c2ecf20Sopenharmony_ci
14258c2ecf20Sopenharmony_ci			if ((!stop_req ||
14268c2ecf20Sopenharmony_ci			     (is_shared && (!is_shared || !stop_req_done))) &&
14278c2ecf20Sopenharmony_ci			    (is_shared || !unshare_watch || !unshare_reserve))
14288c2ecf20Sopenharmony_ci				continue;
14298c2ecf20Sopenharmony_ci
14308c2ecf20Sopenharmony_ci			mutex_lock(&hw->hw_info.lock);
14318c2ecf20Sopenharmony_ci			ret = fjes_hw_unregister_buff_addr(hw, epidx);
14328c2ecf20Sopenharmony_ci			switch (ret) {
14338c2ecf20Sopenharmony_ci			case 0:
14348c2ecf20Sopenharmony_ci				break;
14358c2ecf20Sopenharmony_ci			case -ENOMSG:
14368c2ecf20Sopenharmony_ci			case -EBUSY:
14378c2ecf20Sopenharmony_ci			default:
14388c2ecf20Sopenharmony_ci				if (!work_pending(
14398c2ecf20Sopenharmony_ci					&adapter->force_close_task)) {
14408c2ecf20Sopenharmony_ci					adapter->force_reset = true;
14418c2ecf20Sopenharmony_ci					schedule_work(
14428c2ecf20Sopenharmony_ci						&adapter->force_close_task);
14438c2ecf20Sopenharmony_ci				}
14448c2ecf20Sopenharmony_ci				break;
14458c2ecf20Sopenharmony_ci			}
14468c2ecf20Sopenharmony_ci			mutex_unlock(&hw->hw_info.lock);
14478c2ecf20Sopenharmony_ci			hw->ep_shm_info[epidx].ep_stats
14488c2ecf20Sopenharmony_ci					.com_unregist_buf_exec += 1;
14498c2ecf20Sopenharmony_ci
14508c2ecf20Sopenharmony_ci			spin_lock_irqsave(&hw->rx_status_lock, flags);
14518c2ecf20Sopenharmony_ci			fjes_hw_setup_epbuf(&hw->ep_shm_info[epidx].tx,
14528c2ecf20Sopenharmony_ci					    netdev->dev_addr, netdev->mtu);
14538c2ecf20Sopenharmony_ci			spin_unlock_irqrestore(&hw->rx_status_lock, flags);
14548c2ecf20Sopenharmony_ci
14558c2ecf20Sopenharmony_ci			clear_bit(epidx, &hw->txrx_stop_req_bit);
14568c2ecf20Sopenharmony_ci			clear_bit(epidx, &unshare_watch_bitmask);
14578c2ecf20Sopenharmony_ci			clear_bit(epidx,
14588c2ecf20Sopenharmony_ci				  &hw->hw_info.buffer_unshare_reserve_bit);
14598c2ecf20Sopenharmony_ci		}
14608c2ecf20Sopenharmony_ci
14618c2ecf20Sopenharmony_ci		msleep(100);
14628c2ecf20Sopenharmony_ci		wait_time += 100;
14638c2ecf20Sopenharmony_ci	}
14648c2ecf20Sopenharmony_ci
14658c2ecf20Sopenharmony_ci	if (hw->hw_info.buffer_unshare_reserve_bit) {
14668c2ecf20Sopenharmony_ci		for (epidx = 0; epidx < max_epid; epidx++) {
14678c2ecf20Sopenharmony_ci			if (epidx == my_epid)
14688c2ecf20Sopenharmony_ci				continue;
14698c2ecf20Sopenharmony_ci
14708c2ecf20Sopenharmony_ci			if (test_bit(epidx,
14718c2ecf20Sopenharmony_ci				     &hw->hw_info.buffer_unshare_reserve_bit)) {
14728c2ecf20Sopenharmony_ci				mutex_lock(&hw->hw_info.lock);
14738c2ecf20Sopenharmony_ci
14748c2ecf20Sopenharmony_ci				ret = fjes_hw_unregister_buff_addr(hw, epidx);
14758c2ecf20Sopenharmony_ci				switch (ret) {
14768c2ecf20Sopenharmony_ci				case 0:
14778c2ecf20Sopenharmony_ci					break;
14788c2ecf20Sopenharmony_ci				case -ENOMSG:
14798c2ecf20Sopenharmony_ci				case -EBUSY:
14808c2ecf20Sopenharmony_ci				default:
14818c2ecf20Sopenharmony_ci					if (!work_pending(
14828c2ecf20Sopenharmony_ci						&adapter->force_close_task)) {
14838c2ecf20Sopenharmony_ci						adapter->force_reset = true;
14848c2ecf20Sopenharmony_ci						schedule_work(
14858c2ecf20Sopenharmony_ci							&adapter->force_close_task);
14868c2ecf20Sopenharmony_ci					}
14878c2ecf20Sopenharmony_ci					break;
14888c2ecf20Sopenharmony_ci				}
14898c2ecf20Sopenharmony_ci				mutex_unlock(&hw->hw_info.lock);
14908c2ecf20Sopenharmony_ci
14918c2ecf20Sopenharmony_ci				hw->ep_shm_info[epidx].ep_stats
14928c2ecf20Sopenharmony_ci					.com_unregist_buf_exec += 1;
14938c2ecf20Sopenharmony_ci
14948c2ecf20Sopenharmony_ci				spin_lock_irqsave(&hw->rx_status_lock, flags);
14958c2ecf20Sopenharmony_ci				fjes_hw_setup_epbuf(
14968c2ecf20Sopenharmony_ci					&hw->ep_shm_info[epidx].tx,
14978c2ecf20Sopenharmony_ci					netdev->dev_addr, netdev->mtu);
14988c2ecf20Sopenharmony_ci				spin_unlock_irqrestore(&hw->rx_status_lock,
14998c2ecf20Sopenharmony_ci						       flags);
15008c2ecf20Sopenharmony_ci
15018c2ecf20Sopenharmony_ci				clear_bit(epidx, &hw->txrx_stop_req_bit);
15028c2ecf20Sopenharmony_ci				clear_bit(epidx, &unshare_watch_bitmask);
15038c2ecf20Sopenharmony_ci				clear_bit(epidx, &hw->hw_info.buffer_unshare_reserve_bit);
15048c2ecf20Sopenharmony_ci			}
15058c2ecf20Sopenharmony_ci
15068c2ecf20Sopenharmony_ci			if (test_bit(epidx, &unshare_watch_bitmask)) {
15078c2ecf20Sopenharmony_ci				spin_lock_irqsave(&hw->rx_status_lock, flags);
15088c2ecf20Sopenharmony_ci				hw->ep_shm_info[epidx].tx.info->v1i.rx_status &=
15098c2ecf20Sopenharmony_ci						~FJES_RX_STOP_REQ_DONE;
15108c2ecf20Sopenharmony_ci				spin_unlock_irqrestore(&hw->rx_status_lock,
15118c2ecf20Sopenharmony_ci						       flags);
15128c2ecf20Sopenharmony_ci			}
15138c2ecf20Sopenharmony_ci		}
15148c2ecf20Sopenharmony_ci	}
15158c2ecf20Sopenharmony_ci}
15168c2ecf20Sopenharmony_ci
15178c2ecf20Sopenharmony_cistatic acpi_status
15188c2ecf20Sopenharmony_ciacpi_find_extended_socket_device(acpi_handle obj_handle, u32 level,
15198c2ecf20Sopenharmony_ci				 void *context, void **return_value)
15208c2ecf20Sopenharmony_ci{
15218c2ecf20Sopenharmony_ci	struct acpi_device *device;
15228c2ecf20Sopenharmony_ci	bool *found = context;
15238c2ecf20Sopenharmony_ci	int result;
15248c2ecf20Sopenharmony_ci
15258c2ecf20Sopenharmony_ci	result = acpi_bus_get_device(obj_handle, &device);
15268c2ecf20Sopenharmony_ci	if (result)
15278c2ecf20Sopenharmony_ci		return AE_OK;
15288c2ecf20Sopenharmony_ci
15298c2ecf20Sopenharmony_ci	if (strcmp(acpi_device_hid(device), ACPI_MOTHERBOARD_RESOURCE_HID))
15308c2ecf20Sopenharmony_ci		return AE_OK;
15318c2ecf20Sopenharmony_ci
15328c2ecf20Sopenharmony_ci	if (!is_extended_socket_device(device))
15338c2ecf20Sopenharmony_ci		return AE_OK;
15348c2ecf20Sopenharmony_ci
15358c2ecf20Sopenharmony_ci	if (acpi_check_extended_socket_status(device))
15368c2ecf20Sopenharmony_ci		return AE_OK;
15378c2ecf20Sopenharmony_ci
15388c2ecf20Sopenharmony_ci	*found = true;
15398c2ecf20Sopenharmony_ci	return AE_CTRL_TERMINATE;
15408c2ecf20Sopenharmony_ci}
15418c2ecf20Sopenharmony_ci
15428c2ecf20Sopenharmony_ci/* fjes_init_module - Driver Registration Routine */
15438c2ecf20Sopenharmony_cistatic int __init fjes_init_module(void)
15448c2ecf20Sopenharmony_ci{
15458c2ecf20Sopenharmony_ci	bool found = false;
15468c2ecf20Sopenharmony_ci	int result;
15478c2ecf20Sopenharmony_ci
15488c2ecf20Sopenharmony_ci	acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT, ACPI_UINT32_MAX,
15498c2ecf20Sopenharmony_ci			    acpi_find_extended_socket_device, NULL, &found,
15508c2ecf20Sopenharmony_ci			    NULL);
15518c2ecf20Sopenharmony_ci
15528c2ecf20Sopenharmony_ci	if (!found)
15538c2ecf20Sopenharmony_ci		return -ENODEV;
15548c2ecf20Sopenharmony_ci
15558c2ecf20Sopenharmony_ci	pr_info("%s - version %s - %s\n",
15568c2ecf20Sopenharmony_ci		fjes_driver_string, fjes_driver_version, fjes_copyright);
15578c2ecf20Sopenharmony_ci
15588c2ecf20Sopenharmony_ci	fjes_dbg_init();
15598c2ecf20Sopenharmony_ci
15608c2ecf20Sopenharmony_ci	result = platform_driver_register(&fjes_driver);
15618c2ecf20Sopenharmony_ci	if (result < 0) {
15628c2ecf20Sopenharmony_ci		fjes_dbg_exit();
15638c2ecf20Sopenharmony_ci		return result;
15648c2ecf20Sopenharmony_ci	}
15658c2ecf20Sopenharmony_ci
15668c2ecf20Sopenharmony_ci	result = acpi_bus_register_driver(&fjes_acpi_driver);
15678c2ecf20Sopenharmony_ci	if (result < 0)
15688c2ecf20Sopenharmony_ci		goto fail_acpi_driver;
15698c2ecf20Sopenharmony_ci
15708c2ecf20Sopenharmony_ci	return 0;
15718c2ecf20Sopenharmony_ci
15728c2ecf20Sopenharmony_cifail_acpi_driver:
15738c2ecf20Sopenharmony_ci	platform_driver_unregister(&fjes_driver);
15748c2ecf20Sopenharmony_ci	fjes_dbg_exit();
15758c2ecf20Sopenharmony_ci	return result;
15768c2ecf20Sopenharmony_ci}
15778c2ecf20Sopenharmony_ci
15788c2ecf20Sopenharmony_cimodule_init(fjes_init_module);
15798c2ecf20Sopenharmony_ci
15808c2ecf20Sopenharmony_ci/* fjes_exit_module - Driver Exit Cleanup Routine */
15818c2ecf20Sopenharmony_cistatic void __exit fjes_exit_module(void)
15828c2ecf20Sopenharmony_ci{
15838c2ecf20Sopenharmony_ci	acpi_bus_unregister_driver(&fjes_acpi_driver);
15848c2ecf20Sopenharmony_ci	platform_driver_unregister(&fjes_driver);
15858c2ecf20Sopenharmony_ci	fjes_dbg_exit();
15868c2ecf20Sopenharmony_ci}
15878c2ecf20Sopenharmony_ci
15888c2ecf20Sopenharmony_cimodule_exit(fjes_exit_module);
1589