From e173cecf07af78a6215e0e906b1fb82aec9cc49c Mon Sep 17 00:00:00 2001 From: Ming Wang Date: Thu, 17 Oct 2024 14:02:32 +0800 Subject: [PATCH] drivers: ethernet: Add motorcomm yt6801 support [Upstream: no] - The Asus XC-LS3A6M motherboard(Loongson 3A6000),CE720Z2,CE720Z... uses a yt6801 controller. - This patch introduces an out of tree module to provide support for this NIC. [1] - Refactor module tree to make it work with Kconfig. [2] - Drop unneeded installation script (yt_nic_install.sh). - Drop README. some day,we can write a yt6801.rst in kenenl doc. - enable YT6801 for loongarch as module - Refactor according to the kernel code style [1]: https://www.motor-comm.com/Public/Uploads/uploadfile/files/20240812/yt6801-linux-driver-1.0.29.zip [2]: CONFIG_NET_VENDOR_MOTORCOMM =(y)=> CONFIG_YT8601 (tristate). Signed-off-by: Ming Wang --- arch/loongarch/configs/loongson3_defconfig | 1 + drivers/net/ethernet/Kconfig | 1 + drivers/net/ethernet/Makefile | 1 + drivers/net/ethernet/motorcomm/Kconfig | 27 + drivers/net/ethernet/motorcomm/Makefile | 4 + .../net/ethernet/motorcomm/yt6801/Makefile | 15 + .../net/ethernet/motorcomm/yt6801/fuxi-dbg.h | 14 + .../ethernet/motorcomm/yt6801/fuxi-efuse.c | 1352 ++++ .../ethernet/motorcomm/yt6801/fuxi-efuse.h | 37 + .../motorcomm/yt6801/fuxi-gmac-common.c | 934 +++ .../motorcomm/yt6801/fuxi-gmac-desc.c | 840 +++ .../motorcomm/yt6801/fuxi-gmac-ethtool.c | 1271 ++++ .../ethernet/motorcomm/yt6801/fuxi-gmac-hw.c | 6097 +++++++++++++++++ .../motorcomm/yt6801/fuxi-gmac-ioctl.c | 590 ++ .../ethernet/motorcomm/yt6801/fuxi-gmac-net.c | 3052 +++++++++ .../ethernet/motorcomm/yt6801/fuxi-gmac-pci.c | 249 + .../ethernet/motorcomm/yt6801/fuxi-gmac-phy.c | 417 ++ .../ethernet/motorcomm/yt6801/fuxi-gmac-reg.h | 1932 ++++++ .../net/ethernet/motorcomm/yt6801/fuxi-gmac.h | 938 +++ .../net/ethernet/motorcomm/yt6801/fuxi-os.h | 722 ++ 20 files changed, 18494 insertions(+) create mode 100644 drivers/net/ethernet/motorcomm/Kconfig create mode 100644 drivers/net/ethernet/motorcomm/Makefile create mode 100644 drivers/net/ethernet/motorcomm/yt6801/Makefile create mode 100644 drivers/net/ethernet/motorcomm/yt6801/fuxi-dbg.h create mode 100644 drivers/net/ethernet/motorcomm/yt6801/fuxi-efuse.c create mode 100644 drivers/net/ethernet/motorcomm/yt6801/fuxi-efuse.h create mode 100644 drivers/net/ethernet/motorcomm/yt6801/fuxi-gmac-common.c create mode 100644 drivers/net/ethernet/motorcomm/yt6801/fuxi-gmac-desc.c create mode 100644 drivers/net/ethernet/motorcomm/yt6801/fuxi-gmac-ethtool.c create mode 100644 drivers/net/ethernet/motorcomm/yt6801/fuxi-gmac-hw.c create mode 100644 drivers/net/ethernet/motorcomm/yt6801/fuxi-gmac-ioctl.c create mode 100644 drivers/net/ethernet/motorcomm/yt6801/fuxi-gmac-net.c create mode 100644 drivers/net/ethernet/motorcomm/yt6801/fuxi-gmac-pci.c create mode 100644 drivers/net/ethernet/motorcomm/yt6801/fuxi-gmac-phy.c create mode 100644 drivers/net/ethernet/motorcomm/yt6801/fuxi-gmac-reg.h create mode 100644 drivers/net/ethernet/motorcomm/yt6801/fuxi-gmac.h create mode 100644 drivers/net/ethernet/motorcomm/yt6801/fuxi-os.h diff --git a/arch/loongarch/configs/loongson3_defconfig b/arch/loongarch/configs/loongson3_defconfig index 5c00be21770a..c9f17d33577d 100644 --- a/arch/loongarch/configs/loongson3_defconfig +++ b/arch/loongarch/configs/loongson3_defconfig @@ -877,6 +877,7 @@ CONFIG_8139TOO=m # CONFIG_8139TOO_PIO is not set CONFIG_8139TOO_8129=y CONFIG_R8169=m +CONFIG_YT6801=m # CONFIG_NET_VENDOR_RENESAS is not set # CONFIG_NET_VENDOR_ROCKER is not set # CONFIG_NET_VENDOR_SAMSUNG is not set diff --git a/drivers/net/ethernet/Kconfig b/drivers/net/ethernet/Kconfig index f0a0ad8b23a8..ea8d4fbda89a 100644 --- a/drivers/net/ethernet/Kconfig +++ b/drivers/net/ethernet/Kconfig @@ -128,6 +128,7 @@ source "drivers/net/ethernet/mediatek/Kconfig" source "drivers/net/ethernet/mellanox/Kconfig" source "drivers/net/ethernet/micrel/Kconfig" source "drivers/net/ethernet/microchip/Kconfig" +source "drivers/net/ethernet/motorcomm/Kconfig" source "drivers/net/ethernet/mscc/Kconfig" source "drivers/net/ethernet/microsoft/Kconfig" source "drivers/net/ethernet/moxa/Kconfig" diff --git a/drivers/net/ethernet/Makefile b/drivers/net/ethernet/Makefile index f5c2b0b9281e..d7e4e6910116 100644 --- a/drivers/net/ethernet/Makefile +++ b/drivers/net/ethernet/Makefile @@ -62,6 +62,7 @@ obj-$(CONFIG_NET_VENDOR_MEDIATEK) += mediatek/ obj-$(CONFIG_NET_VENDOR_MELLANOX) += mellanox/ obj-$(CONFIG_NET_VENDOR_MICREL) += micrel/ obj-$(CONFIG_NET_VENDOR_MICROCHIP) += microchip/ +obj-$(CONFIG_NET_VENDOR_MOTORCOMM) += motorcomm/ obj-$(CONFIG_NET_VENDOR_MICROSEMI) += mscc/ obj-$(CONFIG_NET_VENDOR_MOXART) += moxa/ obj-$(CONFIG_NET_VENDOR_MYRI) += myricom/ diff --git a/drivers/net/ethernet/motorcomm/Kconfig b/drivers/net/ethernet/motorcomm/Kconfig new file mode 100644 index 000000000000..2d058928936f --- /dev/null +++ b/drivers/net/ethernet/motorcomm/Kconfig @@ -0,0 +1,27 @@ +# SPDX-License-Identifier: GPL-2.0-only +# Copyright (c) 2023 Motorcomm, Inc. + +config NET_VENDOR_MOTORCOMM + bool "Motorcomm devices" + default y + depends on PCI + help + If you have a network (Ethernet) card belonging to this class, say Y. + + Note that the answer to this question doesn't directly affect the + kernel: saying N will just cause the configurator to skip all + the questions about Motorcomm cards. If you say Y, you will be asked + for your specific card in the following questions. + +if NET_VENDOR_MOTORCOMM + +config YT6801 + tristate "Motorcomm YT6801 Ethernet support" + depends on PCI + help + If you have a network (Ethernet) controller of this type, say Y here. + + To compile this driver as a module, choose M here. The module + will be called forcedeth. + +endif # NET_VENDOR_MOTORCOMM diff --git a/drivers/net/ethernet/motorcomm/Makefile b/drivers/net/ethernet/motorcomm/Makefile new file mode 100644 index 000000000000..af0a439d54a1 --- /dev/null +++ b/drivers/net/ethernet/motorcomm/Makefile @@ -0,0 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0-only +# Copyright (c) 2023 Motorcomm, Inc. + +obj-$(CONFIG_YT6801) += yt6801/ diff --git a/drivers/net/ethernet/motorcomm/yt6801/Makefile b/drivers/net/ethernet/motorcomm/yt6801/Makefile new file mode 100644 index 000000000000..904db987b7ce --- /dev/null +++ b/drivers/net/ethernet/motorcomm/yt6801/Makefile @@ -0,0 +1,15 @@ +# SPDX-License-Identifier: GPL-2.0-only +# Copyright (c) 2023 Motorcomm, Inc. + + +obj-$(CONFIG_YT6801) += yt6801.o + +yt6801-objs := fuxi-gmac-common.o \ + fuxi-gmac-desc.o \ + fuxi-gmac-ethtool.o \ + fuxi-gmac-hw.o \ + fuxi-gmac-net.o \ + fuxi-gmac-pci.o \ + fuxi-gmac-phy.o \ + fuxi-efuse.o \ + fuxi-gmac-ioctl.o diff --git a/drivers/net/ethernet/motorcomm/yt6801/fuxi-dbg.h b/drivers/net/ethernet/motorcomm/yt6801/fuxi-dbg.h new file mode 100644 index 000000000000..6d7365b35a16 --- /dev/null +++ b/drivers/net/ethernet/motorcomm/yt6801/fuxi-dbg.h @@ -0,0 +1,14 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (c) 2021 Motorcomm Corporation. */ + +#ifndef _MP_DBG_H +#define _MP_DBG_H + +#define MP_OFF 0 +#define MP_ERROR 1 +#define MP_WARN 2 +#define MP_TRACE 3 +#define MP_INFO 4 +#define MP_LOUD 5 + +#endif diff --git a/drivers/net/ethernet/motorcomm/yt6801/fuxi-efuse.c b/drivers/net/ethernet/motorcomm/yt6801/fuxi-efuse.c new file mode 100644 index 000000000000..d345e2a44ad3 --- /dev/null +++ b/drivers/net/ethernet/motorcomm/yt6801/fuxi-efuse.c @@ -0,0 +1,1352 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (c) 2021 Motorcomm Corporation. */ + +#include "fuxi-gmac.h" +#include "fuxi-gmac-reg.h" +#include "fuxi-efuse.h" + +#ifdef FXGMAC_USE_ADAPTER_HANDLE +#include "fuxi-mp.h" +#endif + +bool fxgmac_read_patch_from_efuse_per_index( + struct fxgmac_pdata *pdata, u8 index, u32 __far *offset, + u32 __far *value) /* read patch per index. */ +{ + unsigned int wait, i; + u32 regval = 0; + bool succeed = false; + + if (index >= FXGMAC_EFUSE_MAX_ENTRY) { + FXGMAC_PR("Reading efuse out of range, index %d\n", index); + return false; + } + + if (offset) { + *offset = 0; + } + for (i = EFUSE_PATCH_ADDR_START_BYTE; i < EFUSE_PATCH_DATA_START_BYTE; + i++) { + regval = 0; + regval = FXGMAC_SET_REG_BITS( + regval, EFUSE_OP_ADDR_POS, EFUSE_OP_ADDR_LEN, + EFUSE_REGION_A_B_LENGTH + index * EFUSE_EACH_PATH_SIZE + + i); + regval = FXGMAC_SET_REG_BITS(regval, EFUSE_OP_START_POS, + EFUSE_OP_START_LEN, 1); + regval = FXGMAC_SET_REG_BITS(regval, EFUSE_OP_MODE_POS, + EFUSE_OP_MODE_LEN, + EFUSE_OP_MODE_ROW_READ); + writereg(pdata->pAdapter, regval, + pdata->base_mem + EFUSE_OP_CTRL_0); + wait = 1000; + while (wait--) { + usleep_range_ex(pdata->pAdapter, 20, 50); + regval = readreg(pdata->pAdapter, + pdata->base_mem + EFUSE_OP_CTRL_1); + if (FXGMAC_GET_REG_BITS(regval, EFUSE_OP_DONE_POS, + EFUSE_OP_DONE_LEN)) { + succeed = true; + break; + } + } + if (succeed) { + if (offset) { + *offset |= + (FXGMAC_GET_REG_BITS( + regval, EFUSE_OP_RD_DATA_POS, + EFUSE_OP_RD_DATA_LEN) + << (i << 3)); + } + } else { + FXGMAC_PR("Fail to reading efuse Byte%d\n", + index * EFUSE_EACH_PATH_SIZE + i); + return succeed; + } + } + + if (value) { + *value = 0; + } + for (i = EFUSE_PATCH_DATA_START_BYTE; i < EFUSE_EACH_PATH_SIZE; i++) { + regval = 0; + regval = FXGMAC_SET_REG_BITS( + regval, EFUSE_OP_ADDR_POS, EFUSE_OP_ADDR_LEN, + EFUSE_REGION_A_B_LENGTH + index * EFUSE_EACH_PATH_SIZE + + i); + regval = FXGMAC_SET_REG_BITS(regval, EFUSE_OP_START_POS, + EFUSE_OP_START_LEN, 1); + regval = FXGMAC_SET_REG_BITS(regval, EFUSE_OP_MODE_POS, + EFUSE_OP_MODE_LEN, + EFUSE_OP_MODE_ROW_READ); + writereg(pdata->pAdapter, regval, + pdata->base_mem + EFUSE_OP_CTRL_0); + wait = 1000; + while (wait--) { + usleep_range_ex(pdata->pAdapter, 20, 50); + regval = readreg(pdata->pAdapter, + pdata->base_mem + EFUSE_OP_CTRL_1); + if (FXGMAC_GET_REG_BITS(regval, EFUSE_OP_DONE_POS, + EFUSE_OP_DONE_LEN)) { + succeed = true; + break; + } + } + if (succeed) { + if (value) { + *value |= (FXGMAC_GET_REG_BITS( + regval, EFUSE_OP_RD_DATA_POS, + EFUSE_OP_RD_DATA_LEN) + << ((i - 2) << 3)); + } + } else { + FXGMAC_PR("Fail to reading efuse Byte%d\n", + index * EFUSE_EACH_PATH_SIZE + i); + return succeed; + } + } + + return succeed; +} + +bool fxgmac_read_mac_subsys_from_efuse(struct fxgmac_pdata *pdata, u8 *mac_addr, + u32 *subsys, u32 *revid) +{ + u32 offset = 0, value = 0; + u32 machr = 0, maclr = 0; + bool succeed = true; + u8 index = 0; + + for (index = 0;; index++) { + if (!fxgmac_read_patch_from_efuse_per_index(pdata, index, + &offset, &value)) { + succeed = false; + break; + } + if (0x00 == offset) { + break; + } + if (MACA0LR_FROM_EFUSE == offset) { + maclr = value; + } + if (MACA0HR_FROM_EFUSE == offset) { + machr = value; + } + + if ((0x08 == offset) && revid) { + *revid = value; + } + if ((0x2C == offset) && subsys) { + *subsys = value; + } + } + if (mac_addr) { + mac_addr[5] = (u8)(maclr & 0xFF); + mac_addr[4] = (u8)((maclr >> 8) & 0xFF); + mac_addr[3] = (u8)((maclr >> 16) & 0xFF); + mac_addr[2] = (u8)((maclr >> 24) & 0xFF); + mac_addr[1] = (u8)(machr & 0xFF); + mac_addr[0] = (u8)((machr >> 8) & 0xFF); + } + + return succeed; +} + +bool fxgmac_efuse_read_data(struct fxgmac_pdata *pdata, u32 offset, + u32 __far *value) +{ + bool succeed = false; + unsigned int wait; + u32 reg_val = 0; + + if (value) { + *value = 0; + } + + reg_val = FXGMAC_SET_REG_BITS(reg_val, EFUSE_OP_ADDR_POS, + EFUSE_OP_ADDR_LEN, offset); + reg_val = FXGMAC_SET_REG_BITS(reg_val, EFUSE_OP_START_POS, + EFUSE_OP_START_LEN, 1); + reg_val = FXGMAC_SET_REG_BITS(reg_val, EFUSE_OP_MODE_POS, + EFUSE_OP_MODE_LEN, + EFUSE_OP_MODE_ROW_READ); + writereg(pdata->pAdapter, reg_val, pdata->base_mem + EFUSE_OP_CTRL_0); + wait = 1000; + while (wait--) { + usleep_range_ex(pdata->pAdapter, 20, 50); + reg_val = readreg(pdata->pAdapter, + pdata->base_mem + EFUSE_OP_CTRL_1); + if (FXGMAC_GET_REG_BITS(reg_val, EFUSE_OP_DONE_POS, + EFUSE_OP_DONE_LEN)) { + succeed = true; + break; + } + } + + if (succeed) { + if (value) { + *value = FXGMAC_GET_REG_BITS(reg_val, + EFUSE_OP_RD_DATA_POS, + EFUSE_OP_RD_DATA_LEN); + } + } else { + FXGMAC_PR("Fail to reading efuse Byte%d\n", offset); + } + + return succeed; +} + +#ifndef COMMENT_UNUSED_CODE_TO_REDUCE_SIZE +bool fxgmac_read_patch_from_efuse(struct fxgmac_pdata *pdata, u32 offset, + u32 *value) /* read patch per index. */ +{ + u32 reg_offset, reg_val; + u32 cur_val = 0; + bool succeed = true; + u8 index = 0; + + if (offset >> 16) { + FXGMAC_PR( + "Reading efuse out of range, reg %d. reg must be 2bytes.\n", + index); + return false; + } + + for (index = 0; index < FXGMAC_EFUSE_MAX_ENTRY; index++) { + if (!fxgmac_read_patch_from_efuse_per_index( + pdata, index, ®_offset, ®_val)) { + succeed = false; + break; + } else if (reg_offset == offset) { + cur_val = reg_val; + } else if (0 == reg_offset && 0 == reg_val) { + break; + } + } + + if (value) { + *value = cur_val; + } + + return succeed; +} + +bool fxgmac_write_patch_to_efuse_per_index(struct fxgmac_pdata *pdata, u8 index, + u32 offset, u32 value) +{ + unsigned int wait, i; + u32 reg_val; + bool succeed = false; + u32 cur_reg, cur_val; + u8 max_index = FXGMAC_EFUSE_MAX_ENTRY; + + if (offset >> 16) { + FXGMAC_PR( + "Reading efuse out of range, reg %d. reg must be 2bytes.\n", + index); + return false; + } + + fxgmac_efuse_read_data(pdata, EFUSE_LED_ADDR, ®_val); + if (EFUSE_LED_COMMON_SOLUTION == reg_val) { + max_index = FXGMAC_EFUSE_MAX_ENTRY_UNDER_LED_COMMON; + } + + if (index >= max_index) { + FXGMAC_PR("Writing efuse out of range, index %d max index %d\n", + index, max_index); + return false; + } + + if (fxgmac_read_patch_from_efuse_per_index(pdata, index, &cur_reg, + &cur_val)) { + if (cur_reg != 0 || cur_val != 0) { + FXGMAC_PR( + " The index %d has writed value, cannot rewrite it.\n", + index); + return false; + } + } else { + FXGMAC_PR("Cannot read index %d.\n", index); + return false; + } + + for (i = EFUSE_PATCH_ADDR_START_BYTE; i < EFUSE_PATCH_DATA_START_BYTE; + i++) { + reg_val = 0; + reg_val = FXGMAC_SET_REG_BITS( + reg_val, EFUSE_OP_ADDR_POS, EFUSE_OP_ADDR_LEN, + EFUSE_REGION_A_B_LENGTH + index * EFUSE_EACH_PATH_SIZE + + i); + reg_val = FXGMAC_SET_REG_BITS(reg_val, EFUSE_OP_WR_DATA_POS, + EFUSE_OP_WR_DATA_LEN, + (offset >> (i << 3)) & 0xFF); + reg_val = FXGMAC_SET_REG_BITS(reg_val, EFUSE_OP_START_POS, + EFUSE_OP_START_LEN, 1); + reg_val = FXGMAC_SET_REG_BITS(reg_val, EFUSE_OP_MODE_POS, + EFUSE_OP_MODE_LEN, + EFUSE_OP_MODE_ROW_WRITE); + writereg(pdata->pAdapter, reg_val, + pdata->base_mem + EFUSE_OP_CTRL_0); + + succeed = false; + wait = 1000; + while (wait--) { + usleep_range_ex(pdata->pAdapter, 20, 50); + reg_val = readreg(pdata->pAdapter, + pdata->base_mem + EFUSE_OP_CTRL_1); + if (FXGMAC_GET_REG_BITS(reg_val, EFUSE_OP_DONE_POS, + EFUSE_OP_DONE_LEN)) { + succeed = true; + break; + } + } + if (!succeed) { + FXGMAC_PR("Fail to writing efuse Byte%d\n", + index * EFUSE_EACH_PATH_SIZE + i); + return succeed; + } + } + + for (i = 2; i < 6; i++) { + reg_val = 0; + reg_val = FXGMAC_SET_REG_BITS(reg_val, EFUSE_OP_ADDR_POS, + EFUSE_OP_ADDR_LEN, + 18 + index * 6 + i); + reg_val = FXGMAC_SET_REG_BITS(reg_val, EFUSE_OP_WR_DATA_POS, + EFUSE_OP_WR_DATA_LEN, + (value >> ((i - 2) << 3)) & 0xFF); + reg_val = FXGMAC_SET_REG_BITS(reg_val, EFUSE_OP_START_POS, + EFUSE_OP_START_LEN, 1); + reg_val = FXGMAC_SET_REG_BITS(reg_val, EFUSE_OP_MODE_POS, + EFUSE_OP_MODE_LEN, + EFUSE_OP_MODE_ROW_WRITE); + writereg(pdata->pAdapter, reg_val, + pdata->base_mem + EFUSE_OP_CTRL_0); + + succeed = false; + wait = 1000; + while (wait--) { + usleep_range_ex(pdata->pAdapter, 20, 50); + reg_val = readreg(pdata->pAdapter, + pdata->base_mem + EFUSE_OP_CTRL_1); + if (FXGMAC_GET_REG_BITS(reg_val, EFUSE_OP_DONE_POS, + EFUSE_OP_DONE_LEN)) { + succeed = true; + break; + } + } + if (!succeed) { + FXGMAC_PR("Fail to writing efuse Byte%d\n", + index * EFUSE_EACH_PATH_SIZE + i); + return succeed; + } + } + + return succeed; +} + +bool fxgmac_write_patch_to_efuse(struct fxgmac_pdata *pdata, u32 offset, + u32 value) +{ + unsigned int wait, i; + u32 reg_offset, reg_val; + u32 cur_offset = 0, cur_val = 0; + bool succeed = false; + u8 index = 0; + + if (offset >> 16) { + FXGMAC_PR( + "Reading efuse out of range, reg %d. reg must be 2bytes.\n", + index); + return false; + } + + for (index = 0;; index++) { + if (!fxgmac_read_patch_from_efuse_per_index( + pdata, index, ®_offset, ®_val)) { + return false; + } else if (reg_offset == offset) { + cur_offset = reg_offset; + cur_val = reg_val; + } else if (0 == reg_offset && 0 == reg_val) { + break; + } + } + + if (cur_offset == offset) { + if (cur_val == value) { + FXGMAC_PR("0x%x -> Reg0x%x already exists, ignore.\n", + value, offset); + return true; + } else { + FXGMAC_PR( + "Reg0x%x entry current value 0x%x, reprogram.\n", + offset, value); + } + } + + for (i = EFUSE_PATCH_ADDR_START_BYTE; i < EFUSE_PATCH_DATA_START_BYTE; + i++) { + reg_val = 0; + reg_val = FXGMAC_SET_REG_BITS( + reg_val, EFUSE_OP_ADDR_POS, EFUSE_OP_ADDR_LEN, + EFUSE_REGION_A_B_LENGTH + index * EFUSE_EACH_PATH_SIZE + + i); + reg_val = FXGMAC_SET_REG_BITS(reg_val, EFUSE_OP_WR_DATA_POS, + EFUSE_OP_WR_DATA_LEN, + (offset >> (i << 3)) & 0xFF); + reg_val = FXGMAC_SET_REG_BITS(reg_val, EFUSE_OP_START_POS, + EFUSE_OP_START_LEN, 1); + reg_val = FXGMAC_SET_REG_BITS(reg_val, EFUSE_OP_MODE_POS, + EFUSE_OP_MODE_LEN, + EFUSE_OP_MODE_ROW_WRITE); + writereg(pdata->pAdapter, reg_val, + pdata->base_mem + EFUSE_OP_CTRL_0); + + succeed = false; + wait = 1000; + while (wait--) { + usleep_range_ex(pdata->pAdapter, 20, 50); + reg_val = readreg(pdata->pAdapter, + pdata->base_mem + EFUSE_OP_CTRL_1); + if (FXGMAC_GET_REG_BITS(reg_val, EFUSE_OP_DONE_POS, + EFUSE_OP_DONE_LEN)) { + succeed = true; + break; + } + } + if (!succeed) { + FXGMAC_PR("Fail to writing efuse Byte%d\n", + index * EFUSE_EACH_PATH_SIZE + i); + return succeed; + } + } + + for (i = EFUSE_PATCH_DATA_START_BYTE; i < EFUSE_EACH_PATH_SIZE; i++) { + reg_val = 0; + reg_val = FXGMAC_SET_REG_BITS( + reg_val, EFUSE_OP_ADDR_POS, EFUSE_OP_ADDR_LEN, + EFUSE_REGION_A_B_LENGTH + index * EFUSE_EACH_PATH_SIZE + + i); + reg_val = FXGMAC_SET_REG_BITS(reg_val, EFUSE_OP_WR_DATA_POS, + EFUSE_OP_WR_DATA_LEN, + (value >> ((i - 2) << 3)) & 0xFF); + reg_val = FXGMAC_SET_REG_BITS(reg_val, EFUSE_OP_START_POS, + EFUSE_OP_START_LEN, 1); + reg_val = FXGMAC_SET_REG_BITS(reg_val, EFUSE_OP_MODE_POS, + EFUSE_OP_MODE_LEN, + EFUSE_OP_MODE_ROW_WRITE); + writereg(pdata->pAdapter, reg_val, + pdata->base_mem + EFUSE_OP_CTRL_0); + + succeed = false; + wait = 1000; + while (wait--) { + usleep_range_ex(pdata->pAdapter, 20, 50); + reg_val = readreg(pdata->pAdapter, + pdata->base_mem + EFUSE_OP_CTRL_1); + if (FXGMAC_GET_REG_BITS(reg_val, EFUSE_OP_DONE_POS, + EFUSE_OP_DONE_LEN)) { + succeed = true; + break; + } + } + if (!succeed) { + FXGMAC_PR("Fail to writing efuse Byte%d\n", + index * EFUSE_EACH_PATH_SIZE + i); + return succeed; + } + } + + return succeed; +} + +bool fxgmac_write_mac_subsys_to_efuse(struct fxgmac_pdata *pdata, u8 *mac_addr, + u32 *subsys, u32 *revid) +{ +#ifdef DBG + u32 machr = 0, maclr = 0; +#endif + u32 pcie_cfg_ctrl = PCIE_CFG_CTRL_DEFAULT_VAL; + bool succeed = true; + if (mac_addr) { +#ifdef DBG + machr = readreg(pdata->pAdapter, + pdata->base_mem + MACA0HR_FROM_EFUSE); + maclr = readreg(pdata->pAdapter, + pdata->base_mem + MACA0LR_FROM_EFUSE); + DPRINTK("Current mac address from efuse is %02x-%02x-%02x-%02x-%02x-%02x.\n", + (machr >> 8) & 0xFF, machr & 0xFF, (maclr >> 24) & 0xFF, + (maclr >> 16) & 0xFF, (maclr >> 8) & 0xFF, + maclr & 0xFF); +#endif + if (!fxgmac_write_patch_to_efuse(pdata, MACA0HR_FROM_EFUSE, + (((u32)mac_addr[0]) << 8) | + mac_addr[1])) { + succeed = false; + } + if (!fxgmac_write_patch_to_efuse( + pdata, MACA0LR_FROM_EFUSE, + (((u32)mac_addr[2]) << 24) | + (((u32)mac_addr[3]) << 16) | + (((u32)mac_addr[4]) << 8) | mac_addr[5])) { + succeed = false; + } + } + + if (revid) { + if (!fxgmac_write_patch_to_efuse(pdata, EFUSE_REVID_REGISTER, + *revid)) { + succeed = false; + } + } + if (subsys) { + pcie_cfg_ctrl = FXGMAC_SET_REG_BITS( + pcie_cfg_ctrl, MGMT_PCIE_CFG_CTRL_CS_EN_POS, + MGMT_PCIE_CFG_CTRL_CS_EN_LEN, 1); + if (!fxgmac_write_patch_to_efuse(pdata, MGMT_PCIE_CFG_CTRL, + pcie_cfg_ctrl)) { + succeed = false; + } + if (!fxgmac_write_patch_to_efuse(pdata, EFUSE_SUBSYS_REGISTER, + *subsys)) { + succeed = false; + } + pcie_cfg_ctrl = FXGMAC_SET_REG_BITS( + pcie_cfg_ctrl, MGMT_PCIE_CFG_CTRL_CS_EN_POS, + MGMT_PCIE_CFG_CTRL_CS_EN_LEN, 0); + if (!fxgmac_write_patch_to_efuse(pdata, MGMT_PCIE_CFG_CTRL, + pcie_cfg_ctrl)) { + succeed = false; + } + } + return succeed; +} + +bool fxgmac_write_mac_addr_to_efuse(struct fxgmac_pdata *pdata, u8 *mac_addr) +{ +#ifdef DBG + u32 machr = 0, maclr = 0; +#endif + bool succeed = true; + + if (mac_addr) { +#ifdef DBG + machr = readreg(pdata->pAdapter, + pdata->base_mem + MACA0HR_FROM_EFUSE); + maclr = readreg(pdata->pAdapter, + pdata->base_mem + MACA0LR_FROM_EFUSE); + DPRINTK("Current mac address from efuse is %02x-%02x-%02x-%02x-%02x-%02x.\n", + (machr >> 8) & 0xFF, machr & 0xFF, (maclr >> 24) & 0xFF, + (maclr >> 16) & 0xFF, (maclr >> 8) & 0xFF, + maclr & 0xFF); +#endif + if (!fxgmac_write_patch_to_efuse(pdata, MACA0HR_FROM_EFUSE, + (((u32)mac_addr[0]) << 8) | + mac_addr[1])) { + succeed = false; + } + if (!fxgmac_write_patch_to_efuse( + pdata, MACA0LR_FROM_EFUSE, + (((u32)mac_addr[2]) << 24) | + (((u32)mac_addr[3]) << 16) | + (((u32)mac_addr[4]) << 8) | mac_addr[5])) { + succeed = false; + } + } + + return succeed; +} + +bool fxgmac_read_subsys_from_efuse(struct fxgmac_pdata *pdata, u32 *subsys, + u32 *revid) +{ + u32 offset = 0, value = 0; + u8 index; + bool succeed = true; + + for (index = 0;; index++) { + if (!fxgmac_read_patch_from_efuse_per_index(pdata, index, + &offset, &value)) { + succeed = false; + break; + } + if (0x00 == offset) { + break; + } + + if ((EFUSE_REVID_REGISTER == offset) && revid) { + *revid = value; + } else { + succeed = false; + } + if ((EFUSE_SUBSYS_REGISTER == offset) && subsys) { + *subsys = value; + } else { + succeed = false; + } + } + + return succeed; +} + +bool fxgmac_write_subsys_to_efuse(struct fxgmac_pdata *pdata, u32 *subsys, + u32 *revid) +{ + bool succeed = true; + + /* write subsys info */ + if (revid) { + if (!fxgmac_write_patch_to_efuse(pdata, EFUSE_REVID_REGISTER, + *revid)) { + succeed = false; + } + } + if (subsys) { + if (!fxgmac_write_patch_to_efuse(pdata, EFUSE_SUBSYS_REGISTER, + *subsys)) { + succeed = false; + } + } + return succeed; +} + +bool fxgmac_efuse_load(struct fxgmac_pdata *pdata) +{ + bool succeed = false; + unsigned int wait; + u32 reg_val = 0; + reg_val = FXGMAC_SET_REG_BITS(reg_val, EFUSE_OP_START_POS, + EFUSE_OP_START_LEN, 1); + reg_val = FXGMAC_SET_REG_BITS(reg_val, EFUSE_OP_MODE_POS, + EFUSE_OP_MODE_LEN, + EFUSE_OP_MODE_AUTO_LOAD); + writereg(pdata->pAdapter, reg_val, pdata->base_mem + EFUSE_OP_CTRL_0); + + wait = 1000; + while (wait--) { + usleep_range_ex(pdata->pAdapter, 20, 50); + reg_val = readreg(pdata->pAdapter, + pdata->base_mem + EFUSE_OP_CTRL_1); + if (FXGMAC_GET_REG_BITS(reg_val, EFUSE_OP_DONE_POS, + EFUSE_OP_DONE_LEN)) { + succeed = true; + break; + } + } + if (!succeed) { + FXGMAC_PR("Fail to loading efuse, ctrl_1 0x%08x\n", reg_val); + } + return succeed; +} + +bool fxgmac_efuse_write_oob(struct fxgmac_pdata *pdata) +{ + bool succeed = false; + unsigned int wait; + u32 reg_val, value; + + if (!fxgmac_efuse_read_data(pdata, EFUSE_OOB_ADDR, ®_val)) { + return succeed; + } + + if (FXGMAC_GET_REG_BITS(reg_val, EFUSE_OOB_POS, EFUSE_OOB_LEN)) { + FXGMAC_PR("OOB Ctrl bit already exists"); + return true; + } + + value = 0; + value = FXGMAC_SET_REG_BITS(value, EFUSE_OOB_POS, EFUSE_OOB_LEN, 1); + + reg_val = 0; + reg_val = FXGMAC_SET_REG_BITS(reg_val, EFUSE_OP_ADDR_POS, + EFUSE_OP_ADDR_LEN, EFUSE_OOB_ADDR); + reg_val = FXGMAC_SET_REG_BITS(reg_val, EFUSE_OP_WR_DATA_POS, + EFUSE_OP_WR_DATA_LEN, value & 0xFF); + reg_val = FXGMAC_SET_REG_BITS(reg_val, EFUSE_OP_START_POS, + EFUSE_OP_START_LEN, 1); + reg_val = FXGMAC_SET_REG_BITS(reg_val, EFUSE_OP_MODE_POS, + EFUSE_OP_MODE_LEN, + EFUSE_OP_MODE_ROW_WRITE); + writereg(pdata->pAdapter, reg_val, pdata->base_mem + EFUSE_OP_CTRL_0); + + wait = 1000; + while (wait--) { + usleep_range_ex(pdata->pAdapter, 20, 50); + reg_val = readreg(pdata->pAdapter, + pdata->base_mem + EFUSE_OP_CTRL_1); + if (FXGMAC_GET_REG_BITS(reg_val, EFUSE_OP_DONE_POS, + EFUSE_OP_DONE_LEN)) { + succeed = true; + break; + } + } + + if (!succeed) { + FXGMAC_PR("Fail to writing efuse Byte OOB"); + } + + return succeed; +} + +bool fxgmac_efuse_write_led(struct fxgmac_pdata *pdata, u32 value) +{ + bool succeed = false; + unsigned int wait; + u32 reg_val; + + if (!fxgmac_efuse_read_data(pdata, EFUSE_LED_ADDR, ®_val)) { + return succeed; + } + + if (reg_val == value) { + FXGMAC_PR("Led Ctrl option already exists"); + return true; + } + + reg_val = 0; + reg_val = FXGMAC_SET_REG_BITS(reg_val, EFUSE_OP_ADDR_POS, + EFUSE_OP_ADDR_LEN, EFUSE_LED_ADDR); + reg_val = FXGMAC_SET_REG_BITS(reg_val, EFUSE_OP_WR_DATA_POS, + EFUSE_OP_WR_DATA_LEN, value & 0xFF); + reg_val = FXGMAC_SET_REG_BITS(reg_val, EFUSE_OP_START_POS, + EFUSE_OP_START_LEN, 1); + reg_val = FXGMAC_SET_REG_BITS(reg_val, EFUSE_OP_MODE_POS, + EFUSE_OP_MODE_LEN, + EFUSE_OP_MODE_ROW_WRITE); + writereg(pdata->pAdapter, reg_val, pdata->base_mem + EFUSE_OP_CTRL_0); + + wait = 1000; + while (wait--) { + usleep_range_ex(pdata->pAdapter, 20, 50); + reg_val = readreg(pdata->pAdapter, + pdata->base_mem + EFUSE_OP_CTRL_1); + if (FXGMAC_GET_REG_BITS(reg_val, EFUSE_OP_DONE_POS, + EFUSE_OP_DONE_LEN)) { + succeed = true; + break; + } + } + + if (!succeed) { + FXGMAC_PR("Fail to writing efuse Byte LED"); + } + + return succeed; +} + +bool fxgmac_efuse_write_data(struct fxgmac_pdata *pdata, u32 offset, u32 value) +{ + bool succeed = false; + unsigned int wait; + u32 reg_val; + + if (!fxgmac_efuse_read_data(pdata, offset, ®_val)) { + return succeed; + } + + if (reg_val == value) { + FXGMAC_PR("offset 0x%x already exists", offset); + return true; + } + + reg_val = 0; + reg_val = FXGMAC_SET_REG_BITS(reg_val, EFUSE_OP_ADDR_POS, + EFUSE_OP_ADDR_LEN, offset & 0xFF); + reg_val = FXGMAC_SET_REG_BITS(reg_val, EFUSE_OP_WR_DATA_POS, + EFUSE_OP_WR_DATA_LEN, value & 0xFF); + reg_val = FXGMAC_SET_REG_BITS(reg_val, EFUSE_OP_START_POS, + EFUSE_OP_START_LEN, 1); + reg_val = FXGMAC_SET_REG_BITS(reg_val, EFUSE_OP_MODE_POS, + EFUSE_OP_MODE_LEN, + EFUSE_OP_MODE_ROW_WRITE); + writereg(pdata->pAdapter, reg_val, pdata->base_mem + EFUSE_OP_CTRL_0); + + wait = 1000; + while (wait--) { + usleep_range_ex(pdata->pAdapter, 20, 50); + reg_val = readreg(pdata->pAdapter, + pdata->base_mem + EFUSE_OP_CTRL_1); + if (FXGMAC_GET_REG_BITS(reg_val, EFUSE_OP_DONE_POS, + EFUSE_OP_DONE_LEN)) { + succeed = true; + break; + } + } + + if (!succeed) { + FXGMAC_PR("Fail to writing efuse 0x%x Byte LED", offset); + } + + return succeed; +} + +static void fxgmac_read_led_efuse_config(struct fxgmac_pdata *pdata, + struct led_setting *pfirst, + struct led_setting *psecond) +{ + u32 val_high = 0, val_low = 0; + + fxgmac_efuse_read_data(pdata, EFUSE_FISRT_UPDATE_ADDR, &val_high); + fxgmac_efuse_read_data(pdata, (EFUSE_FISRT_UPDATE_ADDR - 1), &val_low); + pfirst->disable_led_setting[4] = ((val_high << 8) + val_low); + + fxgmac_efuse_read_data(pdata, (EFUSE_FISRT_UPDATE_ADDR - 2), &val_high); + fxgmac_efuse_read_data(pdata, (EFUSE_FISRT_UPDATE_ADDR - 3), &val_low); + pfirst->disable_led_setting[3] = ((val_high << 8) + val_low); + + fxgmac_efuse_read_data(pdata, (EFUSE_FISRT_UPDATE_ADDR - 4), &val_high); + fxgmac_efuse_read_data(pdata, (EFUSE_FISRT_UPDATE_ADDR - 5), &val_low); + pfirst->disable_led_setting[2] = ((val_high << 8) + val_low); + + fxgmac_efuse_read_data(pdata, (EFUSE_FISRT_UPDATE_ADDR - 6), &val_high); + fxgmac_efuse_read_data(pdata, (EFUSE_FISRT_UPDATE_ADDR - 7), &val_low); + pfirst->disable_led_setting[1] = ((val_high << 8) + val_low); + + fxgmac_efuse_read_data(pdata, (EFUSE_FISRT_UPDATE_ADDR - 8), &val_high); + fxgmac_efuse_read_data(pdata, (EFUSE_FISRT_UPDATE_ADDR - 9), &val_low); + pfirst->disable_led_setting[0] = ((val_high << 8) + val_low); + + fxgmac_efuse_read_data(pdata, (EFUSE_FISRT_UPDATE_ADDR - 10), + &val_high); + fxgmac_efuse_read_data(pdata, (EFUSE_FISRT_UPDATE_ADDR - 11), &val_low); + pfirst->s5_led_setting[4] = ((val_high << 8) + val_low); + + fxgmac_efuse_read_data(pdata, (EFUSE_FISRT_UPDATE_ADDR - 12), + &val_high); + fxgmac_efuse_read_data(pdata, (EFUSE_FISRT_UPDATE_ADDR - 13), &val_low); + pfirst->s5_led_setting[3] = ((val_high << 8) + val_low); + + fxgmac_efuse_read_data(pdata, (EFUSE_FISRT_UPDATE_ADDR - 14), + &val_high); + fxgmac_efuse_read_data(pdata, (EFUSE_FISRT_UPDATE_ADDR - 15), &val_low); + pfirst->s5_led_setting[2] = ((val_high << 8) + val_low); + + fxgmac_efuse_read_data(pdata, (EFUSE_FISRT_UPDATE_ADDR - 16), + &val_high); + fxgmac_efuse_read_data(pdata, (EFUSE_FISRT_UPDATE_ADDR - 17), &val_low); + pfirst->s5_led_setting[1] = ((val_high << 8) + val_low); + + fxgmac_efuse_read_data(pdata, (EFUSE_FISRT_UPDATE_ADDR - 18), + &val_high); + fxgmac_efuse_read_data(pdata, (EFUSE_FISRT_UPDATE_ADDR - 19), &val_low); + pfirst->s5_led_setting[0] = ((val_high << 8) + val_low); + + fxgmac_efuse_read_data(pdata, (EFUSE_FISRT_UPDATE_ADDR - 20), + &val_high); + fxgmac_efuse_read_data(pdata, (EFUSE_FISRT_UPDATE_ADDR - 21), &val_low); + pfirst->s3_led_setting[4] = ((val_high << 8) + val_low); + + fxgmac_efuse_read_data(pdata, (EFUSE_FISRT_UPDATE_ADDR - 22), + &val_high); + fxgmac_efuse_read_data(pdata, (EFUSE_FISRT_UPDATE_ADDR - 23), &val_low); + pfirst->s3_led_setting[3] = ((val_high << 8) + val_low); + + fxgmac_efuse_read_data(pdata, (EFUSE_FISRT_UPDATE_ADDR - 24), + &val_high); + fxgmac_efuse_read_data(pdata, (EFUSE_FISRT_UPDATE_ADDR - 25), &val_low); + pfirst->s3_led_setting[2] = ((val_high << 8) + val_low); + + fxgmac_efuse_read_data(pdata, (EFUSE_FISRT_UPDATE_ADDR - 26), + &val_high); + fxgmac_efuse_read_data(pdata, (EFUSE_FISRT_UPDATE_ADDR - 27), &val_low); + pfirst->s3_led_setting[1] = ((val_high << 8) + val_low); + + fxgmac_efuse_read_data(pdata, (EFUSE_FISRT_UPDATE_ADDR - 28), + &val_high); + fxgmac_efuse_read_data(pdata, (EFUSE_FISRT_UPDATE_ADDR - 29), &val_low); + pfirst->s3_led_setting[0] = ((val_high << 8) + val_low); + + fxgmac_efuse_read_data(pdata, (EFUSE_FISRT_UPDATE_ADDR - 30), + &val_high); + fxgmac_efuse_read_data(pdata, (EFUSE_FISRT_UPDATE_ADDR - 31), &val_low); + pfirst->s0_led_setting[4] = ((val_high << 8) + val_low); + + fxgmac_efuse_read_data(pdata, (EFUSE_FISRT_UPDATE_ADDR - 32), + &val_high); + fxgmac_efuse_read_data(pdata, (EFUSE_FISRT_UPDATE_ADDR - 33), &val_low); + pfirst->s0_led_setting[3] = ((val_high << 8) + val_low); + + fxgmac_efuse_read_data(pdata, (EFUSE_FISRT_UPDATE_ADDR - 34), + &val_high); + fxgmac_efuse_read_data(pdata, (EFUSE_FISRT_UPDATE_ADDR - 35), &val_low); + pfirst->s0_led_setting[2] = ((val_high << 8) + val_low); + + fxgmac_efuse_read_data(pdata, (EFUSE_FISRT_UPDATE_ADDR - 36), + &val_high); + fxgmac_efuse_read_data(pdata, (EFUSE_FISRT_UPDATE_ADDR - 37), &val_low); + pfirst->s0_led_setting[1] = ((val_high << 8) + val_low); + + fxgmac_efuse_read_data(pdata, (EFUSE_FISRT_UPDATE_ADDR - 38), + &val_high); + fxgmac_efuse_read_data(pdata, (EFUSE_FISRT_UPDATE_ADDR - 39), &val_low); + pfirst->s0_led_setting[0] = ((val_high << 8) + val_low); + + fxgmac_efuse_read_data(pdata, EFUSE_SECOND_UPDATE_ADDR, &val_high); + fxgmac_efuse_read_data(pdata, (EFUSE_SECOND_UPDATE_ADDR - 1), &val_low); + psecond->disable_led_setting[4] = ((val_high << 8) + val_low); + + fxgmac_efuse_read_data(pdata, (EFUSE_SECOND_UPDATE_ADDR - 2), + &val_high); + fxgmac_efuse_read_data(pdata, (EFUSE_SECOND_UPDATE_ADDR - 3), &val_low); + psecond->disable_led_setting[3] = ((val_high << 8) + val_low); + + fxgmac_efuse_read_data(pdata, (EFUSE_SECOND_UPDATE_ADDR - 4), + &val_high); + fxgmac_efuse_read_data(pdata, (EFUSE_SECOND_UPDATE_ADDR - 5), &val_low); + psecond->disable_led_setting[2] = ((val_high << 8) + val_low); + + fxgmac_efuse_read_data(pdata, (EFUSE_SECOND_UPDATE_ADDR - 6), + &val_high); + fxgmac_efuse_read_data(pdata, (EFUSE_SECOND_UPDATE_ADDR - 7), &val_low); + psecond->disable_led_setting[1] = ((val_high << 8) + val_low); + + fxgmac_efuse_read_data(pdata, (EFUSE_SECOND_UPDATE_ADDR - 8), + &val_high); + fxgmac_efuse_read_data(pdata, (EFUSE_SECOND_UPDATE_ADDR - 9), &val_low); + psecond->disable_led_setting[0] = ((val_high << 8) + val_low); + + fxgmac_efuse_read_data(pdata, (EFUSE_SECOND_UPDATE_ADDR - 10), + &val_high); + fxgmac_efuse_read_data(pdata, (EFUSE_SECOND_UPDATE_ADDR - 11), + &val_low); + psecond->s5_led_setting[4] = ((val_high << 8) + val_low); + + fxgmac_efuse_read_data(pdata, (EFUSE_SECOND_UPDATE_ADDR - 12), + &val_high); + fxgmac_efuse_read_data(pdata, (EFUSE_SECOND_UPDATE_ADDR - 13), + &val_low); + psecond->s5_led_setting[3] = ((val_high << 8) + val_low); + + fxgmac_efuse_read_data(pdata, (EFUSE_SECOND_UPDATE_ADDR - 14), + &val_high); + fxgmac_efuse_read_data(pdata, (EFUSE_SECOND_UPDATE_ADDR - 15), + &val_low); + psecond->s5_led_setting[2] = ((val_high << 8) + val_low); + + fxgmac_efuse_read_data(pdata, (EFUSE_SECOND_UPDATE_ADDR - 16), + &val_high); + fxgmac_efuse_read_data(pdata, (EFUSE_SECOND_UPDATE_ADDR - 17), + &val_low); + psecond->s5_led_setting[1] = ((val_high << 8) + val_low); + + fxgmac_efuse_read_data(pdata, (EFUSE_SECOND_UPDATE_ADDR - 18), + &val_high); + fxgmac_efuse_read_data(pdata, (EFUSE_SECOND_UPDATE_ADDR - 19), + &val_low); + psecond->s5_led_setting[0] = ((val_high << 8) + val_low); + + fxgmac_efuse_read_data(pdata, (EFUSE_SECOND_UPDATE_ADDR - 20), + &val_high); + fxgmac_efuse_read_data(pdata, (EFUSE_SECOND_UPDATE_ADDR - 21), + &val_low); + psecond->s3_led_setting[4] = ((val_high << 8) + val_low); + + fxgmac_efuse_read_data(pdata, (EFUSE_SECOND_UPDATE_ADDR - 22), + &val_high); + fxgmac_efuse_read_data(pdata, (EFUSE_SECOND_UPDATE_ADDR - 23), + &val_low); + psecond->s3_led_setting[3] = ((val_high << 8) + val_low); + + fxgmac_efuse_read_data(pdata, (EFUSE_SECOND_UPDATE_ADDR - 24), + &val_high); + fxgmac_efuse_read_data(pdata, (EFUSE_SECOND_UPDATE_ADDR - 25), + &val_low); + psecond->s3_led_setting[2] = ((val_high << 8) + val_low); + + fxgmac_efuse_read_data(pdata, (EFUSE_SECOND_UPDATE_ADDR - 26), + &val_high); + fxgmac_efuse_read_data(pdata, (EFUSE_SECOND_UPDATE_ADDR - 27), + &val_low); + psecond->s3_led_setting[1] = ((val_high << 8) + val_low); + + fxgmac_efuse_read_data(pdata, (EFUSE_SECOND_UPDATE_ADDR - 28), + &val_high); + fxgmac_efuse_read_data(pdata, (EFUSE_SECOND_UPDATE_ADDR - 29), + &val_low); + psecond->s3_led_setting[0] = ((val_high << 8) + val_low); + + fxgmac_efuse_read_data(pdata, (EFUSE_SECOND_UPDATE_ADDR - 30), + &val_high); + fxgmac_efuse_read_data(pdata, (EFUSE_SECOND_UPDATE_ADDR - 31), + &val_low); + psecond->s0_led_setting[4] = ((val_high << 8) + val_low); + + fxgmac_efuse_read_data(pdata, (EFUSE_SECOND_UPDATE_ADDR - 32), + &val_high); + fxgmac_efuse_read_data(pdata, (EFUSE_SECOND_UPDATE_ADDR - 33), + &val_low); + psecond->s0_led_setting[3] = ((val_high << 8) + val_low); + + fxgmac_efuse_read_data(pdata, (EFUSE_SECOND_UPDATE_ADDR - 34), + &val_high); + fxgmac_efuse_read_data(pdata, (EFUSE_SECOND_UPDATE_ADDR - 35), + &val_low); + psecond->s0_led_setting[2] = ((val_high << 8) + val_low); + + fxgmac_efuse_read_data(pdata, (EFUSE_SECOND_UPDATE_ADDR - 36), + &val_high); + fxgmac_efuse_read_data(pdata, (EFUSE_SECOND_UPDATE_ADDR - 37), + &val_low); + psecond->s0_led_setting[1] = ((val_high << 8) + val_low); + + fxgmac_efuse_read_data(pdata, (EFUSE_SECOND_UPDATE_ADDR - 38), + &val_high); + fxgmac_efuse_read_data(pdata, (EFUSE_SECOND_UPDATE_ADDR - 39), + &val_low); + psecond->s0_led_setting[0] = ((val_high << 8) + val_low); +} + +bool fxgmac_write_led_setting_to_efuse(struct fxgmac_pdata *pdata) +{ + struct led_setting led_config_first; + struct led_setting led_config_second; + bool bfirstflag = false, bsecondflag = false; + bool bsucceed = false; + + fxgmac_read_led_efuse_config(pdata, &led_config_first, + &led_config_second); + + if (0x00 == led_config_first.s0_led_setting[0] && + 0x00 == led_config_first.s0_led_setting[1] && + 0x00 == led_config_first.s0_led_setting[2] && + 0x00 == led_config_first.s0_led_setting[3] && + 0x00 == led_config_first.s0_led_setting[4] && + 0x00 == led_config_first.s3_led_setting[0] && + 0x00 == led_config_first.s3_led_setting[1] && + 0x00 == led_config_first.s3_led_setting[2] && + 0x00 == led_config_first.s3_led_setting[3] && + 0x00 == led_config_first.s3_led_setting[4] && + 0x00 == led_config_first.s5_led_setting[0] && + 0x00 == led_config_first.s5_led_setting[1] && + 0x00 == led_config_first.s5_led_setting[2] && + 0x00 == led_config_first.s5_led_setting[3] && + 0x00 == led_config_first.s5_led_setting[4] && + 0x00 == led_config_first.disable_led_setting[0] && + 0x00 == led_config_first.disable_led_setting[1] && + 0x00 == led_config_first.disable_led_setting[2] && + 0x00 == led_config_first.disable_led_setting[3] && + 0x00 == led_config_first.disable_led_setting[4]) { + bfirstflag = true; + } + + if (0x00 == led_config_second.s0_led_setting[0] && + 0x00 == led_config_second.s0_led_setting[1] && + 0x00 == led_config_second.s0_led_setting[2] && + 0x00 == led_config_second.s0_led_setting[3] && + 0x00 == led_config_second.s0_led_setting[4] && + 0x00 == led_config_second.s3_led_setting[0] && + 0x00 == led_config_second.s3_led_setting[1] && + 0x00 == led_config_second.s3_led_setting[2] && + 0x00 == led_config_second.s3_led_setting[3] && + 0x00 == led_config_second.s3_led_setting[4] && + 0x00 == led_config_second.s5_led_setting[0] && + 0x00 == led_config_second.s5_led_setting[1] && + 0x00 == led_config_second.s5_led_setting[2] && + 0x00 == led_config_second.s5_led_setting[3] && + 0x00 == led_config_second.s5_led_setting[4] && + 0x00 == led_config_second.disable_led_setting[0] && + 0x00 == led_config_second.disable_led_setting[1] && + 0x00 == led_config_second.disable_led_setting[2] && + 0x00 == led_config_second.disable_led_setting[3] && + 0x00 == led_config_second.disable_led_setting[4]) { + bsecondflag = true; + } + + if (bfirstflag && bsecondflag) { + fxgmac_efuse_write_data( + pdata, EFUSE_FISRT_UPDATE_ADDR, + (pdata->ledconfig.disable_led_setting[4] >> 8) & 0xFF); + fxgmac_efuse_write_data( + pdata, (EFUSE_FISRT_UPDATE_ADDR - 1), + pdata->ledconfig.disable_led_setting[4]); + fxgmac_efuse_write_data( + pdata, (EFUSE_FISRT_UPDATE_ADDR - 2), + (pdata->ledconfig.disable_led_setting[3] >> 8) & 0xFF); + fxgmac_efuse_write_data( + pdata, (EFUSE_FISRT_UPDATE_ADDR - 3), + pdata->ledconfig.disable_led_setting[3]); + fxgmac_efuse_write_data( + pdata, (EFUSE_FISRT_UPDATE_ADDR - 4), + (pdata->ledconfig.disable_led_setting[2] >> 8) & 0xFF); + fxgmac_efuse_write_data( + pdata, (EFUSE_FISRT_UPDATE_ADDR - 5), + pdata->ledconfig.disable_led_setting[2]); + fxgmac_efuse_write_data( + pdata, (EFUSE_FISRT_UPDATE_ADDR - 6), + (pdata->ledconfig.disable_led_setting[1] >> 8) & 0xFF); + fxgmac_efuse_write_data( + pdata, (EFUSE_FISRT_UPDATE_ADDR - 7), + pdata->ledconfig.disable_led_setting[1]); + fxgmac_efuse_write_data( + pdata, (EFUSE_FISRT_UPDATE_ADDR - 8), + (pdata->ledconfig.disable_led_setting[0] >> 8) & 0xFF); + fxgmac_efuse_write_data( + pdata, (EFUSE_FISRT_UPDATE_ADDR - 9), + pdata->ledconfig.disable_led_setting[0]); + + fxgmac_efuse_write_data(pdata, (EFUSE_FISRT_UPDATE_ADDR - 10), + (pdata->ledconfig.s5_led_setting[4] >> + 8) & 0xFF); + fxgmac_efuse_write_data(pdata, (EFUSE_FISRT_UPDATE_ADDR - 11), + pdata->ledconfig.s5_led_setting[4]); + fxgmac_efuse_write_data(pdata, (EFUSE_FISRT_UPDATE_ADDR - 12), + (pdata->ledconfig.s5_led_setting[3] >> + 8) & 0xFF); + fxgmac_efuse_write_data(pdata, (EFUSE_FISRT_UPDATE_ADDR - 13), + pdata->ledconfig.s5_led_setting[3]); + fxgmac_efuse_write_data(pdata, (EFUSE_FISRT_UPDATE_ADDR - 14), + (pdata->ledconfig.s5_led_setting[2] >> + 8) & 0xFF); + fxgmac_efuse_write_data(pdata, (EFUSE_FISRT_UPDATE_ADDR - 15), + pdata->ledconfig.s5_led_setting[2]); + fxgmac_efuse_write_data(pdata, (EFUSE_FISRT_UPDATE_ADDR - 16), + (pdata->ledconfig.s5_led_setting[1] >> + 8) & 0xFF); + fxgmac_efuse_write_data(pdata, (EFUSE_FISRT_UPDATE_ADDR - 17), + pdata->ledconfig.s5_led_setting[1]); + fxgmac_efuse_write_data(pdata, (EFUSE_FISRT_UPDATE_ADDR - 18), + (pdata->ledconfig.s5_led_setting[0] >> + 8) & 0xFF); + fxgmac_efuse_write_data(pdata, (EFUSE_FISRT_UPDATE_ADDR - 19), + pdata->ledconfig.s5_led_setting[0]); + + fxgmac_efuse_write_data(pdata, (EFUSE_FISRT_UPDATE_ADDR - 20), + (pdata->ledconfig.s3_led_setting[4] >> + 8) & 0xFF); + fxgmac_efuse_write_data(pdata, (EFUSE_FISRT_UPDATE_ADDR - 21), + pdata->ledconfig.s3_led_setting[4]); + fxgmac_efuse_write_data(pdata, (EFUSE_FISRT_UPDATE_ADDR - 22), + (pdata->ledconfig.s3_led_setting[3] >> + 8) & 0xFF); + fxgmac_efuse_write_data(pdata, (EFUSE_FISRT_UPDATE_ADDR - 23), + pdata->ledconfig.s3_led_setting[3]); + fxgmac_efuse_write_data(pdata, (EFUSE_FISRT_UPDATE_ADDR - 24), + (pdata->ledconfig.s3_led_setting[2] >> + 8) & 0xFF); + fxgmac_efuse_write_data(pdata, (EFUSE_FISRT_UPDATE_ADDR - 25), + pdata->ledconfig.s3_led_setting[2]); + fxgmac_efuse_write_data(pdata, (EFUSE_FISRT_UPDATE_ADDR - 26), + (pdata->ledconfig.s3_led_setting[1] >> + 8) & 0xFF); + fxgmac_efuse_write_data(pdata, (EFUSE_FISRT_UPDATE_ADDR - 27), + pdata->ledconfig.s3_led_setting[1]); + fxgmac_efuse_write_data(pdata, (EFUSE_FISRT_UPDATE_ADDR - 28), + (pdata->ledconfig.s3_led_setting[0] >> + 8) & 0xFF); + fxgmac_efuse_write_data(pdata, (EFUSE_FISRT_UPDATE_ADDR - 29), + pdata->ledconfig.s3_led_setting[0]); + + fxgmac_efuse_write_data(pdata, (EFUSE_FISRT_UPDATE_ADDR - 30), + (pdata->ledconfig.s0_led_setting[4] >> + 8) & 0xFF); + fxgmac_efuse_write_data(pdata, (EFUSE_FISRT_UPDATE_ADDR - 31), + pdata->ledconfig.s0_led_setting[4]); + fxgmac_efuse_write_data(pdata, (EFUSE_FISRT_UPDATE_ADDR - 32), + (pdata->ledconfig.s0_led_setting[3] >> + 8) & 0xFF); + fxgmac_efuse_write_data(pdata, (EFUSE_FISRT_UPDATE_ADDR - 33), + pdata->ledconfig.s0_led_setting[3]); + fxgmac_efuse_write_data(pdata, (EFUSE_FISRT_UPDATE_ADDR - 34), + (pdata->ledconfig.s0_led_setting[2] >> + 8) & 0xFF); + fxgmac_efuse_write_data(pdata, (EFUSE_FISRT_UPDATE_ADDR - 35), + pdata->ledconfig.s0_led_setting[2]); + fxgmac_efuse_write_data(pdata, (EFUSE_FISRT_UPDATE_ADDR - 36), + (pdata->ledconfig.s0_led_setting[1] >> + 8) & 0xFF); + fxgmac_efuse_write_data(pdata, (EFUSE_FISRT_UPDATE_ADDR - 37), + pdata->ledconfig.s0_led_setting[1]); + fxgmac_efuse_write_data(pdata, (EFUSE_FISRT_UPDATE_ADDR - 38), + (pdata->ledconfig.s0_led_setting[0] >> + 8) & 0xFF); + fxgmac_efuse_write_data(pdata, (EFUSE_FISRT_UPDATE_ADDR - 39), + pdata->ledconfig.s0_led_setting[0]); + + bsucceed = true; + } else if (!bfirstflag && bsecondflag) { + fxgmac_efuse_write_data( + pdata, EFUSE_SECOND_UPDATE_ADDR, + (pdata->ledconfig.disable_led_setting[4] >> 8) & 0xFF); + fxgmac_efuse_write_data( + pdata, (EFUSE_SECOND_UPDATE_ADDR - 1), + pdata->ledconfig.disable_led_setting[4]); + fxgmac_efuse_write_data( + pdata, (EFUSE_SECOND_UPDATE_ADDR - 2), + (pdata->ledconfig.disable_led_setting[3] >> 8) & 0xFF); + fxgmac_efuse_write_data( + pdata, (EFUSE_SECOND_UPDATE_ADDR - 3), + pdata->ledconfig.disable_led_setting[3]); + fxgmac_efuse_write_data( + pdata, (EFUSE_SECOND_UPDATE_ADDR - 4), + (pdata->ledconfig.disable_led_setting[2] >> 8) & 0xFF); + fxgmac_efuse_write_data( + pdata, (EFUSE_SECOND_UPDATE_ADDR - 5), + pdata->ledconfig.disable_led_setting[2]); + fxgmac_efuse_write_data( + pdata, (EFUSE_SECOND_UPDATE_ADDR - 6), + (pdata->ledconfig.disable_led_setting[1] >> 8) & 0xFF); + fxgmac_efuse_write_data( + pdata, (EFUSE_SECOND_UPDATE_ADDR - 7), + pdata->ledconfig.disable_led_setting[1]); + fxgmac_efuse_write_data( + pdata, (EFUSE_SECOND_UPDATE_ADDR - 8), + (pdata->ledconfig.disable_led_setting[0] >> 8) & 0xFF); + fxgmac_efuse_write_data( + pdata, (EFUSE_SECOND_UPDATE_ADDR - 9), + pdata->ledconfig.disable_led_setting[0]); + + fxgmac_efuse_write_data(pdata, (EFUSE_SECOND_UPDATE_ADDR - 10), + (pdata->ledconfig.s5_led_setting[4] >> + 8) & 0xFF); + fxgmac_efuse_write_data(pdata, (EFUSE_SECOND_UPDATE_ADDR - 11), + pdata->ledconfig.s5_led_setting[4]); + fxgmac_efuse_write_data(pdata, (EFUSE_SECOND_UPDATE_ADDR - 12), + (pdata->ledconfig.s5_led_setting[3] >> + 8) & 0xFF); + fxgmac_efuse_write_data(pdata, (EFUSE_SECOND_UPDATE_ADDR - 13), + pdata->ledconfig.s5_led_setting[3]); + fxgmac_efuse_write_data(pdata, (EFUSE_SECOND_UPDATE_ADDR - 14), + (pdata->ledconfig.s5_led_setting[2] >> + 8) & 0xFF); + fxgmac_efuse_write_data(pdata, (EFUSE_SECOND_UPDATE_ADDR - 15), + pdata->ledconfig.s5_led_setting[2]); + fxgmac_efuse_write_data(pdata, (EFUSE_SECOND_UPDATE_ADDR - 16), + (pdata->ledconfig.s5_led_setting[1] >> + 8) & 0xFF); + fxgmac_efuse_write_data(pdata, (EFUSE_SECOND_UPDATE_ADDR - 17), + pdata->ledconfig.s5_led_setting[1]); + fxgmac_efuse_write_data(pdata, (EFUSE_SECOND_UPDATE_ADDR - 18), + (pdata->ledconfig.s5_led_setting[0] >> + 8) & 0xFF); + fxgmac_efuse_write_data(pdata, (EFUSE_SECOND_UPDATE_ADDR - 19), + pdata->ledconfig.s5_led_setting[0]); + + fxgmac_efuse_write_data(pdata, (EFUSE_SECOND_UPDATE_ADDR - 20), + (pdata->ledconfig.s3_led_setting[4] >> + 8) & 0xFF); + fxgmac_efuse_write_data(pdata, (EFUSE_SECOND_UPDATE_ADDR - 21), + pdata->ledconfig.s3_led_setting[4]); + fxgmac_efuse_write_data(pdata, (EFUSE_SECOND_UPDATE_ADDR - 22), + (pdata->ledconfig.s3_led_setting[3] >> + 8) & 0xFF); + fxgmac_efuse_write_data(pdata, (EFUSE_SECOND_UPDATE_ADDR - 23), + pdata->ledconfig.s3_led_setting[3]); + fxgmac_efuse_write_data(pdata, (EFUSE_SECOND_UPDATE_ADDR - 24), + (pdata->ledconfig.s3_led_setting[2] >> + 8) & 0xFF); + fxgmac_efuse_write_data(pdata, (EFUSE_SECOND_UPDATE_ADDR - 25), + pdata->ledconfig.s3_led_setting[2]); + fxgmac_efuse_write_data(pdata, (EFUSE_SECOND_UPDATE_ADDR - 26), + (pdata->ledconfig.s3_led_setting[1] >> + 8) & 0xFF); + fxgmac_efuse_write_data(pdata, (EFUSE_SECOND_UPDATE_ADDR - 27), + pdata->ledconfig.s3_led_setting[1]); + fxgmac_efuse_write_data(pdata, (EFUSE_SECOND_UPDATE_ADDR - 28), + (pdata->ledconfig.s3_led_setting[0] >> + 8) & 0xFF); + fxgmac_efuse_write_data(pdata, (EFUSE_SECOND_UPDATE_ADDR - 29), + pdata->ledconfig.s3_led_setting[0]); + + fxgmac_efuse_write_data(pdata, (EFUSE_SECOND_UPDATE_ADDR - 30), + (pdata->ledconfig.s0_led_setting[4] >> + 8) & 0xFF); + fxgmac_efuse_write_data(pdata, (EFUSE_SECOND_UPDATE_ADDR - 31), + pdata->ledconfig.s0_led_setting[4]); + fxgmac_efuse_write_data(pdata, (EFUSE_SECOND_UPDATE_ADDR - 32), + (pdata->ledconfig.s0_led_setting[3] >> + 8) & 0xFF); + fxgmac_efuse_write_data(pdata, (EFUSE_SECOND_UPDATE_ADDR - 33), + pdata->ledconfig.s0_led_setting[3]); + fxgmac_efuse_write_data(pdata, (EFUSE_SECOND_UPDATE_ADDR - 34), + (pdata->ledconfig.s0_led_setting[2] >> + 8) & 0xFF); + fxgmac_efuse_write_data(pdata, (EFUSE_SECOND_UPDATE_ADDR - 35), + pdata->ledconfig.s0_led_setting[2]); + fxgmac_efuse_write_data(pdata, (EFUSE_SECOND_UPDATE_ADDR - 36), + (pdata->ledconfig.s0_led_setting[1] >> + 8) & 0xFF); + fxgmac_efuse_write_data(pdata, (EFUSE_SECOND_UPDATE_ADDR - 37), + pdata->ledconfig.s0_led_setting[1]); + fxgmac_efuse_write_data(pdata, (EFUSE_SECOND_UPDATE_ADDR - 38), + (pdata->ledconfig.s0_led_setting[0] >> + 8) & 0xFF); + fxgmac_efuse_write_data(pdata, (EFUSE_SECOND_UPDATE_ADDR - 39), + pdata->ledconfig.s0_led_setting[0]); + + bsucceed = true; + } + + return bsucceed; +} + +bool fxgmac_read_led_setting_from_efuse(struct fxgmac_pdata *pdata) +{ + struct led_setting led_config_first; + struct led_setting led_config_second; + bool bfirstflag = false, bsecondflag = false; + bool bsucceed = false; + + fxgmac_read_led_efuse_config(pdata, &led_config_first, + &led_config_second); + + if (0x00 == led_config_first.s0_led_setting[0] && + 0x00 == led_config_first.s0_led_setting[1] && + 0x00 == led_config_first.s0_led_setting[2] && + 0x00 == led_config_first.s0_led_setting[3] && + 0x00 == led_config_first.s0_led_setting[4] && + 0x00 == led_config_first.s3_led_setting[0] && + 0x00 == led_config_first.s3_led_setting[1] && + 0x00 == led_config_first.s3_led_setting[2] && + 0x00 == led_config_first.s3_led_setting[3] && + 0x00 == led_config_first.s3_led_setting[4] && + 0x00 == led_config_first.s5_led_setting[0] && + 0x00 == led_config_first.s5_led_setting[1] && + 0x00 == led_config_first.s5_led_setting[2] && + 0x00 == led_config_first.s5_led_setting[3] && + 0x00 == led_config_first.s5_led_setting[4] && + 0x00 == led_config_first.disable_led_setting[0] && + 0x00 == led_config_first.disable_led_setting[1] && + 0x00 == led_config_first.disable_led_setting[2] && + 0x00 == led_config_first.disable_led_setting[3] && + 0x00 == led_config_first.disable_led_setting[4]) { + bfirstflag = true; + } + + if (0x00 == led_config_second.s0_led_setting[0] && + 0x00 == led_config_second.s0_led_setting[1] && + 0x00 == led_config_second.s0_led_setting[2] && + 0x00 == led_config_second.s0_led_setting[3] && + 0x00 == led_config_second.s0_led_setting[4] && + 0x00 == led_config_second.s3_led_setting[0] && + 0x00 == led_config_second.s3_led_setting[1] && + 0x00 == led_config_second.s3_led_setting[2] && + 0x00 == led_config_second.s3_led_setting[3] && + 0x00 == led_config_second.s3_led_setting[4] && + 0x00 == led_config_second.s5_led_setting[0] && + 0x00 == led_config_second.s5_led_setting[1] && + 0x00 == led_config_second.s5_led_setting[2] && + 0x00 == led_config_second.s5_led_setting[3] && + 0x00 == led_config_second.s5_led_setting[4] && + 0x00 == led_config_second.disable_led_setting[0] && + 0x00 == led_config_second.disable_led_setting[1] && + 0x00 == led_config_second.disable_led_setting[2] && + 0x00 == led_config_second.disable_led_setting[3] && + 0x00 == led_config_second.disable_led_setting[4]) { + bsecondflag = true; + } + + if (!bfirstflag && bsecondflag) { + memcpy(&pdata->led, &led_config_first, + sizeof(struct led_setting)); + bsucceed = true; + } else if (!bfirstflag && !bsecondflag) { + memcpy(&pdata->led, &led_config_second, + sizeof(struct led_setting)); + bsucceed = true; + } + + return bsucceed; +} +#endif diff --git a/drivers/net/ethernet/motorcomm/yt6801/fuxi-efuse.h b/drivers/net/ethernet/motorcomm/yt6801/fuxi-efuse.h new file mode 100644 index 000000000000..da08d1f7ea0b --- /dev/null +++ b/drivers/net/ethernet/motorcomm/yt6801/fuxi-efuse.h @@ -0,0 +1,37 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (c) 2021 Motorcomm Corporation. */ + +#ifndef __FXGMAC_EFUSE_H__ +#define __FXGMAC_EFUSE_H__ + +bool fxgmac_read_patch_from_efuse_per_index(struct fxgmac_pdata *pdata, + u8 index, u32 __far *offset, + u32 __far *value); +bool fxgmac_read_mac_subsys_from_efuse(struct fxgmac_pdata *pdata, u8 *mac_addr, + u32 *subsys, u32 *revid); +bool fxgmac_efuse_read_data(struct fxgmac_pdata *pdata, u32 offset, + u32 __far *value); + +#ifndef COMMENT_UNUSED_CODE_TO_REDUCE_SIZE +bool fxgmac_read_patch_from_efuse(struct fxgmac_pdata *pdata, u32 offset, + u32 *value); +bool fxgmac_write_patch_to_efuse(struct fxgmac_pdata *pdata, u32 offset, + u32 value); +bool fxgmac_write_patch_to_efuse_per_index(struct fxgmac_pdata *pdata, u8 index, + u32 offset, u32 value); +bool fxgmac_write_mac_subsys_to_efuse(struct fxgmac_pdata *pdata, u8 *mac_addr, + u32 *subsys, u32 *revid); +bool fxgmac_write_mac_addr_to_efuse(struct fxgmac_pdata *pdata, u8 *mac_addr); +bool fxgmac_read_subsys_from_efuse(struct fxgmac_pdata *pdata, u32 *subsys, + u32 *revid); +bool fxgmac_write_subsys_to_efuse(struct fxgmac_pdata *pdata, u32 *subsys, + u32 *revid); +bool fxgmac_efuse_load(struct fxgmac_pdata *pdata); +bool fxgmac_efuse_write_data(struct fxgmac_pdata *pdata, u32 offset, u32 value); +bool fxgmac_efuse_write_oob(struct fxgmac_pdata *pdata); +bool fxgmac_efuse_write_led(struct fxgmac_pdata *pdata, u32 value); +bool fxgmac_read_led_setting_from_efuse(struct fxgmac_pdata *pdata); +bool fxgmac_write_led_setting_to_efuse(struct fxgmac_pdata *pdata); +#endif + +#endif diff --git a/drivers/net/ethernet/motorcomm/yt6801/fuxi-gmac-common.c b/drivers/net/ethernet/motorcomm/yt6801/fuxi-gmac-common.c new file mode 100644 index 000000000000..ee5c42f346ed --- /dev/null +++ b/drivers/net/ethernet/motorcomm/yt6801/fuxi-gmac-common.c @@ -0,0 +1,934 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (c) 2021 Motorcomm Corporation. */ + +#include "fuxi-gmac.h" +#include "fuxi-gmac-reg.h" + +MODULE_LICENSE("GPL"); + +static int debug = 16; +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "FUXI ethernet debug level (0=none,...,16=all)"); + +static unsigned char dev_addr[6] = { 0, 0x55, 0x7b, 0xb5, 0x7d, 0xf7 }; + +static void fxgmac_read_mac_addr(struct fxgmac_pdata *pdata) +{ + struct net_device *netdev = pdata->netdev; + struct fxgmac_hw_ops *hw_ops = &pdata->hw_ops; + /* DPRINTK("read mac from eFuse\n"); */ + + /* if efuse have mac addr,use it.if not,use static mac address. */ + hw_ops->read_mac_subsys_from_efuse(pdata, pdata->mac_addr, NULL, NULL); + if (ETH_IS_ZEROADDRESS(pdata->mac_addr)) { + /* Currently it uses a static mac address for test */ + memcpy(pdata->mac_addr, dev_addr, netdev->addr_len); + } +} + +static void fxgmac_default_config(struct fxgmac_pdata *pdata) +{ + pdata->tx_osp_mode = DMA_OSP_ENABLE; + pdata->tx_sf_mode = MTL_TSF_ENABLE; + pdata->rx_sf_mode = MTL_RSF_ENABLE; + pdata->pblx8 = DMA_PBL_X8_ENABLE; + pdata->tx_pbl = DMA_PBL_16; + pdata->rx_pbl = DMA_PBL_4; + pdata->tx_threshold = MTL_TX_THRESHOLD_128; + pdata->rx_threshold = MTL_RX_THRESHOLD_128; + pdata->tx_pause = 1; + pdata->rx_pause = 1; +#if FXGMAC_RSS_FEATURE_ENABLED + pdata->rss = 1; +#else + pdata->rss = 0; +#endif + + pdata->intr_mod = FXGMAC_INT_MODERATION_ENABLED; + pdata->crc_check = 1; + + pdata->sysclk_rate = FXGMAC_SYSCLOCK; + pdata->phy_autoeng = AUTONEG_ENABLE; + pdata->phy_duplex = DUPLEX_FULL; + pdata->expansion.phy_link = false; + pdata->phy_speed = SPEED_1000; + pdata->expansion.pre_phy_speed = pdata->phy_speed; + pdata->expansion.pre_phy_duplex = pdata->phy_duplex; + pdata->expansion.pre_phy_autoneg = pdata->phy_autoeng; + + pdata->expansion.wol = WAKE_MAGIC; + + strscpy(pdata->drv_name, FXGMAC_DRV_NAME, sizeof(pdata->drv_name)); + strscpy(pdata->drv_ver, FXGMAC_DRV_VERSION, sizeof(pdata->drv_ver)); + dev_info(pdata->dev, "FXGMAC_DRV_NAME:%s, FXGMAC_DRV_VERSION:%s\n", + FXGMAC_DRV_NAME, FXGMAC_DRV_VERSION); +} + +static void fxgmac_init_all_ops(struct fxgmac_pdata *pdata) +{ + fxgmac_init_desc_ops(&pdata->desc_ops); + fxgmac_init_hw_ops(&pdata->hw_ops); + + /* DPRINTK("register desc_ops and hw ops\n"); */ +} + +int fxgmac_init(struct fxgmac_pdata *pdata, bool save_private_reg) +{ + struct fxgmac_hw_ops *hw_ops = &pdata->hw_ops; + struct net_device *netdev = pdata->netdev; + unsigned int i, dma_width; + int ret; + + /* Set all the function pointers */ + fxgmac_init_all_ops(pdata); + + /* Set default configuration data */ + fxgmac_default_config(pdata); + + /* Set irq, base_addr, MAC address, */ + netdev->irq = pdata->dev_irq; + netdev->base_addr = (unsigned long)pdata->base_mem; + fxgmac_read_mac_addr(pdata); + eth_hw_addr_set(netdev, pdata->mac_addr); + + if (save_private_reg) { + hw_ops->save_nonstick_reg(pdata); + } + + hw_ops->exit(pdata); + + /* Populate the hardware features */ + fxgmac_get_all_hw_features(pdata); + fxgmac_print_all_hw_features(pdata); + + /* TODO: Set the PHY mode to XLGMII */ + + /* Set the DMA mask */ +#ifdef CONFIG_ARM64 + dma_width = FXGMAC_DMA_BIT_MASK; +#else + dma_width = pdata->hw_feat.dma_width; +#endif + ret = dma_set_mask_and_coherent(pdata->dev, DMA_BIT_MASK(dma_width)); + if (ret) { + dev_err(pdata->dev, "dma_set_mask_and_coherent failed\n"); + return ret; + } + + /* Channel and ring params initializtion + * pdata->channel_count; + * pdata->tx_ring_count; + * pdata->rx_ring_count; + * pdata->tx_desc_count; + * pdata->rx_desc_count; + */ + BUILD_BUG_ON_NOT_POWER_OF_2(FXGMAC_TX_DESC_CNT); + pdata->tx_desc_count = FXGMAC_TX_DESC_CNT; + if (pdata->tx_desc_count & (pdata->tx_desc_count - 1)) { + dev_err(pdata->dev, "tx descriptor count (%d) is not valid\n", + pdata->tx_desc_count); + ret = -EINVAL; + return ret; + } + BUILD_BUG_ON_NOT_POWER_OF_2(FXGMAC_RX_DESC_CNT); + pdata->rx_desc_count = FXGMAC_RX_DESC_CNT; + if (pdata->rx_desc_count & (pdata->rx_desc_count - 1)) { + dev_err(pdata->dev, "rx descriptor count (%d) is not valid\n", + pdata->rx_desc_count); + ret = -EINVAL; + return ret; + } + + pdata->tx_ring_count = min_t(unsigned int, num_online_cpus(), + pdata->hw_feat.tx_ch_cnt); + pdata->tx_ring_count = min_t(unsigned int, pdata->tx_ring_count, + pdata->hw_feat.tx_q_cnt); + pdata->tx_q_count = pdata->tx_ring_count; + + ret = netif_set_real_num_tx_queues(netdev, pdata->tx_q_count); + DPRINTK("num_online_cpus:%u, tx_ch_cnt:%u, tx_q_cnt:%u, tx_ring_count:%u\n", + num_online_cpus(), pdata->hw_feat.tx_ch_cnt, + pdata->hw_feat.tx_q_cnt, pdata->tx_ring_count); + if (ret) { + dev_err(pdata->dev, "error setting real tx queue count\n"); + return ret; + } + + pdata->rx_ring_count = min_t(unsigned int, + netif_get_num_default_rss_queues(), + pdata->hw_feat.rx_ch_cnt); +#ifdef FXGMAC_ONE_CHANNEL + pdata->rx_ring_count = 1; + pdata->hw_feat.rx_q_cnt = pdata->rx_ring_count; +#else + pdata->rx_ring_count = min_t(unsigned int, pdata->rx_ring_count, + pdata->hw_feat.rx_q_cnt); +#endif + pdata->rx_q_count = pdata->rx_ring_count; + ret = netif_set_real_num_rx_queues(netdev, pdata->rx_q_count); + if (ret) { + dev_err(pdata->dev, "error setting real rx queue count\n"); + return ret; + } + + pdata->channel_count = + max_t(unsigned int, pdata->tx_ring_count, pdata->rx_ring_count); + + DPRINTK("default rss queues:%u, rx_ch_cnt:%u, rx_q_cnt:%u, rx_ring_count:%u\n", + netif_get_num_default_rss_queues(), pdata->hw_feat.rx_ch_cnt, + pdata->hw_feat.rx_q_cnt, pdata->rx_ring_count); + DPRINTK("channel_count:%u, netdev tx channel_num=%u\n", + pdata->channel_count, netdev->real_num_tx_queues); + + /* Initialize RSS hash key and lookup table */ +#if FXGMAC_RSS_HASH_KEY_LINUX + netdev_rss_key_fill(pdata->rss_key, sizeof(pdata->rss_key)); +#else + + hw_ops->get_rss_hash_key(pdata, (u8 *)pdata->rss_key); +#endif + +#if FXGMAC_MSIX_CH0RXDIS_ENABLED + for (i = 0; i < FXGMAC_RSS_MAX_TABLE_SIZE; i++) { + pdata->rss_table[i] = FXGMAC_SET_REG_BITS(pdata->rss_table[i], + MAC_RSSDR_DMCH_POS, + MAC_RSSDR_DMCH_LEN, + (i % 3) + 1); + } +#else + for (i = 0; i < FXGMAC_RSS_MAX_TABLE_SIZE; i++) { + pdata->rss_table[i] = FXGMAC_SET_REG_BITS( + pdata->rss_table[i], MAC_RSSDR_DMCH_POS, + MAC_RSSDR_DMCH_LEN, i % pdata->rx_ring_count); + } +#endif + + pdata->rss_options = FXGMAC_SET_REG_BITS(pdata->rss_options, + MAC_RSSCR_IP4TE_POS, + MAC_RSSCR_IP4TE_LEN, 1); + pdata->rss_options = FXGMAC_SET_REG_BITS(pdata->rss_options, + MAC_RSSCR_TCP4TE_POS, + MAC_RSSCR_TCP4TE_LEN, 1); + pdata->rss_options = FXGMAC_SET_REG_BITS(pdata->rss_options, + MAC_RSSCR_UDP4TE_POS, + MAC_RSSCR_UDP4TE_LEN, 1); + + /* config MTU supported */ + netdev->min_mtu = ETH_MIN_MTU; + netdev->max_mtu = + FXGMAC_JUMBO_PACKET_MTU + (ETH_HLEN + VLAN_HLEN + ETH_FCS_LEN); + + /* + netdev->extended->min_mtu = netdev->min_mtu; + netdev->extended->max_mtu = netdev->max_mtu; + */ + + DPRINTK("rss_options:0x%x\n", pdata->rss_options); + + /* Set device operations */ + netdev->netdev_ops = fxgmac_get_netdev_ops(); + netdev->ethtool_ops = fxgmac_get_ethtool_ops(); + + /* Set device features */ + if (pdata->hw_feat.tso) { + netdev->hw_features = NETIF_F_TSO; + netdev->hw_features |= NETIF_F_TSO6; + netdev->hw_features |= NETIF_F_SG; + netdev->hw_features |= NETIF_F_IP_CSUM; + netdev->hw_features |= NETIF_F_IPV6_CSUM; + } else if (pdata->hw_feat.tx_coe) { + netdev->hw_features = NETIF_F_IP_CSUM; + netdev->hw_features |= NETIF_F_IPV6_CSUM; + } + + if (pdata->hw_feat.rx_coe) { + netdev->hw_features |= NETIF_F_RXCSUM; + netdev->hw_features |= NETIF_F_GRO; + } + + if (pdata->hw_feat.rss) { + netdev->hw_features |= NETIF_F_RXHASH; + } + + netdev->vlan_features |= netdev->hw_features; + + netdev->hw_features |= NETIF_F_HW_VLAN_CTAG_RX; + pdata->vlan_strip = 1; + if (pdata->hw_feat.sa_vlan_ins) { + netdev->hw_features |= NETIF_F_HW_VLAN_CTAG_TX; + } +#if FXGMAC_FILTER_SINGLE_VLAN_ENABLED + /* only can filter one vlan id */ + pdata->hw_feat.vlhash = 1; +#else + pdata->hw_feat.vlhash = 0; +#endif + + if (pdata->hw_feat.vlhash) { + netdev->hw_features |= NETIF_F_HW_VLAN_CTAG_FILTER; + pdata->vlan_filter = 1; + } + + netdev->features |= netdev->hw_features; + pdata->expansion.netdev_features = netdev->features; + + netdev->priv_flags |= IFF_UNICAST_FLT; + + /* Use default watchdog timeout */ + netdev->watchdog_timeo = msecs_to_jiffies(5000); + netif_set_tso_max_size(netdev, NIC_MAX_TCP_OFFLOAD_SIZE); + + /* Tx coalesce parameters initialization */ + pdata->tx_usecs = FXGMAC_INIT_DMA_TX_USECS; + pdata->tx_frames = FXGMAC_INIT_DMA_TX_FRAMES; + + /* Rx coalesce parameters initialization */ + pdata->rx_riwt = hw_ops->usec_to_riwt(pdata, FXGMAC_INIT_DMA_RX_USECS); + + pdata->rx_usecs = FXGMAC_INIT_DMA_RX_USECS; + pdata->rx_frames = FXGMAC_INIT_DMA_RX_FRAMES; + + mutex_init(&pdata->expansion.mutex); + + DPRINTK("fxgmac_init callout,ok.\n"); + + return 0; +} + +static void fxgmac_init_interrupt_scheme(struct fxgmac_pdata *pdata) +{ +#ifdef CONFIG_PCI_MSI + int vectors, rc, i, req_vectors; + /* check cpu core number. + * since we have 4 channels, we must ensure the number of cpu core > 4 + * otherwise, just roll back to legacy + */ + vectors = num_online_cpus(); + if (vectors >= FXGMAC_MAX_DMA_CHANNELS) { + req_vectors = FXGMAC_MSIX_INT_NUMS; + pdata->expansion.msix_entries = kcalloc( + req_vectors, sizeof(struct msix_entry), GFP_KERNEL); + if (!pdata->expansion.msix_entries) { + dev_err(pdata->dev, + "MSIx, kcalloc err for msix entries, \ + rollback to MSI\n"); + goto enable_msi_interrupt; + } else { + for (i = 0; i < req_vectors; i++) + pdata->expansion.msix_entries[i].entry = i; + +#ifndef RHEL_MAJOR + rc = pci_enable_msix_range( + pdata->pdev, pdata->expansion.msix_entries, + req_vectors, req_vectors); +#else +#if (RHEL_RELEASE_CODE >= RHEL_RELEASE_VERSION(7, 0)) && \ + (RHEL_RELEASE_CODE < RHEL_RELEASE_VERSION(7, 8)) + rc = pci_enable_msix(pdata->pdev, + pdata->expansion.msix_entries, + req_vectors); +#else + rc = pci_enable_msix_range( + pdata->pdev, pdata->expansion.msix_entries, + req_vectors, req_vectors); +#endif +#endif + if (rc < 0) { + dev_err(pdata->dev, "enable MSIx failed,%d.\n", + rc); + req_vectors = 0; + } else { + req_vectors = rc; + } + + if (req_vectors >= FXGMAC_MAX_DMA_CHANNELS_PLUS_1TX) { + dev_info(pdata->dev, + "enable MSIx ok, cpu=%d,vectors=%d.\n", + vectors, req_vectors); + pdata->expansion.int_flags = + FXGMAC_SET_REG_BITS( + pdata->expansion.int_flags, + FXGMAC_FLAG_INTERRUPT_POS, + FXGMAC_FLAG_INTERRUPT_LEN, + FXGMAC_FLAG_MSIX_ENABLED); + pdata->per_channel_irq = 1; + pdata->expansion.misc_irq = + pdata->expansion + .msix_entries[MSI_ID_PHY_OTHER] + .vector; + return; + } else if (req_vectors) { + dev_err(pdata->dev, + "enable MSIx with only %d vector, \ + while we need %d, rollback to MSI.\n", + req_vectors, vectors); + + pci_disable_msix(pdata->pdev); + kfree(pdata->expansion.msix_entries); + pdata->expansion.msix_entries = NULL; + req_vectors = 0; + } else { + dev_err(pdata->dev, + "enable MSIx failure and clear msix entries.\n"); + + kfree(pdata->expansion.msix_entries); + pdata->expansion.msix_entries = NULL; + req_vectors = 0; + } + } + } + +enable_msi_interrupt: + rc = pci_enable_msi(pdata->pdev); + if (rc < 0) { + pdata->expansion.int_flags = FXGMAC_SET_REG_BITS( + pdata->expansion.int_flags, FXGMAC_FLAG_INTERRUPT_POS, + FXGMAC_FLAG_INTERRUPT_LEN, FXGMAC_FLAG_LEGACY_ENABLED); + dev_err(pdata->dev, + "dev_err MSI failure, rollback to LEGACY.\n"); + } else { + pdata->expansion.int_flags = FXGMAC_SET_REG_BITS( + pdata->expansion.int_flags, FXGMAC_FLAG_INTERRUPT_POS, + FXGMAC_FLAG_INTERRUPT_LEN, FXGMAC_FLAG_MSI_ENABLED); + pdata->dev_irq = pdata->pdev->irq; + dev_info(pdata->dev, "enable MSI ok, cpu=%d, irq=%d.\n", + vectors, pdata->pdev->irq); + } +#else + pdata = pdata; +#endif +} + +int fxgmac_drv_probe(struct device *dev, struct fxgmac_resources *res) +{ + struct fxgmac_pdata *pdata; + struct net_device *netdev; + int ret; + + netdev = alloc_etherdev_mq(sizeof(struct fxgmac_pdata), + FXGMAC_MAX_DMA_CHANNELS); + + if (!netdev) { + dev_err(dev, "alloc_etherdev failed\n"); + return -ENOMEM; + } + + SET_NETDEV_DEV(netdev, dev); + dev_set_drvdata(dev, netdev); + pdata = netdev_priv(netdev); + pdata->dev = dev; + pdata->pdev = to_pci_dev(dev); + pdata->netdev = netdev; + + pdata->dev_irq = res->irq; + pdata->msg_enable = NETIF_MSG_DRV; + pdata->expansion.dev_state = FXGMAC_DEV_PROBE; + /* default to legacy interrupt */ + pdata->expansion.int_flags = FXGMAC_SET_REG_BITS( + pdata->expansion.int_flags, FXGMAC_FLAG_INTERRUPT_POS, + FXGMAC_FLAG_INTERRUPT_LEN, FXGMAC_FLAG_LEGACY_ENABLED); + pdata->expansion.misc_irq = pdata->dev_irq; + + fxgmac_init_interrupt_scheme(pdata); + + pdata->mac_regs = res->addr; + pdata->base_mem = res->addr; + pdata->mac_regs = pdata->mac_regs + FXGMAC_MAC_REGS_OFFSET; + + ret = fxgmac_init(pdata, true); + if (ret) { + dev_err(dev, "fxgmac init failed\n"); + goto err_free_netdev; + } + + pdata->hw_ops.read_led_config(pdata); + + netif_carrier_off(netdev); + ret = register_netdev(netdev); + if (ret) { + dev_err(dev, "net device registration failed\n"); + goto err_free_netdev; + } + if (netif_msg_drv(pdata)) + DPRINTK("fxgamc_drv_prob callout, netdev num_tx_q=%u\n", + netdev->real_num_tx_queues); + + return 0; + +err_free_netdev: + free_netdev(netdev); + DPRINTK("fxgamc_drv_prob callout with err \n"); + + return ret; +} + +int fxgmac_drv_remove(struct device *dev) +{ + struct net_device *netdev = dev_get_drvdata(dev); + struct fxgmac_pdata *pdata = netdev_priv(netdev); + struct fxgmac_hw_ops *hw_ops = &pdata->hw_ops; + + hw_ops->led_under_shutdown(pdata); + + unregister_netdev(netdev); + free_netdev(netdev); + + return 0; +} + +void fxgmac_dump_tx_desc(struct fxgmac_pdata *pdata, struct fxgmac_ring *ring, + unsigned int idx, unsigned int count, + unsigned int flag) +{ + struct fxgmac_desc_data *desc_data; + struct fxgmac_dma_desc *dma_desc; + + while (count--) { + desc_data = FXGMAC_GET_DESC_DATA(ring, idx); + dma_desc = desc_data->dma_desc; + + netdev_dbg(pdata->netdev, + "TX: dma_desc=%p, dma_desc_addr=%pad\n", + desc_data->dma_desc, &desc_data->dma_desc_addr); + netdev_dbg(pdata->netdev, + "TX_NORMAL_DESC[%d %s] = %08x:%08x:%08x:%08x\n", idx, + (flag == 1) ? "QUEUED FOR TX" : "TX BY DEVICE", + le32_to_cpu(dma_desc->desc0), + le32_to_cpu(dma_desc->desc1), + le32_to_cpu(dma_desc->desc2), + le32_to_cpu(dma_desc->desc3)); + + idx++; + } +} + +void fxgmac_dump_rx_desc(struct fxgmac_pdata *pdata, struct fxgmac_ring *ring, + unsigned int idx) +{ + struct fxgmac_desc_data *desc_data; + struct fxgmac_dma_desc *dma_desc; + + desc_data = FXGMAC_GET_DESC_DATA(ring, idx); + dma_desc = desc_data->dma_desc; + + netdev_dbg(pdata->netdev, "RX: dma_desc=%p, dma_desc_addr=%pad\n", + desc_data->dma_desc, &desc_data->dma_desc_addr); + netdev_dbg(pdata->netdev, + "RX_NORMAL_DESC[%d RX BY DEVICE] = %08x:%08x:%08x:%08x\n", + idx, le32_to_cpu(dma_desc->desc0), + le32_to_cpu(dma_desc->desc1), le32_to_cpu(dma_desc->desc2), + le32_to_cpu(dma_desc->desc3)); +} + +void fxgmac_dbg_pkt(struct net_device *netdev, struct sk_buff *skb, bool tx_rx) +{ + struct ethhdr *eth = (struct ethhdr *)skb->data; + unsigned char buffer[128]; + unsigned int i; + + netdev_dbg(netdev, "\n************** SKB dump ****************\n"); + + netdev_dbg(netdev, "%s packet of %d bytes\n", (tx_rx ? "TX" : "RX"), + skb->len); + + netdev_dbg(netdev, "Dst MAC addr: %pM\n", eth->h_dest); + netdev_dbg(netdev, "Src MAC addr: %pM\n", eth->h_source); + netdev_dbg(netdev, "Protocol: %#06hx\n", ntohs(eth->h_proto)); + + for (i = 0; i < skb->len; i += 32) { + unsigned int len = min(skb->len - i, 32U); + + hex_dump_to_buffer(&skb->data[i], len, 32, 1, buffer, + sizeof(buffer), false); + netdev_dbg(netdev, " %#06x: %s\n", i, buffer); + } + + netdev_dbg(netdev, "\n************** SKB dump ****************\n"); +} + +void fxgmac_print_pkt(struct net_device *netdev, struct sk_buff *skb, + bool tx_rx) +{ +#ifdef FXGMAC_DEBUG + struct ethhdr *eth = (struct ethhdr *)skb->data; +#endif + unsigned char buffer[128]; + unsigned int i; + + DPRINTK("\n************** SKB dump ****************\n"); + DPRINTK("%s packet of %d bytes\n", (tx_rx ? "TX" : "RX"), skb->len); + +#ifdef FXGMAC_DEBUG + DPRINTK("Dst MAC addr: %pM\n", eth->h_dest); + DPRINTK("Src MAC addr: %pM\n", eth->h_source); + DPRINTK("Protocol: %#06hx\n", ntohs(eth->h_proto)); +#endif + for (i = 0; i < skb->len; i += 32) { + unsigned int len = min(skb->len - i, 32U); + + hex_dump_to_buffer(&skb->data[i], len, 32, 1, buffer, + sizeof(buffer), false); + DPRINTK(" %#06x: %s\n", i, buffer); + } + + DPRINTK("\n************** SKB dump ****************\n"); +} + +void fxgmac_get_all_hw_features(struct fxgmac_pdata *pdata) +{ + struct fxgmac_hw_features *hw_feat = &pdata->hw_feat; + unsigned int mac_hfr0, mac_hfr1, mac_hfr2, mac_hfr3; + + mac_hfr0 = readl(pdata->mac_regs + MAC_HWF0R); + mac_hfr1 = readl(pdata->mac_regs + MAC_HWF1R); + mac_hfr2 = readl(pdata->mac_regs + MAC_HWF2R); + mac_hfr3 = readl(pdata->mac_regs + MAC_HWF3R); + + memset(hw_feat, 0, sizeof(*hw_feat)); + + hw_feat->version = readl(pdata->mac_regs + MAC_VR); + if (netif_msg_drv(pdata)) + DPRINTK("Mac ver=%#x\n", hw_feat->version); + + /* Hardware feature register 0 */ + hw_feat->phyifsel = FXGMAC_GET_REG_BITS( + mac_hfr0, MAC_HWF0R_ACTPHYIFSEL_POS, MAC_HWF0R_ACTPHYIFSEL_LEN); + hw_feat->vlhash = FXGMAC_GET_REG_BITS(mac_hfr0, MAC_HWF0R_VLHASH_POS, + MAC_HWF0R_VLHASH_LEN); + hw_feat->sma = FXGMAC_GET_REG_BITS(mac_hfr0, MAC_HWF0R_SMASEL_POS, + MAC_HWF0R_SMASEL_LEN); + hw_feat->rwk = FXGMAC_GET_REG_BITS(mac_hfr0, MAC_HWF0R_RWKSEL_POS, + MAC_HWF0R_RWKSEL_LEN); + hw_feat->mgk = FXGMAC_GET_REG_BITS(mac_hfr0, MAC_HWF0R_MGKSEL_POS, + MAC_HWF0R_MGKSEL_LEN); + hw_feat->mmc = FXGMAC_GET_REG_BITS(mac_hfr0, MAC_HWF0R_MMCSEL_POS, + MAC_HWF0R_MMCSEL_LEN); + hw_feat->aoe = FXGMAC_GET_REG_BITS(mac_hfr0, MAC_HWF0R_ARPOFFSEL_POS, + MAC_HWF0R_ARPOFFSEL_LEN); + hw_feat->ts = FXGMAC_GET_REG_BITS(mac_hfr0, MAC_HWF0R_TSSEL_POS, + MAC_HWF0R_TSSEL_LEN); + hw_feat->eee = FXGMAC_GET_REG_BITS(mac_hfr0, MAC_HWF0R_EEESEL_POS, + MAC_HWF0R_EEESEL_LEN); + hw_feat->tx_coe = FXGMAC_GET_REG_BITS(mac_hfr0, MAC_HWF0R_TXCOESEL_POS, + MAC_HWF0R_TXCOESEL_LEN); + hw_feat->rx_coe = FXGMAC_GET_REG_BITS(mac_hfr0, MAC_HWF0R_RXCOESEL_POS, + MAC_HWF0R_RXCOESEL_LEN); + hw_feat->addn_mac = FXGMAC_GET_REG_BITS(mac_hfr0, + MAC_HWF0R_ADDMACADRSEL_POS, + MAC_HWF0R_ADDMACADRSEL_LEN); + hw_feat->ts_src = FXGMAC_GET_REG_BITS(mac_hfr0, MAC_HWF0R_TSSTSSEL_POS, + MAC_HWF0R_TSSTSSEL_LEN); + hw_feat->sa_vlan_ins = FXGMAC_GET_REG_BITS( + mac_hfr0, MAC_HWF0R_SAVLANINS_POS, MAC_HWF0R_SAVLANINS_LEN); + + /* Hardware feature register 1 */ + hw_feat->rx_fifo_size = FXGMAC_GET_REG_BITS( + mac_hfr1, MAC_HWF1R_RXFIFOSIZE_POS, MAC_HWF1R_RXFIFOSIZE_LEN); + hw_feat->tx_fifo_size = FXGMAC_GET_REG_BITS( + mac_hfr1, MAC_HWF1R_TXFIFOSIZE_POS, MAC_HWF1R_TXFIFOSIZE_LEN); + hw_feat->adv_ts_hi = FXGMAC_GET_REG_BITS( + mac_hfr1, MAC_HWF1R_ADVTHWORD_POS, MAC_HWF1R_ADVTHWORD_LEN); + hw_feat->dma_width = FXGMAC_GET_REG_BITS(mac_hfr1, MAC_HWF1R_ADDR64_POS, + MAC_HWF1R_ADDR64_LEN); + hw_feat->dcb = FXGMAC_GET_REG_BITS(mac_hfr1, MAC_HWF1R_DCBEN_POS, + MAC_HWF1R_DCBEN_LEN); + hw_feat->sph = FXGMAC_GET_REG_BITS(mac_hfr1, MAC_HWF1R_SPHEN_POS, + MAC_HWF1R_SPHEN_LEN); + hw_feat->tso = FXGMAC_GET_REG_BITS(mac_hfr1, MAC_HWF1R_TSOEN_POS, + MAC_HWF1R_TSOEN_LEN); + hw_feat->dma_debug = FXGMAC_GET_REG_BITS( + mac_hfr1, MAC_HWF1R_DBGMEMA_POS, MAC_HWF1R_DBGMEMA_LEN); +#if (FXGMAC_RSS_FEATURE_ENABLED) + hw_feat->rss = 1; +#else + hw_feat->rss = 0; /* = FXGMAC_GET_REG_BITS(mac_hfr1, + MAC_HWF1R_RSSEN_POS, + MAC_HWF1R_RSSEN_LEN);*/ +#endif + hw_feat->tc_cnt = + 3 /*1, yzhang try,0412*/; /* FXGMAC_GET_REG_BITS(mac_hfr1, + MAC_HWF1R_NUMTC_POS, + MAC_HWF1R_NUMTC_LEN); */ + hw_feat->avsel = FXGMAC_GET_REG_BITS(mac_hfr1, MAC_HWF1R_AVSEL_POS, + MAC_HWF1R_AVSEL_LEN); + hw_feat->ravsel = FXGMAC_GET_REG_BITS(mac_hfr1, MAC_HWF1R_RAVSEL_POS, + MAC_HWF1R_RAVSEL_LEN); + hw_feat->hash_table_size = FXGMAC_GET_REG_BITS( + mac_hfr1, MAC_HWF1R_HASHTBLSZ_POS, MAC_HWF1R_HASHTBLSZ_LEN); + hw_feat->l3l4_filter_num = FXGMAC_GET_REG_BITS( + mac_hfr1, MAC_HWF1R_L3L4FNUM_POS, MAC_HWF1R_L3L4FNUM_LEN); + + /* Hardware feature register 2 */ + hw_feat->rx_q_cnt = 3; + hw_feat->tx_q_cnt = FXGMAC_GET_REG_BITS(mac_hfr2, MAC_HWF2R_TXQCNT_POS, + MAC_HWF2R_TXQCNT_LEN); + + hw_feat->tx_ch_cnt = FXGMAC_GET_REG_BITS( + mac_hfr2, MAC_HWF2R_TXCHCNT_POS, MAC_HWF2R_TXCHCNT_LEN); + hw_feat->pps_out_num = FXGMAC_GET_REG_BITS( + mac_hfr2, MAC_HWF2R_PPSOUTNUM_POS, MAC_HWF2R_PPSOUTNUM_LEN); + hw_feat->aux_snap_num = FXGMAC_GET_REG_BITS( + mac_hfr2, MAC_HWF2R_AUXSNAPNUM_POS, MAC_HWF2R_AUXSNAPNUM_LEN); + + /* Translate the Hash Table size into actual number */ + switch (hw_feat->hash_table_size) { + case 0: + break; + case 1: + hw_feat->hash_table_size = 64; + break; + case 2: + hw_feat->hash_table_size = 128; + break; + case 3: + hw_feat->hash_table_size = 256; + break; + } + + /* Translate the address width setting into actual number */ + switch (hw_feat->dma_width) { + case 0: + hw_feat->dma_width = 32; + break; + case 1: + hw_feat->dma_width = 40; + break; + case 2: + hw_feat->dma_width = 48; + break; + default: + hw_feat->dma_width = 32; + } + + /* The Queue, Channel and TC counts are zero based so increment them + * to get the actual number + */ + hw_feat->rx_q_cnt++; + hw_feat->tx_q_cnt++; + hw_feat->rx_ch_cnt++; + hw_feat->tx_ch_cnt++; + hw_feat->tc_cnt++; + + hw_feat->hwfr3 = mac_hfr3; + /* DPRINTK("HWFR3: %u\n", mac_hfr3); */ +} + +void fxgmac_print_all_hw_features(struct fxgmac_pdata *pdata) +{ + char *str = NULL; + + DPRINTK("\n"); + DPRINTK("=====================================================\n"); + DPRINTK("\n"); + DPRINTK("HW support following features,ver=%#x\n", + pdata->hw_feat.version); + DPRINTK("\n"); + /* HW Feature Register0 */ + DPRINTK("VLAN Hash Filter Selected : %s\n", + pdata->hw_feat.vlhash ? "YES" : "NO"); + DPRINTK("SMA (MDIO) Interface : %s\n", + pdata->hw_feat.sma ? "YES" : "NO"); + DPRINTK("PMT Remote Wake-up Packet Enable : %s\n", + pdata->hw_feat.rwk ? "YES" : "NO"); + DPRINTK("PMT Magic Packet Enable : %s\n", + pdata->hw_feat.mgk ? "YES" : "NO"); + DPRINTK("RMON/MMC Module Enable : %s\n", + pdata->hw_feat.mmc ? "YES" : "NO"); + DPRINTK("ARP Offload Enabled : %s\n", + pdata->hw_feat.aoe ? "YES" : "NO"); + DPRINTK("IEEE 1588-2008 Timestamp Enabled : %s\n", + pdata->hw_feat.ts ? "YES" : "NO"); + DPRINTK("Energy Efficient Ethernet Enabled : %s\n", + pdata->hw_feat.eee ? "YES" : "NO"); + DPRINTK("Transmit Checksum Offload Enabled : %s\n", + pdata->hw_feat.tx_coe ? "YES" : "NO"); + DPRINTK("Receive Checksum Offload Enabled : %s\n", + pdata->hw_feat.rx_coe ? "YES" : "NO"); + DPRINTK("Additional MAC Addresses 1-31 Selected : %s\n", + pdata->hw_feat.addn_mac ? "YES" : "NO"); + + switch (pdata->hw_feat.ts_src) { + case 0: + str = "RESERVED"; + break; + case 1: + str = "INTERNAL"; + break; + case 2: + str = "EXTERNAL"; + break; + case 3: + str = "BOTH"; + break; + } + DPRINTK("Timestamp System Time Source : %s\n", str); + + DPRINTK("Source Address or VLAN Insertion Enable : %s\n", + pdata->hw_feat.sa_vlan_ins ? "YES" : "NO"); + + /* HW Feature Register1 */ + switch (pdata->hw_feat.rx_fifo_size) { + case 0: + str = "128 bytes"; + break; + case 1: + str = "256 bytes"; + break; + case 2: + str = "512 bytes"; + break; + case 3: + str = "1 KBytes"; + break; + case 4: + str = "2 KBytes"; + break; + case 5: + str = "4 KBytes"; + break; + case 6: + str = "8 KBytes"; + break; + case 7: + str = "16 KBytes"; + break; + case 8: + str = "32 kBytes"; + break; + case 9: + str = "64 KBytes"; + break; + case 10: + str = "128 KBytes"; + break; + case 11: + str = "256 KBytes"; + break; + default: + str = "RESERVED"; + } + DPRINTK("MTL Receive FIFO Size : %s\n", str); + + switch (pdata->hw_feat.tx_fifo_size) { + case 0: + str = "128 bytes"; + break; + case 1: + str = "256 bytes"; + break; + case 2: + str = "512 bytes"; + break; + case 3: + str = "1 KBytes"; + break; + case 4: + str = "2 KBytes"; + break; + case 5: + str = "4 KBytes"; + break; + case 6: + str = "8 KBytes"; + break; + case 7: + str = "16 KBytes"; + break; + case 8: + str = "32 kBytes"; + break; + case 9: + str = "64 KBytes"; + break; + case 10: + str = "128 KBytes"; + break; + case 11: + str = "256 KBytes"; + break; + default: + str = "RESERVED"; + } + DPRINTK("MTL Transmit FIFO Size : %s\n", str); + + DPRINTK("IEEE 1588 High Word Register Enable : %s\n", + pdata->hw_feat.adv_ts_hi ? "YES" : "NO"); + DPRINTK("Address width : %u\n", + pdata->hw_feat.dma_width); + DPRINTK("DCB Feature Enable : %s\n", + pdata->hw_feat.dcb ? "YES" : "NO"); + DPRINTK("Split Header Feature Enable : %s\n", + pdata->hw_feat.sph ? "YES" : "NO"); + DPRINTK("TCP Segmentation Offload Enable : %s\n", + pdata->hw_feat.tso ? "YES" : "NO"); + DPRINTK("DMA Debug Registers Enabled : %s\n", + pdata->hw_feat.dma_debug ? "YES" : "NO"); + DPRINTK("RSS Feature Enabled : %s\n", + pdata->hw_feat.rss ? "YES" : "NO"); + DPRINTK("*TODO*Number of Traffic classes : %u\n", + (pdata->hw_feat.tc_cnt)); + DPRINTK("AV Feature Enabled : %s\n", + pdata->hw_feat.avsel ? "YES" : "NO"); + DPRINTK("Rx Side Only AV Feature Enabled : %s\n", + (pdata->hw_feat.ravsel ? "YES" : "NO")); + DPRINTK("Hash Table Size : %u\n", + pdata->hw_feat.hash_table_size); + DPRINTK("Total number of L3 or L4 Filters : %u\n", + pdata->hw_feat.l3l4_filter_num); + + /* HW Feature Register2 */ + DPRINTK("Number of MTL Receive Queues : %u\n", + pdata->hw_feat.rx_q_cnt); + DPRINTK("Number of MTL Transmit Queues : %u\n", + pdata->hw_feat.tx_q_cnt); + DPRINTK("Number of DMA Receive Channels : %u\n", + pdata->hw_feat.rx_ch_cnt); + DPRINTK("Number of DMA Transmit Channels : %u\n", + pdata->hw_feat.tx_ch_cnt); + + switch (pdata->hw_feat.pps_out_num) { + case 0: + str = "No PPS output"; + break; + case 1: + str = "1 PPS output"; + break; + case 2: + str = "2 PPS output"; + break; + case 3: + str = "3 PPS output"; + break; + case 4: + str = "4 PPS output"; + break; + default: + str = "RESERVED"; + } + DPRINTK("Number of PPS Outputs : %s\n", str); + + switch (pdata->hw_feat.aux_snap_num) { + case 0: + str = "No auxiliary input"; + break; + case 1: + str = "1 auxiliary input"; + break; + case 2: + str = "2 auxiliary input"; + break; + case 3: + str = "3 auxiliary input"; + break; + case 4: + str = "4 auxiliary input"; + break; + default: + str = "RESERVED"; + } + DPRINTK("Number of Auxiliary Snapshot Inputs : %s", str); + + DPRINTK("\n"); + DPRINTK("=====================================================\n"); + DPRINTK("\n"); +} diff --git a/drivers/net/ethernet/motorcomm/yt6801/fuxi-gmac-desc.c b/drivers/net/ethernet/motorcomm/yt6801/fuxi-gmac-desc.c new file mode 100644 index 000000000000..a24b0e2afe6a --- /dev/null +++ b/drivers/net/ethernet/motorcomm/yt6801/fuxi-gmac-desc.c @@ -0,0 +1,840 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (c) 2021 Motorcomm Corporation. */ + +#include "fuxi-gmac.h" +#include "fuxi-gmac-reg.h" + +#ifdef FXGMAC_USE_ADAPTER_HANDLE +#include "fuxi-mp.h" +#endif + +static void fxgmac_unmap_desc_data(struct fxgmac_pdata *pdata, + struct fxgmac_desc_data *desc_data) +{ + if (desc_data->skb_dma) { + if (desc_data->mapped_as_page) { + dma_unmap_page(pdata->dev, desc_data->skb_dma, + desc_data->skb_dma_len, DMA_TO_DEVICE); + } else { + dma_unmap_single(pdata->dev, desc_data->skb_dma, + desc_data->skb_dma_len, DMA_TO_DEVICE); + } + desc_data->skb_dma = 0; + desc_data->skb_dma_len = 0; + } + + if (desc_data->skb) { + dev_kfree_skb_any(desc_data->skb); + desc_data->skb = NULL; + } + + if (desc_data->rx.hdr.pa.pages) + put_page(desc_data->rx.hdr.pa.pages); + + if (desc_data->rx.hdr.pa_unmap.pages) { + dma_unmap_page(pdata->dev, desc_data->rx.hdr.pa_unmap.pages_dma, + desc_data->rx.hdr.pa_unmap.pages_len, + DMA_FROM_DEVICE); + put_page(desc_data->rx.hdr.pa_unmap.pages); + } + + if (desc_data->rx.buf.pa.pages) + put_page(desc_data->rx.buf.pa.pages); + + if (desc_data->rx.buf.pa_unmap.pages) { + dma_unmap_page(pdata->dev, desc_data->rx.buf.pa_unmap.pages_dma, + desc_data->rx.buf.pa_unmap.pages_len, + DMA_FROM_DEVICE); + put_page(desc_data->rx.buf.pa_unmap.pages); + } + memset(&desc_data->tx, 0, sizeof(desc_data->tx)); + memset(&desc_data->rx, 0, sizeof(desc_data->rx)); + + desc_data->mapped_as_page = 0; + + if (desc_data->state_saved) { + desc_data->state_saved = 0; + desc_data->state.skb = NULL; + desc_data->state.len = 0; + desc_data->state.error = 0; + } +} + +static void fxgmac_free_ring(struct fxgmac_pdata *pdata, + struct fxgmac_ring *ring) +{ + struct fxgmac_desc_data *desc_data; + unsigned int i; + + if (!ring) + return; + + if (ring->desc_data_head) { + for (i = 0; i < ring->dma_desc_count; i++) { + desc_data = FXGMAC_GET_DESC_DATA(ring, i); + fxgmac_unmap_desc_data(pdata, desc_data); + } + + kfree(ring->desc_data_head); + ring->desc_data_head = NULL; + } + + if (ring->rx_hdr_pa.pages) { + dma_unmap_page(pdata->dev, ring->rx_hdr_pa.pages_dma, + ring->rx_hdr_pa.pages_len, DMA_FROM_DEVICE); + put_page(ring->rx_hdr_pa.pages); + + ring->rx_hdr_pa.pages = NULL; + ring->rx_hdr_pa.pages_len = 0; + ring->rx_hdr_pa.pages_offset = 0; + ring->rx_hdr_pa.pages_dma = 0; + } + + if (ring->rx_buf_pa.pages) { + dma_unmap_page(pdata->dev, ring->rx_buf_pa.pages_dma, + ring->rx_buf_pa.pages_len, DMA_FROM_DEVICE); + put_page(ring->rx_buf_pa.pages); + + ring->rx_buf_pa.pages = NULL; + ring->rx_buf_pa.pages_len = 0; + ring->rx_buf_pa.pages_offset = 0; + ring->rx_buf_pa.pages_dma = 0; + } + + if (ring->dma_desc_head) { + dma_free_coherent( + pdata->dev, + (sizeof(struct fxgmac_dma_desc) * ring->dma_desc_count), + ring->dma_desc_head, ring->dma_desc_head_addr); + ring->dma_desc_head = NULL; + } +} + +static int fxgmac_init_ring(struct fxgmac_pdata *pdata, + struct fxgmac_ring *ring, + unsigned int dma_desc_count) +{ + if (!ring) + return 0; + + /* Descriptors */ + ring->dma_desc_count = dma_desc_count; + ring->dma_desc_head = dma_alloc_coherent( + pdata->dev, (sizeof(struct fxgmac_dma_desc) * dma_desc_count), + &ring->dma_desc_head_addr, GFP_KERNEL); + if (!ring->dma_desc_head) + return -ENOMEM; + + /* Array of descriptor data */ + ring->desc_data_head = kcalloc( + dma_desc_count, sizeof(struct fxgmac_desc_data), GFP_KERNEL); + if (!ring->desc_data_head) + return -ENOMEM; + + netif_dbg( + pdata, drv, pdata->netdev, + "dma_desc_head=%p, dma_desc_head_addr=%pad, desc_data_head=%p\n", + ring->dma_desc_head, &ring->dma_desc_head_addr, + ring->desc_data_head); + + return 0; +} + +static void fxgmac_free_rings(struct fxgmac_pdata *pdata) +{ + struct fxgmac_channel *channel; + unsigned int i; + + if (!pdata->channel_head) + return; + + channel = pdata->channel_head; + for (i = 0; i < pdata->channel_count; i++, channel++) { + fxgmac_free_ring(pdata, channel->tx_ring); + fxgmac_free_ring(pdata, channel->rx_ring); + } +} + +static int fxgmac_alloc_rings(struct fxgmac_pdata *pdata) +{ + struct fxgmac_channel *channel; + unsigned int i; + int ret; + + channel = pdata->channel_head; + for (i = 0; i < pdata->channel_count; i++, channel++) { + netif_dbg(pdata, drv, pdata->netdev, "%s - Tx ring:\n", + channel->name); + + if (i < pdata->tx_ring_count) { + ret = fxgmac_init_ring(pdata, channel->tx_ring, + pdata->tx_desc_count); + + if (ret) { + netdev_alert(pdata->netdev, + "error initializing Tx ring"); + goto err_init_ring; + } + } + + netif_dbg(pdata, drv, pdata->netdev, "%s - Rx ring:\n", + channel->name); + + ret = fxgmac_init_ring(pdata, channel->rx_ring, + pdata->rx_desc_count); + if (ret) { + netdev_alert(pdata->netdev, + "error initializing Rx ring\n"); + goto err_init_ring; + } + if (netif_msg_drv(pdata)) { + DPRINTK("fxgmac_alloc_ring..ch=%u,", i); + if (i < pdata->tx_ring_count) + DPRINTK(" tx_desc_cnt=%u,", + pdata->tx_desc_count); + + DPRINTK(" rx_desc_cnt=%u.\n", pdata->rx_desc_count); + } + } + if (netif_msg_drv(pdata)) { + DPRINTK("alloc_rings callout ok ch=%u\n", i); + } + + return 0; + +err_init_ring: + fxgmac_free_rings(pdata); + + DPRINTK("alloc_rings callout err,%d\n", ret); + return ret; +} + +static void fxgmac_free_channels(struct fxgmac_pdata *pdata) +{ + if (!pdata->channel_head) + return; + if (netif_msg_drv(pdata)) + DPRINTK("free_channels,tx_ring=%p", + pdata->channel_head->tx_ring); + kfree(pdata->channel_head->tx_ring); + pdata->channel_head->tx_ring = NULL; + + if (netif_msg_drv(pdata)) + DPRINTK(" ,rx_ring=%p", pdata->channel_head->rx_ring); + kfree(pdata->channel_head->rx_ring); + pdata->channel_head->rx_ring = NULL; + + if (netif_msg_drv(pdata)) + DPRINTK(" ,channel=%p\n", pdata->channel_head); + kfree(pdata->channel_head); + + pdata->channel_head = NULL; +} + +static int fxgmac_alloc_channels(struct fxgmac_pdata *pdata) +{ + struct fxgmac_channel *channel_head, *channel; + struct fxgmac_ring *tx_ring, *rx_ring; + int ret = -ENOMEM; + unsigned int i; + +#ifdef CONFIG_PCI_MSI + u32 msix = FXGMAC_GET_REG_BITS(pdata->expansion.int_flags, + FXGMAC_FLAG_MSIX_POS, + FXGMAC_FLAG_MSIX_LEN); +#endif + + channel_head = kcalloc(pdata->channel_count, + sizeof(struct fxgmac_channel), GFP_KERNEL); + if (netif_msg_drv(pdata)) + DPRINTK("alloc_channels,channel_head=%p,size=%d*%d\n", + channel_head, pdata->channel_count, + (u32)sizeof(struct fxgmac_channel)); + + if (!channel_head) + return ret; + + tx_ring = kcalloc(pdata->tx_ring_count, sizeof(struct fxgmac_ring), + GFP_KERNEL); + if (!tx_ring) + goto err_tx_ring; + + if (netif_msg_drv(pdata)) + DPRINTK("alloc_channels,tx_ring=%p,size=%d*%d\n", tx_ring, + pdata->tx_ring_count, (u32)sizeof(struct fxgmac_ring)); + rx_ring = kcalloc(pdata->rx_ring_count, sizeof(struct fxgmac_ring), + GFP_KERNEL); + if (!rx_ring) + goto err_rx_ring; + + if (netif_msg_drv(pdata)) + DPRINTK("alloc_channels,rx_ring=%p,size=%d*%d\n", rx_ring, + pdata->rx_ring_count, (u32)sizeof(struct fxgmac_ring)); + + for (i = 0, channel = channel_head; i < pdata->channel_count; + i++, channel++) { + snprintf(channel->name, sizeof(channel->name), "channel-%u", i); + channel->pdata = pdata; + channel->queue_index = i; + channel->dma_regs = + pdata->mac_regs + DMA_CH_BASE + (DMA_CH_INC * i); + + if (pdata->per_channel_irq) { + /* Get the per DMA interrupt */ +#ifdef CONFIG_PCI_MSI + + if (msix) { + pdata->channel_irq[i] = + pdata->expansion.msix_entries[i].vector; + if (FXGMAC_IS_CHANNEL_WITH_TX_IRQ(i)) { + pdata->channel_irq + [FXGMAC_MAX_DMA_CHANNELS] = + pdata->expansion + .msix_entries + [FXGMAC_MAX_DMA_CHANNELS] + .vector; + + if (pdata->channel_irq + [FXGMAC_MAX_DMA_CHANNELS] < + 0) { + netdev_err( + pdata->netdev, + "get_irq %u for tx failed\n", + i + 1); + goto err_irq; + } + + channel->expansion.dma_irq_tx = + pdata->channel_irq + [FXGMAC_MAX_DMA_CHANNELS]; + DPRINTK("fxgmac_alloc_channels, for MSIx, channel %d dma_irq_tx=%u\n", + i, + channel->expansion.dma_irq_tx); + } + } +#endif + ret = pdata->channel_irq[i]; + if (ret < 0) { + netdev_err(pdata->netdev, "get_irq %u failed\n", + i + 1); + goto err_irq; + } + channel->dma_irq = ret; + DPRINTK("fxgmac_alloc_channels, for MSIx, channel %d dma_irq=%u\n", + i, channel->dma_irq); + } + + if (i < pdata->tx_ring_count) + channel->tx_ring = tx_ring++; + + if (i < pdata->rx_ring_count) + channel->rx_ring = rx_ring++; + } + + pdata->channel_head = channel_head; + + if (netif_msg_drv(pdata)) + DPRINTK("alloc_channels callout ok\n"); + return 0; + +err_irq: + kfree(rx_ring); + +err_rx_ring: + kfree(tx_ring); + +err_tx_ring: + kfree(channel_head); + + DPRINTK("fxgmac alloc_channels callout err,%d\n", ret); + return ret; +} + +static void fxgmac_free_channels_and_rings(struct fxgmac_pdata *pdata) +{ + fxgmac_free_rings(pdata); + + fxgmac_free_channels(pdata); +} + +static int fxgmac_alloc_channels_and_rings(struct fxgmac_pdata *pdata) +{ + int ret; + + ret = fxgmac_alloc_channels(pdata); + if (ret) + goto err_alloc; + + ret = fxgmac_alloc_rings(pdata); + if (ret) + goto err_alloc; + + return 0; + +err_alloc: + fxgmac_free_channels_and_rings(pdata); + + return ret; +} + +#if !(defined(UEFI) || defined(UBOOT) || defined(PXE)) +static void fxgmac_set_buffer_data(struct fxgmac_buffer_data *bd, + struct fxgmac_page_alloc *pa, + unsigned int len) +{ + get_page(pa->pages); + bd->pa = *pa; + + bd->dma_base = pa->pages_dma; + bd->dma_off = pa->pages_offset; + bd->dma_len = len; + + pa->pages_offset += len; + if ((pa->pages_offset + len) > pa->pages_len) { + /* This data descriptor is responsible for unmapping page(s) */ + bd->pa_unmap = *pa; + + /* Get a new allocation next time */ + pa->pages = NULL; + pa->pages_len = 0; + pa->pages_offset = 0; + pa->pages_dma = 0; + } +} +#endif + +static int fxgmac_alloc_pages(struct fxgmac_pdata *pdata, + struct fxgmac_page_alloc *pa, gfp_t gfp, + int order) +{ + struct page *pages = NULL; + dma_addr_t pages_dma; + + /* Try to obtain pages, decreasing order if necessary */ + gfp |= __GFP_COMP | __GFP_NOWARN; + while (order >= 0) { + pages = alloc_pages(gfp, order); + if (pages) + break; + + order--; + } + if (!pages) + return -ENOMEM; + + /* Map the pages */ + pages_dma = dma_map_page(pdata->dev, pages, 0, PAGE_SIZE << order, + DMA_FROM_DEVICE); + if (dma_mapping_error(pdata->dev, pages_dma)) { + put_page(pages); + return -ENOMEM; + } + + pa->pages = pages; + pa->pages_len = PAGE_SIZE << order; + pa->pages_offset = 0; + pa->pages_dma = pages_dma; + + return 0; +} + +static int fxgmac_map_rx_buffer(struct fxgmac_pdata *pdata, + struct fxgmac_ring *ring, + struct fxgmac_desc_data *desc_data) +{ + int ret; + + if (!ring->rx_hdr_pa.pages) { + ret = fxgmac_alloc_pages(pdata, &ring->rx_hdr_pa, GFP_ATOMIC, + 0); + if (ret) + return ret; + } + + /* Set up the header page info */ + fxgmac_set_buffer_data(&desc_data->rx.hdr, &ring->rx_hdr_pa, + pdata->rx_buf_size); + return 0; +} + +static void fxgmac_tx_desc_reset(struct fxgmac_desc_data *desc_data) +{ + struct fxgmac_dma_desc *dma_desc = desc_data->dma_desc; + + /* Reset the Tx descriptor + * Set buffer 1 (lo) address to zero + * Set buffer 1 (hi) address to zero + * Reset all other control bits (IC, TTSE, B2L & B1L) + * Reset all other control bits (OWN, CTXT, FD, LD, CPC, CIC, etc) + */ + dma_desc->desc0 = 0; + dma_desc->desc1 = 0; + dma_desc->desc2 = 0; + dma_desc->desc3 = 0; + + /* Make sure ownership is written to the descriptor */ + dma_wmb(); +} + +static void fxgmac_tx_desc_init_channel(struct fxgmac_channel *channel) +{ +#ifndef KDNET + struct fxgmac_ring *ring = channel->tx_ring; + struct fxgmac_desc_data *desc_data; + int start_index = ring->cur; + unsigned int i; + start_index = start_index; + /* Initialize all descriptors */ + for (i = 0; i < ring->dma_desc_count; i++) { + desc_data = FXGMAC_GET_DESC_DATA(ring, i); + + /* Initialize Tx descriptor */ + fxgmac_tx_desc_reset(desc_data); + } +#endif + +#ifndef PXE + writereg(channel->pdata->pAdapter, channel->pdata->tx_desc_count - 1, + FXGMAC_DMA_REG(channel, DMA_CH_TDRLR)); +#endif + + /* Update the starting address of descriptor ring */ + desc_data = FXGMAC_GET_DESC_DATA(ring, start_index); + writereg(channel->pdata->pAdapter, + upper_32_bits(desc_data->dma_desc_addr), + FXGMAC_DMA_REG(channel, DMA_CH_TDLR_HI)); + writereg(channel->pdata->pAdapter, + lower_32_bits(desc_data->dma_desc_addr), + FXGMAC_DMA_REG(channel, DMA_CH_TDLR_LO)); +} + +static void fxgmac_tx_desc_init(struct fxgmac_pdata *pdata) +{ + struct fxgmac_desc_data *desc_data; + struct fxgmac_dma_desc *dma_desc; + struct fxgmac_channel *channel; + struct fxgmac_ring *ring; + dma_addr_t dma_desc_addr; + unsigned int i, j; + + channel = pdata->channel_head; + for (i = 0; i < pdata->channel_count; i++, channel++) { + ring = channel->tx_ring; + if (!ring) + break; + + /* reset the tx timer status. 20220104 */ + channel->tx_timer_active = 0; + + dma_desc = ring->dma_desc_head; + dma_desc_addr = ring->dma_desc_head_addr; + + for (j = 0; j < ring->dma_desc_count; j++) { + desc_data = FXGMAC_GET_DESC_DATA(ring, j); + + desc_data->dma_desc = dma_desc; + desc_data->dma_desc_addr = dma_desc_addr; + + dma_desc++; + dma_desc_addr += sizeof(struct fxgmac_dma_desc); + } + + ring->cur = 0; + ring->dirty = 0; + memset(&ring->tx, 0, sizeof(ring->tx)); + + fxgmac_tx_desc_init_channel(channel); + } +} + +static void fxgmac_rx_desc_reset(struct fxgmac_pdata *pdata, + struct fxgmac_desc_data *desc_data, + unsigned int index) +{ + struct fxgmac_dma_desc *dma_desc = desc_data->dma_desc; + dma_addr_t hdr_dma; + + /* Reset the Rx descriptor + * Set buffer 1 (lo) address to header dma address (lo) + * Set buffer 1 (hi) address to header dma address (hi) + * Set buffer 2 (lo) address to buffer dma address (lo) + * Set buffer 2 (hi) address to buffer dma address (hi) and + * set control bits OWN and INTE + */ + hdr_dma = desc_data->rx.hdr.dma_base + desc_data->rx.hdr.dma_off; + + dma_desc->desc0 = cpu_to_le32(lower_32_bits(hdr_dma)); + dma_desc->desc1 = cpu_to_le32(upper_32_bits(hdr_dma)); + dma_desc->desc2 = 0; + dma_desc->desc3 = 0; + dma_desc->desc3 = FXGMAC_SET_REG_BITS_LE(dma_desc->desc3, + RX_NORMAL_DESC3_INTE_POS, + RX_NORMAL_DESC3_INTE_LEN, 1); + dma_desc->desc3 = FXGMAC_SET_REG_BITS_LE(dma_desc->desc3, + RX_NORMAL_DESC3_BUF2V_POS, + RX_NORMAL_DESC3_BUF2V_LEN, 0); + dma_desc->desc3 = FXGMAC_SET_REG_BITS_LE(dma_desc->desc3, + RX_NORMAL_DESC3_BUF1V_POS, + RX_NORMAL_DESC3_BUF1V_LEN, 1); + + /* Since the Rx DMA engine is likely running, make sure everything + * is written to the descriptor(s) before setting the OWN bit + * for the descriptor + */ + dma_wmb(); + + dma_desc->desc3 = FXGMAC_SET_REG_BITS_LE(dma_desc->desc3, + RX_NORMAL_DESC3_OWN_POS, + RX_NORMAL_DESC3_OWN_LEN, 1); + + /* Make sure ownership is written to the descriptor */ + dma_wmb(); +} + +static void fxgmac_rx_desc_init_channel(struct fxgmac_channel *channel) +{ + struct fxgmac_pdata *pdata = channel->pdata; + struct fxgmac_ring *ring = channel->rx_ring; + unsigned int start_index = ring->cur; + struct fxgmac_desc_data *desc_data; + unsigned int i; +#if defined(UEFI) + UINT64 HwRbdPa; +#elif defined(KDNET) + PHYSICAL_ADDRESS HwRbdPa; +#elif defined(_WIN64) || defined(_WIN32) + unsigned int Qid; + NDIS_PHYSICAL_ADDRESS HwRbdPa; +#elif defined(PXE) +#endif + + /* Initialize all descriptors */ + for (i = 0; i < ring->dma_desc_count; i++) { + desc_data = FXGMAC_GET_DESC_DATA(ring, i); + + /* Initialize Rx descriptor */ + fxgmac_rx_desc_reset(pdata, desc_data, i); + } + + /* Update the total number of Rx descriptors */ + writereg(pdata->pAdapter, ring->dma_desc_count - 1, + FXGMAC_DMA_REG(channel, DMA_CH_RDRLR)); + + /* Update the starting address of descriptor ring */ + desc_data = FXGMAC_GET_DESC_DATA(ring, start_index); + writereg(pdata->pAdapter, upper_32_bits(desc_data->dma_desc_addr), + FXGMAC_DMA_REG(channel, DMA_CH_RDLR_HI)); + writereg(pdata->pAdapter, lower_32_bits(desc_data->dma_desc_addr), + FXGMAC_DMA_REG(channel, DMA_CH_RDLR_LO)); + + /* Update the Rx Descriptor Tail Pointer */ + desc_data = FXGMAC_GET_DESC_DATA( + ring, start_index + ring->dma_desc_count - 1); + writereg(pdata->pAdapter, lower_32_bits(desc_data->dma_desc_addr), + FXGMAC_DMA_REG(channel, DMA_CH_RDTR_LO)); +} + +static void fxgmac_rx_desc_init(struct fxgmac_pdata *pdata) +{ + struct fxgmac_desc_data *desc_data; + struct fxgmac_dma_desc *dma_desc; + struct fxgmac_channel *channel; + struct fxgmac_ring *ring; + dma_addr_t dma_desc_addr; + unsigned int i, j; + + channel = pdata->channel_head; + for (i = 0; i < pdata->channel_count; i++, channel++) { + ring = channel->rx_ring; + if (!ring) + break; + + dma_desc = ring->dma_desc_head; + dma_desc_addr = ring->dma_desc_head_addr; + + for (j = 0; j < ring->dma_desc_count; j++) { + desc_data = FXGMAC_GET_DESC_DATA(ring, j); + + desc_data->dma_desc = dma_desc; + desc_data->dma_desc_addr = dma_desc_addr; + + if (fxgmac_map_rx_buffer(pdata, ring, desc_data)) + break; + + dma_desc++; + dma_desc_addr += sizeof(struct fxgmac_dma_desc); + } + + ring->cur = 0; + ring->dirty = 0; + + fxgmac_rx_desc_init_channel(channel); + } +} + +static int fxgmac_map_tx_skb(struct fxgmac_channel *channel, + struct sk_buff *skb) +{ + struct fxgmac_pdata *pdata = channel->pdata; + struct fxgmac_ring *ring = channel->tx_ring; + unsigned int start_index, cur_index; + struct fxgmac_desc_data *desc_data; + unsigned int offset, datalen, len; + struct fxgmac_pkt_info *pkt_info; + skb_frag_t *frag; + unsigned int tso, vlan; + dma_addr_t skb_dma; + unsigned int i; + + offset = 0; + start_index = ring->cur; + cur_index = ring->cur; + + pkt_info = &ring->pkt_info; + pkt_info->desc_count = 0; + pkt_info->length = 0; + + tso = FXGMAC_GET_REG_BITS(pkt_info->attributes, + TX_PACKET_ATTRIBUTES_TSO_ENABLE_POS, + TX_PACKET_ATTRIBUTES_TSO_ENABLE_LEN); + vlan = FXGMAC_GET_REG_BITS(pkt_info->attributes, + TX_PACKET_ATTRIBUTES_VLAN_CTAG_POS, + TX_PACKET_ATTRIBUTES_VLAN_CTAG_LEN); + + /* Save space for a context descriptor if needed */ + if ((tso && (pkt_info->mss != ring->tx.cur_mss)) || + (vlan && (pkt_info->vlan_ctag != ring->tx.cur_vlan_ctag))) { + cur_index = FXGMAC_GET_ENTRY(cur_index, ring->dma_desc_count); + } + desc_data = FXGMAC_GET_DESC_DATA(ring, cur_index); + + if (tso) { + /* Map the TSO header */ + skb_dma = dma_map_single(pdata->dev, skb->data, + pkt_info->header_len, DMA_TO_DEVICE); + if (dma_mapping_error(pdata->dev, skb_dma)) { + netdev_alert(pdata->netdev, "dma_map_single failed\n"); + goto err_out; + } + desc_data->skb_dma = skb_dma; + desc_data->skb_dma_len = pkt_info->header_len; + netif_dbg(pdata, tx_queued, pdata->netdev, + "skb header: index=%u, dma=%pad, len=%u\n", cur_index, + &skb_dma, pkt_info->header_len); + + offset = pkt_info->header_len; + + pkt_info->length += pkt_info->header_len; + + cur_index = FXGMAC_GET_ENTRY(cur_index, ring->dma_desc_count); + desc_data = FXGMAC_GET_DESC_DATA(ring, cur_index); + } + + /* Map the (remainder of the) packet */ + for (datalen = skb_headlen(skb) - offset; datalen;) { + len = min_t(unsigned int, datalen, FXGMAC_TX_MAX_BUF_SIZE); + + skb_dma = dma_map_single(pdata->dev, skb->data + offset, len, + DMA_TO_DEVICE); + if (dma_mapping_error(pdata->dev, skb_dma)) { + netdev_alert(pdata->netdev, "dma_map_single failed\n"); + goto err_out; + } + desc_data->skb_dma = skb_dma; + desc_data->skb_dma_len = len; + netif_dbg(pdata, tx_queued, pdata->netdev, + "skb data: index=%u, dma=%pad, len=%u\n", cur_index, + &skb_dma, len); + + datalen -= len; + offset += len; + + pkt_info->length += len; + + cur_index = FXGMAC_GET_ENTRY(cur_index, ring->dma_desc_count); + desc_data = FXGMAC_GET_DESC_DATA(ring, cur_index); + } + + for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) { + netif_dbg(pdata, tx_queued, pdata->netdev, "mapping frag %u\n", + i); + + offset = 0; + + for (datalen = skb_frag_size(frag); datalen;) { + len = min_t(unsigned int, datalen, + FXGMAC_TX_MAX_BUF_SIZE); + + skb_dma = skb_frag_dma_map(pdata->dev, frag, offset, + len, DMA_TO_DEVICE); + if (dma_mapping_error(pdata->dev, skb_dma)) { + netdev_alert(pdata->netdev, + "skb_frag_dma_map failed\n"); + goto err_out; + } + desc_data->skb_dma = skb_dma; + desc_data->skb_dma_len = len; + desc_data->mapped_as_page = 1; + netif_dbg(pdata, tx_queued, pdata->netdev, + "skb frag: index=%u, dma=%pad, len=%u\n", + cur_index, &skb_dma, len); + + datalen -= len; + offset += len; + + pkt_info->length += len; + + cur_index = FXGMAC_GET_ENTRY(cur_index, + ring->dma_desc_count); + desc_data = FXGMAC_GET_DESC_DATA(ring, cur_index); + } + } + + /* Save the skb address in the last entry. We always have some data + * that has been mapped so desc_data is always advanced past the last + * piece of mapped data - use the entry pointed to by cur_index - 1. + */ + desc_data = FXGMAC_GET_DESC_DATA( + ring, (cur_index - 1) & (ring->dma_desc_count - 1)); + desc_data->skb = skb; + + /* Save the number of descriptor entries used */ + if (start_index <= cur_index) + pkt_info->desc_count = cur_index - start_index; + else + pkt_info->desc_count = + ring->dma_desc_count - start_index + cur_index; + + return pkt_info->desc_count; + +err_out: + while (start_index < cur_index) { + desc_data = FXGMAC_GET_DESC_DATA(ring, start_index); + start_index = + FXGMAC_GET_ENTRY(start_index, ring->dma_desc_count); + fxgmac_unmap_desc_data(pdata, desc_data); + } + + return 0; +} + +void fxgmac_init_desc_ops(struct fxgmac_desc_ops *desc_ops) +{ +#ifdef UBOOT + desc_ops->alloc_channels_and_rings = fxgmac_alloc_channels; +#else + desc_ops->alloc_channels_and_rings = fxgmac_alloc_channels_and_rings; +#endif + desc_ops->free_channels_and_rings = fxgmac_free_channels_and_rings; + desc_ops->map_tx_skb = fxgmac_map_tx_skb; + desc_ops->map_rx_buffer = fxgmac_map_rx_buffer; + desc_ops->unmap_desc_data = fxgmac_unmap_desc_data; + desc_ops->tx_desc_init = fxgmac_tx_desc_init; + desc_ops->rx_desc_init = fxgmac_rx_desc_init; +#ifndef UBOOT + desc_ops->tx_desc_init_channel = fxgmac_tx_desc_init_channel; + desc_ops->rx_desc_init_channel = fxgmac_rx_desc_init_channel; + desc_ops->tx_desc_reset = fxgmac_tx_desc_reset; + desc_ops->rx_desc_reset = fxgmac_rx_desc_reset; +#endif +} diff --git a/drivers/net/ethernet/motorcomm/yt6801/fuxi-gmac-ethtool.c b/drivers/net/ethernet/motorcomm/yt6801/fuxi-gmac-ethtool.c new file mode 100644 index 000000000000..922bc91bfbbf --- /dev/null +++ b/drivers/net/ethernet/motorcomm/yt6801/fuxi-gmac-ethtool.c @@ -0,0 +1,1271 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (c) 2021 Motorcomm Corporation. */ + +#include "fuxi-gmac.h" +#include "fuxi-gmac-reg.h" + +struct fxgmac_stats_desc { + char stat_string[ETH_GSTRING_LEN]; + int stat_offset; +}; + +#define FXGMAC_STAT(str, var) \ + { \ + str, offsetof(struct fxgmac_pdata, stats.var), \ + } + +static const struct fxgmac_stats_desc fxgmac_gstring_stats[] = { + /* MMC TX counters */ + FXGMAC_STAT("tx_bytes", txoctetcount_gb), + FXGMAC_STAT("tx_bytes_good", txoctetcount_g), + FXGMAC_STAT("tx_packets", txframecount_gb), + FXGMAC_STAT("tx_packets_good", txframecount_g), + FXGMAC_STAT("tx_unicast_packets", txunicastframes_gb), + FXGMAC_STAT("tx_broadcast_packets", txbroadcastframes_gb), + FXGMAC_STAT("tx_broadcast_packets_good", txbroadcastframes_g), + FXGMAC_STAT("tx_multicast_packets", txmulticastframes_gb), + FXGMAC_STAT("tx_multicast_packets_good", txmulticastframes_g), + FXGMAC_STAT("tx_vlan_packets_good", txvlanframes_g), + FXGMAC_STAT("tx_64_byte_packets", tx64octets_gb), + FXGMAC_STAT("tx_65_to_127_byte_packets", tx65to127octets_gb), + FXGMAC_STAT("tx_128_to_255_byte_packets", tx128to255octets_gb), + FXGMAC_STAT("tx_256_to_511_byte_packets", tx256to511octets_gb), + FXGMAC_STAT("tx_512_to_1023_byte_packets", tx512to1023octets_gb), + FXGMAC_STAT("tx_1024_to_max_byte_packets", tx1024tomaxoctets_gb), + FXGMAC_STAT("tx_underflow_errors", txunderflowerror), + FXGMAC_STAT("tx_pause_frames", txpauseframes), + FXGMAC_STAT("tx_single_collision", txsinglecollision_g), + FXGMAC_STAT("tx_multiple_collision", txmultiplecollision_g), + FXGMAC_STAT("tx_deferred_frames", txdeferredframes), + FXGMAC_STAT("tx_late_collision_frames", txlatecollisionframes), + FXGMAC_STAT("tx_excessive_collision_frames", + txexcessivecollisionframes), + FXGMAC_STAT("tx_carrier_error_frames", txcarriererrorframes), + FXGMAC_STAT("tx_excessive_deferral_error", txexcessivedeferralerror), + FXGMAC_STAT("tx_oversize_frames_good", txoversize_g), + + /* MMC RX counters */ + FXGMAC_STAT("rx_bytes", rxoctetcount_gb), + FXGMAC_STAT("rx_bytes_good", rxoctetcount_g), + FXGMAC_STAT("rx_packets", rxframecount_gb), + FXGMAC_STAT("rx_unicast_packets_good", rxunicastframes_g), + FXGMAC_STAT("rx_broadcast_packets_good", rxbroadcastframes_g), + FXGMAC_STAT("rx_multicast_packets_good", rxmulticastframes_g), + FXGMAC_STAT("rx_vlan_packets_mac", rxvlanframes_gb), + FXGMAC_STAT("rx_64_byte_packets", rx64octets_gb), + FXGMAC_STAT("rx_65_to_127_byte_packets", rx65to127octets_gb), + FXGMAC_STAT("rx_128_to_255_byte_packets", rx128to255octets_gb), + FXGMAC_STAT("rx_256_to_511_byte_packets", rx256to511octets_gb), + FXGMAC_STAT("rx_512_to_1023_byte_packets", rx512to1023octets_gb), + FXGMAC_STAT("rx_1024_to_max_byte_packets", rx1024tomaxoctets_gb), + FXGMAC_STAT("rx_undersize_packets_good", rxundersize_g), + FXGMAC_STAT("rx_oversize_packets_good", rxoversize_g), + FXGMAC_STAT("rx_crc_errors", rxcrcerror), + FXGMAC_STAT("rx_align_error", rxalignerror), + FXGMAC_STAT("rx_crc_errors_small_packets", rxrunterror), + FXGMAC_STAT("rx_crc_errors_giant_packets", rxjabbererror), + FXGMAC_STAT("rx_length_errors", rxlengtherror), + FXGMAC_STAT("rx_out_of_range_errors", rxoutofrangetype), + FXGMAC_STAT("rx_fifo_overflow_errors", rxfifooverflow), + FXGMAC_STAT("rx_watchdog_errors", rxwatchdogerror), + FXGMAC_STAT("rx_pause_frames", rxpauseframes), + FXGMAC_STAT("rx_receive_error_frames", rxreceiveerrorframe), + FXGMAC_STAT("rx_control_frames_good", rxcontrolframe_g), + + /* Extra counters */ + FXGMAC_STAT("tx_tso_packets", tx_tso_packets), + FXGMAC_STAT("rx_split_header_packets", rx_split_header_packets), + FXGMAC_STAT("tx_process_stopped", tx_process_stopped), + FXGMAC_STAT("rx_process_stopped", rx_process_stopped), + FXGMAC_STAT("tx_buffer_unavailable", tx_buffer_unavailable), + FXGMAC_STAT("rx_buffer_unavailable", rx_buffer_unavailable), + FXGMAC_STAT("fatal_bus_error", fatal_bus_error), + FXGMAC_STAT("tx_vlan_packets_net", tx_vlan_packets), + FXGMAC_STAT("rx_vlan_packets_net", rx_vlan_packets), + FXGMAC_STAT("napi_poll_isr", napi_poll_isr), + FXGMAC_STAT("napi_poll_txtimer", napi_poll_txtimer), + FXGMAC_STAT("alive_cnt_txtimer", cnt_alive_txtimer), + + FXGMAC_STAT("ephy_poll_timer", ephy_poll_timer_cnt), + FXGMAC_STAT("mgmt_int_isr", mgmt_int_isr), +}; + +#define FXGMAC_STATS_COUNT ARRAY_SIZE(fxgmac_gstring_stats) + +static void fxgmac_ethtool_get_drvinfo(struct net_device *netdev, + struct ethtool_drvinfo *drvinfo) +{ + struct fxgmac_pdata *pdata = netdev_priv(netdev); + u32 ver = pdata->hw_feat.version; + u32 sver, devid, userver; + + strscpy(drvinfo->driver, pdata->drv_name, sizeof(drvinfo->driver)); + strscpy(drvinfo->version, pdata->drv_ver, sizeof(drvinfo->version)); + strscpy(drvinfo->bus_info, dev_name(pdata->dev), + sizeof(drvinfo->bus_info)); + /* + * D|DEVID: Indicates the Device family + * U|USERVER: User-defined Version + */ + sver = FXGMAC_GET_REG_BITS(ver, MAC_VR_SVER_POS, MAC_VR_SVER_LEN); + devid = FXGMAC_GET_REG_BITS(ver, MAC_VR_DEVID_POS, MAC_VR_DEVID_LEN); + userver = FXGMAC_GET_REG_BITS(ver, MAC_VR_USERVER_POS, + MAC_VR_USERVER_LEN); + snprintf(drvinfo->fw_version, sizeof(drvinfo->fw_version), + "S.D.U: %x.%x.%x", sver, devid, userver); +} + +static u32 fxgmac_ethtool_get_msglevel(struct net_device *netdev) +{ + struct fxgmac_pdata *pdata = netdev_priv(netdev); + + return pdata->msg_enable; +} + +static void fxgmac_ethtool_set_msglevel(struct net_device *netdev, u32 msglevel) +{ + struct fxgmac_pdata *pdata = netdev_priv(netdev); + + DPRINTK("fxmac, set msglvl from %08x to %08x\n", pdata->msg_enable, + msglevel); + pdata->msg_enable = msglevel; +} + +static void fxgmac_ethtool_get_channels(struct net_device *netdev, + struct ethtool_channels *channel) +{ + struct fxgmac_pdata *pdata = netdev_priv(netdev); +#if (FXGMAC_RSS_FEATURE_ENABLED) + /* report maximum channels */ + channel->max_combined = FXGMAC_MAX_DMA_CHANNELS; + channel->max_other = 0; + channel->other_count = 0; + + /* record RSS queues */ + channel->combined_count = FXGMAC_MAX_DMA_CHANNELS; + + /* nothing else to report if RSS is disabled */ + if (channel->combined_count == 1) + return; + DPRINTK("fxmac rss, get channels max=(combined %d,other %d),count(combined %d,other %d)\n", + channel->max_combined, channel->max_other, + channel->combined_count, channel->other_count); +#endif + + channel->max_rx = FXGMAC_MAX_DMA_CHANNELS; + channel->max_tx = FXGMAC_MAX_DMA_CHANNELS; + channel->rx_count = pdata->rx_q_count; + channel->tx_count = pdata->tx_q_count; + DPRINTK("fxmac, get channels max=(rx %d,tx %d),count(%d,%d)\n", + channel->max_rx, channel->max_tx, channel->rx_count, + channel->tx_count); +} + +static int +fxgmac_ethtool_get_coalesce(struct net_device *netdev, + struct ethtool_coalesce *ec, + struct kernel_ethtool_coalesce *kernel_coal, + struct netlink_ext_ack *extack) +{ + struct fxgmac_pdata *pdata = netdev_priv(netdev); + + memset(ec, 0, sizeof(struct ethtool_coalesce)); + ec->rx_coalesce_usecs = pdata->rx_usecs; + ec->tx_coalesce_usecs = pdata->tx_usecs; + /*If we need to assign values to other members, + * we need to modify the supported_coalesce_params of fxgmac_ethtool_ops synchronously + */ + + DPRINTK("fxmac, get coalesce\n"); + return 0; +} + +static int +fxgmac_ethtool_set_coalesce(struct net_device *netdev, + struct ethtool_coalesce *ec, + struct kernel_ethtool_coalesce *kernel_coal, + struct netlink_ext_ack *extack) +{ + struct fxgmac_pdata *pdata = netdev_priv(netdev); + struct fxgmac_hw_ops *hw_ops = &pdata->hw_ops; + unsigned int rx_frames, rx_riwt, rx_usecs; + unsigned int tx_frames; + + /* Check for not supported parameters */ + if ((ec->rx_coalesce_usecs_irq) || (ec->rx_max_coalesced_frames_irq) || + (ec->tx_coalesce_usecs_high) || (ec->tx_max_coalesced_frames_irq) || + (ec->tx_coalesce_usecs_irq) || (ec->stats_block_coalesce_usecs) || + (ec->pkt_rate_low) || (ec->use_adaptive_rx_coalesce) || + (ec->use_adaptive_tx_coalesce) || + (ec->rx_max_coalesced_frames_low) || (ec->rx_coalesce_usecs_low) || + (ec->tx_coalesce_usecs_low) || (ec->tx_max_coalesced_frames_low) || + (ec->pkt_rate_high) || (ec->rx_coalesce_usecs_high) || + (ec->rx_max_coalesced_frames_high) || + (ec->tx_max_coalesced_frames_high) || (ec->rate_sample_interval)) + return -EOPNOTSUPP; + + rx_usecs = ec->rx_coalesce_usecs; + rx_riwt = hw_ops->usec_to_riwt(pdata, rx_usecs); + rx_frames = ec->rx_max_coalesced_frames; + tx_frames = ec->tx_max_coalesced_frames; + + if ((rx_riwt > FXGMAC_MAX_DMA_RIWT) || + (rx_riwt < FXGMAC_MIN_DMA_RIWT) || + (rx_frames > pdata->rx_desc_count)) + return -EINVAL; + + if (tx_frames > pdata->tx_desc_count) + return -EINVAL; + + pdata->rx_riwt = rx_riwt; + pdata->rx_usecs = rx_usecs; + pdata->rx_frames = rx_frames; + hw_ops->config_rx_coalesce(pdata); + + pdata->tx_frames = tx_frames; + hw_ops->config_tx_coalesce(pdata); + + pdata->tx_usecs = ec->tx_coalesce_usecs; + hw_ops->set_interrupt_moderation(pdata); + + DPRINTK("fxmac, set coalesce\n"); + return 0; +} + +#if (FXGMAC_RSS_FEATURE_ENABLED) +static u32 fxgmac_get_rxfh_key_size(struct net_device *netdev) +{ + return FXGMAC_RSS_HASH_KEY_SIZE; +} + +static u32 fxgmac_rss_indir_size(struct net_device *netdev) +{ + return FXGMAC_RSS_MAX_TABLE_SIZE; +} + +static void fxgmac_get_reta(struct fxgmac_pdata *pdata, u32 *indir) +{ + int i, reta_size = FXGMAC_RSS_MAX_TABLE_SIZE; + u16 rss_m; +#ifdef FXGMAC_ONE_CHANNEL + rss_m = FXGMAC_MAX_DMA_CHANNELS; +#else + rss_m = FXGMAC_MAX_DMA_CHANNELS - 1; +#endif + + for (i = 0; i < reta_size; i++) + indir[i] = pdata->rss_table[i] & rss_m; +} + +static int fxgmac_get_rxfh(struct net_device *netdev, u32 *indir, u8 *key, + u8 *hfunc) +{ + struct fxgmac_pdata *pdata = netdev_priv(netdev); + + if (hfunc) { + *hfunc = ETH_RSS_HASH_TOP; + DPRINTK("fxmac, get_rxfh for hash function\n"); + } + + if (indir) { + fxgmac_get_reta(pdata, indir); + DPRINTK("fxmac, get_rxfh for indirection tab\n"); + } + + if (key) { + memcpy(key, pdata->rss_key, fxgmac_get_rxfh_key_size(netdev)); + DPRINTK("fxmac, get_rxfh for hash key\n"); + } + + return 0; +} + +static int fxgmac_set_rxfh(struct net_device *netdev, const u32 *indir, + const u8 *key, const u8 hfunc) +{ + struct fxgmac_pdata *pdata = netdev_priv(netdev); + struct fxgmac_hw_ops *hw_ops = &pdata->hw_ops; + int i; + u32 reta_entries = fxgmac_rss_indir_size(netdev); + int max_queues = FXGMAC_MAX_DMA_CHANNELS; + + DPRINTK("fxmac, set_rxfh callin, indir=%lx, key=%lx, func=%02x\n", + (unsigned long)indir, (unsigned long)key, hfunc); + + if (hfunc) + return -EINVAL; + + /* Fill out the redirection table */ + if (indir) { +#if FXGMAC_MSIX_CH0RXDIS_ENABLED + max_queues = max_queues; + reta_entries = reta_entries; + i = i; + DPRINTK("fxmac, set_rxfh, change of indirect talbe is not supported.\n"); + return -EINVAL; +#else + /* double check user input. */ + for (i = 0; i < reta_entries; i++) + if (indir[i] >= max_queues) + return -EINVAL; + + for (i = 0; i < reta_entries; i++) + pdata->rss_table[i] = indir[i]; + + hw_ops->write_rss_lookup_table(pdata); +#endif + } + + /* Fill out the rss hash key */ + if (FXGMAC_RSS_HASH_KEY_LINUX && key) { + hw_ops->set_rss_hash_key(pdata, key); + } + + return 0; +} + +static int fxgmac_get_rss_hash_opts(struct fxgmac_pdata *pdata, + struct ethtool_rxnfc *cmd) +{ + u32 reg_opt; + struct fxgmac_hw_ops *hw_ops = &pdata->hw_ops; + cmd->data = 0; + + reg_opt = hw_ops->get_rss_options(pdata); + DPRINTK("fxgmac_get_rss_hash_opts, hw=%02x, %02x\n", reg_opt, + pdata->rss_options); + + if (reg_opt != pdata->rss_options) { + DPRINTK("fxgmac_get_rss_hash_opts, warning, options are not consistent\n"); + } + + /* Report default options for RSS */ + switch (cmd->flow_type) { + case TCP_V4_FLOW: + case UDP_V4_FLOW: + if (((TCP_V4_FLOW == (cmd->flow_type)) && + (FXGMAC_GET_REG_BITS(pdata->rss_options, + MAC_RSSCR_TCP4TE_POS, + MAC_RSSCR_TCP4TE_LEN))) || + ((UDP_V4_FLOW == (cmd->flow_type)) && + (FXGMAC_GET_REG_BITS(pdata->rss_options, + MAC_RSSCR_UDP4TE_POS, + MAC_RSSCR_UDP4TE_LEN)))) { + cmd->data |= RXH_L4_B_0_1 | RXH_L4_B_2_3; + } + fallthrough; + case SCTP_V4_FLOW: + case AH_ESP_V4_FLOW: + case AH_V4_FLOW: + case ESP_V4_FLOW: + case IPV4_FLOW: + if (((TCP_V4_FLOW == (cmd->flow_type)) && + (FXGMAC_GET_REG_BITS(pdata->rss_options, + MAC_RSSCR_TCP4TE_POS, + MAC_RSSCR_TCP4TE_LEN))) || + ((UDP_V4_FLOW == (cmd->flow_type)) && + (FXGMAC_GET_REG_BITS(pdata->rss_options, + MAC_RSSCR_UDP4TE_POS, + MAC_RSSCR_UDP4TE_LEN))) || + (FXGMAC_GET_REG_BITS(pdata->rss_options, + MAC_RSSCR_IP4TE_POS, + MAC_RSSCR_IP4TE_LEN))) { + cmd->data |= RXH_IP_SRC | RXH_IP_DST; + } + break; + case TCP_V6_FLOW: + case UDP_V6_FLOW: + if (((TCP_V6_FLOW == (cmd->flow_type)) && + (FXGMAC_GET_REG_BITS(pdata->rss_options, + MAC_RSSCR_TCP6TE_POS, + MAC_RSSCR_TCP6TE_LEN))) || + ((UDP_V6_FLOW == (cmd->flow_type)) && + (FXGMAC_GET_REG_BITS(pdata->rss_options, + MAC_RSSCR_UDP6TE_POS, + MAC_RSSCR_UDP6TE_LEN)))) { + cmd->data |= RXH_L4_B_0_1 | RXH_L4_B_2_3; + } + fallthrough; + case SCTP_V6_FLOW: + case AH_ESP_V6_FLOW: + case AH_V6_FLOW: + case ESP_V6_FLOW: + case IPV6_FLOW: + if (((TCP_V6_FLOW == (cmd->flow_type)) && + (FXGMAC_GET_REG_BITS(pdata->rss_options, + MAC_RSSCR_TCP6TE_POS, + MAC_RSSCR_TCP6TE_LEN))) || + ((UDP_V6_FLOW == (cmd->flow_type)) && + (FXGMAC_GET_REG_BITS(pdata->rss_options, + MAC_RSSCR_UDP6TE_POS, + MAC_RSSCR_UDP6TE_LEN))) || + (FXGMAC_GET_REG_BITS(pdata->rss_options, + MAC_RSSCR_IP6TE_POS, + MAC_RSSCR_IP6TE_LEN))) { + cmd->data |= RXH_IP_SRC | RXH_IP_DST; + } + break; + default: + return -EINVAL; + } + + return 0; +} + +static int fxgmac_get_rxnfc(struct net_device *dev, struct ethtool_rxnfc *cmd, + u32 *rule_locs) +{ + struct fxgmac_pdata *pdata = netdev_priv(dev); + int ret = -EOPNOTSUPP; + + switch (cmd->cmd) { + case ETHTOOL_GRXRINGS: + cmd->data = pdata->rx_q_count; + ret = 0; + DPRINTK("fxmac, get_rxnfc for rx ring cnt\n"); + break; + case ETHTOOL_GRXCLSRLCNT: + cmd->rule_cnt = 0; + ret = 0; + DPRINTK("fxmac, get_rxnfc for classify rule cnt\n"); + break; + case ETHTOOL_GRXCLSRULE: + DPRINTK("fxmac, get_rxnfc for classify rules\n"); + ret = 0; + break; + case ETHTOOL_GRXCLSRLALL: + cmd->rule_cnt = 0; + ret = 0; + DPRINTK("fxmac, get_rxnfc for classify both cnt and rules\n"); + break; + case ETHTOOL_GRXFH: + ret = fxgmac_get_rss_hash_opts(pdata, cmd); + DPRINTK("fxmac, get_rxnfc for hash options\n"); + break; + default: + break; + } + + return ret; +} + +static int fxgmac_set_rss_hash_opt(struct fxgmac_pdata *pdata, + struct ethtool_rxnfc *nfc) +{ + u32 rssopt = 0; + struct fxgmac_hw_ops *hw_ops = &pdata->hw_ops; + + DPRINTK("fxgmac_set_rss_hash_opt call in,nfc_data=%llx,cur opt=%x\n", + nfc->data, pdata->rss_options); + + /* + * For RSS, it does not support anything other than hashing + * to queues on src,dst IPs and L4 ports + */ + if (nfc->data & + ~(RXH_IP_SRC | RXH_IP_DST | RXH_L4_B_0_1 | RXH_L4_B_2_3)) + return -EINVAL; + + switch (nfc->flow_type) { + case TCP_V4_FLOW: + case TCP_V6_FLOW: + /* default to TCP flow and do nothting */ + if (!(nfc->data & RXH_IP_SRC) || !(nfc->data & RXH_IP_DST) || + !(nfc->data & RXH_L4_B_0_1) || !(nfc->data & RXH_L4_B_2_3)) + return -EINVAL; + if (TCP_V4_FLOW == (nfc->flow_type)) { + rssopt = FXGMAC_SET_REG_BITS(rssopt, + MAC_RSSCR_IP4TE_POS, + MAC_RSSCR_IP4TE_LEN, 1); + rssopt = FXGMAC_SET_REG_BITS(rssopt, + MAC_RSSCR_TCP4TE_POS, + MAC_RSSCR_TCP4TE_LEN, 1); + } + + if (TCP_V6_FLOW == (nfc->flow_type)) { + rssopt = FXGMAC_SET_REG_BITS(rssopt, + MAC_RSSCR_IP6TE_POS, + MAC_RSSCR_IP6TE_LEN, 1); + rssopt = FXGMAC_SET_REG_BITS(rssopt, + MAC_RSSCR_TCP6TE_POS, + MAC_RSSCR_TCP6TE_LEN, 1); + } + break; + + case UDP_V4_FLOW: + if (!(nfc->data & RXH_IP_SRC) || !(nfc->data & RXH_IP_DST)) + return -EINVAL; + rssopt = FXGMAC_SET_REG_BITS(rssopt, MAC_RSSCR_IP4TE_POS, + MAC_RSSCR_IP4TE_LEN, 1); + switch (nfc->data & (RXH_L4_B_0_1 | RXH_L4_B_2_3)) { + case 0: + break; + case (RXH_L4_B_0_1 | RXH_L4_B_2_3): + rssopt = FXGMAC_SET_REG_BITS(rssopt, + MAC_RSSCR_UDP4TE_POS, + MAC_RSSCR_UDP4TE_LEN, 1); + break; + default: + return -EINVAL; + } + break; + case UDP_V6_FLOW: + if (!(nfc->data & RXH_IP_SRC) || !(nfc->data & RXH_IP_DST)) + return -EINVAL; + rssopt = FXGMAC_SET_REG_BITS(rssopt, MAC_RSSCR_IP6TE_POS, + MAC_RSSCR_IP6TE_LEN, 1); + + switch (nfc->data & (RXH_L4_B_0_1 | RXH_L4_B_2_3)) { + case 0: + break; + case (RXH_L4_B_0_1 | RXH_L4_B_2_3): + rssopt = FXGMAC_SET_REG_BITS(rssopt, + MAC_RSSCR_UDP6TE_POS, + MAC_RSSCR_UDP6TE_LEN, 1); + break; + default: + return -EINVAL; + } + break; + case AH_ESP_V4_FLOW: + case AH_V4_FLOW: + case ESP_V4_FLOW: + case SCTP_V4_FLOW: + case AH_ESP_V6_FLOW: + case AH_V6_FLOW: + case ESP_V6_FLOW: + case SCTP_V6_FLOW: + if (!(nfc->data & RXH_IP_SRC) || !(nfc->data & RXH_IP_DST) || + (nfc->data & RXH_L4_B_0_1) || (nfc->data & RXH_L4_B_2_3)) + return -EINVAL; + break; + default: + return -EINVAL; + } + + /* if options are changed, then update to hw */ + if (rssopt != pdata->rss_options) { + if ((rssopt & UDP_RSS_FLAGS) && + !(pdata->rss_options & UDP_RSS_FLAGS)) + DPRINTK("enabling UDP RSS: fragmented packets" + " may arrive out of order to the stack above\n"); + + DPRINTK("rss option changed from %x to %x\n", + pdata->rss_options, rssopt); + pdata->rss_options = rssopt; + hw_ops->set_rss_options(pdata); + } + + return 0; +} + +static int fxgmac_set_rxnfc(struct net_device *dev, struct ethtool_rxnfc *cmd) +{ + struct fxgmac_pdata *pdata = netdev_priv(dev); + + int ret = -EOPNOTSUPP; + + switch (cmd->cmd) { + case ETHTOOL_SRXCLSRLINS: + DPRINTK("set_rxnfc for rx cls rule insert-n\\a\n"); + break; + case ETHTOOL_SRXCLSRLDEL: + DPRINTK("set_rxnfc for rx cls rule del-n\\a\n"); + break; + case ETHTOOL_SRXFH: + DPRINTK("set_rxnfc for rx rss option\n"); + ret = fxgmac_set_rss_hash_opt(pdata, cmd); + break; + default: + break; + } + + return ret; +} +#endif + +static void fxgmac_get_ringparam(struct net_device *netdev, + struct ethtool_ringparam *ring, + struct kernel_ethtool_ringparam *kernel_ring, + struct netlink_ext_ack *exact) + +{ + struct fxgmac_pdata *pdata = netdev_priv(netdev); + + DPRINTK("fxmac, get_ringparam callin\n"); + + ring->rx_max_pending = FXGMAC_RX_DESC_CNT; + ring->tx_max_pending = FXGMAC_TX_DESC_CNT; + ring->rx_mini_max_pending = 0; + ring->rx_jumbo_max_pending = 0; + ring->rx_pending = pdata->rx_desc_count; + ring->tx_pending = pdata->tx_desc_count; + ring->rx_mini_pending = 0; + ring->rx_jumbo_pending = 0; +} + +static int fxgmac_set_ringparam(struct net_device *netdev, + struct ethtool_ringparam *ring, + struct kernel_ethtool_ringparam *kernel_ring, + struct netlink_ext_ack *exact) +{ + struct fxgmac_pdata *pdata = netdev_priv(netdev); + struct fxgmac_desc_ops *desc_ops = &pdata->desc_ops; + + if (pdata->expansion.dev_state != FXGMAC_DEV_START) + return 0; + + fxgmac_lock(pdata); + DPRINTK("fxmac, set_ringparam callin\n"); + + pdata->tx_desc_count = ring->tx_pending; + pdata->rx_desc_count = ring->rx_pending; + + fxgmac_stop(pdata); + fxgmac_free_tx_data(pdata); + fxgmac_free_rx_data(pdata); + desc_ops->alloc_channels_and_rings(pdata); + fxgmac_start(pdata); + fxgmac_unlock(pdata); + + return 0; +} + +#if FXGMAC_WOL_FEATURE_ENABLED +static void fxgmac_get_wol(struct net_device *netdev, + struct ethtool_wolinfo *wol) +{ + struct fxgmac_pdata *pdata = netdev_priv(netdev); + + wol->supported = WAKE_UCAST | WAKE_MCAST | WAKE_BCAST | WAKE_MAGIC | + WAKE_ARP; +#if FXGMAC_WOL_UPON_EPHY_LINK + wol->supported |= WAKE_PHY; +#endif + + wol->wolopts = 0; + if (!(pdata->hw_feat.rwk) || + !device_can_wakeup(/*pci_dev_to_dev*/ (pdata->dev))) { + DPRINTK("fxgmac get_wol, pci does not support wakeup\n"); + return; + } + wol->wolopts = pdata->expansion.wol; +} + +static void fxgmac_set_pattern_data(struct fxgmac_pdata *pdata) +{ + struct fxgmac_hw_ops *hw_ops = &pdata->hw_ops; + u32 ip_addr, i = 0; + u8 type_offset, op_offset, tip_offset; + struct pattern_packet packet; + struct wol_bitmap_pattern pattern[4]; + + memset(pattern, 0, sizeof(struct wol_bitmap_pattern) * 4); + + if (pdata->expansion.wol & WAKE_UCAST) { + pattern[i].mask_info[0] = 0x3F; + pattern[i].mask_size = sizeof(pattern[0].mask_info); + memcpy(pattern[i].pattern_info, pdata->mac_addr, ETH_ALEN); + pattern[i].pattern_offset = 0; + i++; + } + + if (pdata->expansion.wol & WAKE_BCAST) { + pattern[i].mask_info[0] = 0x3F; + pattern[i].mask_size = sizeof(pattern[0].mask_info); + memset(pattern[i].pattern_info, 0xFF, ETH_ALEN); + pattern[i].pattern_offset = 0; + i++; + } + + if (pdata->expansion.wol & WAKE_MCAST) { + pattern[i].mask_info[0] = 0x7; + pattern[i].mask_size = sizeof(pattern[0].mask_info); + pattern[i].pattern_info[0] = 0x1; + pattern[i].pattern_info[1] = 0x0; + pattern[i].pattern_info[2] = 0x5E; + pattern[i].pattern_offset = 0; + i++; + } + + if (pdata->expansion.wol & WAKE_ARP) { + memset(pattern[i].mask_info, 0, sizeof(pattern[0].mask_info)); + type_offset = offsetof(struct pattern_packet, ar_pro); + pattern[i].mask_info[type_offset / 8] |= 1 << type_offset % 8; + type_offset++; + pattern[i].mask_info[type_offset / 8] |= 1 << type_offset % 8; + op_offset = offsetof(struct pattern_packet, ar_op); + pattern[i].mask_info[op_offset / 8] |= 1 << op_offset % 8; + op_offset++; + pattern[i].mask_info[op_offset / 8] |= 1 << op_offset % 8; + tip_offset = offsetof(struct pattern_packet, ar_tip); + pattern[i].mask_info[tip_offset / 8] |= 1 << tip_offset % 8; + tip_offset++; + pattern[i].mask_info[tip_offset / 8] |= 1 << type_offset % 8; + tip_offset++; + pattern[i].mask_info[tip_offset / 8] |= 1 << type_offset % 8; + tip_offset++; + pattern[i].mask_info[tip_offset / 8] |= 1 << type_offset % 8; + + packet.ar_pro = 0x0 << 8 | 0x08; + packet.ar_op = 0x1 << 8; + ip_addr = fxgmac_get_netdev_ip4addr(pdata); + packet.ar_tip[0] = ip_addr & 0xFF; + packet.ar_tip[1] = (ip_addr >> 8) & 0xFF; + packet.ar_tip[2] = (ip_addr >> 16) & 0xFF; + packet.ar_tip[3] = (ip_addr >> 24) & 0xFF; + memcpy(pattern[i].pattern_info, &packet, MAX_PATTERN_SIZE); + pattern[i].mask_size = sizeof(pattern[0].mask_info); + pattern[i].pattern_offset = 0; + i++; + } + + hw_ops->set_wake_pattern(pdata, pattern, i); +} + +void fxgmac_config_wol(struct fxgmac_pdata *pdata, int en) +{ + /* enable or disable WOL. this function only set wake-up type, and power related configure + * will be in other place, see power management. + */ + struct fxgmac_hw_ops *hw_ops = &pdata->hw_ops; + if (!pdata->hw_feat.rwk) { + netdev_err(pdata->netdev, + "error configuring WOL - not supported.\n"); + return; + } + + hw_ops->disable_wake_magic_pattern(pdata); + hw_ops->disable_wake_pattern(pdata); + hw_ops->disable_wake_link_change(pdata); + + if (en) { + /* config mac address for rx of magic or ucast */ + hw_ops->set_mac_address(pdata, (u8 *)(pdata->netdev->dev_addr)); + + /* Enable Magic packet */ + if (pdata->expansion.wol & WAKE_MAGIC) { + hw_ops->enable_wake_magic_pattern(pdata); + } + + /* Enable global unicast packet */ + if (pdata->expansion.wol & WAKE_UCAST || + pdata->expansion.wol & WAKE_MCAST || + pdata->expansion.wol & WAKE_BCAST || + pdata->expansion.wol & WAKE_ARP) { + hw_ops->enable_wake_pattern(pdata); + } + + /* Enable ephy link change */ + if ((FXGMAC_WOL_UPON_EPHY_LINK) && + (pdata->expansion.wol & WAKE_PHY)) { + hw_ops->enable_wake_link_change(pdata); + } + } + device_set_wakeup_enable(/*pci_dev_to_dev*/ (pdata->dev), en); + + DPRINTK("config_wol callout\n"); +} + +static int fxgmac_set_wol(struct net_device *netdev, + struct ethtool_wolinfo *wol) +{ + struct fxgmac_pdata *pdata = netdev_priv(netdev); + +#if FXGMAC_WOL_UPON_EPHY_LINK + if (wol->wolopts & (WAKE_MAGICSECURE | WAKE_FILTER)) { +#else + if (wol->wolopts & (WAKE_PHY | WAKE_MAGICSECURE | WAKE_FILTER)) { +#endif + DPRINTK("fxmac, set_wol, not supported wol options, 0x%x\n", + wol->wolopts); + return -EOPNOTSUPP; + } + + if (!(pdata->hw_feat.rwk)) { + DPRINTK("fxmac, set_wol, hw wol feature is n/a\n"); + return (wol->wolopts ? -EOPNOTSUPP : 0); + } + + pdata->expansion.wol = 0; + if (wol->wolopts & WAKE_UCAST) + pdata->expansion.wol |= WAKE_UCAST; + + if (wol->wolopts & WAKE_MCAST) + pdata->expansion.wol |= WAKE_MCAST; + + if (wol->wolopts & WAKE_BCAST) + pdata->expansion.wol |= WAKE_BCAST; + + if (wol->wolopts & WAKE_MAGIC) + pdata->expansion.wol |= WAKE_MAGIC; + + if (wol->wolopts & WAKE_PHY) + pdata->expansion.wol |= WAKE_PHY; + + if (wol->wolopts & WAKE_ARP) + pdata->expansion.wol |= WAKE_ARP; + + fxgmac_set_pattern_data(pdata); + + fxgmac_config_wol(pdata, (!!(pdata->expansion.wol))); + DPRINTK("fxmac, set_wol, opt=0x%x, 0x%x\n", wol->wolopts, + pdata->expansion.wol); + + return 0; +} +#endif /*FXGMAC_WOL_FEATURE_ENABLED*/ + +static int fxgmac_get_regs_len(struct net_device __always_unused *netdev) +{ + return FXGMAC_EPHY_REGS_LEN * sizeof(u32); +} + +static void fxgmac_get_regs(struct net_device *netdev, + struct ethtool_regs *regs, void *p) +{ + struct fxgmac_pdata *pdata = netdev_priv(netdev); + struct fxgmac_hw_ops *hw_ops = &pdata->hw_ops; + + u32 *regs_buff = p; + u8 i; + + memset(p, 0, FXGMAC_EPHY_REGS_LEN * sizeof(u32)); + for (i = REG_MII_BMCR; i < FXGMAC_EPHY_REGS_LEN; i++) { + hw_ops->read_ephy_reg(pdata, i, (unsigned int *)®s_buff[i]); + } + regs->version = regs_buff[REG_MII_PHYSID1] << 16 | + regs_buff[REG_MII_PHYSID2]; +} + +static int fxgmac_get_link_ksettings(struct net_device *netdev, + struct ethtool_link_ksettings *cmd) +{ + struct fxgmac_pdata *pdata = netdev_priv(netdev); + struct fxgmac_hw_ops *hw_ops = &pdata->hw_ops; + u32 duplex, regval, link_status; + u32 adv = 0xFFFFFFFF; + int ret; + + ret = fxgmac_ephy_autoneg_ability_get(pdata, &adv); + if (ret < 0) + return -ETIMEDOUT; + + ethtool_link_ksettings_zero_link_mode(cmd, supported); + ethtool_link_ksettings_zero_link_mode(cmd, advertising); + + /* set the supported link speeds */ + ethtool_link_ksettings_add_link_mode(cmd, supported, 1000baseT_Full); + ethtool_link_ksettings_add_link_mode(cmd, supported, 100baseT_Full); + ethtool_link_ksettings_add_link_mode(cmd, supported, 100baseT_Half); + ethtool_link_ksettings_add_link_mode(cmd, supported, 10baseT_Full); + ethtool_link_ksettings_add_link_mode(cmd, supported, 10baseT_Half); + + /* Indicate pause support */ + ethtool_link_ksettings_add_link_mode(cmd, supported, Pause); + ethtool_link_ksettings_add_link_mode(cmd, supported, Asym_Pause); + ret = hw_ops->read_ephy_reg(pdata, REG_MII_ADVERTISE, ®val); + if (ret < 0) + return ret; + + if (FXGMAC_GET_REG_BITS(regval, PHY_MII_ADVERTISE_PAUSE_POS, + PHY_MII_ADVERTISE_PAUSE_LEN)) + ethtool_link_ksettings_add_link_mode(cmd, advertising, Pause); + if (FXGMAC_GET_REG_BITS(regval, PHY_MII_ADVERTISE_ASYPAUSE_POS, + PHY_MII_ADVERTISE_ASYPAUSE_LEN)) + ethtool_link_ksettings_add_link_mode(cmd, advertising, + Asym_Pause); + + ethtool_link_ksettings_add_link_mode(cmd, supported, MII); + cmd->base.port = PORT_MII; + + ethtool_link_ksettings_add_link_mode(cmd, supported, Autoneg); + ret = hw_ops->read_ephy_reg(pdata, REG_MII_BMCR, ®val); + if (ret < 0) + return ret; + + regval = FXGMAC_GET_REG_BITS(regval, PHY_CR_AUTOENG_POS, + PHY_CR_AUTOENG_LEN); + if (regval) { + if (pdata->phy_autoeng) + ethtool_link_ksettings_add_link_mode(cmd, advertising, + Autoneg); + else + clear_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, + cmd->link_modes.advertising); + + if (adv & FXGMAC_ADVERTISE_10HALF) + ethtool_link_ksettings_add_link_mode(cmd, advertising, + 10baseT_Half); + if (adv & FXGMAC_ADVERTISE_10FULL) + ethtool_link_ksettings_add_link_mode(cmd, advertising, + 10baseT_Full); + if (adv & FXGMAC_ADVERTISE_100HALF) + ethtool_link_ksettings_add_link_mode(cmd, advertising, + 100baseT_Half); + if (adv & FXGMAC_ADVERTISE_100FULL) + ethtool_link_ksettings_add_link_mode(cmd, advertising, + 100baseT_Full); + if (adv & FXGMAC_ADVERTISE_1000FULL) + ethtool_link_ksettings_add_link_mode(cmd, advertising, + 1000baseT_Full); + } else { + clear_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, + cmd->link_modes.advertising); + switch (pdata->phy_speed) { + case SPEED_1000M: + if (pdata->phy_duplex) + ethtool_link_ksettings_add_link_mode( + cmd, advertising, 1000baseT_Full); + else + ethtool_link_ksettings_add_link_mode( + cmd, advertising, 1000baseT_Half); + break; + case SPEED_100M: + if (pdata->phy_duplex) + ethtool_link_ksettings_add_link_mode( + cmd, advertising, 100baseT_Full); + else + ethtool_link_ksettings_add_link_mode( + cmd, advertising, 100baseT_Half); + break; + case SPEED_10M: + if (pdata->phy_duplex) + ethtool_link_ksettings_add_link_mode( + cmd, advertising, 10baseT_Full); + else + ethtool_link_ksettings_add_link_mode( + cmd, advertising, 10baseT_Half); + break; + default: + break; + } + } + cmd->base.autoneg = pdata->phy_autoeng ? regval : 0; + + regval = 0; + ret = hw_ops->read_ephy_reg(pdata, REG_MII_SPEC_STATUS, ®val); + if (ret < 0) + return ret; + + link_status = regval & (BIT(FXGMAC_EPHY_LINK_STATUS_BIT)); + if (link_status) { + duplex = FXGMAC_GET_REG_BITS(regval, PHY_MII_SPEC_DUPLEX_POS, + PHY_MII_SPEC_DUPLEX_LEN); + cmd->base.duplex = duplex; + cmd->base.speed = pdata->phy_speed; + } else { + cmd->base.duplex = DUPLEX_UNKNOWN; + cmd->base.speed = SPEED_UNKNOWN; + } + + return 0; +} + +static int fxgmac_set_link_ksettings(struct net_device *netdev, + const struct ethtool_link_ksettings *cmd) +{ + u32 advertising, support, adv; + int ret; + struct fxphy_ag_adv; + struct fxgmac_pdata *pdata = netdev_priv(netdev); + struct fxgmac_hw_ops *hw_ops = &pdata->hw_ops; + + if (cmd->base.speed == SPEED_1000 && cmd->base.duplex == DUPLEX_HALF) + return -EINVAL; + + pdata->phy_autoeng = cmd->base.autoneg; + + ethtool_convert_link_mode_to_legacy_u32(&advertising, + cmd->link_modes.advertising); + ethtool_convert_link_mode_to_legacy_u32(&support, + cmd->link_modes.supported); + advertising &= support; + + if (pdata->phy_autoeng || + (!pdata->phy_autoeng && cmd->base.speed == SPEED_1000)) { + ret = hw_ops->read_ephy_reg(pdata, REG_MII_ADVERTISE, &adv); + if (ret < 0) + return ret; + adv &= ~REG_BIT_ADVERTISE_100_10_CAP; + adv |= ethtool_adv_to_mii_adv_t(advertising); + ret = hw_ops->write_ephy_reg(pdata, REG_MII_ADVERTISE, adv); + if (ret < 0) + return ret; + ret = hw_ops->read_ephy_reg(pdata, REG_MII_CTRL1000, &adv); + if (ret < 0) + return ret; + adv &= ~REG_BIT_ADVERTISE_1000_CAP; + adv |= ethtool_adv_to_mii_ctrl1000_t(advertising); + ret = hw_ops->write_ephy_reg(pdata, REG_MII_CTRL1000, adv); + if (ret < 0) + return ret; + + ret = hw_ops->read_ephy_reg(pdata, REG_MII_BMCR, &adv); + if (ret < 0) + return ret; + adv = FXGMAC_SET_REG_BITS(adv, PHY_CR_AUTOENG_POS, + PHY_CR_AUTOENG_LEN, 1); + ret = hw_ops->write_ephy_reg(pdata, REG_MII_BMCR, adv); + if (ret < 0) + return ret; + + ret = hw_ops->read_ephy_reg(pdata, REG_MII_BMCR, &adv); + if (ret < 0) + return ret; + adv = FXGMAC_SET_REG_BITS(adv, PHY_CR_RE_AUTOENG_POS, + PHY_CR_RE_AUTOENG_LEN, 1); + ret = hw_ops->write_ephy_reg(pdata, REG_MII_BMCR, adv); + if (ret < 0) + return ret; + } else { + pdata->phy_duplex = cmd->base.duplex; + pdata->phy_speed = cmd->base.speed; + fxgmac_phy_force_mode(pdata); + } + + /* Save speed is used to restore it when resuming */ + pdata->expansion.pre_phy_speed = cmd->base.speed; + pdata->expansion.pre_phy_autoneg = cmd->base.autoneg; + pdata->expansion.pre_phy_duplex = cmd->base.duplex; + + return 0; +} + +#if FXGMAC_PAUSE_FEATURE_ENABLED +static void fxgmac_get_pauseparam(struct net_device *netdev, + struct ethtool_pauseparam *pause) +{ + struct fxgmac_pdata *pdata = netdev_priv(netdev); + + pause->autoneg = pdata->phy_autoeng; + pause->rx_pause = pdata->rx_pause; + pause->tx_pause = pdata->tx_pause; + + DPRINTK("fxmac get_pauseparam done, rx=%d, tx=%d\n", pdata->rx_pause, + pdata->tx_pause); +} + +static int fxgmac_set_pauseparam(struct net_device *netdev, + struct ethtool_pauseparam *pause) +{ + struct fxgmac_pdata *pdata = netdev_priv(netdev); + struct fxgmac_hw_ops *hw_ops = &pdata->hw_ops; + unsigned int pre_rx_pause = pdata->rx_pause; + unsigned int pre_tx_pause = pdata->tx_pause; + u32 adv; + int ret; + int enable_pause = 0; + + pdata->rx_pause = pause->rx_pause; + pdata->tx_pause = pause->tx_pause; + + if (pdata->rx_pause || pdata->tx_pause) + enable_pause = 1; + + if (pre_rx_pause != pdata->rx_pause) { + hw_ops->config_rx_flow_control(pdata); + DPRINTK("fxgmac set pause parameter, rx from %d to %d\n", + pre_rx_pause, pdata->rx_pause); + } + if (pre_tx_pause != pdata->tx_pause) { + hw_ops->config_tx_flow_control(pdata); + DPRINTK("fxgmac set pause parameter, tx from %d to %d\n", + pre_tx_pause, pdata->tx_pause); + } + + if (pause->autoneg) { + ret = hw_ops->read_ephy_reg(pdata, REG_MII_ADVERTISE, &adv); + if (ret < 0) + return ret; + adv = FXGMAC_SET_REG_BITS(adv, PHY_MII_ADVERTISE_PAUSE_POS, + PHY_MII_ADVERTISE_PAUSE_LEN, + enable_pause); + adv = FXGMAC_SET_REG_BITS(adv, PHY_MII_ADVERTISE_ASYPAUSE_POS, + PHY_MII_ADVERTISE_ASYPAUSE_LEN, + enable_pause); + ret = hw_ops->write_ephy_reg(pdata, REG_MII_ADVERTISE, adv); + if (ret < 0) { + return ret; + } + + ret = hw_ops->read_ephy_reg(pdata, REG_MII_BMCR, &adv); + if (ret < 0) + return ret; + adv = FXGMAC_SET_REG_BITS(adv, PHY_CR_RE_AUTOENG_POS, + PHY_CR_RE_AUTOENG_LEN, 1); + ret = hw_ops->write_ephy_reg(pdata, REG_MII_BMCR, adv); + if (ret < 0) + return ret; + } else { + DPRINTK("Can't set phy pause because autoneg is off.\n"); + } + + DPRINTK("fxgmac set pause parameter, autoneg=%d, rx=%d, tx=%d\n", + pause->autoneg, pause->rx_pause, pause->tx_pause); + + return 0; +} +#endif /*FXGMAC_PAUSE_FEATURE_ENABLED*/ + +static void fxgmac_ethtool_get_strings(struct net_device *netdev, u32 stringset, + u8 *data) +{ + int i; + + switch (stringset) { + case ETH_SS_STATS: + for (i = 0; i < FXGMAC_STATS_COUNT; i++) { + memcpy(data, fxgmac_gstring_stats[i].stat_string, + strlen(fxgmac_gstring_stats[i].stat_string)); + data += ETH_GSTRING_LEN; + } + break; + default: + WARN_ON(1); + break; + } +} + +static int fxgmac_ethtool_get_sset_count(struct net_device *netdev, + int stringset) +{ + int ret; + + switch (stringset) { + case ETH_SS_STATS: + ret = FXGMAC_STATS_COUNT; + break; + + default: + ret = -EOPNOTSUPP; + } + + return ret; +} + +static void fxgmac_ethtool_get_ethtool_stats(struct net_device *netdev, + struct ethtool_stats *stats, + u64 *data) +{ + struct fxgmac_pdata *pdata = netdev_priv(netdev); + u8 *stat; + int i; + +#if FXGMAC_PM_FEATURE_ENABLED + if (!test_bit(FXGMAC_POWER_STATE_DOWN, &pdata->expansion.powerstate)) +#endif + { + pdata->hw_ops.read_mmc_stats(pdata); + } + + for (i = 0; i < FXGMAC_STATS_COUNT; i++) { + stat = (u8 *)pdata + fxgmac_gstring_stats[i].stat_offset; + *data++ = *(u64 *)stat; + } +} + +static inline bool fxgmac_removed(void __iomem *addr) +{ + return unlikely(!addr); +} + +static int fxgmac_ethtool_reset(struct net_device *netdev, u32 *flag) +{ + struct fxgmac_pdata *pdata = netdev_priv(netdev); + struct fxgmac_hw_ops *hw_ops = &pdata->hw_ops; + u32 val; + int ret = 0; + + val = (*flag & ETH_RESET_ALL) || (*flag & ETH_RESET_PHY); + if (!val) { + DPRINTK("Operation not support.\n"); + return -EINVAL; + } + + switch (*flag) { + case ETH_RESET_ALL: + fxgmac_restart_dev(pdata); + *flag = 0; + break; + case ETH_RESET_PHY: + /* + * power off and on the phy in order to properly + * configure the MAC timing + */ + hw_ops->read_ephy_reg(pdata, REG_MII_BMCR, &val); + val = FXGMAC_SET_REG_BITS(val, PHY_CR_POWER_POS, + PHY_CR_POWER_LEN, PHY_POWER_DOWN); + ret = hw_ops->write_ephy_reg(pdata, REG_MII_BMCR, val); + if (ret < 0) + return ret; + + usleep_range_ex(pdata->pAdapter, 9000, 10000); + val = FXGMAC_SET_REG_BITS(val, PHY_CR_POWER_POS, + PHY_CR_POWER_LEN, PHY_POWER_UP); + ret = hw_ops->write_ephy_reg(pdata, REG_MII_BMCR, val); + if (ret < 0) + return ret; + + *flag = 0; + break; + default: + break; + } + + return 0; +} + +static const struct ethtool_ops fxgmac_ethtool_ops = { + .get_drvinfo = fxgmac_ethtool_get_drvinfo, + .get_link = ethtool_op_get_link, + .get_msglevel = fxgmac_ethtool_get_msglevel, + .set_msglevel = fxgmac_ethtool_set_msglevel, + .get_channels = fxgmac_ethtool_get_channels, + .get_coalesce = fxgmac_ethtool_get_coalesce, + .set_coalesce = fxgmac_ethtool_set_coalesce, + .reset = fxgmac_ethtool_reset, + +/* + * The process of set is to get first and then set, + * and the result of get is preserved for values that have not been modified. + * + * Therefore, when using, it is necessary to ensure that this macro and the + * assignment operation in the get_coalesce are one-to-one correspondence, + * otherwise the macro and parameters will be verified when set, and the error + * of "Operation not supported " will be reported if the verification fails + */ +#ifdef ETHTOOL_COALESCE_USECS + .supported_coalesce_params = ETHTOOL_COALESCE_USECS, +#endif + .get_strings = fxgmac_ethtool_get_strings, + .get_sset_count = fxgmac_ethtool_get_sset_count, + .get_ethtool_stats = fxgmac_ethtool_get_ethtool_stats, + .get_regs_len = fxgmac_get_regs_len, + .get_regs = fxgmac_get_regs, + .get_ringparam = fxgmac_get_ringparam, + .set_ringparam = fxgmac_set_ringparam, +#if (FXGMAC_RSS_FEATURE_ENABLED) + .get_rxnfc = fxgmac_get_rxnfc, + .set_rxnfc = fxgmac_set_rxnfc, + .get_rxfh_indir_size = fxgmac_rss_indir_size, + .get_rxfh_key_size = fxgmac_get_rxfh_key_size, + .get_rxfh = fxgmac_get_rxfh, + .set_rxfh = fxgmac_set_rxfh, +#endif +#if (FXGMAC_WOL_FEATURE_ENABLED) + .get_wol = fxgmac_get_wol, + .set_wol = fxgmac_set_wol, +#endif +#if (FXGMAC_PAUSE_FEATURE_ENABLED) + .get_link_ksettings = fxgmac_get_link_ksettings, + .set_link_ksettings = fxgmac_set_link_ksettings, + .get_pauseparam = fxgmac_get_pauseparam, + .set_pauseparam = fxgmac_set_pauseparam, +#endif +}; + +const struct ethtool_ops *fxgmac_get_ethtool_ops(void) +{ + return &fxgmac_ethtool_ops; +} diff --git a/drivers/net/ethernet/motorcomm/yt6801/fuxi-gmac-hw.c b/drivers/net/ethernet/motorcomm/yt6801/fuxi-gmac-hw.c new file mode 100644 index 000000000000..56f91e0419b1 --- /dev/null +++ b/drivers/net/ethernet/motorcomm/yt6801/fuxi-gmac-hw.c @@ -0,0 +1,6097 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (c) 2021 Motorcomm Corporation. */ + +#include "fuxi-gmac.h" +#include "fuxi-gmac-reg.h" +#include "fuxi-efuse.h" + +#ifdef FXGMAC_USE_ADAPTER_HANDLE +#include "fuxi-mp.h" +#endif + +void fxgmac_release_phy(struct fxgmac_pdata *pdata); +static void fxgmac_pwr_clock_ungate(struct fxgmac_pdata *pdata); +static void fxgmac_pwr_clock_gate(struct fxgmac_pdata *pdata); + +static int fxgmac_tx_complete(struct fxgmac_dma_desc *dma_desc) +{ + return !FXGMAC_GET_REG_BITS_LE(dma_desc->desc3, TX_NORMAL_DESC3_OWN_POS, + TX_NORMAL_DESC3_OWN_LEN); +} + +static int fxgmac_disable_rx_csum(struct fxgmac_pdata *pdata) +{ + u32 regval; + + regval = readreg(pdata->pAdapter, pdata->mac_regs + MAC_CR); + regval = FXGMAC_SET_REG_BITS(regval, MAC_CR_IPC_POS, MAC_CR_IPC_LEN, 0); + writereg(pdata->pAdapter, regval, pdata->mac_regs + MAC_CR); + + DPRINTK("fxgmac disable rx checksum, set val = %x.\n", regval); + return 0; +} + +static int fxgmac_enable_rx_csum(struct fxgmac_pdata *pdata) +{ + u32 regval; + + regval = readreg(pdata->pAdapter, pdata->mac_regs + MAC_CR); + regval = FXGMAC_SET_REG_BITS(regval, MAC_CR_IPC_POS, MAC_CR_IPC_LEN, 1); + writereg(pdata->pAdapter, regval, pdata->mac_regs + MAC_CR); + + DPRINTK("fxgmac enable rx checksum, set val = %x.\n", regval); + return 0; +} + +static int fxgmac_set_mac_address(struct fxgmac_pdata *pdata, u8 *addr) +{ + u32 mac_addr_hi, mac_addr_lo; + + mac_addr_hi = (((u32)(addr[5]) << 8) | ((u32)(addr[4]) << 0)); + mac_addr_lo = (((u32)(addr[3]) << 24) | ((u32)(addr[2]) << 16) | + ((u32)(addr[1]) << 8) | ((u32)(addr[0]) << 0)); + + writereg(pdata->pAdapter, mac_addr_hi, pdata->mac_regs + MAC_MACA0HR); + writereg(pdata->pAdapter, mac_addr_lo, pdata->mac_regs + MAC_MACA0LR); + + return 0; +} + +#if !defined(DPDK) +static void fxgmac_set_mac_reg(struct fxgmac_pdata *pdata, + struct netdev_hw_addr *ha, + unsigned int __far *mac_reg) +{ + u32 mac_addr_hi, mac_addr_lo; + u8 *mac_addr; + + mac_addr_lo = 0; + mac_addr_hi = 0; + + if (ha) { + mac_addr = (u8 *)&mac_addr_lo; + mac_addr[0] = ha->addr[0]; + mac_addr[1] = ha->addr[1]; + mac_addr[2] = ha->addr[2]; + mac_addr[3] = ha->addr[3]; + mac_addr = (u8 *)&mac_addr_hi; + mac_addr[0] = ha->addr[4]; + mac_addr[1] = ha->addr[5]; + + netif_dbg(pdata, drv, pdata->netdev, "adding mac address %pM\n", + ha->addr); + netif_dbg(pdata, drv, pdata->netdev, + "adding mac addredd at %#x\n", *mac_reg); + + mac_addr_hi = FXGMAC_SET_REG_BITS( + mac_addr_hi, MAC_MACA1HR_AE_POS, MAC_MACA1HR_AE_LEN, 1); + } + + writereg(pdata->pAdapter, mac_addr_hi, pdata->mac_regs + *mac_reg); + *mac_reg += MAC_MACA_INC; + writereg(pdata->pAdapter, mac_addr_lo, pdata->mac_regs + *mac_reg); + *mac_reg += MAC_MACA_INC; +} +#endif + +static int fxgmac_enable_tx_vlan(struct fxgmac_pdata *pdata) +{ + u32 regval; + + regval = readreg(pdata->pAdapter, pdata->mac_regs + MAC_VLANIR); + /* Indicate that VLAN Tx CTAGs come from mac_vlan_incl register */ + regval = FXGMAC_SET_REG_BITS(regval, MAC_VLANIR_VLTI_POS, + MAC_VLANIR_VLTI_LEN, 0); + regval = FXGMAC_SET_REG_BITS(regval, MAC_VLANIR_CSVL_POS, + MAC_VLANIR_CSVL_LEN, 0); + regval = FXGMAC_SET_REG_BITS(regval, MAC_VLANIR_VLP_POS, + MAC_VLANIR_VLP_LEN, 1); + regval = FXGMAC_SET_REG_BITS(regval, MAC_VLANIR_VLC_POS, + MAC_VLANIR_VLC_LEN, 2); + regval = FXGMAC_SET_REG_BITS(regval, MAC_VLANIR_VLT_POS, + MAC_VLANIR_VLT_LEN, pdata->vlan); + writereg(pdata->pAdapter, regval, pdata->mac_regs + MAC_VLANIR); + + regval = readreg(pdata->pAdapter, pdata->mac_regs + MAC_VLANTR); + regval = FXGMAC_SET_REG_BITS(regval, MAC_VLANTR_VL_POS, + MAC_VLANTR_VL_LEN, pdata->vlan); + writereg(pdata->pAdapter, regval, pdata->mac_regs + MAC_VLANTR); + + return 0; +} + +static int fxgmac_disable_tx_vlan(struct fxgmac_pdata *pdata) +{ + u32 regval; + + regval = readreg(pdata->pAdapter, pdata->mac_regs + MAC_VLANIR); + + /* Indicate that VLAN Tx CTAGs come from mac_vlan_incl register */ + + regval = FXGMAC_SET_REG_BITS(regval, MAC_VLANIR_CSVL_POS, + MAC_VLANIR_CSVL_LEN, 0); + regval = FXGMAC_SET_REG_BITS(regval, MAC_VLANIR_VLTI_POS, + MAC_VLANIR_VLTI_LEN, /*0*/ 1); + + regval = FXGMAC_SET_REG_BITS(regval, MAC_VLANIR_VLP_POS, + MAC_VLANIR_VLP_LEN, /*1*/ 0); + regval = FXGMAC_SET_REG_BITS(regval, MAC_VLANIR_VLC_POS, + MAC_VLANIR_VLC_LEN, 0); + writereg(pdata->pAdapter, regval, pdata->mac_regs + MAC_VLANIR); + + /* In order for the VLAN Hash Table filtering to be effective, + * the VLAN tag identifier in the VLAN Tag Register must not + * be zero. Set the VLAN tag identifier to "1" to enable the + * VLAN Hash Table filtering. This implies that a VLAN tag of + * 1 will always pass filtering. + */ + + /*regval = readreg(pdata->mac_regs + MAC_VLANTR); + regval = FXGMAC_SET_REG_BITS(regval, MAC_VLANTR_VL_POS, + MAC_VLANTR_VL_LEN, 1); + writereg(regval, pdata->mac_regs + MAC_VLANTR);*/ + + return 0; +} + +static int fxgmac_enable_rx_vlan_stripping(struct fxgmac_pdata *pdata) +{ + u32 regval; + + regval = readreg(pdata->pAdapter, pdata->mac_regs + MAC_VLANTR); + /* Put the VLAN tag in the Rx descriptor */ + regval = FXGMAC_SET_REG_BITS(regval, MAC_VLANTR_EVLRXS_POS, + MAC_VLANTR_EVLRXS_LEN, 1); + /* Don't check the VLAN type */ + regval = FXGMAC_SET_REG_BITS(regval, MAC_VLANTR_DOVLTC_POS, + MAC_VLANTR_DOVLTC_LEN, 1); + /* Check only C-TAG (0x8100) packets */ + regval = FXGMAC_SET_REG_BITS(regval, MAC_VLANTR_ERSVLM_POS, + MAC_VLANTR_ERSVLM_LEN, 0); + /* Don't consider an S-TAG (0x88A8) packet as a VLAN packet */ + regval = FXGMAC_SET_REG_BITS(regval, MAC_VLANTR_ESVL_POS, + MAC_VLANTR_ESVL_LEN, 0); + /* Enable VLAN tag stripping */ + regval = FXGMAC_SET_REG_BITS(regval, MAC_VLANTR_EVLS_POS, + MAC_VLANTR_EVLS_LEN, 0x3); + writereg(pdata->pAdapter, regval, pdata->mac_regs + MAC_VLANTR); + DPRINTK("fxgmac enable MAC rx vlan stripping , set val = %x\n", regval); + + return 0; +} + +static int fxgmac_disable_rx_vlan_stripping(struct fxgmac_pdata *pdata) +{ + u32 regval; + + regval = readreg(pdata->pAdapter, pdata->mac_regs + MAC_VLANTR); + regval = FXGMAC_SET_REG_BITS(regval, MAC_VLANTR_EVLS_POS, + MAC_VLANTR_EVLS_LEN, 0); + writereg(pdata->pAdapter, regval, pdata->mac_regs + MAC_VLANTR); + DPRINTK("fxgmac disable MAC rx vlan stripping, set val = %x\n", regval); + + return 0; +} + +static int fxgmac_enable_rx_vlan_filtering(struct fxgmac_pdata *pdata) +{ + u32 regval; + + regval = readreg(pdata->pAdapter, pdata->mac_regs + MAC_PFR); + /* Enable VLAN filtering */ + regval = FXGMAC_SET_REG_BITS(regval, MAC_PFR_VTFE_POS, MAC_PFR_VTFE_LEN, + 1); + writereg(pdata->pAdapter, regval, pdata->mac_regs + MAC_PFR); + +#if FXGMAC_FILTER_SINGLE_VLAN_ENABLED + regval = readreg(pdata->pAdapter, pdata->mac_regs + MAC_VLANTR); + regval = FXGMAC_SET_REG_BITS(regval, MAC_VLANTR_VL_POS, + MAC_VLANTR_VL_LEN, pdata->vlan); + writereg(pdata->pAdapter, regval, pdata->mac_regs + MAC_VLANTR); +#else + regval = readreg(pdata->pAdapter, pdata->mac_regs + MAC_VLANTR); + /* Enable VLAN Hash Table filtering */ + regval = FXGMAC_SET_REG_BITS(regval, MAC_VLANTR_VTHM_POS, + MAC_VLANTR_VTHM_LEN, 1); + /* Disable VLAN tag inverse matching */ + regval = FXGMAC_SET_REG_BITS(regval, MAC_VLANTR_VTIM_POS, + MAC_VLANTR_VTIM_LEN, 0); + /* Only filter on the lower 12-bits of the VLAN tag */ + regval = FXGMAC_SET_REG_BITS(regval, MAC_VLANTR_ETV_POS, + MAC_VLANTR_ETV_LEN, 1); +#endif + + return 0; +} + +static int fxgmac_disable_rx_vlan_filtering(struct fxgmac_pdata *pdata) +{ + u32 regval; + + regval = readreg(pdata->pAdapter, pdata->mac_regs + MAC_PFR); + /* Disable VLAN filtering */ + regval = FXGMAC_SET_REG_BITS(regval, MAC_PFR_VTFE_POS, MAC_PFR_VTFE_LEN, + 0); + writereg(pdata->pAdapter, regval, pdata->mac_regs + MAC_PFR); + +#if FXGMAC_FILTER_SINGLE_VLAN_ENABLED + regval = readreg(pdata->pAdapter, pdata->mac_regs + MAC_VLANTR); + regval = FXGMAC_SET_REG_BITS(regval, MAC_VLANTR_VL_POS, + MAC_VLANTR_VL_LEN, pdata->vlan); + writereg(pdata->pAdapter, regval, pdata->mac_regs + MAC_VLANTR); +#endif + + return 0; +} + +#if FXGMAC_FILTER_MULTIPLE_VLAN_ENABLED +static u32 fxgmac_vid_crc32_le(__le16 vid_le) +{ + unsigned char *data = (unsigned char *)&vid_le; + unsigned char data_byte = 0; + u32 crc = ~0; + u32 temp = 0; + int i, bits; + + bits = get_bitmask_order(VLAN_VID_MASK); + for (i = 0; i < bits; i++) { + if ((i % 8) == 0) + data_byte = data[i / 8]; + + temp = ((crc & 1) ^ data_byte) & 1; + crc >>= 1; + data_byte >>= 1; + + if (temp) + crc ^= CRC32_POLY_LE; + } + + return crc; +} +#endif + +static int fxgmac_update_vlan_hash_table(struct fxgmac_pdata *pdata) +{ + u16 vlan_hash_table = 0; + u32 regval; + +#if FXGMAC_FILTER_MULTIPLE_VLAN_ENABLED + __le16 vid_le; + u32 crc; + u16 vid; + /* Generate the VLAN Hash Table value */ + for_each_set_bit(vid, pdata->active_vlans, VLAN_N_VID) { + /* Get the CRC32 value of the VLAN ID */ + vid_le = cpu_to_le16(vid); + crc = bitrev32(~fxgmac_vid_crc32_le(vid_le)) >> 28; + + vlan_hash_table |= (1 << crc); + } +#endif + regval = readreg(pdata->pAdapter, pdata->mac_regs + MAC_VLANHTR); + /* Set the VLAN Hash Table filtering register */ + regval = FXGMAC_SET_REG_BITS(regval, MAC_VLANHTR_VLHT_POS, + MAC_VLANHTR_VLHT_LEN, vlan_hash_table); + writereg(pdata->pAdapter, regval, pdata->mac_regs + MAC_VLANHTR); + + DPRINTK("fxgmac_update_vlan_hash_tabl done,hash tbl=%08x.\n", + vlan_hash_table); + return 0; +} + +static int fxgmac_set_promiscuous_mode(struct fxgmac_pdata *pdata, + unsigned int enable) +{ + unsigned int val = enable ? 1 : 0; + u32 regval; + + regval = readreg(pdata->pAdapter, pdata->mac_regs + MAC_PFR); + + if (FXGMAC_GET_REG_BITS(regval, MAC_PFR_PR_POS, MAC_PFR_PR_LEN) == + val) { + return 0; + } + netif_dbg(pdata, drv, pdata->netdev, + "" STR_FORMAT " promiscuous mode\n", + enable ? "entering" : "leaving"); + + regval = FXGMAC_SET_REG_BITS(regval, MAC_PFR_PR_POS, MAC_PFR_PR_LEN, + val); + writereg(pdata->pAdapter, regval, pdata->mac_regs + MAC_PFR); + + DbgPrintF(MP_TRACE, "promiscuous mode=%d", enable); + DbgPrintF(MP_TRACE, "set val = %x", regval); + DbgPrintF(MP_TRACE, + "note, vlan filter is called when set promiscuous mode=%d", + enable); + + /* Hardware will still perform VLAN filtering in promiscuous mode */ + if (enable) { + fxgmac_disable_rx_vlan_filtering(pdata); + } else { + if (FXGMAC_RX_VLAN_FILTERING_ENABLED) { + fxgmac_enable_rx_vlan_filtering(pdata); + } + } + + DPRINTK("fxgmac set promisc mode=%d\n", enable); + return 0; +} + +static int fxgmac_enable_rx_broadcast(struct fxgmac_pdata *pdata, + unsigned int enable) +{ + unsigned int val = enable ? 0 : 1; + u32 regval; + + regval = readreg(pdata->pAdapter, pdata->mac_regs + MAC_PFR); + + if (FXGMAC_GET_REG_BITS(regval, MAC_PFR_DBF_POS, MAC_PFR_DBF_LEN) == + val) { + return 0; + } + + regval = FXGMAC_SET_REG_BITS(regval, MAC_PFR_DBF_POS, MAC_PFR_DBF_LEN, + val); + writereg(pdata->pAdapter, regval, pdata->mac_regs + MAC_PFR); + + DbgPrintF(MP_TRACE, "bcast en=%d", enable); + DbgPrintF(MP_TRACE, "bit-val=%d", val); + DbgPrintF(MP_TRACE, "reg=%x", regval); + return 0; +} + +static int fxgmac_set_all_multicast_mode(struct fxgmac_pdata *pdata, + unsigned int enable) +{ + unsigned int val = enable ? 1 : 0; + u32 regval; + + regval = readreg(pdata->pAdapter, pdata->mac_regs + MAC_PFR); + if (FXGMAC_GET_REG_BITS(regval, MAC_PFR_PM_POS, MAC_PFR_PM_LEN) == + val) { + return 0; + } + netif_dbg(pdata, drv, pdata->netdev, "" STR_FORMAT " allmulti mode\n", + enable ? "entering" : "leaving"); + + regval = FXGMAC_SET_REG_BITS(regval, MAC_PFR_PM_POS, MAC_PFR_PM_LEN, + val); + writereg(pdata->pAdapter, regval, pdata->mac_regs + MAC_PFR); + + DbgPrintF(MP_TRACE, "Enable all Multicast=%d", enable); + DbgPrintF(MP_TRACE, "set val = %#x.", regval); + + return 0; +} + +static void fxgmac_set_mac_addn_addrs(struct fxgmac_pdata *pdata) +{ +#ifndef DPDK +#if FXGMAC_FILTER_MULTIPLE_MAC_ADDR_ENABLED + struct net_device *netdev = pdata->netdev; + struct netdev_hw_addr *ha; +#endif + u32 addn_macs; + unsigned int mac_reg; + + mac_reg = MAC_MACA1HR; + addn_macs = pdata->hw_feat.addn_mac; +#if FXGMAC_FILTER_MULTIPLE_MAC_ADDR_ENABLED + DPRINTK("xlgamc add mac addr callin\n"); + if (netdev_uc_count(netdev) > addn_macs) { + fxgmac_set_promiscuous_mode(pdata, 1); + } else { + netdev_for_each_uc_addr(ha, netdev) { + fxgmac_set_mac_reg(pdata, ha, &mac_reg); + addn_macs--; + } + + if (netdev_mc_count(netdev) > addn_macs) { + fxgmac_set_all_multicast_mode(pdata, 1); + } else { + netdev_for_each_mc_addr(ha, netdev) { + fxgmac_set_mac_reg(pdata, ha, &mac_reg); + addn_macs--; + } + } + } +#endif + /* Clear remaining additional MAC address entries */ + while (addn_macs--) { + fxgmac_set_mac_reg(pdata, NULL, &mac_reg); + } +#else + (void)pdata; +#endif +} + +#define GET_REG_AND_BIT_POS(reversalval, regOut, bitOut) \ + do { \ + regOut = (((reversalval) >> 5) & 0x7); \ + bitOut = ((reversalval) & 0x1f); \ + } while (0) + +static u32 fxgmac_crc32(unsigned char *Data, int Length) +{ + u32 Crc = (u32)~0; /* Initial value. 0xFFFFFFFF */ + + while (--Length >= 0) { + unsigned char Byte = *Data++; + int Bit; + + for (Bit = 8; --Bit >= 0; Byte >>= 1) { + if ((Crc ^ Byte) & 1) { + Crc >>= 1; + Crc ^= 0xedb88320; + } else { + Crc >>= 1; + } + } + } + + return ~Crc; +} + +/* + * configure multicast hash table, reg 0x2010~202c + * input: pmc_mac, pointer to mcast MAC. if it is null, then clean all registers. + * b_add, 1 to set the bit; 0 to clear the bit. + */ +static void fxgmac_config_multicast_mac_hash_table(struct fxgmac_pdata *pdata, + unsigned char *pmc_mac, + int b_add) +{ + u32 hash_reg, reg_bit; + unsigned int j; + u32 crc, reversal_crc, regval; + + if (!pmc_mac) { + for (j = 0; j < FXGMAC_MAC_HASH_TABLE_SIZE; j++) { + hash_reg = j; + hash_reg = (MAC_HTR0 + hash_reg * MAC_HTR_INC); + writereg(pdata->pAdapter, 0, + pdata->mac_regs + hash_reg); + } + DBGPRINT( + MP_TRACE, + ("fxgmac_config_mcast_mac_hash_table, clear all mcast mac hash table size %d", + j)); + return; + } + + crc = fxgmac_crc32(pmc_mac, ETH_ALEN); + + /* reverse the crc */ + for (j = 0, reversal_crc = 0; j < 32; j++) { + if (crc & ((u32)1 << j)) + reversal_crc |= ((u32)1 << (31 - j)); + } + + GET_REG_AND_BIT_POS((reversal_crc >> 24), hash_reg, reg_bit); + /* Set the MAC Hash Table registers */ + hash_reg = (MAC_HTR0 + hash_reg * MAC_HTR_INC); + regval = readreg(pdata->pAdapter, pdata->mac_regs + hash_reg); + + regval = FXGMAC_SET_REG_BITS(regval, reg_bit, 1, (b_add ? 1 : 0)); + + writereg(pdata->pAdapter, regval, pdata->mac_regs + hash_reg); +} + +static void fxgmac_set_mac_hash_table(struct fxgmac_pdata *pdata) +{ +#ifndef DPDK +#if FXGMAC_MAC_HASH_TABLE + struct net_device *netdev = pdata->netdev; + struct netdev_hw_addr *ha; + + fxgmac_config_multicast_mac_hash_table(pdata, (unsigned char *)0, 1); + netdev_for_each_mc_addr(ha, netdev) { + fxgmac_config_multicast_mac_hash_table(pdata, ha->addr, 1); + } +#else + (void)pdata; +#endif +#else + (void)pdata; +#endif +} + +static int fxgmac_set_mc_addresses(struct fxgmac_pdata *pdata) +{ + if (pdata->hw_feat.hash_table_size) + fxgmac_set_mac_hash_table(pdata); + else + fxgmac_set_mac_addn_addrs(pdata); + + return 0; +} + +static void fxgmac_set_multicast_mode(struct fxgmac_pdata *pdata, + unsigned int enable) +{ + if (enable) + fxgmac_set_mc_addresses(pdata); + else + fxgmac_config_multicast_mac_hash_table(pdata, + (unsigned char *)0, 1); +} + +static void fxgmac_config_mac_address(struct fxgmac_pdata *pdata) +{ + u32 regval; + + fxgmac_set_mac_address(pdata, pdata->mac_addr); + + /* Filtering is done using perfect filtering and hash filtering */ + if (pdata->hw_feat.hash_table_size) { + regval = readreg(pdata->pAdapter, pdata->mac_regs + MAC_PFR); + regval = FXGMAC_SET_REG_BITS(regval, MAC_PFR_HPF_POS, + MAC_PFR_HPF_LEN, 1); +#if FXGMAC_MAC_HASH_TABLE + regval = FXGMAC_SET_REG_BITS(regval, MAC_PFR_HUC_POS, + MAC_PFR_HUC_LEN, 1); +#endif + regval = FXGMAC_SET_REG_BITS(regval, MAC_PFR_HMC_POS, + MAC_PFR_HMC_LEN, 1); + writereg(pdata->pAdapter, regval, pdata->mac_regs + MAC_PFR); + } +} + +static int fxgmac_config_crc_check(struct fxgmac_pdata *pdata) +{ + u32 regval, value; + + regval = readreg(pdata->pAdapter, pdata->mac_regs + MAC_ECR); + value = (pdata->crc_check) ? 0 : 1; + regval = FXGMAC_SET_REG_BITS(regval, MAC_ECR_DCRCC_POS, + MAC_ECR_DCRCC_LEN, value); + writereg(pdata->pAdapter, regval, pdata->mac_regs + MAC_ECR); + + return 0; +} + +static int fxgmac_config_jumbo(struct fxgmac_pdata *pdata) +{ + u32 regval; + + regval = readreg(pdata->pAdapter, pdata->mac_regs + MAC_CR); + regval = FXGMAC_SET_REG_BITS(regval, MAC_CR_JE_POS, MAC_CR_JE_LEN, + pdata->jumbo); + writereg(pdata->pAdapter, regval, pdata->mac_regs + MAC_CR); + return 0; +} + +static void fxgmac_config_checksum_offload(struct fxgmac_pdata *pdata) +{ + if (FXGMAC_RX_CHECKSUM_ENABLED) + fxgmac_enable_rx_csum(pdata); + else + fxgmac_disable_rx_csum(pdata); +} + +static void fxgmac_config_vlan_support(struct fxgmac_pdata *pdata) +{ + /*if (pdata->vlan_exist) + fxgmac_enable_tx_vlan(pdata); + else*/ + fxgmac_disable_tx_vlan(pdata); + + /* Set the current VLAN Hash Table register value */ + fxgmac_update_vlan_hash_table(pdata); + + if (pdata->vlan_filter) + fxgmac_enable_rx_vlan_filtering(pdata); + else + fxgmac_disable_rx_vlan_filtering(pdata); + + if (pdata->vlan_strip) + fxgmac_enable_rx_vlan_stripping(pdata); + else + fxgmac_disable_rx_vlan_stripping(pdata); +} + +static int fxgmac_config_rx_mode(struct fxgmac_pdata *pdata) +{ + unsigned int pr_mode, am_mode, mu_mode, bd_mode; + +#ifndef FXGMAC_NETDEV_MU_MODE_ENABLED +#define FXGMAC_NETDEV_MU_MODE_ENABLED 0 +#endif + +#ifndef FXGMAC_NETDEV_BD_MODE_ENABLED +#define FXGMAC_NETDEV_BD_MODE_ENABLED 0 +#endif + + pr_mode = FXGMAC_NETDEV_PR_MODE_ENABLED; + am_mode = FXGMAC_NETDEV_AM_MODE_ENABLED; + mu_mode = FXGMAC_NETDEV_MU_MODE_ENABLED; + bd_mode = FXGMAC_NETDEV_BD_MODE_ENABLED; + + fxgmac_enable_rx_broadcast(pdata, bd_mode); + fxgmac_set_promiscuous_mode(pdata, pr_mode); + fxgmac_set_all_multicast_mode(pdata, am_mode); + fxgmac_set_multicast_mode(pdata, mu_mode); + + return 0; +} + +static void fxgmac_prepare_tx_stop(struct fxgmac_pdata *pdata, + struct fxgmac_channel *channel) +{ +#ifdef FXGMAC_WAIT_TX_STOP + unsigned int tx_dsr, tx_pos, tx_qidx; + unsigned long tx_timeout; + unsigned int tx_status; + + pdata = pdata; + + /* Calculate the status register to read and the position within */ + if (channel->queue_index < DMA_DSRX_FIRST_QUEUE) { + tx_dsr = DMA_DSR0; + tx_pos = (channel->queue_index * DMA_DSR_Q_LEN) + + DMA_DSR0_TPS_START; + } else { + tx_qidx = channel->queue_index - DMA_DSRX_FIRST_QUEUE; + + tx_dsr = DMA_DSR1 + ((tx_qidx / DMA_DSRX_QPR) * DMA_DSRX_INC); + tx_pos = ((tx_qidx % DMA_DSRX_QPR) * DMA_DSR_Q_LEN) + + DMA_DSRX_TPS_START; + } + + /* The Tx engine cannot be stopped if it is actively processing + * descriptors. Wait for the Tx engine to enter the stopped or + * suspended state. Don't wait forever though... + */ +#if FXGMAC_TX_HANG_TIMER_ENABLED + tx_timeout = jiffies + msecs_to_jiffies(100); /* 100ms */ +#else + tx_timeout = jiffies + (FXGMAC_DMA_STOP_TIMEOUT * HZ); +#endif + while (time_before(jiffies, tx_timeout)) { + tx_status = readreg(pdata->pAdapter, pdata->mac_regs + tx_dsr); + tx_status = + FXGMAC_GET_REG_BITS(tx_status, tx_pos, DMA_DSR_TPS_LEN); + if ((tx_status == DMA_TPS_STOPPED) || + (tx_status == DMA_TPS_SUSPENDED)) + break; + + usleep_range_ex(pdata->pAdapter, 500, 1000); + } + + if (!time_before(jiffies, tx_timeout)) + netdev_info(pdata->netdev, + "timed out waiting for Tx DMA channel %u to stop\n", + channel->queue_index); +#else + pdata = pdata; + channel = channel; +#endif +} + +static void fxgmac_enable_tx(struct fxgmac_pdata *pdata) +{ +#ifndef DPDK + struct fxgmac_channel *channel; +#endif + unsigned int i; + u32 regval; + +#if FXGMAC_TX_HANG_TIMER_ENABLED + pdata->tx_hang_restart_queuing = 0; +#endif + + /* Enable each Tx DMA channel */ +#ifndef DPDK + channel = pdata->channel_head; + for (i = 0; i < pdata->channel_count; i++, channel++) { +#ifndef UBOOT + if (!channel->tx_ring) + break; +#endif + regval = readreg(pdata->pAdapter, + FXGMAC_DMA_REG(channel, DMA_CH_TCR)); + regval = FXGMAC_SET_REG_BITS(regval, DMA_CH_TCR_ST_POS, + DMA_CH_TCR_ST_LEN, 1); + writereg(pdata->pAdapter, regval, + FXGMAC_DMA_REG(channel, DMA_CH_TCR)); + } +#else + PMD_INIT_FUNC_TRACE(); + struct fxgmac_tx_queue *txq; + struct rte_eth_dev *dev = pdata->expansion.eth_dev; + + for (i = 0; i < dev->data->nb_tx_queues; i++) { + txq = dev->data->tx_queues[i]; + if (!txq) { + DPRINTK("Tx queue not setup for port %d\n", + pdata->expansion.eth_dev->data->port_id); + return; + } + + /* Enable Tx DMA channel */ + FXGMAC_DMA_IOWRITE_BITS(txq, DMA_CH_TCR, ST, 1); + } +#endif + + /* Enable each Tx queue */ + for (i = 0; i < pdata->tx_q_count; i++) { +#if FXGMAC_FAKE_4_TX_QUEUE_ENABLED + if (i > 0) + break; +#endif + + regval = readreg(pdata->pAdapter, + FXGMAC_MTL_REG(pdata, i, MTL_Q_TQOMR)); + regval = FXGMAC_SET_REG_BITS(regval, MTL_Q_TQOMR_TXQEN_POS, + MTL_Q_TQOMR_TXQEN_LEN, + MTL_Q_ENABLED); + writereg(pdata->pAdapter, regval, + FXGMAC_MTL_REG(pdata, i, MTL_Q_TQOMR)); + } + + /* Enable MAC Tx */ + regval = readreg(pdata->pAdapter, pdata->mac_regs + MAC_CR); + regval = FXGMAC_SET_REG_BITS(regval, MAC_CR_TE_POS, MAC_CR_TE_LEN, 1); + writereg(pdata->pAdapter, regval, pdata->mac_regs + MAC_CR); +} + +static void fxgmac_disable_tx(struct fxgmac_pdata *pdata) +{ +#ifndef DPDK + struct fxgmac_channel *channel; +#endif + unsigned int i; + u32 regval; + + /* Prepare for Tx DMA channel stop */ +#ifndef DPDK + channel = pdata->channel_head; + if (channel != NULL) { + for (i = 0; i < pdata->channel_count; i++, channel++) { + if (!channel->tx_ring) + break; + + fxgmac_prepare_tx_stop(pdata, channel); + +#if FXGMAC_TX_HANG_TIMER_ENABLED + pdata->tx_hang_restart_queuing = 0; +#endif + } + } + +#else + PMD_INIT_FUNC_TRACE(); + struct fxgmac_tx_queue *txq; + struct rte_eth_dev *dev = pdata->expansion.eth_dev; + + for (i = 0; i < pdata->tx_q_count; i++) { + txq = dev->data->tx_queues[i]; + if (!txq) { + DPRINTK("Tx queue not setup for port %d\n", + dev->data->port_id); + return; + } + + fxgmac_txq_prepare_tx_stop(pdata, i); + } +#endif + + /* Disable MAC Tx */ + regval = readreg(pdata->pAdapter, pdata->mac_regs + MAC_CR); + regval = FXGMAC_SET_REG_BITS(regval, MAC_CR_TE_POS, MAC_CR_TE_LEN, 0); + writereg(pdata->pAdapter, regval, pdata->mac_regs + MAC_CR); + + /* Disable each Tx queue */ + for (i = 0; i < pdata->tx_q_count; i++) { + regval = readreg(pdata->pAdapter, + FXGMAC_MTL_REG(pdata, i, MTL_Q_TQOMR)); + regval = FXGMAC_SET_REG_BITS(regval, MTL_Q_TQOMR_TXQEN_POS, + MTL_Q_TQOMR_TXQEN_LEN, 0); + writereg(pdata->pAdapter, regval, + FXGMAC_MTL_REG(pdata, i, MTL_Q_TQOMR)); + } + + /* Disable each Tx DMA channel */ +#ifndef DPDK + channel = pdata->channel_head; + if (channel != NULL) { + for (i = 0; i < pdata->channel_count; i++, channel++) { + if (!channel->tx_ring) + break; + + regval = readreg(pdata->pAdapter, + FXGMAC_DMA_REG(channel, DMA_CH_TCR)); + regval = FXGMAC_SET_REG_BITS(regval, DMA_CH_TCR_ST_POS, + DMA_CH_TCR_ST_LEN, 0); + writereg(pdata->pAdapter, regval, + FXGMAC_DMA_REG(channel, DMA_CH_TCR)); + } + } +#else + for (i = 0; i < dev->data->nb_tx_queues; i++) { + txq = dev->data->tx_queues[i]; + if (!txq) { + DPRINTK("Tx queue not setup for port %d\n", + dev->data->port_id); + return; + } + + FXGMAC_DMA_IOWRITE_BITS(txq, DMA_CH_TCR, ST, 0); + } +#endif +} + +static void fxgmac_prepare_rx_stop(struct fxgmac_pdata *pdata, + unsigned int queue) +{ + u32 rx_status, prxq; +#if defined(FXGMAC_WAIT_RX_STOP_BY_PRXQ_RXQSTS) + unsigned int rxqsts; + unsigned long rx_timeout; + + /* The Rx engine cannot be stopped if it is actively processing + * packets. Wait for the Rx queue to empty the Rx fifo. Don't + * wait forever though... + */ +#if FXGMAC_TX_HANG_TIMER_ENABLED + rx_timeout = + jiffies + msecs_to_jiffies(500); /* 500ms, larger is better */ +#else + rx_timeout = jiffies + (FXGMAC_DMA_STOP_TIMEOUT * HZ); +#endif + while (time_before(jiffies, rx_timeout)) { + rx_status = readreg(pdata->pAdapter, + FXGMAC_MTL_REG(pdata, queue, MTL_Q_RQDR)); + prxq = FXGMAC_GET_REG_BITS(rx_status, MTL_Q_RQDR_PRXQ_POS, + MTL_Q_RQDR_PRXQ_LEN); + rxqsts = FXGMAC_GET_REG_BITS(rx_status, MTL_Q_RQDR_RXQSTS_POS, + MTL_Q_RQDR_RXQSTS_LEN); + if ((prxq == 0) && (rxqsts == 0)) + break; + + usleep_range_ex(pdata->pAdapter, 500, 1000); + } + + if (!time_before(jiffies, rx_timeout)) + netdev_info(pdata->netdev, + "timed out waiting for Rx queue %u to empty\n", + queue); +#else + unsigned int busy = 100; + do { + rx_status = readreg(pdata->pAdapter, + FXGMAC_MTL_REG(pdata, queue, MTL_Q_RQDR)); + prxq = FXGMAC_GET_REG_BITS(rx_status, MTL_Q_RQDR_PRXQ_POS, + MTL_Q_RQDR_PRXQ_LEN); + busy--; + usleep_range_ex(pdata->pAdapter, 500, 1000); + } while ((prxq) && (busy)); + if (0 == busy) { + rx_status = readreg(pdata->pAdapter, + FXGMAC_MTL_REG(pdata, queue, MTL_Q_RQDR)); + DbgPrintF( + MP_WARN, + "warning !!!timed out waiting for Rx queue %u to empty\n", + queue); + } +#endif +} + +static void fxgmac_enable_rx(struct fxgmac_pdata *pdata) +{ +#ifndef DPDK + struct fxgmac_channel *channel; +#endif + unsigned int i; + u32 regval; + + /* Enable each Rx DMA channel */ +#ifndef DPDK + channel = pdata->channel_head; + for (i = 0; i < pdata->channel_count; i++, channel++) { +#ifndef UBOOT + if (!channel->rx_ring) + break; +#endif + regval = readreg(pdata->pAdapter, + FXGMAC_DMA_REG(channel, DMA_CH_RCR)); + regval = FXGMAC_SET_REG_BITS(regval, DMA_CH_RCR_SR_POS, + DMA_CH_RCR_SR_LEN, 1); + writereg(pdata->pAdapter, regval, + FXGMAC_DMA_REG(channel, DMA_CH_RCR)); + } + +#else + PMD_INIT_FUNC_TRACE(); + struct fxgmac_rx_queue *rxq; + struct rte_eth_dev *dev = pdata->expansion.eth_dev; + + for (i = 0; i < dev->data->nb_rx_queues; i++) { + rxq = dev->data->rx_queues[i]; + if (!rxq) { + DPRINTK("Rx queue not setup for port %d\n", + dev->data->port_id); + return; + } + /* Enable Rx DMA channel */ + FXGMAC_DMA_IOWRITE_BITS(rxq, DMA_CH_RCR, SR, 1); + } +#endif + + /* Enable each Rx queue */ + regval = 0; + for (i = 0; i < pdata->rx_q_count; i++) + regval |= (0x02 << (i << 1)); + writereg(pdata->pAdapter, regval, pdata->mac_regs + MAC_RQC0R); + +#ifndef DPDK + /* Enable MAC Rx */ + regval = readreg(pdata->pAdapter, pdata->mac_regs + MAC_CR); + regval = FXGMAC_SET_REG_BITS(regval, MAC_CR_CST_POS, MAC_CR_CST_LEN, 1); + regval = FXGMAC_SET_REG_BITS(regval, MAC_CR_ACS_POS, MAC_CR_ACS_LEN, 1); + regval = FXGMAC_SET_REG_BITS(regval, MAC_CR_RE_POS, MAC_CR_RE_LEN, 1); + writereg(pdata->pAdapter, regval, pdata->mac_regs + MAC_CR); +#else + /* Enable MAC Rx */ + FXGMAC_IOWRITE_BITS(pdata, MAC_ECR, DCRCC, 1); + + /* Frame is forwarded after stripping CRC to application*/ + if (pdata->expansion.crc_strip_enable) { + FXGMAC_IOWRITE_BITS(pdata, MAC_CR, CST, 1); + FXGMAC_IOWRITE_BITS(pdata, MAC_CR, ACS, 1); + } + FXGMAC_IOWRITE_BITS(pdata, MAC_CR, RE, 1); +#endif +} +static void fxgmac_enable_channel_rx(struct fxgmac_pdata *pdata, + unsigned int queue) +{ + struct fxgmac_channel *channel; + u32 regval; + + /* Enable Rx DMA channel */ + channel = pdata->channel_head + queue; + + if (!channel->rx_ring) + return; + regval = readreg(pdata->pAdapter, FXGMAC_DMA_REG(channel, DMA_CH_RCR)); + regval = FXGMAC_SET_REG_BITS(regval, DMA_CH_RCR_SR_POS, + DMA_CH_RCR_SR_LEN, 1); + writereg(pdata->pAdapter, regval, FXGMAC_DMA_REG(channel, DMA_CH_RCR)); + /* Enable Rx queue */ + regval = readreg(pdata->pAdapter, pdata->mac_regs + MAC_RQC0R); + regval |= (0x02 << (queue << 1)); + writereg(pdata->pAdapter, regval, pdata->mac_regs + MAC_RQC0R); + + /* Enable MAC Rx */ + regval = readreg(pdata->pAdapter, pdata->mac_regs + MAC_CR); + if (!(regval & ((0x01 << MAC_CR_CST_POS) | (0x01 << MAC_CR_ACS_POS) | + (0x01 << MAC_CR_RE_POS)))) { + regval = FXGMAC_SET_REG_BITS(regval, MAC_CR_CST_POS, + MAC_CR_CST_LEN, 1); + regval = FXGMAC_SET_REG_BITS(regval, MAC_CR_ACS_POS, + MAC_CR_ACS_LEN, 1); + regval = FXGMAC_SET_REG_BITS(regval, MAC_CR_RE_POS, + MAC_CR_RE_LEN, 1); + writereg(pdata->pAdapter, regval, pdata->mac_regs + MAC_CR); + } +} + +static void fxgmac_disable_rx(struct fxgmac_pdata *pdata) +{ +#ifndef DPDK + struct fxgmac_channel *channel; +#endif + unsigned int i; + u32 regval; + + /* Disable MAC Rx */ + regval = readreg(pdata->pAdapter, pdata->mac_regs + MAC_CR); + regval = FXGMAC_SET_REG_BITS(regval, MAC_CR_CST_POS, MAC_CR_CST_LEN, 0); + regval = FXGMAC_SET_REG_BITS(regval, MAC_CR_ACS_POS, MAC_CR_ACS_LEN, 0); + regval = FXGMAC_SET_REG_BITS(regval, MAC_CR_RE_POS, MAC_CR_RE_LEN, 0); + writereg(pdata->pAdapter, regval, pdata->mac_regs + MAC_CR); + + /* Prepare for Rx DMA channel stop */ +#ifndef DPDK + for (i = 0; i < pdata->rx_q_count; i++) + fxgmac_prepare_rx_stop(pdata, i); +#else + PMD_INIT_FUNC_TRACE(); + struct fxgmac_rx_queue *rxq; + struct rte_eth_dev *dev = pdata->expansion.eth_dev; + + for (i = 0; i < dev->data->nb_rx_queues; i++) { + rxq = dev->data->rx_queues[i]; + if (!rxq) { + DPRINTK("Rx queue not setup for port %d\n", + dev->data->port_id); + return; + } + + fxgmac_prepare_rx_stop(pdata, i); + } +#endif + + /* Disable each Rx queue */ + writereg(pdata->pAdapter, 0, pdata->mac_regs + MAC_RQC0R); + + /* Disable each Rx DMA channel */ +#ifndef DPDK + channel = pdata->channel_head; + if (channel != NULL) { + for (i = 0; i < pdata->channel_count; i++, channel++) { + if (!channel->rx_ring) + break; + + regval = readreg(pdata->pAdapter, + FXGMAC_DMA_REG(channel, DMA_CH_RCR)); + regval = FXGMAC_SET_REG_BITS(regval, DMA_CH_RCR_SR_POS, + DMA_CH_RCR_SR_LEN, 0); + writereg(pdata->pAdapter, regval, + FXGMAC_DMA_REG(channel, DMA_CH_RCR)); + } + } +#else + for (i = 0; i < dev->data->nb_rx_queues; i++) { + rxq = dev->data->rx_queues[i]; + if (!rxq) { + DPRINTK("Rx queue not setup for port %d\n", + dev->data->port_id); + return; + } + FXGMAC_DMA_IOWRITE_BITS(rxq, DMA_CH_RCR, SR, 0); + } +#endif +} + +static int fxgmac_is_context_desc(struct fxgmac_dma_desc *dma_desc) +{ + /* Rx and Tx share CTXT bit, so check TDES3.CTXT bit */ + int regval; + regval = (int)FXGMAC_GET_REG_BITS_LE(dma_desc->desc3, + TX_NORMAL_DESC3_CTXT_POS, + TX_NORMAL_DESC3_CTXT_LEN); + return regval; +} + +static int fxgmac_is_last_desc(struct fxgmac_dma_desc *dma_desc) +{ + /* Rx and Tx share LD bit, so check TDES3.LD bit */ + int regval; + regval = (int)FXGMAC_GET_REG_BITS_LE(dma_desc->desc3, + TX_NORMAL_DESC3_LD_POS, + TX_NORMAL_DESC3_LD_LEN); + return regval; +} + +static int fxgmac_disable_tx_flow_control(struct fxgmac_pdata *pdata) +{ + unsigned int max_q_count, q_count; + unsigned int reg; + unsigned int i; + u32 regval; + + /* Clear MTL flow control */ + for (i = 0; i < pdata->rx_q_count; i++) { + regval = readreg(pdata->pAdapter, + FXGMAC_MTL_REG(pdata, i, MTL_Q_RQOMR)); + regval = FXGMAC_SET_REG_BITS(regval, MTL_Q_RQOMR_EHFC_POS, + MTL_Q_RQOMR_EHFC_LEN, 0); + writereg(pdata->pAdapter, regval, + FXGMAC_MTL_REG(pdata, i, MTL_Q_RQOMR)); + } + + /* Clear MAC flow control */ + max_q_count = FXGMAC_MAX_FLOW_CONTROL_QUEUES; + q_count = min_t(unsigned int, pdata->tx_q_count, max_q_count); + reg = MAC_Q0TFCR; + for (i = 0; i < q_count; i++) { + regval = readreg(pdata->pAdapter, pdata->mac_regs + reg); + regval = FXGMAC_SET_REG_BITS(regval, MAC_Q0TFCR_TFE_POS, + MAC_Q0TFCR_TFE_LEN, 0); + writereg(pdata->pAdapter, regval, pdata->mac_regs + reg); + + reg += MAC_QTFCR_INC; + } + + return 0; +} + +static int fxgmac_enable_tx_flow_control(struct fxgmac_pdata *pdata) +{ + unsigned int max_q_count, q_count; + unsigned int reg; + unsigned int i; + u32 regval; + + /* Set MTL flow control */ + for (i = 0; i < pdata->rx_q_count; i++) { + regval = readreg(pdata->pAdapter, + FXGMAC_MTL_REG(pdata, i, MTL_Q_RQOMR)); + regval = FXGMAC_SET_REG_BITS(regval, MTL_Q_RQOMR_EHFC_POS, + MTL_Q_RQOMR_EHFC_LEN, 1); + writereg(pdata->pAdapter, regval, + FXGMAC_MTL_REG(pdata, i, MTL_Q_RQOMR)); + } + + /* Set MAC flow control */ + max_q_count = FXGMAC_MAX_FLOW_CONTROL_QUEUES; + q_count = min_t(unsigned int, pdata->tx_q_count, max_q_count); + reg = MAC_Q0TFCR; + for (i = 0; i < q_count; i++) { + regval = readreg(pdata->pAdapter, pdata->mac_regs + reg); + + /* Enable transmit flow control */ + regval = FXGMAC_SET_REG_BITS(regval, MAC_Q0TFCR_TFE_POS, + MAC_Q0TFCR_TFE_LEN, 1); + /* Set pause time */ + regval = FXGMAC_SET_REG_BITS(regval, MAC_Q0TFCR_PT_POS, + MAC_Q0TFCR_PT_LEN, 0xffff); + + writereg(pdata->pAdapter, regval, pdata->mac_regs + reg); + + reg += MAC_QTFCR_INC; + } + + return 0; +} + +static int fxgmac_disable_rx_flow_control(struct fxgmac_pdata *pdata) +{ + u32 regval; + + regval = readreg(pdata->pAdapter, pdata->mac_regs + MAC_RFCR); + regval = FXGMAC_SET_REG_BITS(regval, MAC_RFCR_RFE_POS, MAC_RFCR_RFE_LEN, + 0); + writereg(pdata->pAdapter, regval, pdata->mac_regs + MAC_RFCR); + + return 0; +} + +static int fxgmac_enable_rx_flow_control(struct fxgmac_pdata *pdata) +{ + u32 regval; + + regval = readreg(pdata->pAdapter, pdata->mac_regs + MAC_RFCR); + regval = FXGMAC_SET_REG_BITS(regval, MAC_RFCR_RFE_POS, MAC_RFCR_RFE_LEN, + 1); + writereg(pdata->pAdapter, regval, pdata->mac_regs + MAC_RFCR); + + return 0; +} + +static int fxgmac_config_tx_flow_control(struct fxgmac_pdata *pdata) +{ + if (pdata->tx_pause) + fxgmac_enable_tx_flow_control(pdata); + else + fxgmac_disable_tx_flow_control(pdata); + + return 0; +} + +static int fxgmac_config_rx_flow_control(struct fxgmac_pdata *pdata) +{ + if (pdata->rx_pause) + fxgmac_enable_rx_flow_control(pdata); + else + fxgmac_disable_rx_flow_control(pdata); + + return 0; +} + +static int fxgmac_config_rx_coalesce(struct fxgmac_pdata *pdata) +{ +#ifndef DPDK + struct fxgmac_channel *channel; + unsigned int i; + u32 regval; + + channel = pdata->channel_head; + for (i = 0; i < pdata->channel_count; i++, channel++) { + if (!channel->rx_ring) + break; + + regval = readreg(pdata->pAdapter, + FXGMAC_DMA_REG(channel, DMA_CH_RIWT)); + regval = FXGMAC_SET_REG_BITS(regval, DMA_CH_RIWT_RWT_POS, + DMA_CH_RIWT_RWT_LEN, + pdata->rx_riwt); + writereg(pdata->pAdapter, regval, + FXGMAC_DMA_REG(channel, DMA_CH_RIWT)); + } +#else + struct fxgmac_rx_queue *rxq; + unsigned int i; + + for (i = 0; i < pdata->expansion.eth_dev->data->nb_rx_queues; i++) { + rxq = pdata->expansion.eth_dev->data->rx_queues[i]; + if (!rxq) { + DPRINTK("Rx queue not setup for port %d\n", + pdata->expansion.eth_dev->data->port_id); + return -1; + } + FXGMAC_DMA_IOWRITE_BITS(rxq, DMA_CH_RIWT, RWT, pdata->rx_riwt); + } +#endif + + return 0; +} + +static void fxgmac_config_rx_fep_disable(struct fxgmac_pdata *pdata) +{ + unsigned int i; + u32 regval; + + for (i = 0; i < pdata->rx_q_count; i++) { + regval = readreg(pdata->pAdapter, + FXGMAC_MTL_REG(pdata, i, MTL_Q_RQOMR)); + regval = FXGMAC_SET_REG_BITS(regval, MTL_Q_RQOMR_FEP_POS, + MTL_Q_RQOMR_FEP_LEN, + MTL_FEP_ENABLE); + writereg(pdata->pAdapter, regval, + FXGMAC_MTL_REG(pdata, i, MTL_Q_RQOMR)); + } +} + +static void fxgmac_config_rx_fup_enable(struct fxgmac_pdata *pdata) +{ + unsigned int i; + u32 regval; + + for (i = 0; i < pdata->rx_q_count; i++) { + regval = readreg(pdata->pAdapter, + FXGMAC_MTL_REG(pdata, i, MTL_Q_RQOMR)); + regval = FXGMAC_SET_REG_BITS(regval, MTL_Q_RQOMR_FUP_POS, + MTL_Q_RQOMR_FUP_LEN, 1); + writereg(pdata->pAdapter, regval, + FXGMAC_MTL_REG(pdata, i, MTL_Q_RQOMR)); + } +} + +static int fxgmac_config_tx_coalesce(struct fxgmac_pdata *pdata) +{ + pdata = pdata; + return 0; +} + +static void fxgmac_config_rx_buffer_size(struct fxgmac_pdata *pdata) +{ +#ifndef DPDK + struct fxgmac_channel *channel; + unsigned int i; + u32 regval; + + channel = pdata->channel_head; + for (i = 0; i < pdata->channel_count; i++, channel++) { + if (!channel->rx_ring) + break; + + regval = readreg(pdata->pAdapter, + FXGMAC_DMA_REG(channel, DMA_CH_RCR)); + regval = FXGMAC_SET_REG_BITS(regval, DMA_CH_RCR_RBSZ_POS, + DMA_CH_RCR_RBSZ_LEN, + pdata->rx_buf_size); + writereg(pdata->pAdapter, regval, + FXGMAC_DMA_REG(channel, DMA_CH_RCR)); + } +#else + struct fxgmac_rx_queue *rxq; + unsigned int i; + + for (i = 0; i < pdata->expansion.eth_dev->data->nb_rx_queues; i++) { + rxq = pdata->expansion.eth_dev->data->rx_queues[i]; + if (!rxq) { + DPRINTK("Rx queue not setup for port %d\n", + pdata->expansion.eth_dev->data->port_id); + return; + } + + rxq->buf_size = rte_pktmbuf_data_room_size(rxq->mb_pool) - + RTE_PKTMBUF_HEADROOM; + rxq->buf_size = (rxq->buf_size + FXGMAC_RX_BUF_ALIGN - 1) & + ~(FXGMAC_RX_BUF_ALIGN - 1); + + if (rxq->buf_size > pdata->rx_buf_size) + pdata->rx_buf_size = rxq->buf_size; + + FXGMAC_DMA_IOWRITE_BITS(rxq, DMA_CH_RCR, RBSZ, rxq->buf_size); + } +#endif +} + +static void fxgmac_config_tso_mode(struct fxgmac_pdata *pdata) +{ + u32 tso; +#ifndef DPDK + struct fxgmac_channel *channel; + unsigned int i; + u32 regval; + tso = pdata->hw_feat.tso; + + channel = pdata->channel_head; + for (i = 0; i < pdata->channel_count; i++, channel++) { + if (!channel->tx_ring) + break; + + regval = readreg(pdata->pAdapter, + FXGMAC_DMA_REG(channel, DMA_CH_TCR)); + regval = FXGMAC_SET_REG_BITS(regval, DMA_CH_TCR_TSE_POS, + DMA_CH_TCR_TSE_LEN, tso); + writereg(pdata->pAdapter, regval, + FXGMAC_DMA_REG(channel, DMA_CH_TCR)); + } +#else + struct fxgmac_tx_queue *txq; + unsigned int i; + tso = pdata->hw_feat.tso; + for (i = 0; i < pdata->expansion.eth_dev->data->nb_tx_queues; i++) { + txq = pdata->expansion.eth_dev->data->tx_queues[i]; + if (!txq) { + DPRINTK("Tx queue not setup for port %d\n", + pdata->expansion.eth_dev->data->port_id); + return; + } + + FXGMAC_DMA_IOWRITE_BITS(txq, DMA_CH_TCR, TSE, tso); + } +#endif +} + +static void fxgmac_config_sph_mode(struct fxgmac_pdata *pdata) +{ + unsigned int i; + u32 regval; + +#ifndef DPDK + struct fxgmac_channel *channel; + channel = pdata->channel_head; + for (i = 0; i < pdata->channel_count; i++, channel++) { + if (!channel->rx_ring) + break; + + regval = readreg(pdata->pAdapter, + FXGMAC_DMA_REG(channel, DMA_CH_CR)); + regval = FXGMAC_SET_REG_BITS(regval, DMA_CH_CR_SPH_POS, + DMA_CH_CR_SPH_LEN, 0); + writereg(pdata->pAdapter, regval, + FXGMAC_DMA_REG(channel, DMA_CH_CR)); + } +#else + struct fxgmac_rx_queue *rxq; + + for (i = 0; i < pdata->expansion.eth_dev->data->nb_rx_queues; i++) { + rxq = pdata->expansion.eth_dev->data->rx_queues[i]; + if (!rxq) { + DPRINTK("Rx queue not setup for port %d\n", + pdata->expansion.eth_dev->data->port_id); + return; + } + FXGMAC_DMA_IOWRITE_BITS(rxq, DMA_CH_CR, SPH, 0); + } +#endif + + regval = readreg(pdata->pAdapter, pdata->mac_regs + MAC_ECR); + regval = FXGMAC_SET_REG_BITS(regval, MAC_ECR_HDSMS_POS, + MAC_ECR_HDSMS_LEN, FXGMAC_SPH_HDSMS_SIZE); + writereg(pdata->pAdapter, regval, pdata->mac_regs + MAC_ECR); +} + +static unsigned long fxgmac_usec_to_riwt(struct fxgmac_pdata *pdata, + unsigned int usec) +{ + unsigned long rate; + unsigned long ret; + + rate = pdata->sysclk_rate; + + /* Convert the input usec value to the watchdog timer value. Each + * watchdog timer value is equivalent to 256 clock cycles. + * Calculate the required value as: + * ( usec * ( system_clock_mhz / 10^6 ) / 256 + */ + ret = (usec * (rate / 1000000)) / 256; + + return ret; +} + +static unsigned long fxgmac_riwt_to_usec(struct fxgmac_pdata *pdata, + unsigned int riwt) +{ + unsigned long rate; + unsigned long ret; + + rate = pdata->sysclk_rate; + + /* Convert the input watchdog timer value to the usec value. Each + * watchdog timer value is equivalent to 256 clock cycles. + * Calculate the required value as: + * ( riwt * 256 ) / ( system_clock_mhz / 10^6 ) + */ + ret = (riwt * 256) / (rate / 1000000); + + return ret; +} + +static int fxgmac_config_rx_threshold(struct fxgmac_pdata *pdata, + unsigned int val) +{ + unsigned int i; + u32 regval; + + for (i = 0; i < pdata->rx_q_count; i++) { + regval = readreg(pdata->pAdapter, + FXGMAC_MTL_REG(pdata, i, MTL_Q_RQOMR)); + regval = FXGMAC_SET_REG_BITS(regval, MTL_Q_RQOMR_RTC_POS, + MTL_Q_RQOMR_RTC_LEN, val); + writereg(pdata->pAdapter, regval, + FXGMAC_MTL_REG(pdata, i, MTL_Q_RQOMR)); + } + + return 0; +} + +static void fxgmac_config_mtl_mode(struct fxgmac_pdata *pdata) +{ + unsigned int i; + u32 regval; + + /* Set Tx to weighted round robin scheduling algorithm */ + regval = readreg(pdata->pAdapter, pdata->mac_regs + MTL_OMR); + regval = FXGMAC_SET_REG_BITS(regval, MTL_OMR_ETSALG_POS, + MTL_OMR_ETSALG_LEN, MTL_ETSALG_WRR); + writereg(pdata->pAdapter, regval, pdata->mac_regs + MTL_OMR); + /* Set Tx traffic classes to use WRR algorithm with equal weights */ + for (i = 0; i < pdata->tx_q_count /*hw_feat.tc_cnt*/; i++) { + regval = readreg(pdata->pAdapter, + FXGMAC_MTL_REG(pdata, i, MTL_TC_QWR)); + regval = FXGMAC_SET_REG_BITS(regval, MTL_TC_QWR_QW_POS, + MTL_TC_QWR_QW_LEN, 1); + writereg(pdata->pAdapter, regval, + FXGMAC_MTL_REG(pdata, i, MTL_TC_QWR)); + } + + /* Set Rx to strict priority algorithm */ + regval = readreg(pdata->pAdapter, pdata->mac_regs + MTL_OMR); + regval = FXGMAC_SET_REG_BITS(regval, MTL_OMR_RAA_POS, MTL_OMR_RAA_LEN, + MTL_RAA_SP); + writereg(pdata->pAdapter, regval, pdata->mac_regs + MTL_OMR); +} + +static void fxgmac_config_queue_mapping(struct fxgmac_pdata *pdata) +{ + unsigned int ppq, ppq_extra, prio, prio_queues; + + unsigned int reg; + unsigned int mask; + unsigned int i, j; + u32 regval; + + /* Map the MTL Tx Queues to Traffic Classes + * Note: Tx Queues >= Traffic Classes + */ + + /* Map the 8 VLAN priority values to available MTL Rx queues */ + prio_queues = + min_t(unsigned int, IEEE_8021QAZ_MAX_TCS, pdata->rx_q_count); + ppq = IEEE_8021QAZ_MAX_TCS / prio_queues; + ppq_extra = IEEE_8021QAZ_MAX_TCS % prio_queues; + + reg = MAC_RQC2R; + regval = 0; + for (i = 0, prio = 0; i < prio_queues;) { + mask = 0; + for (j = 0; j < ppq; j++) { + netif_dbg(pdata, drv, pdata->netdev, "PRIO%u,", prio); + netif_dbg(pdata, drv, pdata->netdev, + " mapped to RXq%u\n", i); + mask |= (1 << prio); + prio++; + } + + if (i < ppq_extra) { + netif_dbg(pdata, drv, pdata->netdev, "PRIO%u.", i); + netif_dbg(pdata, drv, pdata->netdev, " mapped to Rxq%u", + i); + mask |= (1 << prio); + prio++; + } + + regval |= (mask << ((i++ % MAC_RQC2_Q_PER_REG) << 3)); + + if ((i % MAC_RQC2_Q_PER_REG) && (i != prio_queues)) + continue; + + writereg(pdata->pAdapter, regval, pdata->mac_regs + reg); + reg += MAC_RQC2_INC; + regval = 0; + } + + /* Configure one to one, MTL Rx queue to DMA Rx channel mapping + * ie Q0 <--> CH0, Q1 <--> CH1 ... Q11 <--> CH11 + */ + reg = MTL_RQDCM0R; + regval = readreg(pdata->pAdapter, pdata->mac_regs + reg); + regval |= (MTL_RQDCM0R_Q0MDMACH | MTL_RQDCM0R_Q1MDMACH | + MTL_RQDCM0R_Q2MDMACH | MTL_RQDCM0R_Q3MDMACH); + + if (pdata->rss) { + /* in version later 0617, need to enable DA-based DMA Channel Selection to let RSS work, + * ie, bit4,12,20,28 for Q0,1,2,3 individual + */ + regval |= (MTL_RQDCM0R_Q0DDMACH | MTL_RQDCM0R_Q1DDMACH | + MTL_RQDCM0R_Q2DDMACH | MTL_RQDCM0R_Q3DDMACH); + } + + writereg(pdata->pAdapter, regval, pdata->mac_regs + reg); + + reg += MTL_RQDCM_INC; + regval = readreg(pdata->pAdapter, pdata->mac_regs + reg); + regval |= (MTL_RQDCM1R_Q4MDMACH | MTL_RQDCM1R_Q5MDMACH | + MTL_RQDCM1R_Q6MDMACH | MTL_RQDCM1R_Q7MDMACH); + writereg(pdata->pAdapter, regval, pdata->mac_regs + reg); +} + +static u32 fxgmac_calculate_per_queue_fifo(unsigned long fifo_size, + unsigned int queue_count) +{ + unsigned long q_fifo_size; + unsigned long p_fifo; + + /* Calculate the configured fifo size */ + q_fifo_size = 1 << (fifo_size + 7); + + /* The configured value may not be the actual amount of fifo RAM */ + q_fifo_size = min_t(unsigned int, FXGMAC_MAX_FIFO, q_fifo_size); + + q_fifo_size = q_fifo_size / queue_count; + + /* Each increment in the queue fifo size represents 256 bytes of + * fifo, with 0 representing 256 bytes. Distribute the fifo equally + * between the queues. + */ + p_fifo = q_fifo_size / 256; + if (p_fifo) + p_fifo--; + + return p_fifo; +} + +static u32 fxgmac_calculate_max_checksum_size(struct fxgmac_pdata *pdata) +{ + u32 fifo_size; + + fifo_size = fxgmac_calculate_per_queue_fifo(pdata->hw_feat.tx_fifo_size, + pdata->tx_q_count); + + /* Each increment in the queue fifo size represents 256 bytes of + * fifo, with 0 representing 256 bytes. Distribute the fifo equally + * between the queues. + */ + fifo_size = (fifo_size + 1) * 256; + + /* Packet size < TxQSize - (PBL + N)*(DATAWIDTH/8), + * Datawidth = 128 + * If Datawidth = 32, N = 7, elseif Datawidth != 32, N = 5. + * TxQSize is indicated by TQS field of MTL_TxQ#_Operation_Mode register + * PBL = TxPBL field in the DMA_CH#_TX_Control register in all DMA configurations. + */ + fifo_size -= (pdata->tx_pbl * (pdata->pblx8 ? 8 : 1) + 5) * + (FXGMAC_DATA_WIDTH / 8); + fifo_size -= 256; + + return fifo_size; +} + +static void fxgmac_config_tx_fifo_size(struct fxgmac_pdata *pdata) +{ + u32 fifo_size; + unsigned int i; + u32 regval; + + fifo_size = fxgmac_calculate_per_queue_fifo(pdata->hw_feat.tx_fifo_size, +#if FXGMAC_FAKE_4_TX_QUEUE_ENABLED + 1); +#else + pdata->tx_q_count); +#endif + + for (i = 0; i < pdata->tx_q_count; i++) { +#if FXGMAC_FAKE_4_TX_QUEUE_ENABLED + + if (i > 0) + break; +#endif + regval = readreg(pdata->pAdapter, + FXGMAC_MTL_REG(pdata, i, MTL_Q_TQOMR)); + regval = FXGMAC_SET_REG_BITS(regval, MTL_Q_TQOMR_TQS_POS, + MTL_Q_TQOMR_TQS_LEN, fifo_size); + writereg(pdata->pAdapter, regval, + FXGMAC_MTL_REG(pdata, i, MTL_Q_TQOMR)); + } + + netif_info(pdata, drv, pdata->netdev, "%d Tx hardware queues,", + pdata->tx_q_count); + netif_info(pdata, drv, pdata->netdev, " %d byte fifo per queue\n", + ((fifo_size + 1) * 256)); +} + +static void fxgmac_config_rx_fifo_size(struct fxgmac_pdata *pdata) +{ + u32 fifo_size; + unsigned int i; + u32 regval; + + fifo_size = fxgmac_calculate_per_queue_fifo(pdata->hw_feat.rx_fifo_size, + pdata->rx_q_count); + + for (i = 0; i < pdata->rx_q_count; i++) { + regval = readreg(pdata->pAdapter, + FXGMAC_MTL_REG(pdata, i, MTL_Q_RQOMR)); + regval = FXGMAC_SET_REG_BITS(regval, MTL_Q_RQOMR_RQS_POS, + MTL_Q_RQOMR_RQS_LEN, fifo_size); + writereg(pdata->pAdapter, regval, + FXGMAC_MTL_REG(pdata, i, MTL_Q_RQOMR)); + } + + netif_info(pdata, drv, pdata->netdev, "%d Rx hardware queues,", + pdata->rx_q_count); + netif_info(pdata, drv, pdata->netdev, " %d byte fifo per queue\n", + ((fifo_size + 1) * 256)); +} + +static void fxgmac_config_flow_control_threshold(struct fxgmac_pdata *pdata) +{ + unsigned int i; + u32 regval; + + for (i = 0; i < pdata->rx_q_count; i++) { + regval = readreg(pdata->pAdapter, + FXGMAC_MTL_REG(pdata, i, MTL_Q_RQOMR)); + /* Activate flow control when less than 4k left in fifo */ + regval = FXGMAC_SET_REG_BITS(regval, MTL_Q_RQOMR_RFA_POS, + MTL_Q_RQOMR_RFA_LEN, 6); + /* De-activate flow control when more than 6k left in fifo */ + regval = FXGMAC_SET_REG_BITS(regval, MTL_Q_RQOMR_RFD_POS, + MTL_Q_RQOMR_RFD_LEN, 10); + writereg(pdata->pAdapter, regval, + FXGMAC_MTL_REG(pdata, i, MTL_Q_RQOMR)); + } +} + +static int fxgmac_config_tx_threshold(struct fxgmac_pdata *pdata, + unsigned int val) +{ + unsigned int i; + u32 regval; + + for (i = 0; i < pdata->tx_q_count; i++) { + regval = readreg(pdata->pAdapter, + FXGMAC_MTL_REG(pdata, i, MTL_Q_TQOMR)); + regval = FXGMAC_SET_REG_BITS(regval, MTL_Q_TQOMR_TTC_POS, + MTL_Q_TQOMR_TTC_LEN, val); + writereg(pdata->pAdapter, regval, + FXGMAC_MTL_REG(pdata, i, MTL_Q_TQOMR)); + } + + return 0; +} + +static int fxgmac_config_rsf_mode(struct fxgmac_pdata *pdata, unsigned int val) +{ + unsigned int i; + u32 regval; + + for (i = 0; i < pdata->rx_q_count; i++) { + regval = readreg(pdata->pAdapter, + FXGMAC_MTL_REG(pdata, i, MTL_Q_RQOMR)); + regval = FXGMAC_SET_REG_BITS(regval, MTL_Q_RQOMR_RSF_POS, + MTL_Q_RQOMR_RSF_LEN, val); + writereg(pdata->pAdapter, regval, + FXGMAC_MTL_REG(pdata, i, MTL_Q_RQOMR)); + } + + return 0; +} + +static int fxgmac_config_tsf_mode(struct fxgmac_pdata *pdata, unsigned int val) +{ + unsigned int i; + u32 regval; + + for (i = 0; i < pdata->tx_q_count; i++) { + regval = readreg(pdata->pAdapter, + FXGMAC_MTL_REG(pdata, i, MTL_Q_TQOMR)); + regval = FXGMAC_SET_REG_BITS(regval, MTL_Q_TQOMR_TSF_POS, + MTL_Q_TQOMR_TSF_LEN, val); + writereg(pdata->pAdapter, regval, + FXGMAC_MTL_REG(pdata, i, MTL_Q_TQOMR)); + } + + return 0; +} + +static int fxgmac_config_osp_mode(struct fxgmac_pdata *pdata) +{ +#ifndef DPDK + struct fxgmac_channel *channel; + unsigned int i; + u32 regval; + + channel = pdata->channel_head; + for (i = 0; i < pdata->channel_count; i++, channel++) { + if (!channel->tx_ring) + break; + + regval = readreg(pdata->pAdapter, + FXGMAC_DMA_REG(channel, DMA_CH_TCR)); + regval = FXGMAC_SET_REG_BITS(regval, DMA_CH_TCR_OSP_POS, + DMA_CH_TCR_OSP_LEN, + pdata->tx_osp_mode); + writereg(pdata->pAdapter, regval, + FXGMAC_DMA_REG(channel, DMA_CH_TCR)); + } +#else + /* Force DMA to operate on second packet before closing descriptors + * of first packet + */ + struct fxgmac_tx_queue *txq; + unsigned int i; + + for (i = 0; i < pdata->expansion.eth_dev->data->nb_tx_queues; i++) { + txq = pdata->expansion.eth_dev->data->tx_queues[i]; + if (!txq) { + DPRINTK("Tx queue not setup for port %d\n", + pdata->expansion.eth_dev->data->port_id); + return -1; + } + + FXGMAC_DMA_IOWRITE_BITS(txq, DMA_CH_TCR, OSP, + pdata->tx_osp_mode); + } +#endif + return 0; +} + +static int fxgmac_config_pblx8(struct fxgmac_pdata *pdata) +{ +#ifndef DPDK + struct fxgmac_channel *channel; + unsigned int i; + u32 regval; + + channel = pdata->channel_head; + for (i = 0; i < pdata->channel_count; i++, channel++) { + regval = readreg(pdata->pAdapter, + FXGMAC_DMA_REG(channel, DMA_CH_CR)); + regval = FXGMAC_SET_REG_BITS(regval, DMA_CH_CR_PBLX8_POS, + DMA_CH_CR_PBLX8_LEN, pdata->pblx8); + writereg(pdata->pAdapter, regval, + FXGMAC_DMA_REG(channel, DMA_CH_CR)); + } +#else + struct fxgmac_tx_queue *txq; + unsigned int i; + + for (i = 0; i < pdata->expansion.eth_dev->data->nb_tx_queues; i++) { + txq = pdata->expansion.eth_dev->data->tx_queues[i]; + if (!txq) { + DPRINTK("Tx queue not setup for port %d\n", + pdata->expansion.eth_dev->data->port_id); + return -1; + } + + FXGMAC_DMA_IOWRITE_BITS(txq, DMA_CH_CR, PBLX8, pdata->pblx8); + } +#endif + + return 0; +} + +static u32 fxgmac_get_tx_pbl_val(struct fxgmac_pdata *pdata) +{ + u32 regval; + + regval = readreg(pdata->pAdapter, + FXGMAC_DMA_REG(pdata->channel_head, DMA_CH_TCR)); + regval = FXGMAC_GET_REG_BITS(regval, DMA_CH_TCR_PBL_POS, + DMA_CH_TCR_PBL_LEN); + return regval; +} + +static int fxgmac_config_tx_pbl_val(struct fxgmac_pdata *pdata) +{ +#ifndef DPDK + struct fxgmac_channel *channel; + unsigned int i; + u32 regval; + + channel = pdata->channel_head; + for (i = 0; i < pdata->channel_count; i++, channel++) { + if (!channel->tx_ring) + break; + + regval = readreg(pdata->pAdapter, + FXGMAC_DMA_REG(channel, DMA_CH_TCR)); + regval = FXGMAC_SET_REG_BITS(regval, DMA_CH_TCR_PBL_POS, + DMA_CH_TCR_PBL_LEN, pdata->tx_pbl); + writereg(pdata->pAdapter, regval, + FXGMAC_DMA_REG(channel, DMA_CH_TCR)); + } +#else + struct fxgmac_tx_queue *txq; + unsigned int i; + + for (i = 0; i < pdata->expansion.eth_dev->data->nb_tx_queues; i++) { + txq = pdata->expansion.eth_dev->data->tx_queues[i]; + if (!txq) { + DPRINTK("Tx queue not setup for port %d\n", + pdata->expansion.eth_dev->data->port_id); + return -1; + } + FXGMAC_DMA_IOWRITE_BITS(txq, DMA_CH_TCR, PBL, pdata->tx_pbl); + } +#endif + + return 0; +} + +static u32 fxgmac_get_rx_pbl_val(struct fxgmac_pdata *pdata) +{ + u32 regval; + + regval = readreg(pdata->pAdapter, + FXGMAC_DMA_REG(pdata->channel_head, DMA_CH_RCR)); + regval = FXGMAC_GET_REG_BITS(regval, DMA_CH_RCR_PBL_POS, + DMA_CH_RCR_PBL_LEN); + return regval; +} + +static int fxgmac_config_rx_pbl_val(struct fxgmac_pdata *pdata) +{ +#ifndef DPDK + struct fxgmac_channel *channel; + unsigned int i; + u32 regval; + + channel = pdata->channel_head; + for (i = 0; i < pdata->channel_count; i++, channel++) { + if (!channel->rx_ring) + break; + + regval = readreg(pdata->pAdapter, + FXGMAC_DMA_REG(channel, DMA_CH_RCR)); + regval = FXGMAC_SET_REG_BITS(regval, DMA_CH_RCR_PBL_POS, + DMA_CH_RCR_PBL_LEN, pdata->rx_pbl); + writereg(pdata->pAdapter, regval, + FXGMAC_DMA_REG(channel, DMA_CH_RCR)); + } +#else + struct fxgmac_rx_queue *rxq; + unsigned int i; + + for (i = 0; i < pdata->expansion.eth_dev->data->nb_rx_queues; i++) { + rxq = pdata->expansion.eth_dev->data->rx_queues[i]; + if (!rxq) { + DPRINTK("Rx queue not setup for port %d\n", + pdata->expansion.eth_dev->data->port_id); + return -1; + } + FXGMAC_DMA_IOWRITE_BITS(rxq, DMA_CH_RCR, PBL, pdata->rx_pbl); + } +#endif + + return 0; +} + +static u64 fxgmac_mmc_read(struct fxgmac_pdata *pdata, unsigned int reg_lo) +{ + /* bool read_hi; */ + u64 val; + + val = (u64)readreg(pdata->pAdapter, pdata->mac_regs + reg_lo); + + /* + if (read_hi) + val |= ((u64)readreg(pdata->mac_regs + reg_lo + 4) << 32); + */ + + return val; +} + +static void fxgmac_tx_mmc_int(struct fxgmac_pdata *pdata) +{ + u32 mmc_isr = readreg(pdata->pAdapter, pdata->mac_regs + MMC_TISR); + struct fxgmac_stats *stats = &pdata->stats; + + if (FXGMAC_GET_REG_BITS(mmc_isr, MMC_TISR_TXOCTETCOUNT_GB_POS, + MMC_TISR_TXOCTETCOUNT_GB_LEN)) + stats->txoctetcount_gb += + fxgmac_mmc_read(pdata, MMC_TXOCTETCOUNT_GB_LO); + + if (FXGMAC_GET_REG_BITS(mmc_isr, MMC_TISR_TXFRAMECOUNT_GB_POS, + MMC_TISR_TXFRAMECOUNT_GB_LEN)) + stats->txframecount_gb += + fxgmac_mmc_read(pdata, MMC_TXFRAMECOUNT_GB_LO); + + if (FXGMAC_GET_REG_BITS(mmc_isr, MMC_TISR_TXBROADCASTFRAMES_G_POS, + MMC_TISR_TXBROADCASTFRAMES_G_LEN)) + stats->txbroadcastframes_g += + fxgmac_mmc_read(pdata, MMC_TXBROADCASTFRAMES_G_LO); + + if (FXGMAC_GET_REG_BITS(mmc_isr, MMC_TISR_TXMULTICASTFRAMES_G_POS, + MMC_TISR_TXMULTICASTFRAMES_G_LEN)) + stats->txmulticastframes_g += + fxgmac_mmc_read(pdata, MMC_TXMULTICASTFRAMES_G_LO); + + if (FXGMAC_GET_REG_BITS(mmc_isr, MMC_TISR_TX64OCTETS_GB_POS, + MMC_TISR_TX64OCTETS_GB_LEN)) + stats->tx64octets_gb += + fxgmac_mmc_read(pdata, MMC_TX64OCTETS_GB_LO); + + if (FXGMAC_GET_REG_BITS(mmc_isr, MMC_TISR_TX65TO127OCTETS_GB_POS, + MMC_TISR_TX65TO127OCTETS_GB_LEN)) + stats->tx65to127octets_gb += + fxgmac_mmc_read(pdata, MMC_TX65TO127OCTETS_GB_LO); + + if (FXGMAC_GET_REG_BITS(mmc_isr, MMC_TISR_TX128TO255OCTETS_GB_POS, + MMC_TISR_TX128TO255OCTETS_GB_LEN)) + stats->tx128to255octets_gb += + fxgmac_mmc_read(pdata, MMC_TX128TO255OCTETS_GB_LO); + + if (FXGMAC_GET_REG_BITS(mmc_isr, MMC_TISR_TX256TO511OCTETS_GB_POS, + MMC_TISR_TX256TO511OCTETS_GB_LEN)) + stats->tx256to511octets_gb += + fxgmac_mmc_read(pdata, MMC_TX256TO511OCTETS_GB_LO); + + if (FXGMAC_GET_REG_BITS(mmc_isr, MMC_TISR_TX512TO1023OCTETS_GB_POS, + MMC_TISR_TX512TO1023OCTETS_GB_LEN)) + stats->tx512to1023octets_gb += + fxgmac_mmc_read(pdata, MMC_TX512TO1023OCTETS_GB_LO); + + if (FXGMAC_GET_REG_BITS(mmc_isr, MMC_TISR_TX1024TOMAXOCTETS_GB_POS, + MMC_TISR_TX1024TOMAXOCTETS_GB_LEN)) + stats->tx1024tomaxoctets_gb += + fxgmac_mmc_read(pdata, MMC_TX1024TOMAXOCTETS_GB_LO); + + if (FXGMAC_GET_REG_BITS(mmc_isr, MMC_TISR_TXUNICASTFRAMES_GB_POS, + MMC_TISR_TXUNICASTFRAMES_GB_LEN)) + stats->txunicastframes_gb += + fxgmac_mmc_read(pdata, MMC_TXUNICASTFRAMES_GB_LO); + + if (FXGMAC_GET_REG_BITS(mmc_isr, MMC_TISR_TXMULTICASTFRAMES_GB_POS, + MMC_TISR_TXMULTICASTFRAMES_GB_LEN)) + stats->txmulticastframes_gb += + fxgmac_mmc_read(pdata, MMC_TXMULTICASTFRAMES_GB_LO); + + if (FXGMAC_GET_REG_BITS(mmc_isr, MMC_TISR_TXBROADCASTFRAMES_GB_POS, + MMC_TISR_TXBROADCASTFRAMES_GB_LEN)) + stats->txbroadcastframes_g += + fxgmac_mmc_read(pdata, MMC_TXBROADCASTFRAMES_GB_LO); + + if (FXGMAC_GET_REG_BITS(mmc_isr, MMC_TISR_TXUNDERFLOWERROR_POS, + MMC_TISR_TXUNDERFLOWERROR_LEN)) + stats->txunderflowerror += + fxgmac_mmc_read(pdata, MMC_TXUNDERFLOWERROR_LO); + + if (FXGMAC_GET_REG_BITS(mmc_isr, MMC_TISR_TXSINGLECOLLISION_G_POS, + MMC_TISR_TXSINGLECOLLISION_G_LEN)) + stats->txsinglecollision_g += + fxgmac_mmc_read(pdata, MMC_TXSINGLECOLLISION_G); + + if (FXGMAC_GET_REG_BITS(mmc_isr, MMC_TISR_TXMULTIPLECOLLISION_G_POS, + MMC_TISR_TXMULTIPLECOLLISION_G_LEN)) + stats->txmultiplecollision_g += + fxgmac_mmc_read(pdata, MMC_TXMULTIPLECOLLISION_G); + + if (FXGMAC_GET_REG_BITS(mmc_isr, MMC_TISR_TXDEFERREDFRAMES_POS, + MMC_TISR_TXDEFERREDFRAMES_LEN)) + stats->txdeferredframes += + fxgmac_mmc_read(pdata, MMC_TXDEFERREDFRAMES); + + if (FXGMAC_GET_REG_BITS(mmc_isr, MMC_TISR_TXLATECOLLISIONFRAMES_POS, + MMC_TISR_TXLATECOLLISIONFRAMES_LEN)) + stats->txlatecollisionframes += + fxgmac_mmc_read(pdata, MMC_TXLATECOLLISIONFRAMES); + + if (FXGMAC_GET_REG_BITS(mmc_isr, + MMC_TISR_TXEXCESSIVECOLLISIONFRAMES_POS, + MMC_TISR_TXEXCESSIVECOLLISIONFRAMES_LEN)) + stats->txexcessivecollisionframes += + fxgmac_mmc_read(pdata, MMC_TXEXCESSIVECOLLSIONFRAMES); + + if (FXGMAC_GET_REG_BITS(mmc_isr, MMC_TISR_TXCARRIERERRORFRAMES_POS, + MMC_TISR_TXCARRIERERRORFRAMES_LEN)) + stats->txcarriererrorframes += + fxgmac_mmc_read(pdata, MMC_TXCARRIERERRORFRAMES); + + if (FXGMAC_GET_REG_BITS(mmc_isr, MMC_TISR_TXOCTETCOUNT_G_POS, + MMC_TISR_TXOCTETCOUNT_G_LEN)) + stats->txoctetcount_g += + fxgmac_mmc_read(pdata, MMC_TXOCTETCOUNT_G_LO); + + if (FXGMAC_GET_REG_BITS(mmc_isr, MMC_TISR_TXFRAMECOUNT_G_POS, + MMC_TISR_TXFRAMECOUNT_G_LEN)) + stats->txframecount_g += + fxgmac_mmc_read(pdata, MMC_TXFRAMECOUNT_G_LO); + + if (FXGMAC_GET_REG_BITS(mmc_isr, MMC_TISR_TXEXCESSIVEDEFERRALFRAMES_POS, + MMC_TISR_TXEXCESSIVEDEFERRALFRAMES_LEN)) + stats->txexcessivedeferralerror += + fxgmac_mmc_read(pdata, MMC_TXEXCESSIVEDEFERRALERROR); + + if (FXGMAC_GET_REG_BITS(mmc_isr, MMC_TISR_TXPAUSEFRAMES_POS, + MMC_TISR_TXPAUSEFRAMES_LEN)) + stats->txpauseframes += + fxgmac_mmc_read(pdata, MMC_TXPAUSEFRAMES_LO); + + if (FXGMAC_GET_REG_BITS(mmc_isr, MMC_TISR_TXVLANFRAMES_G_POS, + MMC_TISR_TXVLANFRAMES_G_LEN)) + stats->txvlanframes_g += + fxgmac_mmc_read(pdata, MMC_TXVLANFRAMES_G_LO); + + if (FXGMAC_GET_REG_BITS(mmc_isr, MMC_TISR_TXOVERSIZE_G_POS, + MMC_TISR_TXOVERSIZE_G_LEN)) + stats->txoversize_g += + fxgmac_mmc_read(pdata, MMC_TXOVERSIZEFRAMES); +} + +static void fxgmac_rx_mmc_int(struct fxgmac_pdata *pdata) +{ + u32 mmc_isr = readreg(pdata->pAdapter, pdata->mac_regs + MMC_RISR); + struct fxgmac_stats *stats = &pdata->stats; + + if (FXGMAC_GET_REG_BITS(mmc_isr, MMC_RISR_RXFRAMECOUNT_GB_POS, + MMC_RISR_RXFRAMECOUNT_GB_LEN)) + stats->rxframecount_gb += + fxgmac_mmc_read(pdata, MMC_RXFRAMECOUNT_GB_LO); + + if (FXGMAC_GET_REG_BITS(mmc_isr, MMC_RISR_RXOCTETCOUNT_GB_POS, + MMC_RISR_RXOCTETCOUNT_GB_LEN)) + stats->rxoctetcount_gb += + fxgmac_mmc_read(pdata, MMC_RXOCTETCOUNT_GB_LO); + + if (FXGMAC_GET_REG_BITS(mmc_isr, MMC_RISR_RXOCTETCOUNT_G_POS, + MMC_RISR_RXOCTETCOUNT_G_LEN)) + stats->rxoctetcount_g += + fxgmac_mmc_read(pdata, MMC_RXOCTETCOUNT_G_LO); + + if (FXGMAC_GET_REG_BITS(mmc_isr, MMC_RISR_RXBROADCASTFRAMES_G_POS, + MMC_RISR_RXBROADCASTFRAMES_G_LEN)) + stats->rxbroadcastframes_g += + fxgmac_mmc_read(pdata, MMC_RXBROADCASTFRAMES_G_LO); + + if (FXGMAC_GET_REG_BITS(mmc_isr, MMC_RISR_RXMULTICASTFRAMES_G_POS, + MMC_RISR_RXMULTICASTFRAMES_G_LEN)) + stats->rxmulticastframes_g += + fxgmac_mmc_read(pdata, MMC_RXMULTICASTFRAMES_G_LO); + + if (FXGMAC_GET_REG_BITS(mmc_isr, MMC_RISR_RXCRCERROR_POS, + MMC_RISR_RXCRCERROR_LEN)) + stats->rxcrcerror += fxgmac_mmc_read(pdata, MMC_RXCRCERROR_LO); + + if (FXGMAC_GET_REG_BITS(mmc_isr, MMC_RISR_RXALIGNERROR_POS, + MMC_RISR_RXALIGNERROR_LEN)) + stats->rxalignerror += fxgmac_mmc_read(pdata, MMC_RXALIGNERROR); + + if (FXGMAC_GET_REG_BITS(mmc_isr, MMC_RISR_RXRUNTERROR_POS, + MMC_RISR_RXRUNTERROR_LEN)) + stats->rxrunterror += fxgmac_mmc_read(pdata, MMC_RXRUNTERROR); + + if (FXGMAC_GET_REG_BITS(mmc_isr, MMC_RISR_RXJABBERERROR_POS, + MMC_RISR_RXJABBERERROR_LEN)) + stats->rxjabbererror += + fxgmac_mmc_read(pdata, MMC_RXJABBERERROR); + + if (FXGMAC_GET_REG_BITS(mmc_isr, MMC_RISR_RXUNDERSIZE_G_POS, + MMC_RISR_RXUNDERSIZE_G_LEN)) + stats->rxundersize_g += + fxgmac_mmc_read(pdata, MMC_RXUNDERSIZE_G); + + if (FXGMAC_GET_REG_BITS(mmc_isr, MMC_RISR_RXOVERSIZE_G_POS, + MMC_RISR_RXOVERSIZE_G_LEN)) + stats->rxoversize_g += fxgmac_mmc_read(pdata, MMC_RXOVERSIZE_G); + + if (FXGMAC_GET_REG_BITS(mmc_isr, MMC_RISR_RX64OCTETS_GB_POS, + MMC_RISR_RX64OCTETS_GB_LEN)) + stats->rx64octets_gb += + fxgmac_mmc_read(pdata, MMC_RX64OCTETS_GB_LO); + + if (FXGMAC_GET_REG_BITS(mmc_isr, MMC_RISR_RX65TO127OCTETS_GB_POS, + MMC_RISR_RX65TO127OCTETS_GB_LEN)) + stats->rx65to127octets_gb += + fxgmac_mmc_read(pdata, MMC_RX65TO127OCTETS_GB_LO); + + if (FXGMAC_GET_REG_BITS(mmc_isr, MMC_RISR_RX128TO255OCTETS_GB_POS, + MMC_RISR_RX128TO255OCTETS_GB_LEN)) + stats->rx128to255octets_gb += + fxgmac_mmc_read(pdata, MMC_RX128TO255OCTETS_GB_LO); + + if (FXGMAC_GET_REG_BITS(mmc_isr, MMC_RISR_RX256TO511OCTETS_GB_POS, + MMC_RISR_RX256TO511OCTETS_GB_LEN)) + stats->rx256to511octets_gb += + fxgmac_mmc_read(pdata, MMC_RX256TO511OCTETS_GB_LO); + + if (FXGMAC_GET_REG_BITS(mmc_isr, MMC_RISR_RX512TO1023OCTETS_GB_POS, + MMC_RISR_RX512TO1023OCTETS_GB_LEN)) + stats->rx512to1023octets_gb += + fxgmac_mmc_read(pdata, MMC_RX512TO1023OCTETS_GB_LO); + + if (FXGMAC_GET_REG_BITS(mmc_isr, MMC_RISR_RX1024TOMAXOCTETS_GB_POS, + MMC_RISR_RX1024TOMAXOCTETS_GB_LEN)) + stats->rx1024tomaxoctets_gb += + fxgmac_mmc_read(pdata, MMC_RX1024TOMAXOCTETS_GB_LO); + + if (FXGMAC_GET_REG_BITS(mmc_isr, MMC_RISR_RXUNICASTFRAMES_G_POS, + MMC_RISR_RXUNICASTFRAMES_G_LEN)) + stats->rxunicastframes_g += + fxgmac_mmc_read(pdata, MMC_RXUNICASTFRAMES_G_LO); + + if (FXGMAC_GET_REG_BITS(mmc_isr, MMC_RISR_RXLENGTHERROR_POS, + MMC_RISR_RXLENGTHERROR_LEN)) + stats->rxlengtherror += + fxgmac_mmc_read(pdata, MMC_RXLENGTHERROR_LO); + + if (FXGMAC_GET_REG_BITS(mmc_isr, MMC_RISR_RXOUTOFRANGETYPE_POS, + MMC_RISR_RXOUTOFRANGETYPE_LEN)) + stats->rxoutofrangetype += + fxgmac_mmc_read(pdata, MMC_RXOUTOFRANGETYPE_LO); + + if (FXGMAC_GET_REG_BITS(mmc_isr, MMC_RISR_RXPAUSEFRAMES_POS, + MMC_RISR_RXPAUSEFRAMES_LEN)) + stats->rxpauseframes += + fxgmac_mmc_read(pdata, MMC_RXPAUSEFRAMES_LO); + + if (FXGMAC_GET_REG_BITS(mmc_isr, MMC_RISR_RXFIFOOVERFLOW_POS, + MMC_RISR_RXFIFOOVERFLOW_LEN)) + stats->rxfifooverflow += + fxgmac_mmc_read(pdata, MMC_RXFIFOOVERFLOW_LO); + + if (FXGMAC_GET_REG_BITS(mmc_isr, MMC_RISR_RXVLANFRAMES_GB_POS, + MMC_RISR_RXVLANFRAMES_GB_LEN)) + stats->rxvlanframes_gb += + fxgmac_mmc_read(pdata, MMC_RXVLANFRAMES_GB_LO); + + if (FXGMAC_GET_REG_BITS(mmc_isr, MMC_RISR_RXWATCHDOGERROR_POS, + MMC_RISR_RXWATCHDOGERROR_LEN)) + stats->rxwatchdogerror += + fxgmac_mmc_read(pdata, MMC_RXWATCHDOGERROR); + + if (FXGMAC_GET_REG_BITS(mmc_isr, MMC_RISR_RXERRORFRAMES_POS, + MMC_RISR_RXERRORFRAMES_LEN)) + stats->rxreceiveerrorframe += + fxgmac_mmc_read(pdata, MMC_RXRECEIVEERRORFRAME); + + if (FXGMAC_GET_REG_BITS(mmc_isr, MMC_RISR_RXERRORCONTROLFRAMES_POS, + MMC_RISR_RXERRORCONTROLFRAMES_LEN)) + stats->rxcontrolframe_g += + fxgmac_mmc_read(pdata, MMC_RXCONTROLFRAME_G); +} + +static void fxgmac_read_mmc_stats(struct fxgmac_pdata *pdata) +{ + struct fxgmac_stats *stats = &pdata->stats; + u32 regval; + + /* Freeze counters */ + regval = readreg(pdata->pAdapter, pdata->mac_regs + MMC_CR); + regval = FXGMAC_SET_REG_BITS(regval, MMC_CR_MCF_POS, MMC_CR_MCF_LEN, 1); + writereg(pdata->pAdapter, regval, pdata->mac_regs + MMC_CR); + + stats->txoctetcount_gb += + fxgmac_mmc_read(pdata, MMC_TXOCTETCOUNT_GB_LO); + + stats->txframecount_gb += + fxgmac_mmc_read(pdata, MMC_TXFRAMECOUNT_GB_LO); + + stats->txbroadcastframes_g += + fxgmac_mmc_read(pdata, MMC_TXBROADCASTFRAMES_G_LO); + + stats->txmulticastframes_g += + fxgmac_mmc_read(pdata, MMC_TXMULTICASTFRAMES_G_LO); + + stats->tx64octets_gb += fxgmac_mmc_read(pdata, MMC_TX64OCTETS_GB_LO); + + stats->tx65to127octets_gb += + fxgmac_mmc_read(pdata, MMC_TX65TO127OCTETS_GB_LO); + + stats->tx128to255octets_gb += + fxgmac_mmc_read(pdata, MMC_TX128TO255OCTETS_GB_LO); + + stats->tx256to511octets_gb += + fxgmac_mmc_read(pdata, MMC_TX256TO511OCTETS_GB_LO); + + stats->tx512to1023octets_gb += + fxgmac_mmc_read(pdata, MMC_TX512TO1023OCTETS_GB_LO); + + stats->tx1024tomaxoctets_gb += + fxgmac_mmc_read(pdata, MMC_TX1024TOMAXOCTETS_GB_LO); + + stats->txunicastframes_gb += + fxgmac_mmc_read(pdata, MMC_TXUNICASTFRAMES_GB_LO); + + stats->txmulticastframes_gb += + fxgmac_mmc_read(pdata, MMC_TXMULTICASTFRAMES_GB_LO); + + stats->txbroadcastframes_g += + fxgmac_mmc_read(pdata, MMC_TXBROADCASTFRAMES_GB_LO); + + stats->txunderflowerror += + fxgmac_mmc_read(pdata, MMC_TXUNDERFLOWERROR_LO); + + stats->txsinglecollision_g += + fxgmac_mmc_read(pdata, MMC_TXSINGLECOLLISION_G); + + stats->txmultiplecollision_g += + fxgmac_mmc_read(pdata, MMC_TXMULTIPLECOLLISION_G); + + stats->txdeferredframes += fxgmac_mmc_read(pdata, MMC_TXDEFERREDFRAMES); + + stats->txlatecollisionframes += + fxgmac_mmc_read(pdata, MMC_TXLATECOLLISIONFRAMES); + + stats->txexcessivecollisionframes += + fxgmac_mmc_read(pdata, MMC_TXEXCESSIVECOLLSIONFRAMES); + + stats->txcarriererrorframes += + fxgmac_mmc_read(pdata, MMC_TXCARRIERERRORFRAMES); + + stats->txoctetcount_g += fxgmac_mmc_read(pdata, MMC_TXOCTETCOUNT_G_LO); + + stats->txframecount_g += fxgmac_mmc_read(pdata, MMC_TXFRAMECOUNT_G_LO); + + stats->txexcessivedeferralerror += + fxgmac_mmc_read(pdata, MMC_TXEXCESSIVEDEFERRALERROR); + + stats->txpauseframes += fxgmac_mmc_read(pdata, MMC_TXPAUSEFRAMES_LO); + + stats->txvlanframes_g += fxgmac_mmc_read(pdata, MMC_TXVLANFRAMES_G_LO); + + stats->txoversize_g += fxgmac_mmc_read(pdata, MMC_TXOVERSIZEFRAMES); + + stats->rxframecount_gb += + fxgmac_mmc_read(pdata, MMC_RXFRAMECOUNT_GB_LO); + + stats->rxoctetcount_gb += + fxgmac_mmc_read(pdata, MMC_RXOCTETCOUNT_GB_LO); + + stats->rxoctetcount_g += fxgmac_mmc_read(pdata, MMC_RXOCTETCOUNT_G_LO); + + stats->rxbroadcastframes_g += + fxgmac_mmc_read(pdata, MMC_RXBROADCASTFRAMES_G_LO); + + stats->rxmulticastframes_g += + fxgmac_mmc_read(pdata, MMC_RXMULTICASTFRAMES_G_LO); + + stats->rxcrcerror += fxgmac_mmc_read(pdata, MMC_RXCRCERROR_LO); + + stats->rxalignerror += fxgmac_mmc_read(pdata, MMC_RXALIGNERROR); + + stats->rxrunterror += fxgmac_mmc_read(pdata, MMC_RXRUNTERROR); + + stats->rxjabbererror += fxgmac_mmc_read(pdata, MMC_RXJABBERERROR); + + stats->rxundersize_g += fxgmac_mmc_read(pdata, MMC_RXUNDERSIZE_G); + + stats->rxoversize_g += fxgmac_mmc_read(pdata, MMC_RXOVERSIZE_G); + + stats->rx64octets_gb += fxgmac_mmc_read(pdata, MMC_RX64OCTETS_GB_LO); + + stats->rx65to127octets_gb += + fxgmac_mmc_read(pdata, MMC_RX65TO127OCTETS_GB_LO); + + stats->rx128to255octets_gb += + fxgmac_mmc_read(pdata, MMC_RX128TO255OCTETS_GB_LO); + + stats->rx256to511octets_gb += + fxgmac_mmc_read(pdata, MMC_RX256TO511OCTETS_GB_LO); + + stats->rx512to1023octets_gb += + fxgmac_mmc_read(pdata, MMC_RX512TO1023OCTETS_GB_LO); + + stats->rx1024tomaxoctets_gb += + fxgmac_mmc_read(pdata, MMC_RX1024TOMAXOCTETS_GB_LO); + + stats->rxunicastframes_g += + fxgmac_mmc_read(pdata, MMC_RXUNICASTFRAMES_G_LO); + + stats->rxlengtherror += fxgmac_mmc_read(pdata, MMC_RXLENGTHERROR_LO); + + stats->rxoutofrangetype += + fxgmac_mmc_read(pdata, MMC_RXOUTOFRANGETYPE_LO); + + stats->rxpauseframes += fxgmac_mmc_read(pdata, MMC_RXPAUSEFRAMES_LO); + + stats->rxfifooverflow += fxgmac_mmc_read(pdata, MMC_RXFIFOOVERFLOW_LO); + + stats->rxvlanframes_gb += + fxgmac_mmc_read(pdata, MMC_RXVLANFRAMES_GB_LO); + + stats->rxwatchdogerror += fxgmac_mmc_read(pdata, MMC_RXWATCHDOGERROR); + + stats->rxreceiveerrorframe += + fxgmac_mmc_read(pdata, MMC_RXRECEIVEERRORFRAME); + + stats->rxcontrolframe_g += fxgmac_mmc_read(pdata, MMC_RXCONTROLFRAME_G); + + /* Un-freeze counters */ + regval = readreg(pdata->pAdapter, pdata->mac_regs + MMC_CR); + regval = FXGMAC_SET_REG_BITS(regval, MMC_CR_MCF_POS, MMC_CR_MCF_LEN, 0); + writereg(pdata->pAdapter, regval, pdata->mac_regs + MMC_CR); +} + +static void fxgmac_config_mmc(struct fxgmac_pdata *pdata) +{ + u32 regval; + + regval = readreg(pdata->pAdapter, pdata->mac_regs + MMC_CR); + /* Set counters to reset on read */ + regval = FXGMAC_SET_REG_BITS(regval, MMC_CR_ROR_POS, MMC_CR_ROR_LEN, 1); + /* Reset the counters */ + regval = FXGMAC_SET_REG_BITS(regval, MMC_CR_CR_POS, MMC_CR_CR_LEN, 1); + writereg(pdata->pAdapter, regval, pdata->mac_regs + MMC_CR); + +#if FXGMAC_MISC_INT_HANDLE_FEATURE_ENABLED + + writereg(pdata->pAdapter, 0xffffffff, + pdata->mac_regs + MMC_IPCRXINTMASK); +#endif +} + +static int fxgmac_write_rss_reg(struct fxgmac_pdata *pdata, unsigned int type, + unsigned int index, u32 val) +{ + int ret = 0; + type = type; + + writereg(pdata->pAdapter, val, (pdata->base_mem + index)); + + return ret; +} + +static u32 fxgmac_read_rss_options(struct fxgmac_pdata *pdata) +{ + u32 regval; + + regval = readreg(pdata->pAdapter, pdata->base_mem + MGMT_RSS_CTRL); + + /* Get the RSS options bits */ + regval = FXGMAC_GET_REG_BITS(regval, MGMT_RSS_CTRL_OPT_POS, + MGMT_RSS_CTRL_OPT_LEN); + + return regval; +} + +static int fxgmac_write_rss_options(struct fxgmac_pdata *pdata) +{ + u32 regval; + + regval = readreg(pdata->pAdapter, pdata->base_mem + MGMT_RSS_CTRL); + + /* Set the RSS options */ + regval = FXGMAC_SET_REG_BITS(regval, MGMT_RSS_CTRL_OPT_POS, + MGMT_RSS_CTRL_OPT_LEN, pdata->rss_options); + + writereg(pdata->pAdapter, regval, (pdata->base_mem + MGMT_RSS_CTRL)); + + return 0; +} + +static int fxgmac_read_rss_hash_key(struct fxgmac_pdata *pdata, u8 *key_buf) +{ + unsigned int key_regs = sizeof(pdata->rss_key) / sizeof(u32); + u32 *key = (u32 *)key_buf; + + while (key_regs--) { + (*key) = cpu_to_be32(readreg( + pdata->pAdapter, + pdata->base_mem + (MGMT_RSS_KEY0 + + key_regs * MGMT_RSS_KEY_REG_INC))); + + DBGPRINT( + MP_LOUD, + ("fxgmac_read_rss_hash_key: idx=%d, reg=%x, key=0x%08x\n", + key_regs, + MGMT_RSS_KEY0 + key_regs * MGMT_RSS_KEY_REG_INC, + (u32)(*key))); + key++; + } + + return 0; +} + +static int fxgmac_write_rss_hash_key(struct fxgmac_pdata *pdata) +{ + unsigned int key_regs = sizeof(pdata->rss_key) / sizeof(u32); + u32 *key = (u32 *)&pdata->rss_key; + int ret; + + while (key_regs--) { + ret = fxgmac_write_rss_reg( + pdata, (unsigned int)FXGMAC_RSS_HASH_KEY_TYPE, + (unsigned int)(MGMT_RSS_KEY0 + + key_regs * MGMT_RSS_KEY_REG_INC), + (unsigned int)(cpu_to_be32(*key))); + if (ret) + return ret; + key++; + } + + return 0; +} + +static int fxgmac_write_rss_lookup_table(struct fxgmac_pdata *pdata) +{ + unsigned int i, j; + u32 regval = 0; + int ret; + + for (i = 0, j = 0; i < ARRAY_SIZE(pdata->rss_table); i++, j++) { + if (j < MGMT_RSS_IDT_ENTRY_PER_REG) { + regval |= + ((pdata->rss_table[i] & MGMT_RSS_IDT_ENTRY_MASK) + << (j * 2)); + } else { + ret = fxgmac_write_rss_reg( + pdata, FXGMAC_RSS_LOOKUP_TABLE_TYPE, + MGMT_RSS_IDT + (i / MGMT_RSS_IDT_ENTRY_PER_REG - + 1) * MGMT_RSS_IDT_REG_INC, + regval); + if (ret) + return ret; + + regval = pdata->rss_table[i]; + j = 0; + } + } + + if (j == MGMT_RSS_IDT_ENTRY_PER_REG) { + fxgmac_write_rss_reg( + pdata, FXGMAC_RSS_LOOKUP_TABLE_TYPE, + MGMT_RSS_IDT + (i / MGMT_RSS_IDT_ENTRY_PER_REG - 1) * + MGMT_RSS_IDT_REG_INC, + regval); + } + + return 0; +} + +static int fxgmac_set_rss_hash_key(struct fxgmac_pdata *pdata, const u8 *key) +{ + memcpy(pdata->rss_key, (void *)key, sizeof(pdata->rss_key)); + + return fxgmac_write_rss_hash_key(pdata); +} + +static int fxgmac_set_rss_lookup_table(struct fxgmac_pdata *pdata, + const u32 *table) +{ + unsigned int i; + u32 tval; + +#if FXGMAC_MSIX_CH0RXDIS_ENABLED + DPRINTK("Set_rss_table, rss ctrl eth=0x%08x\n", 0); + + return 0; +#endif + + for (i = 0; i < ARRAY_SIZE(pdata->rss_table); i++) { + tval = table[i]; + pdata->rss_table[i] = FXGMAC_SET_REG_BITS(pdata->rss_table[i], + MAC_RSSDR_DMCH_POS, + MAC_RSSDR_DMCH_LEN, + tval); + } + + return fxgmac_write_rss_lookup_table(pdata); +} + +static u32 log2ex(u32 value) +{ + u32 i = 31; + while (i > 0) { + if (value & 0x80000000) { + break; + } + value <<= 1; + i--; + } + return i; +} + +static int fxgmac_enable_rss(struct fxgmac_pdata *pdata) +{ + u32 regval; + u32 size = 0; + +#ifdef FXGMAC_USE_DEFAULT_RSS_KEY_TBALE + int ret; + + if (!pdata->hw_feat.rss) { + return -EOPNOTSUPP; + } + + /* Program the hash key */ + ret = fxgmac_write_rss_hash_key(pdata); + if (ret) { + return ret; + } + + /* Program the lookup table */ + ret = fxgmac_write_rss_lookup_table(pdata); + if (ret) { + return ret; + } +#endif + + regval = readreg(pdata->pAdapter, pdata->base_mem + MGMT_RSS_CTRL); + + /* Set RSS IDT table size */ + size = log2ex(FXGMAC_RSS_MAX_TABLE_SIZE) - 1; + regval = FXGMAC_SET_REG_BITS(regval, MGMT_RSS_CTRL_TBL_SIZE_POS, + MGMT_RSS_CTRL_TBL_SIZE_LEN, size); + +#if FXGMAC_MSIX_CH0RXDIS_ENABLED + /* set default cpu id to 1 */ + regval = FXGMAC_SET_REG_BITS(regval, 8, 2, 1); +#endif + /* Enable RSS */ + regval = FXGMAC_SET_REG_BITS(regval, MAC_RSSCR_RSSE_POS, + MAC_RSSCR_RSSE_LEN, 1); + + /* Set the RSS options */ + regval = FXGMAC_SET_REG_BITS(regval, MGMT_RSS_CTRL_OPT_POS, + MGMT_RSS_CTRL_OPT_LEN, pdata->rss_options); + + writereg(pdata->pAdapter, regval, (pdata->base_mem + MGMT_RSS_CTRL)); + DPRINTK("enable_rss callout, set val = 0x%08x\n", regval); + + return 0; +} + +static int fxgmac_disable_rss(struct fxgmac_pdata *pdata) +{ + u32 regval; + + if (!pdata->hw_feat.rss) + return -EOPNOTSUPP; + +#if FXGMAC_MSIX_CH0RXDIS_ENABLED + DPRINTK("Disable_rss, rss ctrl eth=0x%08x\n", 0); + + return 0; +#endif + + regval = readreg(pdata->pAdapter, pdata->base_mem + MGMT_RSS_CTRL); + regval = FXGMAC_SET_REG_BITS(regval, MAC_RSSCR_RSSE_POS, + MAC_RSSCR_RSSE_LEN, 0); + + writereg(pdata->pAdapter, regval, (pdata->base_mem + MGMT_RSS_CTRL)); + DPRINTK("disable_rss, set val = 0x%08x\n", regval); + + return 0; +} + +static void fxgmac_config_rss(struct fxgmac_pdata *pdata) +{ + int ret; + + if (!pdata->hw_feat.rss) + return; + + if (pdata->rss) + ret = fxgmac_enable_rss(pdata); + else + ret = fxgmac_disable_rss(pdata); + + if (ret) + DPRINTK("fxgmac_config_rss: error configuring RSS\n"); +} + +#if defined(FXGMAC_POWER_MANAGEMENT) + +static void fxgmac_update_aoe_ipv4addr(struct fxgmac_pdata *pdata, u8 *ip_addr) +{ + unsigned int regval, ipval = 0; + + /* enable or disable ARP offload engine. */ + if (!pdata->hw_feat.aoe) { + netdev_err( + pdata->netdev, + "error update ip addr - arp offload not supported.\n"); + return; + } + + if (ip_addr) { + ipval = (ip_addr[0] << 24) | (ip_addr[1] << 16) | + (ip_addr[2] << 8) | (ip_addr[3] << 0); + DPRINTK("%s, covert IP dotted-addr %s to binary 0x%08x ok.\n", + __FUNCTION__, ip_addr, cpu_to_be32(ipval)); + } else { +#ifdef FXGMAC_AOE_FEATURE_ENABLED + /* get ipv4 addr from net device */ + + ipval = fxgmac_get_netdev_ip4addr(pdata); + DPRINTK("%s, Get net device binary IP ok, 0x%08x\n", + __FUNCTION__, cpu_to_be32(ipval)); + + ipval = cpu_to_be32(ipval); +#endif + } + + regval = readreg(pdata->pAdapter, pdata->mac_regs + MAC_ARP_PROTO_ADDR); + if (regval != /*cpu_to_be32*/ (ipval)) { + writereg(pdata->pAdapter, /*cpu_to_be32*/ (ipval), + pdata->mac_regs + MAC_ARP_PROTO_ADDR); + DPRINTK("%s, update arp ipaddr reg from 0x%08x to 0x%08x\n", + __FUNCTION__, regval, /*cpu_to_be32*/ (ipval)); + } +} + +static int fxgmac_enable_arp_offload(struct fxgmac_pdata *pdata) +{ + u32 regval; + + if (!pdata->hw_feat.aoe) + return -EOPNOTSUPP; + + /* Enable arpoffload */ + regval = readreg(pdata->pAdapter, pdata->mac_regs + MAC_CR); + regval = FXGMAC_SET_REG_BITS(regval, MAC_CR_ARPEN_POS, MAC_CR_ARPEN_LEN, + 1); + writereg(pdata->pAdapter, regval, pdata->mac_regs + MAC_CR); + + return 0; +} + +static int fxgmac_disable_arp_offload(struct fxgmac_pdata *pdata) +{ + u32 regval; + + if (!pdata->hw_feat.aoe) + return -EOPNOTSUPP; + /* disable arpoffload */ + regval = readreg(pdata->pAdapter, pdata->mac_regs + MAC_CR); + regval = FXGMAC_SET_REG_BITS(regval, MAC_CR_ARPEN_POS, MAC_CR_ARPEN_LEN, + 0); + writereg(pdata->pAdapter, regval, pdata->mac_regs + MAC_CR); + + return 0; +} + +/* this function config register for NS offload function + * parameters: + * index - 0~1, index to NS look up table. one entry of the lut is like this |remote|solicited|target0|target1| + * remote_addr - ipv6 addr where fuxi gets the NS solicitation pkt(request). in common, it is 0 to match any remote machine. + * solicited_addr - the solicited node multicast group address which fuxi computes and joins. + * target_addr1 - it is the target address in NS solicitation pkt. + * target_addr2 - second target address, any address (with last 6B same with target address?). + */ +static int fxgmac_set_ns_offload(struct fxgmac_pdata *pdata, unsigned int index, + unsigned char *remote_addr, + unsigned char *solicited_addr, + unsigned char *target_addr1, + unsigned char *target_addr2, + unsigned char *mac_addr) + +{ + u32 regval; + u32 Address[4], mac_addr_hi, mac_addr_lo; + u8 i, remote_not_zero = 0; + + regval = readreg(pdata->pAdapter, pdata->base_mem + NS_TPID_PRO); + regval = FXGMAC_SET_REG_BITS(regval, NS_TPID_PRO_STPID_POS, + NS_TPID_PRO_STPID_LEN, 0X8100); + regval = FXGMAC_SET_REG_BITS(regval, NS_TPID_PRO_CTPID_POS, + NS_TPID_PRO_CTPID_LEN, 0X9100); + writereg(pdata->pAdapter, regval, pdata->base_mem + NS_TPID_PRO); + regval = readreg(pdata->pAdapter, + pdata->base_mem + 0X38 * index + NS_LUT_MAC_ADDR_CTL); + regval = FXGMAC_SET_REG_BITS(regval, NS_LUT_DST_CMP_TYPE_POS, + NS_LUT_DST_CMP_TYPE_LEN, 1); + regval = FXGMAC_SET_REG_BITS(regval, NS_LUT_DST_IGNORED_POS, + NS_LUT_DST_IGNORED_LEN, 1); + regval = FXGMAC_SET_REG_BITS(regval, NS_LUT_REMOTE_AWARED_POS, + NS_LUT_REMOTE_AWARED_LEN, 1); + regval = FXGMAC_SET_REG_BITS(regval, NS_LUT_TARGET_ISANY_POS, + NS_LUT_TARGET_ISANY_LEN, 0); + writereg(pdata->pAdapter, regval, + pdata->base_mem + 0X38 * index + NS_LUT_MAC_ADDR_CTL); + + for (i = 0; i < 16 / 4; i++) { + Address[i] = (remote_addr[i * 4 + 0] << 24) | + (remote_addr[i * 4 + 1] << 16) | + (remote_addr[i * 4 + 2] << 8) | + (remote_addr[i * 4 + 3] << 0); + writereg(pdata->pAdapter, Address[i], + pdata->base_mem + 0X38 * index + NS_LUT_ROMOTE0 + + 4 * i); + if (Address[i]) { + remote_not_zero = 1; + } + Address[i] = (target_addr1[i * 4 + 0] << 24) | + (target_addr1[i * 4 + 1] << 16) | + (target_addr1[i * 4 + 2] << 8) | + (target_addr1[i * 4 + 3] << 0); + writereg(pdata->pAdapter, Address[i], + pdata->base_mem + 0X38 * index + NS_LUT_TARGET0 + + 4 * i); + Address[i] = (solicited_addr[i * 4 + 0] << 24) | + (solicited_addr[i * 4 + 1] << 16) | + (solicited_addr[i * 4 + 2] << 8) | + (solicited_addr[i * 4 + 3] << 0); + writereg(pdata->pAdapter, Address[i], + pdata->base_mem + 0X38 * index + NS_LUT_SOLICITED0 + + 4 * i); + Address[i] = (target_addr2[i * 4 + 0] << 24) | + (target_addr2[i * 4 + 1] << 16) | + (target_addr2[i * 4 + 2] << 8) | + (target_addr2[i * 4 + 3] << 0); + writereg(pdata->pAdapter, Address[i], + pdata->base_mem + 0X10 * index + NS_LUT_TARGET4 + + 4 * i); + } + mac_addr_hi = (mac_addr[0] << 24) | (mac_addr[1] << 16) | + (mac_addr[2] << 8) | (mac_addr[3] << 0); + mac_addr_lo = (mac_addr[4] << 8) | (mac_addr[5] << 0); + + writereg(pdata->pAdapter, mac_addr_hi, + pdata->base_mem + 0X38 * index + NS_LUT_MAC_ADDR); + if (remote_not_zero == 0) { + regval = readreg(pdata->pAdapter, pdata->base_mem + + 0X38 * index + + NS_LUT_MAC_ADDR_CTL); + regval = FXGMAC_SET_REG_BITS(regval, NS_LUT_REMOTE_AWARED_POS, + NS_LUT_REMOTE_AWARED_LEN, 0); + regval = FXGMAC_SET_REG_BITS(regval, NS_LUT_MAC_ADDR_LOW_POS, + NS_LUT_MAC_ADDR_LOW_LEN, + mac_addr_lo); + writereg(pdata->pAdapter, regval, + pdata->base_mem + 0X38 * index + NS_LUT_MAC_ADDR_CTL); + } else { + regval = readreg(pdata->pAdapter, pdata->base_mem + + 0X38 * index + + NS_LUT_MAC_ADDR_CTL); + regval = FXGMAC_SET_REG_BITS(regval, NS_LUT_REMOTE_AWARED_POS, + NS_LUT_REMOTE_AWARED_LEN, 1); + regval = FXGMAC_SET_REG_BITS(regval, NS_LUT_MAC_ADDR_LOW_POS, + NS_LUT_MAC_ADDR_LOW_LEN, + mac_addr_lo); + writereg(pdata->pAdapter, regval, + pdata->base_mem + 0X38 * index + NS_LUT_MAC_ADDR_CTL); + } + + return 0; +} + +#ifdef FXGMAC_NS_OFFLOAD_ENABLED +static void fxgmac_update_ns_offload_ipv6addr(struct fxgmac_pdata *pdata, + unsigned int param) +{ + struct net_device *netdev = pdata->netdev; + unsigned char addr_buf[5][16]; + + unsigned char *remote_addr = (unsigned char *)&addr_buf[0][0]; + unsigned char *solicited_addr = (unsigned char *)&addr_buf[1][0]; + unsigned char *target_addr1 = (unsigned char *)&addr_buf[2][0]; + + unsigned char *mac_addr = (unsigned char *)&addr_buf[4][0]; + + /* get ipv6 addr from net device */ + if (NULL == fxgmac_get_netdev_ip6addr(pdata, target_addr1, + solicited_addr, + (FXGMAC_NS_IFA_LOCAL_LINK | + FXGMAC_NS_IFA_GLOBAL_UNICAST) & + param)) { + DPRINTK("%s, get net device ipv6 addr with err and ignore NS offload.\n", + __FUNCTION__); + + return; + } + + DPRINTK("%s, Get net device binary IPv6 ok, local-link=%pI6\n", + __FUNCTION__, target_addr1); + DPRINTK("%s, Get net device binary IPv6 ok, solicited =%pI6\n", + __FUNCTION__, solicited_addr); + + memcpy(mac_addr, netdev->dev_addr, netdev->addr_len); + DPRINTK("%s, Get net device MAC addr ok, ns_tab idx=%d, %02x:%02x:%02x:%02x:%02x:%02x\n", + __FUNCTION__, pdata->expansion.ns_offload_tab_idx, mac_addr[0], + mac_addr[1], mac_addr[2], mac_addr[3], mac_addr[4], + mac_addr[5]); + + memset(remote_addr, 0, 16); + fxgmac_set_ns_offload(pdata, pdata->expansion.ns_offload_tab_idx++, + remote_addr, solicited_addr, target_addr1, + target_addr1, mac_addr); + if (pdata->expansion.ns_offload_tab_idx >= 2) + pdata->expansion.ns_offload_tab_idx = 0; +} +#endif + +static int fxgmac_enable_ns_offload(struct fxgmac_pdata *pdata) +{ + writereg(pdata->pAdapter, 0X00000011, pdata->base_mem + NS_OF_GLB_CTL); + return 0; +} + +static int fxgmac_disable_ns_offload(struct fxgmac_pdata *pdata) +{ + writereg(pdata->pAdapter, 0X00000000, pdata->base_mem + NS_OF_GLB_CTL); + return 0; +} + +static int fxgmac_check_wake_pattern_fifo_pointer(struct fxgmac_pdata *pdata) +{ + u32 regval; + int ret = 0; + + regval = readreg(pdata->pAdapter, pdata->mac_regs + MAC_PMT_STA); + regval = FXGMAC_SET_REG_BITS(regval, MAC_PMT_STA_RWKFILTERST_POS, + MAC_PMT_STA_RWKFILTERST_LEN, 1); + writereg(pdata->pAdapter, regval, pdata->mac_regs + MAC_PMT_STA); + + regval = readreg(pdata->pAdapter, pdata->mac_regs + MAC_PMT_STA); + regval = FXGMAC_GET_REG_BITS(regval, MAC_PMT_STA_RWKPTR_POS, + MAC_PMT_STA_RWKPTR_LEN); + if (regval != 0) { + DPRINTK("Remote fifo pointer is not 0\n"); + ret = -EINVAL; + } + return ret; +} + +static int fxgmac_set_wake_pattern_mask(struct fxgmac_pdata *pdata, + u32 filter_index, u8 register_index, + u32 Data) +{ + const u16 address_offset[16][3] = { + { 0x1020, 0x1024, 0x1028 }, { 0x102c, 0x1030, 0x1034 }, + { 0x1038, 0x103c, 0x1040 }, { 0x1044, 0x1050, 0x1054 }, + { 0x1058, 0x105c, 0x1060 }, { 0x1064, 0x1068, 0x106c }, + { 0x1070, 0x1074, 0x1078 }, { 0x107c, 0x1080, 0x1084 }, + { 0x1088, 0x108c, 0x1090 }, { 0x1134, 0x113c, 0x1140 }, + { 0x1208, 0x1200, 0x1204 }, { 0x1218, 0x1210, 0x1214 }, + { 0x1228, 0x1220, 0x1224 }, { 0x1238, 0x1230, 0x1234 }, + { 0x1248, 0x1240, 0x1244 }, { 0x1258, 0x1250, 0x1254 }, + }; + if (filter_index > 15 || register_index > 2) { + DbgPrintF( + MP_TRACE, + "%s - Remote mask pointer is over range, filter_index:%d, register_index:0x%x\n", + __FUNCTION__, filter_index, register_index); + return -1; + } + writereg(pdata->pAdapter, Data, + pdata->base_mem + + address_offset[filter_index][register_index]); + return 0; +} + +static u16 wol_crc16(u8 *pucframe, u16 uslen) +{ + int i; + + union type16 { + u16 raw; + struct { + u16 bit_0 : 1; + u16 bit_1 : 1; + u16 bit_2 : 1; + u16 bit_3 : 1; + u16 bit_4 : 1; + u16 bit_5 : 1; + u16 bit_6 : 1; + u16 bit_7 : 1; + u16 bit_8 : 1; + u16 bit_9 : 1; + u16 bit_10 : 1; + u16 bit_11 : 1; + u16 bit_12 : 1; + u16 bit_13 : 1; + u16 bit_14 : 1; + u16 bit_15 : 1; + } bits; + }; + + union type8 { + u16 raw; + + struct { + u16 bit_0 : 1; + u16 bit_1 : 1; + u16 bit_2 : 1; + u16 bit_3 : 1; + u16 bit_4 : 1; + u16 bit_5 : 1; + u16 bit_6 : 1; + u16 bit_7 : 1; + } bits; + }; + + union type16 crc, crc_comb; + union type8 next_crc, rrpe_data; + next_crc.raw = 0; + crc.raw = 0xffff; + for (i = 0; i < uslen; i++) { + rrpe_data.raw = pucframe[i]; + next_crc.bits.bit_0 = crc.bits.bit_15 ^ rrpe_data.bits.bit_0; + next_crc.bits.bit_1 = crc.bits.bit_14 ^ next_crc.bits.bit_0 ^ + rrpe_data.bits.bit_1; + next_crc.bits.bit_2 = crc.bits.bit_13 ^ next_crc.bits.bit_1 ^ + rrpe_data.bits.bit_2; + next_crc.bits.bit_3 = crc.bits.bit_12 ^ next_crc.bits.bit_2 ^ + rrpe_data.bits.bit_3; + next_crc.bits.bit_4 = crc.bits.bit_11 ^ next_crc.bits.bit_3 ^ + rrpe_data.bits.bit_4; + next_crc.bits.bit_5 = crc.bits.bit_10 ^ next_crc.bits.bit_4 ^ + rrpe_data.bits.bit_5; + next_crc.bits.bit_6 = crc.bits.bit_9 ^ next_crc.bits.bit_5 ^ + rrpe_data.bits.bit_6; + next_crc.bits.bit_7 = crc.bits.bit_8 ^ next_crc.bits.bit_6 ^ + rrpe_data.bits.bit_7; + + crc_comb.bits.bit_15 = crc.bits.bit_7 ^ next_crc.bits.bit_7; + crc_comb.bits.bit_14 = crc.bits.bit_6; + crc_comb.bits.bit_13 = crc.bits.bit_5; + crc_comb.bits.bit_12 = crc.bits.bit_4; + crc_comb.bits.bit_11 = crc.bits.bit_3; + crc_comb.bits.bit_10 = crc.bits.bit_2; + crc_comb.bits.bit_9 = crc.bits.bit_1 ^ next_crc.bits.bit_0; + crc_comb.bits.bit_8 = crc.bits.bit_0 ^ next_crc.bits.bit_1; + crc_comb.bits.bit_7 = next_crc.bits.bit_0 ^ next_crc.bits.bit_2; + crc_comb.bits.bit_6 = next_crc.bits.bit_1 ^ next_crc.bits.bit_3; + crc_comb.bits.bit_5 = next_crc.bits.bit_2 ^ next_crc.bits.bit_4; + crc_comb.bits.bit_4 = next_crc.bits.bit_3 ^ next_crc.bits.bit_5; + crc_comb.bits.bit_3 = next_crc.bits.bit_4 ^ next_crc.bits.bit_6; + crc_comb.bits.bit_2 = next_crc.bits.bit_5 ^ next_crc.bits.bit_7; + crc_comb.bits.bit_1 = next_crc.bits.bit_6; + crc_comb.bits.bit_0 = next_crc.bits.bit_7; + crc.raw = crc_comb.raw; + } + return crc.raw; +} + +static int fxgmac_set_wake_pattern(struct fxgmac_pdata *pdata, + struct wol_bitmap_pattern *wol_pattern, + u32 pattern_cnt) +{ + u32 i, j, kp, km, mask_index; + int z; + u16 map_index; + u8 mask[MAX_PATTERN_SIZE]; + u32 regval = 0; + u32 total_cnt = 0, pattern_inherited_cnt = 0; + u8 *ptdata, *ptmask; + (void)ptdata; + (void)ptmask; + + if (pattern_cnt > MAX_PATTERN_COUNT) { + DbgPrintF( + MP_TRACE, + "%s - Error: %d patterns, exceed %d, not supported!\n", + __FUNCTION__, pattern_cnt, MAX_PATTERN_COUNT); + return -1; + } + + /* Reset the FIFO head pointer. */ + if (fxgmac_check_wake_pattern_fifo_pointer(pdata)) { + DbgPrintF( + MP_TRACE, + "%s - Warning: the remote pattern array pointer is not be 0\n", + __FUNCTION__); + return -1; + } + + for (i = 0; i < pattern_cnt; i++) { + memcpy(&pdata->pattern[i], wol_pattern + i, + sizeof(wol_pattern[0])); + if (pattern_cnt + pattern_inherited_cnt < MAX_PATTERN_COUNT) { + if (wol_pattern[i].pattern_offset || + !(wol_pattern[i].mask_info[0] & 0x01)) { + memcpy(&pdata->pattern[pattern_cnt + + pattern_inherited_cnt], + wol_pattern + i, sizeof(wol_pattern[0])); + pattern_inherited_cnt++; + } + } + } + total_cnt = pattern_cnt + pattern_inherited_cnt; + + /* + * calculate the crc-16 of the mask pattern + * print the pattern and mask for debug purpose. + */ + for (i = 0; i < total_cnt; i++) { + /* Please program pattern[i] to NIC for pattern match wakeup. + * pattern_size, pattern_info, mask_info + */ + + mask_index = 0; + map_index = 0; + for (j = 0; j < pdata->pattern[i].mask_size; j++) { + for (z = 0; + z < ((j == (MAX_PATTERN_SIZE / 8 - 1)) ? 7 : 8); + z++) { + if (pdata->pattern[i].mask_info[j] & + (0x01 << z)) { + mask[map_index] = + pdata->pattern[i].pattern_info + [pdata->pattern[i] + .pattern_offset + + mask_index]; + map_index++; + } + mask_index++; + } + } + + pdata->pattern[i].pattern_crc = wol_crc16(mask, map_index); + + DbgPrintF(MP_LOUD, "%s - Pattern[%d]:", __FUNCTION__, i); + for (kp = 0, km = 0; + kp < sizeof(pdata->pattern[i].pattern_info); + kp += 16, km += 2) { + ptdata = &pdata->pattern[i].pattern_info[kp]; + ptmask = &pdata->pattern[i].mask_info[km]; + DBGPRINT( + MP_LOUD, + ("\n %02x-%02x-%02x-%02x-%02x-%02x-%02x-%02x %02x-%02x-%02x-%02x-%02x-%02x-%02x-%02x Mask %02x-%02x", + ptdata[0], ptdata[1], ptdata[2], ptdata[3], + ptdata[4], ptdata[5], ptdata[6], ptdata[7], + ptdata[8], ptdata[9], ptdata[10], ptdata[11], + ptdata[12], ptdata[13], ptdata[14], ptdata[15], + ptmask[0], ptmask[1])); + } + + DbgPrintF( + MP_LOUD, + "WritePatternToNic62 the %d patterns crc = %x mask length = %d, mask_offset=%x.\n", + i, pdata->pattern[i].pattern_crc, map_index, + pdata->pattern[i].pattern_offset); + memset(mask, 0, sizeof(mask)); + } + + for (i = 0; i < (total_cnt + 3) / 4; i++) { + writereg(pdata->pAdapter, + ((pdata->pattern[i * 4 + 0].mask_info[3] & 0x7f) + << 24) | + (pdata->pattern[i * 4 + 0].mask_info[2] + << 16) | + (pdata->pattern[i * 4 + 0].mask_info[1] << 8) | + (pdata->pattern[i * 4 + 0].mask_info[0] << 0), + pdata->mac_regs + MAC_RWK_PAC); + + writereg(pdata->pAdapter, + ((pdata->pattern[i * 4 + 1].mask_info[3] & 0x7f) + << 24) | + (pdata->pattern[i * 4 + 1].mask_info[2] + << 16) | + (pdata->pattern[i * 4 + 1].mask_info[1] << 8) | + (pdata->pattern[i * 4 + 1].mask_info[0] << 0), + pdata->mac_regs + MAC_RWK_PAC); + + writereg(pdata->pAdapter, + ((pdata->pattern[i * 4 + 2].mask_info[3] & 0x7f) + << 24) | + (pdata->pattern[i * 4 + 2].mask_info[2] + << 16) | + (pdata->pattern[i * 4 + 2].mask_info[1] << 8) | + (pdata->pattern[i * 4 + 2].mask_info[0] << 0), + pdata->mac_regs + MAC_RWK_PAC); + + writereg(pdata->pAdapter, + ((pdata->pattern[i * 4 + 3].mask_info[3] & 0x7f) + << 24) | + (pdata->pattern[i * 4 + 3].mask_info[2] + << 16) | + (pdata->pattern[i * 4 + 3].mask_info[1] << 8) | + (pdata->pattern[i * 4 + 3].mask_info[0] << 0), + pdata->mac_regs + MAC_RWK_PAC); + + regval = 0; + + regval |= ((i * 4 + 0) < total_cnt) ? (0x1 << 0) : 0x0; + regval |= ((i * 4 + 1) < total_cnt) ? (0x1 << 8) : 0x0; + regval |= ((i * 4 + 2) < total_cnt) ? (0x1 << 16) : 0x0; + regval |= ((i * 4 + 3) < total_cnt) ? (0x1 << 24) : 0x0; + + regval |= (i * 4 + 0 >= total_cnt) ? 0x0 : + (i * 4 + 0 >= pattern_cnt) ? (0x1 << (3 + 0)) : + pdata->pattern[i * 4 + 0].pattern_offset ? + 0x0 : + !(pdata->pattern[i * 4 + 0].mask_info[0] & 0x01) ? + 0x0 : + (pdata->pattern[i * 4 + 0].pattern_info[0] & 0x01) ? + (0x1 << (3 + 0)) : + 0x0; + regval |= (i * 4 + 1 >= total_cnt) ? 0x0 : + (i * 4 + 1 >= pattern_cnt) ? (0x1 << (3 + 8)) : + pdata->pattern[i * 4 + 1].pattern_offset ? + 0x0 : + !(pdata->pattern[i * 4 + 1].mask_info[0] & 0x01) ? + 0x0 : + (pdata->pattern[i * 4 + 1].pattern_info[0] & 0x01) ? + (0x1 << (3 + 8)) : + 0x0; + regval |= (i * 4 + 2 >= total_cnt) ? 0x0 : + (i * 4 + 2 >= pattern_cnt) ? (0x1 << (3 + 16)) : + pdata->pattern[i * 4 + 2].pattern_offset ? + 0x0 : + !(pdata->pattern[i * 4 + 2].mask_info[0] & 0x01) ? + 0x0 : + (pdata->pattern[i * 4 + 2].pattern_info[0] & 0x01) ? + (0x1 << (3 + 16)) : + 0x0; + regval |= (i * 4 + 3 >= total_cnt) ? 0x0 : + (i * 4 + 3 >= pattern_cnt) ? (0x1 << (3 + 24)) : + pdata->pattern[i * 4 + 3].pattern_offset ? + 0x0 : + !(pdata->pattern[i * 4 + 3].mask_info[0] & 0x01) ? + 0x0 : + (pdata->pattern[i * 4 + 3].pattern_info[0] & 0x01) ? + (0x1 << (3 + 24)) : + 0x0; + writereg(pdata->pAdapter, regval, + pdata->mac_regs + MAC_RWK_PAC); + + writereg(pdata->pAdapter, + (pdata->pattern[i * 4 + 3].pattern_offset << 24) | + (pdata->pattern[i * 4 + 2].pattern_offset + << 16) | + (pdata->pattern[i * 4 + 1].pattern_offset + << 8) | + (pdata->pattern[i * 4 + 0].pattern_offset + << 0), + pdata->mac_regs + MAC_RWK_PAC); + + writereg(pdata->pAdapter, + (pdata->pattern[i * 4 + 1].pattern_crc << 16) | + (pdata->pattern[i * 4 + 0].pattern_crc << 0), + pdata->mac_regs + MAC_RWK_PAC); + writereg(pdata->pAdapter, + (pdata->pattern[i * 4 + 3].pattern_crc << 16) | + (pdata->pattern[i * 4 + 2].pattern_crc << 0), + pdata->mac_regs + MAC_RWK_PAC); + } + + for (i = 0; i < total_cnt; i++) { + fxgmac_set_wake_pattern_mask( + pdata, i, 0, + ((pdata->pattern[i].mask_info[7] & 0x7f) << (24 + 1)) | + (pdata->pattern[i].mask_info[6] << (16 + 1)) | + (pdata->pattern[i].mask_info[5] << (8 + 1)) | + (pdata->pattern[i].mask_info[4] << (0 + 1)) | + ((pdata->pattern[i].mask_info[3] & 0x80) >> 7)); + fxgmac_set_wake_pattern_mask( + pdata, i, 1, + ((pdata->pattern[i].mask_info[11] & 0x7f) << (24 + 1)) | + (pdata->pattern[i].mask_info[10] << (16 + 1)) | + (pdata->pattern[i].mask_info[9] << (8 + 1)) | + (pdata->pattern[i].mask_info[8] << (0 + 1)) | + ((pdata->pattern[i].mask_info[7] & 0x80) >> 7)); + fxgmac_set_wake_pattern_mask( + pdata, i, 2, + ((pdata->pattern[i].mask_info[15] & 0x7f) << (24 + 1)) | + (pdata->pattern[i].mask_info[14] << (16 + 1)) | + (pdata->pattern[i].mask_info[13] << (8 + 1)) | + (pdata->pattern[i].mask_info[12] << (0 + 1)) | + ((pdata->pattern[i].mask_info[11] & 0x80) >> + 7)); + } + + return 0; +} + +static int fxgmac_enable_wake_pattern(struct fxgmac_pdata *pdata) +{ + u32 regval; + regval = readreg(pdata->pAdapter, pdata->mac_regs + MAC_PMT_STA); + regval = FXGMAC_SET_REG_BITS(regval, MAC_PMT_STA_RWKFILTERST_POS, + MAC_PMT_STA_RWKFILTERST_LEN, 1); + regval = FXGMAC_SET_REG_BITS(regval, MAC_PMT_STA_RWKPKTEN_POS, + MAC_PMT_STA_RWKPKTEN_LEN, 1); + writereg(pdata->pAdapter, regval, pdata->mac_regs + MAC_PMT_STA); + regval = readreg(pdata->pAdapter, pdata->base_mem + WOL_CTL); + regval = FXGMAC_SET_REG_BITS(regval, WOL_PKT_EN_POS, WOL_PKT_EN_LEN, 1); + writereg(pdata->pAdapter, regval, pdata->base_mem + WOL_CTL); + return 0; +} + +static int fxgmac_disable_wake_pattern(struct fxgmac_pdata *pdata) +{ + u32 regval; + + regval = readreg(pdata->pAdapter, pdata->mac_regs + MAC_PMT_STA); + regval = FXGMAC_SET_REG_BITS(regval, MAC_PMT_STA_RWKFILTERST_POS, + MAC_PMT_STA_RWKFILTERST_LEN, 1); + regval = FXGMAC_SET_REG_BITS(regval, MAC_PMT_STA_RWKPKTEN_POS, + MAC_PMT_STA_RWKPKTEN_LEN, 0); + writereg(pdata->pAdapter, regval, pdata->mac_regs + MAC_PMT_STA); + regval = readreg(pdata->pAdapter, pdata->base_mem + WOL_CTL); + regval = FXGMAC_SET_REG_BITS(regval, WOL_PKT_EN_POS, WOL_PKT_EN_LEN, 0); + writereg(pdata->pAdapter, regval, pdata->base_mem + WOL_CTL); + return 0; +} +static int fxgmac_enable_wake_magic_pattern(struct fxgmac_pdata *pdata) +{ + u32 regval; + + regval = readreg(pdata->pAdapter, pdata->mac_regs + MAC_PMT_STA); + regval = FXGMAC_SET_REG_BITS(regval, MAC_PMT_STA_MGKPKTEN_POS, + MAC_PMT_STA_MGKPKTEN_LEN, 1); + writereg(pdata->pAdapter, regval, pdata->mac_regs + MAC_PMT_STA); + regval = readreg(pdata->pAdapter, pdata->base_mem + WOL_CTL); + regval = FXGMAC_SET_REG_BITS(regval, WOL_PKT_EN_POS, WOL_PKT_EN_LEN, 1); + writereg(pdata->pAdapter, regval, pdata->base_mem + WOL_CTL); + + /* Enable PME Enable Bit. */ + cfg_r32(pdata, REG_PM_STATCTRL, ®val); + regval = FXGMAC_SET_REG_BITS(regval, PM_CTRLSTAT_PME_EN_POS, + PM_CTRLSTAT_PME_EN_LEN, 1); + cfg_w32(pdata, REG_PM_STATCTRL, regval); + + return 0; +} + +static int fxgmac_disable_wake_magic_pattern(struct fxgmac_pdata *pdata) +{ + u32 regval; + regval = readreg(pdata->pAdapter, pdata->base_mem + WOL_CTL); + regval = FXGMAC_SET_REG_BITS(regval, WOL_PKT_EN_POS, WOL_PKT_EN_LEN, 0); + writereg(pdata->pAdapter, regval, pdata->base_mem + WOL_CTL); + regval = readreg(pdata->pAdapter, pdata->mac_regs + MAC_PMT_STA); + regval = FXGMAC_SET_REG_BITS(regval, MAC_PMT_STA_MGKPKTEN_POS, + MAC_PMT_STA_MGKPKTEN_LEN, 0); + writereg(pdata->pAdapter, regval, pdata->mac_regs + MAC_PMT_STA); + return 0; +} + +#if FXGMAC_PM_WPI_READ_FEATURE_ENABLED +/* + * enable Wake packet indication. called to enable before sleep/hibernation + * and no needed to call disable for that, fxgmac_get_wake_packet_indication will clear to normal once done. + */ +static void fxgmac_enable_wake_packet_indication(struct fxgmac_pdata *pdata, + int en) +{ + u32 val_wpi_crtl0; + + /* read-clear WoL event. */ + readreg(pdata->pAdapter, pdata->base_mem + MGMT_WOL_CTRL); + + /* get wake packet information */ + val_wpi_crtl0 = + (u32)readreg(pdata->pAdapter, pdata->base_mem + MGMT_WPI_CTRL0); + + /* prepare to write packet data by write wpi_mode to 1 */ + val_wpi_crtl0 = + FXGMAC_SET_REG_BITS(val_wpi_crtl0, MGMT_WPI_CTRL0_WPI_MODE_POS, + MGMT_WPI_CTRL0_WPI_MODE_LEN, + (en ? MGMT_WPI_CTRL0_WPI_MODE_WR : + MGMT_WPI_CTRL0_WPI_MODE_NORMAL)); + writereg(pdata->pAdapter, val_wpi_crtl0, + pdata->base_mem + MGMT_WPI_CTRL0); + + DbgPrintF(MP_TRACE, "%s - WPI pkt enable=%d, reg=%08x.\n", __FUNCTION__, + en, val_wpi_crtl0); + + return; +} + +/* + * this function read Wake up packet after MDIS resume + * input: + * pdata + * wpi_buf container of a packet. + * buf_size size of the packet container. since HW limit to 14bits, ie 16KB all together. + * output: + * wake_reason from HW, we can indentify 1)magic packet, or 2)pattern(remote wake packet) or WAKE_REASON_HW_ERR indicates err + * packet_size length of the wake packet. 0 indicates exception. + * + */ +static void fxgmac_get_wake_packet_indication(struct fxgmac_pdata *pdata, + int *wake_reason, + u32 *wake_pattern_number, + u8 *wpi_buf, u32 buf_size, + u32 *packet_size) +{ + u32 i, regval, val_wpi_crtl0, *dw_wpi_buf; + u32 data_len, data_len_dw, b_need_pkt = 0; + + *wake_reason = WAKE_REASON_NONE; + *packet_size = 0; + fxgmac_release_phy(pdata); + + /* try to check wake reason. GMAC reg 20c0 only tells Magic or remote-pattern + * read from MGMT_WOL_CTRL, 1530 instead. + */ + regval = (u32)readreg(pdata->pAdapter, pdata->base_mem + MGMT_WOL_CTRL); + DbgPrintF(MP_TRACE, "%s - 0x1530=%x.\n", __FUNCTION__, regval); + if (!regval) { + DbgPrintF(MP_TRACE, "%s - nothing for WPI pkt.\n", + __FUNCTION__); + return; + } + + if (regval & MGMT_WOL_CTRL_WPI_MGC_PKT) { + *wake_reason = WAKE_REASON_MAGIC; + b_need_pkt = 1; + } else if (regval & MGMT_WOL_CTRL_WPI_RWK_PKT) { + *wake_reason = WAKE_REASON_PATTERNMATCH; + b_need_pkt = 1; + *wake_pattern_number = 0; + + /* + * wake_pattern_number, HW should tell,,tbd + */ + for (i = 0; i < MAX_PATTERN_COUNT; i++) { + if (regval & + ((u32)MGMT_WOL_CTRL_WPI_RWK_PKT_NUMBER << i)) { + *wake_pattern_number = i; + break; + } + } + + } else if (regval & MGMT_WOL_CTRL_WPI_LINK_CHG) { + *wake_reason = WAKE_REASON_LINK; + } + + if (!b_need_pkt) { + DbgPrintF(MP_TRACE, "%s - wake by link and no WPI pkt.\n", + __FUNCTION__); + return; + } + + /* get wake packet information */ + val_wpi_crtl0 = + (u32)readreg(pdata->pAdapter, pdata->base_mem + MGMT_WPI_CTRL0); + + if (val_wpi_crtl0 & MGMT_WPI_CTRL0_WPI_FAIL) { + *wake_reason = WAKE_REASON_HW_ERR; + DbgPrintF(MP_TRACE, "%s - WPI pkt fail from hw.\n", + __FUNCTION__); + return; + } + + *packet_size = FXGMAC_GET_REG_BITS(val_wpi_crtl0, + MGMT_WPI_CTRL0_WPI_PKT_LEN_POS, + MGMT_WPI_CTRL0_WPI_PKT_LEN_LEN); + + if (0 == *packet_size) { + *wake_reason = WAKE_REASON_HW_ERR; + DbgPrintF(MP_TRACE, "%s - WPI pkt len is 0 from hw.\n", + __FUNCTION__); + return; + } + + DbgPrintF(MP_TRACE, "%s - WPI pkt len from hw, *packet_size=%u.\n", + __FUNCTION__, *packet_size); + + if (buf_size < *packet_size) { + DbgPrintF(MP_WARN, + "%s - too small buf_size=%u, WPI pkt len is %u.\n", + __FUNCTION__, buf_size, *packet_size); + data_len = buf_size; + } else { + data_len = *packet_size; + } + + /* prepare to read packet data by write wpi_mode to 2 */ + val_wpi_crtl0 = FXGMAC_SET_REG_BITS(val_wpi_crtl0, + MGMT_WPI_CTRL0_WPI_MODE_POS, + MGMT_WPI_CTRL0_WPI_MODE_LEN, + MGMT_WPI_CTRL0_WPI_MODE_RD); + writereg(pdata->pAdapter, val_wpi_crtl0, + pdata->base_mem + MGMT_WPI_CTRL0); + + dw_wpi_buf = (u32 *)wpi_buf; + data_len_dw = (data_len + 3) / 4; + + i = 0; + DbgPrintF( + MP_TRACE, + "%s - before retrieve, len=%d, len_dw=%d, reg_wpi_ctrl0=%08x.\n", + __FUNCTION__, data_len, data_len_dw, val_wpi_crtl0); + while ((0 == (val_wpi_crtl0 & MGMT_WPI_CTRL0_WPI_OP_DONE))) { + if (i < data_len_dw) { + regval = (u32)readreg(pdata->pAdapter, + pdata->base_mem + + MGMT_WPI_CTRL1_DATA); + /*dw_wpi_buf[i] = SWAP_BYTES_32(regval);*/ + dw_wpi_buf[i] = regval; + + } else { + break; + } + + val_wpi_crtl0 = (u32)readreg(pdata->pAdapter, + pdata->base_mem + MGMT_WPI_CTRL0); + i++; + } + if (*packet_size <= MAC_CRC_LENGTH) { + DbgPrintF(MP_TRACE, + "%s - Warning, WPI pkt len is less 4 from hw.\n", + __FUNCTION__); + return; + } + *packet_size -= MAC_CRC_LENGTH; + + /* once read data complete and write wpi_mode to 0, normal */ + val_wpi_crtl0 = FXGMAC_SET_REG_BITS(val_wpi_crtl0, + MGMT_WPI_CTRL0_WPI_MODE_POS, + MGMT_WPI_CTRL0_WPI_MODE_LEN, + MGMT_WPI_CTRL0_WPI_MODE_NORMAL); + writereg(pdata->pAdapter, val_wpi_crtl0, + pdata->base_mem + MGMT_WPI_CTRL0); + + DbgPrintF( + MP_TRACE, + "%s - WPI done and back to normal mode, reg=%08x, read data=%dB.\n", + __FUNCTION__, val_wpi_crtl0, i * 4); + + return; +} +#endif /* FXGMAC_PM_WPI_READ_FEATURE_ENABLED */ + +static int fxgmac_enable_wake_link_change(struct fxgmac_pdata *pdata) +{ + u32 regval; + + regval = readreg(pdata->pAdapter, pdata->base_mem + WOL_CTL); + regval = FXGMAC_SET_REG_BITS(regval, WOL_LINKCHG_EN_POS, + WOL_LINKCHG_EN_LEN, 1); + writereg(pdata->pAdapter, regval, pdata->base_mem + WOL_CTL); + return 0; +} +static int fxgmac_disable_wake_link_change(struct fxgmac_pdata *pdata) +{ + u32 regval; + + regval = readreg(pdata->pAdapter, pdata->base_mem + WOL_CTL); + regval = FXGMAC_SET_REG_BITS(regval, WOL_LINKCHG_EN_POS, + WOL_LINKCHG_EN_LEN, 0); + writereg(pdata->pAdapter, regval, pdata->base_mem + WOL_CTL); + return 0; +} +#endif + +static u32 fxgmac_get_ephy_state(struct fxgmac_pdata *pdata) +{ + u32 value; + value = readreg(pdata->pAdapter, pdata->base_mem + MGMT_EPHY_CTRL); + return value; +} + +static void fxgmac_enable_dma_interrupts(struct fxgmac_pdata *pdata) +{ +#ifndef DPDK + u32 dma_ch_isr, dma_ch_ier; + struct fxgmac_channel *channel; + unsigned int i; + +#ifdef NIC_NET_ADAPETERCX + u32 regval; + + regval = (u32)readreg(pdata->pAdapter, pdata->mac_regs + DMA_MR); + regval = FXGMAC_SET_REG_BITS(regval, DMA_MR_INTM_POS, DMA_MR_INTM_LEN, + 1); + regval = FXGMAC_SET_REG_BITS(regval, DMA_MR_QUREAD_POS, + DMA_MR_QUREAD_LEN, 1); + writereg(pdata->pAdapter, regval, pdata->mac_regs + DMA_MR); +#endif + + channel = pdata->channel_head; + for (i = 0; i < pdata->channel_count; i++, channel++) { + /* Clear all the interrupts which are set */ + dma_ch_isr = readreg(pdata->pAdapter, + FXGMAC_DMA_REG(channel, DMA_CH_SR)); + writereg(pdata->pAdapter, dma_ch_isr, + FXGMAC_DMA_REG(channel, DMA_CH_SR)); + + /* Clear all interrupt enable bits */ + dma_ch_ier = 0; + + /* Enable following interrupts + * NIE - Normal Interrupt Summary Enable + * AIE - Abnormal Interrupt Summary Enable + * FBEE - Fatal Bus Error Enable + */ + dma_ch_ier = FXGMAC_SET_REG_BITS(dma_ch_ier, DMA_CH_IER_NIE_POS, + DMA_CH_IER_NIE_LEN, 1); + /* + dma_ch_ier = FXGMAC_SET_REG_BITS(dma_ch_ier, + DMA_CH_IER_AIE_POS, + DMA_CH_IER_AIE_LEN, 1); + */ + dma_ch_ier = FXGMAC_SET_REG_BITS(dma_ch_ier, + DMA_CH_IER_FBEE_POS, + DMA_CH_IER_FBEE_LEN, 1); + + if (channel->tx_ring) { + /* Enable the following Tx interrupts + * TIE - Transmit Interrupt Enable (unless using + * per channel interrupts) + */ + if (!pdata->per_channel_irq) + dma_ch_ier = FXGMAC_SET_REG_BITS( + dma_ch_ier, DMA_CH_IER_TIE_POS, + DMA_CH_IER_TIE_LEN, 1); + if (FXGMAC_IS_CHANNEL_WITH_TX_IRQ(i)) { + if (pdata->per_channel_irq) { + dma_ch_ier = FXGMAC_SET_REG_BITS( + dma_ch_ier, DMA_CH_IER_TIE_POS, + DMA_CH_IER_TIE_LEN, 1); + + /*dma_ch_ier = FXGMAC_SET_REG_BITS( + dma_ch_ier, + DMA_CH_IER_TBUE_POS, + DMA_CH_IER_TBUE_LEN, + 1);*/ + } + } + } + if (channel->rx_ring) { + /* Enable following Rx interrupts + * RBUE - Receive Buffer Unavailable Enable + * RIE - Receive Interrupt Enable (unless using + * per channel interrupts) + */ + dma_ch_ier = FXGMAC_SET_REG_BITS(dma_ch_ier, + DMA_CH_IER_RBUE_POS, + DMA_CH_IER_RBUE_LEN, + 1); + + dma_ch_ier = FXGMAC_SET_REG_BITS(dma_ch_ier, + DMA_CH_IER_RIE_POS, + DMA_CH_IER_RIE_LEN, 1); + } + + writereg(pdata->pAdapter, dma_ch_ier, + FXGMAC_DMA_REG(channel, DMA_CH_IER)); + } +#else + struct fxgmac_tx_queue *txq; + unsigned int dma_ch_isr, dma_ch_ier; + unsigned int i; + + for (i = 0; i < pdata->expansion.eth_dev->data->nb_tx_queues; i++) { + txq = pdata->expansion.eth_dev->data->tx_queues[i]; + if (!txq) { + DPRINTK("Tx queue not setup for port %d\n", + pdata->expansion.eth_dev->data->port_id); + return; + } + + /* Clear all the interrupts which are set */ + dma_ch_isr = FXGMAC_DMA_IOREAD(txq, DMA_CH_SR); + FXGMAC_DMA_IOWRITE(txq, DMA_CH_SR, dma_ch_isr); + + /* Clear all interrupt enable bits */ + dma_ch_ier = 0; + + /* Enable following interrupts + * NIE - Normal Interrupt Summary Enable + * AIE - Abnormal Interrupt Summary Enable + * FBEE - Fatal Bus Error Enable + */ + FXGMAC_SET_BITS(dma_ch_ier, DMA_CH_IER, NIE, 1); + FXGMAC_SET_BITS(dma_ch_ier, DMA_CH_IER, AIE, 1); + FXGMAC_SET_BITS(dma_ch_ier, DMA_CH_IER, FBEE, 1); + + /* Enable following Rx interrupts + * RBUE - Receive Buffer Unavailable Enable + * RIE - Receive Interrupt Enable (unless using + * per channel interrupts in edge triggered + * mode) + */ + FXGMAC_SET_BITS(dma_ch_ier, DMA_CH_IER, RBUE, 1); + FXGMAC_SET_BITS(dma_ch_ier, DMA_CH_IER, RIE, 0); + + FXGMAC_DMA_IOWRITE(txq, DMA_CH_IER, dma_ch_ier); + } +#endif +} + +static void fxgmac_enable_mtl_interrupts(struct fxgmac_pdata *pdata) +{ + unsigned int i; + u32 mtl_q_isr, q_count; + + q_count = max(pdata->hw_feat.tx_q_cnt, pdata->hw_feat.rx_q_cnt); + for (i = 0; i < q_count; i++) { + /* Clear all the interrupts which are set */ + mtl_q_isr = readreg(pdata->pAdapter, + FXGMAC_MTL_REG(pdata, i, MTL_Q_ISR)); + writereg(pdata->pAdapter, mtl_q_isr, + FXGMAC_MTL_REG(pdata, i, MTL_Q_ISR)); + + /* No MTL interrupts to be enabled */ + writereg(pdata->pAdapter, 0, + FXGMAC_MTL_REG(pdata, i, MTL_Q_IER)); + } +} + +static void fxgmac_enable_mac_interrupts(struct fxgmac_pdata *pdata) +{ + u32 mac_ier = 0; + u32 regval; + + /* Enable Timestamp interrupt */ + mac_ier = FXGMAC_SET_REG_BITS(mac_ier, MAC_IER_TSIE_POS, + MAC_IER_TSIE_LEN, 1); + + writereg(pdata->pAdapter, mac_ier, pdata->mac_regs + MAC_IER); + + regval = readreg(pdata->pAdapter, pdata->mac_regs + MMC_RIER); + regval = FXGMAC_SET_REG_BITS(regval, MMC_RIER_ALL_INTERRUPTS_POS, + MMC_RIER_ALL_INTERRUPTS_LEN, + FXGMAC_MMC_IER_ALL_DEFAULT); + writereg(pdata->pAdapter, regval, pdata->mac_regs + MMC_RIER); + regval = readreg(pdata->pAdapter, pdata->mac_regs + MMC_TIER); + regval = FXGMAC_SET_REG_BITS(regval, MMC_TIER_ALL_INTERRUPTS_POS, + MMC_TIER_ALL_INTERRUPTS_LEN, + FXGMAC_MMC_IER_ALL_DEFAULT); + writereg(pdata->pAdapter, regval, pdata->mac_regs + MMC_TIER); +} + +static int fxgmac_set_fxgmii_2500_speed(struct fxgmac_pdata *pdata) +{ + u32 regval; + + regval = readreg(pdata->pAdapter, pdata->mac_regs + MAC_CR); + regval = FXGMAC_SET_REG_BITS(regval, MAC_CR_PS_POS, MAC_CR_PS_LEN, 0); + regval = FXGMAC_SET_REG_BITS(regval, MAC_CR_FES_POS, MAC_CR_FES_LEN, 1); + regval = FXGMAC_SET_REG_BITS(regval, MAC_CR_DM_POS, MAC_CR_DM_LEN, + pdata->phy_duplex); + writereg(pdata->pAdapter, regval, pdata->mac_regs + MAC_CR); + + return 0; +} + +static int fxgmac_set_fxgmii_1000_speed(struct fxgmac_pdata *pdata) +{ + u32 regval; + + regval = readreg(pdata->pAdapter, pdata->mac_regs + MAC_CR); + regval = FXGMAC_SET_REG_BITS(regval, MAC_CR_PS_POS, MAC_CR_PS_LEN, 0); + regval = FXGMAC_SET_REG_BITS(regval, MAC_CR_FES_POS, MAC_CR_FES_LEN, 0); + regval = FXGMAC_SET_REG_BITS(regval, MAC_CR_DM_POS, MAC_CR_DM_LEN, + pdata->phy_duplex); + writereg(pdata->pAdapter, regval, pdata->mac_regs + MAC_CR); + + return 0; +} + +static int fxgmac_set_fxgmii_100_speed(struct fxgmac_pdata *pdata) +{ + u32 regval; + + regval = readreg(pdata->pAdapter, pdata->mac_regs + MAC_CR); + regval = FXGMAC_SET_REG_BITS(regval, MAC_CR_PS_POS, MAC_CR_PS_LEN, 1); + regval = FXGMAC_SET_REG_BITS(regval, MAC_CR_FES_POS, MAC_CR_FES_LEN, 1); + regval = FXGMAC_SET_REG_BITS(regval, MAC_CR_DM_POS, MAC_CR_DM_LEN, + pdata->phy_duplex); + writereg(pdata->pAdapter, regval, pdata->mac_regs + MAC_CR); + + return 0; +} + +static int fxgmac_set_fxgmii_10_speed(struct fxgmac_pdata *pdata) +{ + u32 regval; + + regval = readreg(pdata->pAdapter, pdata->mac_regs + MAC_CR); + regval = FXGMAC_SET_REG_BITS(regval, MAC_CR_PS_POS, MAC_CR_PS_LEN, 1); + regval = FXGMAC_SET_REG_BITS(regval, MAC_CR_FES_POS, MAC_CR_FES_LEN, 0); + regval = FXGMAC_SET_REG_BITS(regval, MAC_CR_DM_POS, MAC_CR_DM_LEN, + pdata->phy_duplex); + writereg(pdata->pAdapter, regval, pdata->mac_regs + MAC_CR); + + return 0; +} + +/** + * fxgmac_check_phy_link - Get link/speed status + * @pdata: pointer to gmac structure + * @speed: pointer to link speed + * @link_up: true is link is up, false otherwise + * @link_up_wait_to_complete: bool used to wait for link up or not + * + * Reads the links register to determine if link is up and the current speed + **/ +static int fxgmac_check_phy_link(struct fxgmac_pdata *pdata, u32 *speed, + bool *link_up, bool link_up_wait_to_complete) +{ + u16 link_reg = 0; + (void)link_up_wait_to_complete; + if (pdata->base_mem) { + link_reg = (u16)readreg(pdata->pAdapter, + pdata->base_mem + MGMT_EPHY_CTRL); + + pdata->phy_duplex = !!(link_reg & 0x4); + + /* + * check register address 0x1004 + * b[6:5] ephy_pause + * b[4:3] ephy_speed 0b10 1000m 0b01 100m + * b[2] ephy_duplex + * b[1] ephy_link + * b[0] ephy_reset. should be set to 1 before use phy. + */ + + *link_up = false; + if (link_reg & MGMT_EPHY_CTRL_STA_EPHY_RELEASE) { + if (link_up) { + *link_up = (link_reg & + MGMT_EPHY_CTRL_STA_EPHY_LINKUP) ? + true : + false; + } + if (speed) + *speed = (link_reg & + MGMT_EPHY_CTRL_STA_SPEED_MASK) >> + MGMT_EPHY_CTRL_STA_SPEED_POS; + } else { + DPRINTK("fxgmac_check_phy_link ethernet PHY not released link reg %d.\n", + link_reg); + return -1; + } + } else { + DPRINTK("fxgmac_check_phy_link null base addr err link reg %d\n", + link_reg); + return -1; + } + + return 0; +} + +static int fxgmac_config_mac_speed(struct fxgmac_pdata *pdata) +{ + switch (pdata->phy_speed) { + case SPEED_2500: + fxgmac_set_fxgmii_2500_speed(pdata); + break; + case SPEED_1000: + fxgmac_set_fxgmii_1000_speed(pdata); + break; + case SPEED_100: + fxgmac_set_fxgmii_100_speed(pdata); + break; + case SPEED_10: + fxgmac_set_fxgmii_10_speed(pdata); + break; + } + return 0; +} + +static int fxgmac_write_ephy_reg(struct fxgmac_pdata *pdata, u32 reg_id, + u32 data) +{ + u32 regval; + u32 mdioctrl = reg_id * 0x10000 + 0x8000205; + int busy = 15; + + writereg(pdata->pAdapter, data, pdata->mac_regs + MAC_MDIO_DATA); + writereg(pdata->pAdapter, mdioctrl, pdata->mac_regs + MAC_MDIO_ADDRESS); + do { + regval = readreg(pdata->pAdapter, + pdata->mac_regs + MAC_MDIO_ADDRESS); + busy--; + } while ((regval & MAC_MDIO_ADDRESS_BUSY) && (busy)); + + DPRINTK("fxgmac_write_ephy_reg id %d,", reg_id); + DPRINTK(" %s,", (regval & 0x1) ? "err" : "ok"); + DPRINTK(" ctrl=0x%08x,", regval); + DPRINTK(" data=0x%08x\n", data); + + return (regval & MAC_MDIO_ADDRESS_BUSY) ? -ETIMEDOUT : 0; +} + +static int fxgmac_read_ephy_reg(struct fxgmac_pdata *pdata, u32 reg_id, + u32 __far *data) +{ + u32 regval = 0, regret; + u32 mdioctrl = reg_id * 0x10000 + 0x800020d; + int busy = 15; + + writereg(pdata->pAdapter, mdioctrl, pdata->mac_regs + MAC_MDIO_ADDRESS); + do { + regval = readreg(pdata->pAdapter, + pdata->mac_regs + MAC_MDIO_ADDRESS); + busy--; + + } while ((regval & MAC_MDIO_ADDRESS_BUSY) && (busy)); + + if (0 == (regval & MAC_MDIO_ADDRESS_BUSY)) { + regret = readreg(pdata->pAdapter, + pdata->mac_regs + MAC_MDIO_DATA); + if (data) { + *data = regret; + + return 0; + } else { + return -ENOBUFS; + } + } + + DPRINTK("fxgmac_read_ephy_reg id=0x%02x err,", reg_id); + DPRINTK(" busy=%d,", busy); + DPRINTK(" ctrl=0x%08x\n", regval); + + return -ETIMEDOUT; +} + +static int fxgmac_write_ephy_mmd_reg(struct fxgmac_pdata *pdata, u32 reg_id, + u32 mmd, u32 data) +{ + u32 regval; + u32 mdioctrl = (mmd << 16) + 0x8000207; + u32 regdata = (reg_id << 16) + data; + + int busy = 60; + + writereg(pdata->pAdapter, regdata, pdata->mac_regs + MAC_MDIO_DATA); + writereg(pdata->pAdapter, mdioctrl, pdata->mac_regs + MAC_MDIO_ADDRESS); + do { + regval = readreg(pdata->pAdapter, + pdata->mac_regs + MAC_MDIO_ADDRESS); + busy--; + } while ((regval & MAC_MDIO_ADDRESS_BUSY) && (busy)); + + DPRINTK("fxgmac_write_ephy_mmd_reg id %d,", reg_id); + DPRINTK(" mmd %d,", mmd); + DPRINTK(" %s,", (regval & 0x1) ? "err" : "ok"); + DPRINTK(" ctrl=0x%08x,", regval); + DPRINTK(" data=0x%08x\n", data); + + return (regval & MAC_MDIO_ADDRESS_BUSY) ? -1 : 0; +} + +/* +#if !defined(LINUX) && !defined(DPDK) +static int fxgmac_read_ephy_mmd_reg(struct fxgmac_pdata* pdata, u32 reg_id, u32 mmd, u32* data) +{ + u32 regval = 0, regret; + u32 mdioctrl = (mmd << 16) + 0x800020f; + u32 regdata = (reg_id << 16); + + int busy = 60; + + writereg(pdata->pAdapter, regdata, pdata->mac_regs + MAC_MDIO_DATA); + writereg(pdata->pAdapter, mdioctrl, pdata->mac_regs + MAC_MDIO_ADDRESS); + + do { + regval = readreg(pdata->pAdapter, pdata->mac_regs + MAC_MDIO_ADDRESS); + busy--; + } while ((regval & MAC_MDIO_ADDRESS_BUSY) && (busy)); + + if (0 == (regval & MAC_MDIO_ADDRESS_BUSY)) { + regret = readreg(pdata->pAdapter, pdata->mac_regs + MAC_MDIO_DATA); + if (data) *data = (regret & 0xffff); + return regret; + } + + DPRINTK("fxgmac_read_ephy_mmd_reg id=0x%02x mmd %d err, busy=%d, ctrl=0x%08x\n", reg_id, mmd, busy, regval); + + return -1; +} +#endif +*/ + +static void fxgmac_config_flow_control(struct fxgmac_pdata *pdata) +{ +#ifndef FXGMAC_NOT_REPORT_PHY_FC_CAPABILITY + u32 regval = 0; +#endif + + fxgmac_config_tx_flow_control(pdata); + fxgmac_config_rx_flow_control(pdata); + +#ifndef FXGMAC_NOT_REPORT_PHY_FC_CAPABILITY + fxgmac_read_ephy_reg(pdata, REG_MII_ADVERTISE, ®val); + + if (pdata->tx_pause || pdata->rx_pause) { + regval = FXGMAC_SET_REG_BITS(regval, + PHY_MII_ADVERTISE_PAUSE_POS, + PHY_MII_ADVERTISE_PAUSE_LEN, 1); + regval = FXGMAC_SET_REG_BITS(regval, + PHY_MII_ADVERTISE_ASYPAUSE_POS, + PHY_MII_ADVERTISE_ASYPAUSE_LEN, 1); + } else { + regval = FXGMAC_SET_REG_BITS(regval, + PHY_MII_ADVERTISE_PAUSE_POS, + PHY_MII_ADVERTISE_PAUSE_LEN, 0); + regval = FXGMAC_SET_REG_BITS(regval, + PHY_MII_ADVERTISE_ASYPAUSE_POS, + PHY_MII_ADVERTISE_ASYPAUSE_LEN, 0); + } + fxgmac_write_ephy_reg(pdata, REG_MII_ADVERTISE, regval); + + fxgmac_read_ephy_reg(pdata, REG_MII_BMCR, ®val); + regval = FXGMAC_SET_REG_BITS(regval, PHY_CR_RESET_POS, PHY_CR_RESET_LEN, + 1); + fxgmac_write_ephy_reg(pdata, REG_MII_BMCR, regval); +#endif +} + +static int fxgmac_set_ephy_autoneg_advertise(struct fxgmac_pdata *pdata, + struct fxphy_ag_adv phy_ag_adv) +{ + u32 regval = 0; + int ret = 0; + + if (phy_ag_adv.auto_neg_en) { + fxgmac_read_ephy_reg(pdata, REG_MII_BMCR, ®val); + regval = FXGMAC_SET_REG_BITS(regval, PHY_CR_AUTOENG_POS, + PHY_CR_AUTOENG_LEN, 1); + } else { + fxgmac_read_ephy_reg(pdata, REG_MII_BMCR, ®val); + regval = FXGMAC_SET_REG_BITS(regval, PHY_CR_AUTOENG_POS, + PHY_CR_AUTOENG_LEN, 0); + } + ret = fxgmac_write_ephy_reg(pdata, REG_MII_BMCR, regval); + if (ret < 0) + return ret; + + fxgmac_read_ephy_reg(pdata, REG_MII_CTRL1000, ®val); + if (phy_ag_adv.full_1000m) { + regval = FXGMAC_SET_REG_BITS(regval, + PHY_MII_CTRL1000_1000FULL_POS, + PHY_MII_CTRL1000_1000FULL_LEN, 1); + } else { + regval = FXGMAC_SET_REG_BITS(regval, + PHY_MII_CTRL1000_1000FULL_POS, + PHY_MII_CTRL1000_1000FULL_LEN, 0); + } + if (phy_ag_adv.half_1000m) { + regval = FXGMAC_SET_REG_BITS(regval, + PHY_MII_CTRL1000_1000HALF_POS, + PHY_MII_CTRL1000_1000HALF_LEN, 1); + } else { + regval = FXGMAC_SET_REG_BITS(regval, + PHY_MII_CTRL1000_1000HALF_POS, + PHY_MII_CTRL1000_1000HALF_LEN, 0); + } + ret = fxgmac_write_ephy_reg(pdata, REG_MII_CTRL1000, regval); + if (ret < 0) + return ret; + + fxgmac_read_ephy_reg(pdata, REG_MII_ADVERTISE, ®val); + + if (phy_ag_adv.full_100m) { + regval = FXGMAC_SET_REG_BITS(regval, + PHY_MII_ADVERTISE_100FULL_POS, + PHY_MII_ADVERTISE_100FULL_LEN, 1); + } else { + regval = FXGMAC_SET_REG_BITS(regval, + PHY_MII_ADVERTISE_100FULL_POS, + PHY_MII_ADVERTISE_100FULL_LEN, 0); + } + if (phy_ag_adv.half_100m) { + regval = FXGMAC_SET_REG_BITS(regval, + PHY_MII_ADVERTISE_100HALF_POS, + PHY_MII_ADVERTISE_100HALF_LEN, 1); + } else { + regval = FXGMAC_SET_REG_BITS(regval, + PHY_MII_ADVERTISE_100HALF_POS, + PHY_MII_ADVERTISE_100HALF_LEN, 0); + } + if (phy_ag_adv.full_10m) { + regval = FXGMAC_SET_REG_BITS(regval, + PHY_MII_ADVERTISE_10FULL_POS, + PHY_MII_ADVERTISE_10FULL_LEN, 1); + } else { + regval = FXGMAC_SET_REG_BITS(regval, + PHY_MII_ADVERTISE_10FULL_POS, + PHY_MII_ADVERTISE_10FULL_LEN, 0); + } + if (phy_ag_adv.half_10m) { + regval = FXGMAC_SET_REG_BITS(regval, + PHY_MII_ADVERTISE_10HALF_POS, + PHY_MII_ADVERTISE_10HALF_LEN, 1); + } else { + regval = FXGMAC_SET_REG_BITS(regval, + PHY_MII_ADVERTISE_10HALF_POS, + PHY_MII_ADVERTISE_10HALF_LEN, 0); + } + + ret = fxgmac_write_ephy_reg(pdata, REG_MII_ADVERTISE, regval); + if (ret < 0) + return ret; + + fxgmac_read_ephy_reg(pdata, REG_MII_BMCR, ®val); + regval = FXGMAC_SET_REG_BITS(regval, PHY_CR_RESET_POS, PHY_CR_RESET_LEN, + 1); + ret = fxgmac_write_ephy_reg(pdata, REG_MII_BMCR, regval); + + return ret; +} + +static int fxgmac_phy_config(struct fxgmac_pdata *pdata) +{ + struct fxphy_ag_adv phy_ag_adv; + + if (pdata->phy_autoeng) { + phy_ag_adv.auto_neg_en = 1; + } else { + phy_ag_adv.auto_neg_en = 0; + } + switch (pdata->phy_speed) { + case SPEED_1000: + phy_ag_adv.full_1000m = 1, phy_ag_adv.half_1000m = 0, + phy_ag_adv.full_100m = 1, phy_ag_adv.half_100m = 1, + phy_ag_adv.full_10m = 1, phy_ag_adv.half_10m = 1; + break; + + case SPEED_100: + phy_ag_adv.full_1000m = 0, phy_ag_adv.half_1000m = 0; + if (pdata->phy_duplex) { + phy_ag_adv.full_100m = 1; + } else { + phy_ag_adv.full_100m = 0; + } + phy_ag_adv.half_100m = 1, phy_ag_adv.full_10m = 1, + phy_ag_adv.half_10m = 1; + break; + + case SPEED_10: + phy_ag_adv.full_1000m = 0, phy_ag_adv.half_1000m = 0; + phy_ag_adv.full_100m = 0, phy_ag_adv.half_100m = 0; + if (pdata->phy_duplex) { + phy_ag_adv.full_10m = 1; + } else { + phy_ag_adv.full_10m = 0; + } + phy_ag_adv.half_10m = 1; + break; + + default: + break; + } + return fxgmac_set_ephy_autoneg_advertise(pdata, phy_ag_adv); +} + +static void fxgmac_phy_green_ethernet(struct fxgmac_pdata *pdata) +{ + u32 regval = 0; + + fxgmac_write_ephy_reg(pdata, REG_MII_EXT_ADDR, + REG_MII_EXT_REG_PMA_DBG0_ADC); + fxgmac_write_ephy_reg( + pdata, REG_MII_EXT_DATA, + REG_MII_EXT_ENABLE_GIGA_POWER_SAVING_FOR_SHORT_CABLE); + + fxgmac_write_ephy_reg(pdata, REG_MII_EXT_ADDR, + REG_MII_EXT_REG_CLD_REG0); + fxgmac_write_ephy_reg(pdata, REG_MII_EXT_DATA, + REG_MII_EXT_ENABLE_CLD_NP_WP); + fxgmac_write_ephy_reg(pdata, REG_MII_EXT_ADDR, + REG_MII_EXT_REG_CLD_REG1); + fxgmac_write_ephy_reg(pdata, REG_MII_EXT_DATA, + REG_MII_EXT_ENABLE_CLD_GT_HT_BT); + + fxgmac_read_ephy_reg(pdata, REG_MII_BMCR, ®val); + regval = FXGMAC_SET_REG_BITS(regval, PHY_CR_RESET_POS, PHY_CR_RESET_LEN, + 1); + fxgmac_write_ephy_reg(pdata, REG_MII_BMCR, regval); +} + +static void fxgmac_phy_eee_feature(struct fxgmac_pdata *pdata) +{ + u32 regval = 0; + + regval = readreg(pdata->pAdapter, pdata->mac_regs + DMA_SBMR); + regval = FXGMAC_SET_REG_BITS(regval, DMA_SBMR_EN_LPI_POS, + DMA_SBMR_EN_LPI_LEN, 1); + regval = FXGMAC_SET_REG_BITS(regval, DMA_SBMR_LPI_XIT_PKT_POS, + DMA_SBMR_LPI_XIT_PKT_LEN, 1); + regval = FXGMAC_SET_REG_BITS(regval, DMA_SBMR_AALE_POS, + DMA_SBMR_AALE_LEN, 1); + writereg(pdata->pAdapter, regval, pdata->mac_regs + DMA_SBMR); + + regval = readreg(pdata->pAdapter, pdata->mac_regs + MAC_LPI_STA); + regval = FXGMAC_SET_REG_BITS(regval, MAC_LPIATE_POS, MAC_LPIATE_LEN, 1); + regval = FXGMAC_SET_REG_BITS(regval, MAC_LPITXA_POS, MAC_LPITXA_LEN, 1); + regval = FXGMAC_SET_REG_BITS(regval, MAC_PLS_POS, MAC_PLS_LEN, 1); + regval = FXGMAC_SET_REG_BITS(regval, MAC_LPIEN_POS, MAC_LPIEN_LEN, 1); + writereg(pdata->pAdapter, regval, pdata->mac_regs + MAC_LPI_STA); + + regval = readreg(pdata->pAdapter, pdata->mac_regs + MAC_LPI_TIMER); + regval = FXGMAC_SET_REG_BITS(regval, MAC_LPIET_POS, MAC_LPIET_LEN, + MAC_LPI_ENTRY_TIMER); + writereg(pdata->pAdapter, regval, pdata->mac_regs + MAC_LPI_TIMER); + + regval = readreg(pdata->pAdapter, pdata->mac_regs + MAC_LPI_CONTROL); + regval = FXGMAC_SET_REG_BITS(regval, MAC_TWT_POS, MAC_TWT_LEN, + MAC_TWT_TIMER); + regval = FXGMAC_SET_REG_BITS(regval, MAC_LST_POS, MAC_LST_LEN, + MAC_LST_TIMER); + writereg(pdata->pAdapter, regval, pdata->mac_regs + MAC_LPI_CONTROL); + + regval = readreg(pdata->pAdapter, pdata->mac_regs + MAC_MS_TIC_COUNTER); + regval = FXGMAC_SET_REG_BITS(regval, MAC_MS_TIC_POS, MAC_MS_TIC_LEN, + MAC_MS_TIC); + writereg(pdata->pAdapter, regval, pdata->mac_regs + MAC_MS_TIC_COUNTER); + + fxgmac_write_ephy_mmd_reg(pdata, REG_MMD_EEE_ABILITY_REG, 0x07, + REG_MMD_EEE_ABILITY_VALUE); + + fxgmac_read_ephy_reg(pdata, REG_MII_BMCR, ®val); + regval = FXGMAC_SET_REG_BITS(regval, PHY_CR_RESET_POS, PHY_CR_RESET_LEN, + 1); + fxgmac_write_ephy_reg(pdata, REG_MII_BMCR, regval); +} + +static void fxgmac_reset_phy(struct fxgmac_pdata *pdata) +{ + u32 value = 0; + + value = FXGMAC_SET_REG_BITS(value, MGMT_EPHY_CTRL_RESET_POS, + MGMT_EPHY_CTRL_RESET_LEN, + MGMT_EPHY_CTRL_STA_EPHY_RESET); + writereg(pdata->pAdapter, value, pdata->base_mem + MGMT_EPHY_CTRL); + usleep_range_ex(pdata->pAdapter, 1500, 1500); +} + +void fxgmac_release_phy(struct fxgmac_pdata *pdata) +{ + u32 value = 0; + + value = FXGMAC_SET_REG_BITS(value, MGMT_EPHY_CTRL_RESET_POS, + MGMT_EPHY_CTRL_RESET_LEN, + MGMT_EPHY_CTRL_STA_EPHY_RELEASE); + writereg(pdata->pAdapter, value, pdata->base_mem + MGMT_EPHY_CTRL); + usleep_range_ex(pdata->pAdapter, 100, 150); + value = readreg(pdata->pAdapter, pdata->base_mem + MGMT_EPHY_CTRL); + DBGPRINT(MP_LOUD, ("0x1004: 0x%x\n", value)); +#ifdef AISC_MODE + fxgmac_read_ephy_reg(pdata, REG_MII_SPEC_CTRL, &value); + value = FXGMAC_SET_REG_BITS(value, PHY_MII_SPEC_CTRL_CRS_ON_POS, + PHY_MII_SPEC_CTRL_CRS_ON_LEN, 1); + fxgmac_write_ephy_reg(pdata, REG_MII_SPEC_CTRL, value); + + fxgmac_write_ephy_reg(pdata, REG_MII_EXT_ADDR, REG_MII_EXT_ANALOG_CFG3); + fxgmac_read_ephy_reg(pdata, REG_MII_EXT_DATA, &value); + + value = FXGMAC_SET_REG_BITS(value, + MII_EXT_ANALOG_CFG3_ADC_START_CFG_POS, + MII_EXT_ANALOG_CFG3_ADC_START_CFG_LEN, + MII_EXT_ANALOG_CFG3_ADC_START_CFG_DEFAULT); + fxgmac_write_ephy_reg(pdata, REG_MII_EXT_DATA, value); + + fxgmac_efuse_read_data(pdata, EFUSE_LED_ADDR, &value); + + value = FXGMAC_GET_REG_BITS(value, EFUSE_LED_POS, EFUSE_LED_LEN); + fxgmac_write_ephy_reg(pdata, REG_MII_EXT_ADDR, REG_MII_EXT_ANALOG_CFG2); + fxgmac_write_ephy_reg(pdata, REG_MII_EXT_DATA, + REG_MII_EXT_ANALOG_CFG2_VALUE); + + cfg_r32(pdata, REG_PCI_SUB_VENDOR_ID, &value); + if (AISTONEID_137D1D05_ADJUST_SI == value) { + fxgmac_write_ephy_reg(pdata, REG_MII_EXT_ADDR, + REG_MII_EXT_ANALOG_CFG8); + fxgmac_write_ephy_reg(pdata, REG_MII_EXT_DATA, + REG_MII_EXT_ANALOG_CFG8_137D1D05_VALUE); + } else { + fxgmac_write_ephy_reg(pdata, REG_MII_EXT_ADDR, + REG_MII_EXT_ANALOG_CFG8); + fxgmac_write_ephy_reg(pdata, REG_MII_EXT_DATA, + REG_MII_EXT_ANALOG_CFG8_VALUE); + } + + if (EFUSE_LED_COMMON_SOLUTION != value) { + fxgmac_write_ephy_reg(pdata, REG_MII_EXT_ADDR, + REG_MII_EXT_COMMON_LED0_CFG); + switch (value) { + case EFUSE_LED_SOLUTION1: + fxgmac_write_ephy_reg( + pdata, REG_MII_EXT_DATA, + REG_MII_EXT_COMMON_LED0_CFG_VALUE_SOLUTION1); + break; + case EFUSE_LED_SOLUTION2: + fxgmac_write_ephy_reg( + pdata, REG_MII_EXT_DATA, + REG_MII_EXT_COMMON_LED0_CFG_VALUE_SOLUTION2); + break; + case EFUSE_LED_SOLUTION3: + case EFUSE_LED_SOLUTION4: + fxgmac_write_ephy_reg( + pdata, REG_MII_EXT_DATA, + REG_MII_EXT_COMMON_LED0_CFG_VALUE_SOLUTION3); + break; + default: + + fxgmac_write_ephy_reg( + pdata, REG_MII_EXT_DATA, + REG_MII_EXT_COMMON_LED0_CFG_VALUE_SOLUTION0); + break; + } + + fxgmac_write_ephy_reg(pdata, REG_MII_EXT_ADDR, + REG_MII_EXT_COMMON_LED1_CFG); + switch (value) { + case EFUSE_LED_SOLUTION1: + case EFUSE_LED_SOLUTION4: + fxgmac_write_ephy_reg( + pdata, REG_MII_EXT_DATA, + REG_MII_EXT_COMMON_LED1_CFG_VALUE_SOLUTION1); + break; + case EFUSE_LED_SOLUTION2: + case EFUSE_LED_SOLUTION3: + fxgmac_write_ephy_reg( + pdata, REG_MII_EXT_DATA, + REG_MII_EXT_COMMON_LED1_CFG_VALUE_SOLUTION2); + break; + default: + + fxgmac_write_ephy_reg( + pdata, REG_MII_EXT_DATA, + REG_MII_EXT_COMMON_LED1_CFG_VALUE_SOLUTION0); + break; + } + + fxgmac_write_ephy_reg(pdata, REG_MII_EXT_ADDR, + REG_MII_EXT_COMMON_LED2_CFG); + switch (value) { + case EFUSE_LED_SOLUTION1: + fxgmac_write_ephy_reg( + pdata, REG_MII_EXT_DATA, + REG_MII_EXT_COMMON_LED2_CFG_VALUE_SOLUTION0); + break; + case EFUSE_LED_SOLUTION2: + fxgmac_write_ephy_reg( + pdata, REG_MII_EXT_DATA, + REG_MII_EXT_COMMON_LED2_CFG_VALUE_SOLUTION2); + break; + case EFUSE_LED_SOLUTION3: + fxgmac_write_ephy_reg( + pdata, REG_MII_EXT_DATA, + REG_MII_EXT_COMMON_LED2_CFG_VALUE_SOLUTION3); + break; + case EFUSE_LED_SOLUTION4: + fxgmac_write_ephy_reg( + pdata, REG_MII_EXT_DATA, + REG_MII_EXT_COMMON_LED2_CFG_VALUE_SOLUTION4); + break; + default: + + fxgmac_write_ephy_reg( + pdata, REG_MII_EXT_DATA, + REG_MII_EXT_COMMON_LED2_CFG_VALUE_SOLUTION0); + break; + } + + if (EFUSE_LED_SOLUTION2 == value) { + fxgmac_write_ephy_reg(pdata, REG_MII_EXT_ADDR, + REG_MII_EXT_COMMON_LED_BLINK_CFG); + fxgmac_write_ephy_reg( + pdata, REG_MII_EXT_DATA, + REG_MII_EXT_COMMON_LED_BLINK_CFG_SOLUTION2); + } + } +#endif +} + +static void fxgmac_enable_phy_check(struct fxgmac_pdata *pdata) +{ + u32 value = 0; + + fxgmac_write_ephy_reg(pdata, REG_MII_EXT_ADDR, REG_MII_EXT_PKG_CFG0); + fxgmac_read_ephy_reg(pdata, REG_MII_EXT_DATA, &value); + value = FXGMAC_SET_REG_BITS(value, REG_MII_EXT_PKG_CHECK_POS, + REG_MII_EXT_PKG_CHECK_LEN, + REG_MII_EXT_PKG_ENABLE_CHECK); + + fxgmac_write_ephy_reg(pdata, REG_MII_EXT_ADDR, REG_MII_EXT_PKG_CFG0); + fxgmac_write_ephy_reg(pdata, REG_MII_EXT_DATA, value); +} + +static void fxgmac_disable_phy_check(struct fxgmac_pdata *pdata) +{ + u32 value = 0; + + fxgmac_write_ephy_reg(pdata, REG_MII_EXT_ADDR, REG_MII_EXT_PKG_CFG0); + fxgmac_read_ephy_reg(pdata, REG_MII_EXT_DATA, &value); + value = FXGMAC_SET_REG_BITS(value, REG_MII_EXT_PKG_CHECK_POS, + REG_MII_EXT_PKG_CHECK_LEN, + REG_MII_EXT_PKG_DISABLE_CHECK); + + fxgmac_write_ephy_reg(pdata, REG_MII_EXT_ADDR, REG_MII_EXT_PKG_CFG0); + fxgmac_write_ephy_reg(pdata, REG_MII_EXT_DATA, value); +} + +static void fxgmac_setup_cable_loopback(struct fxgmac_pdata *pdata) +{ + fxgmac_write_ephy_reg(pdata, REG_MII_EXT_ADDR, + REG_MII_EXT_SLEEP_CONTROL_REG); + fxgmac_write_ephy_reg(pdata, REG_MII_EXT_DATA, + REG_MII_EXT_SLEEP_REG_ENABLE_LOOPBACK); + + fxgmac_write_ephy_reg(pdata, REG_MII_EXT_ADDR, REG_MII_EXT_LPBK_REG); + fxgmac_write_ephy_reg(pdata, REG_MII_EXT_DATA, + REG_MII_EXT_LPBK_REG_ENABLE_LOOPBACK); + + fxgmac_write_ephy_reg(pdata, REG_MII_BMCR, + REG_MII_BMCR_ENABLE_LOOPBACK); +} + +static void fxgmac_clean_cable_loopback(struct fxgmac_pdata *pdata) +{ + fxgmac_write_ephy_reg(pdata, REG_MII_EXT_ADDR, + REG_MII_EXT_SLEEP_CONTROL_REG); + fxgmac_write_ephy_reg(pdata, REG_MII_EXT_DATA, + REG_MII_EXT_SLEEP_REG_CLEAN_LOOPBACK); + + fxgmac_write_ephy_reg(pdata, REG_MII_EXT_ADDR, REG_MII_EXT_LPBK_REG); + fxgmac_write_ephy_reg(pdata, REG_MII_EXT_DATA, + REG_MII_EXT_LPBK_REG_CLEAN_LOOPBACK); + + fxgmac_write_ephy_reg(pdata, REG_MII_BMCR, + REG_MII_BMCR_DISABLE_LOOPBACK); +} + +static void fxgmac_disable_phy_sleep(struct fxgmac_pdata *pdata) +{ + u32 value = 0; + fxgmac_write_ephy_reg(pdata, REG_MII_EXT_ADDR, + REG_MII_EXT_SLEEP_CONTROL_REG); + fxgmac_read_ephy_reg(pdata, REG_MII_EXT_DATA, &value); + + value = FXGMAC_SET_REG_BITS(value, MII_EXT_SLEEP_CONTROL1_EN_POS, + MII_EXT_SLEEP_CONTROL1_EN_LEN, 0); + fxgmac_write_ephy_reg(pdata, REG_MII_EXT_ADDR, + REG_MII_EXT_SLEEP_CONTROL_REG); + fxgmac_write_ephy_reg(pdata, REG_MII_EXT_DATA, value); +} + +static void fxgmac_enable_phy_sleep(struct fxgmac_pdata *pdata) +{ + u32 value = 0; + fxgmac_write_ephy_reg(pdata, REG_MII_EXT_ADDR, + REG_MII_EXT_SLEEP_CONTROL_REG); + fxgmac_read_ephy_reg(pdata, REG_MII_EXT_DATA, &value); + + value = FXGMAC_SET_REG_BITS(value, MII_EXT_SLEEP_CONTROL1_EN_POS, + MII_EXT_SLEEP_CONTROL1_EN_LEN, 1); + fxgmac_write_ephy_reg(pdata, REG_MII_EXT_ADDR, + REG_MII_EXT_SLEEP_CONTROL_REG); + fxgmac_write_ephy_reg(pdata, REG_MII_EXT_DATA, value); +} + +static void fxgmac_close_phy_led(struct fxgmac_pdata *pdata) +{ + fxgmac_write_ephy_reg(pdata, REG_MII_EXT_ADDR, + REG_MII_EXT_COMMON_LED0_CFG); + fxgmac_write_ephy_reg(pdata, REG_MII_EXT_DATA, 0x00); + + fxgmac_write_ephy_reg(pdata, REG_MII_EXT_ADDR, + REG_MII_EXT_COMMON_LED1_CFG); + fxgmac_write_ephy_reg(pdata, REG_MII_EXT_DATA, 0x00); + + fxgmac_write_ephy_reg(pdata, REG_MII_EXT_ADDR, + REG_MII_EXT_COMMON_LED2_CFG); + fxgmac_write_ephy_reg(pdata, REG_MII_EXT_DATA, 0x00); +} + +static void fxmgac_config_led_under_active(struct fxgmac_pdata *pdata) +{ + u32 regval = 0; + fxgmac_efuse_read_data(pdata, EFUSE_LED_ADDR, ®val); + + regval = FXGMAC_GET_REG_BITS(regval, EFUSE_LED_POS, EFUSE_LED_LEN); + if (EFUSE_LED_COMMON_SOLUTION == regval) { + DbgPrintF(MP_TRACE, "%s >>>", __FUNCTION__); + fxgmac_write_ephy_reg(pdata, REG_MII_EXT_ADDR, + REG_MII_EXT_COMMON_LED_CFG); + fxgmac_write_ephy_reg(pdata, REG_MII_EXT_DATA, + pdata->led.s0_led_setting[0]); + fxgmac_write_ephy_reg(pdata, REG_MII_EXT_ADDR, + REG_MII_EXT_COMMON_LED0_CFG); + fxgmac_write_ephy_reg(pdata, REG_MII_EXT_DATA, + pdata->led.s0_led_setting[1]); + fxgmac_write_ephy_reg(pdata, REG_MII_EXT_ADDR, + REG_MII_EXT_COMMON_LED1_CFG); + fxgmac_write_ephy_reg(pdata, REG_MII_EXT_DATA, + pdata->led.s0_led_setting[2]); + fxgmac_write_ephy_reg(pdata, REG_MII_EXT_ADDR, + REG_MII_EXT_COMMON_LED2_CFG); + fxgmac_write_ephy_reg(pdata, REG_MII_EXT_DATA, + pdata->led.s0_led_setting[3]); + fxgmac_write_ephy_reg(pdata, REG_MII_EXT_ADDR, + REG_MII_EXT_COMMON_LED_BLINK_CFG); + fxgmac_write_ephy_reg(pdata, REG_MII_EXT_DATA, + pdata->led.s0_led_setting[4]); + } +} + +static void fxgmac_config_led_under_sleep(struct fxgmac_pdata *pdata) +{ + u32 regval = 0; + fxgmac_efuse_read_data(pdata, EFUSE_LED_ADDR, ®val); + + regval = FXGMAC_GET_REG_BITS(regval, EFUSE_LED_POS, EFUSE_LED_LEN); + if (EFUSE_LED_COMMON_SOLUTION == regval) { + DbgPrintF(MP_TRACE, "%s >>>", __FUNCTION__); + fxgmac_write_ephy_reg(pdata, REG_MII_EXT_ADDR, + REG_MII_EXT_COMMON_LED_CFG); + fxgmac_write_ephy_reg(pdata, REG_MII_EXT_DATA, + pdata->led.s3_led_setting[0]); + fxgmac_write_ephy_reg(pdata, REG_MII_EXT_ADDR, + REG_MII_EXT_COMMON_LED0_CFG); + fxgmac_write_ephy_reg(pdata, REG_MII_EXT_DATA, + pdata->led.s3_led_setting[1]); + fxgmac_write_ephy_reg(pdata, REG_MII_EXT_ADDR, + REG_MII_EXT_COMMON_LED1_CFG); + fxgmac_write_ephy_reg(pdata, REG_MII_EXT_DATA, + pdata->led.s3_led_setting[2]); + fxgmac_write_ephy_reg(pdata, REG_MII_EXT_ADDR, + REG_MII_EXT_COMMON_LED2_CFG); + fxgmac_write_ephy_reg(pdata, REG_MII_EXT_DATA, + pdata->led.s3_led_setting[3]); + fxgmac_write_ephy_reg(pdata, REG_MII_EXT_ADDR, + REG_MII_EXT_COMMON_LED_BLINK_CFG); + fxgmac_write_ephy_reg(pdata, REG_MII_EXT_DATA, + pdata->led.s3_led_setting[4]); + } +} + +static void fxgmac_config_led_under_shutdown(struct fxgmac_pdata *pdata) +{ + u32 regval = 0; + fxgmac_efuse_read_data(pdata, EFUSE_LED_ADDR, ®val); + + regval = FXGMAC_GET_REG_BITS(regval, EFUSE_LED_POS, EFUSE_LED_LEN); + if (EFUSE_LED_COMMON_SOLUTION == regval) { + DbgPrintF(MP_TRACE, "%s >>>", __FUNCTION__); + fxgmac_write_ephy_reg(pdata, REG_MII_EXT_ADDR, + REG_MII_EXT_COMMON_LED_CFG); + fxgmac_write_ephy_reg(pdata, REG_MII_EXT_DATA, + pdata->led.s5_led_setting[0]); + fxgmac_write_ephy_reg(pdata, REG_MII_EXT_ADDR, + REG_MII_EXT_COMMON_LED0_CFG); + fxgmac_write_ephy_reg(pdata, REG_MII_EXT_DATA, + pdata->led.s5_led_setting[1]); + fxgmac_write_ephy_reg(pdata, REG_MII_EXT_ADDR, + REG_MII_EXT_COMMON_LED1_CFG); + fxgmac_write_ephy_reg(pdata, REG_MII_EXT_DATA, + pdata->led.s5_led_setting[2]); + fxgmac_write_ephy_reg(pdata, REG_MII_EXT_ADDR, + REG_MII_EXT_COMMON_LED2_CFG); + fxgmac_write_ephy_reg(pdata, REG_MII_EXT_DATA, + pdata->led.s5_led_setting[3]); + fxgmac_write_ephy_reg(pdata, REG_MII_EXT_ADDR, + REG_MII_EXT_COMMON_LED_BLINK_CFG); + fxgmac_write_ephy_reg(pdata, REG_MII_EXT_DATA, + pdata->led.s5_led_setting[4]); + } +} + +static void fxgmac_config_led_under_disable(struct fxgmac_pdata *pdata) +{ + u32 regval = 0; + fxgmac_efuse_read_data(pdata, EFUSE_LED_ADDR, ®val); + + regval = FXGMAC_GET_REG_BITS(regval, EFUSE_LED_POS, EFUSE_LED_LEN); + if (EFUSE_LED_COMMON_SOLUTION == regval) { + DbgPrintF(MP_TRACE, "%s >>>", __FUNCTION__); + fxgmac_write_ephy_reg(pdata, REG_MII_EXT_ADDR, + REG_MII_EXT_COMMON_LED_CFG); + fxgmac_write_ephy_reg(pdata, REG_MII_EXT_DATA, + pdata->led.disable_led_setting[0]); + fxgmac_write_ephy_reg(pdata, REG_MII_EXT_ADDR, + REG_MII_EXT_COMMON_LED0_CFG); + fxgmac_write_ephy_reg(pdata, REG_MII_EXT_DATA, + pdata->led.disable_led_setting[1]); + fxgmac_write_ephy_reg(pdata, REG_MII_EXT_ADDR, + REG_MII_EXT_COMMON_LED1_CFG); + fxgmac_write_ephy_reg(pdata, REG_MII_EXT_DATA, + pdata->led.disable_led_setting[2]); + fxgmac_write_ephy_reg(pdata, REG_MII_EXT_ADDR, + REG_MII_EXT_COMMON_LED2_CFG); + fxgmac_write_ephy_reg(pdata, REG_MII_EXT_DATA, + pdata->led.disable_led_setting[3]); + fxgmac_write_ephy_reg(pdata, REG_MII_EXT_ADDR, + REG_MII_EXT_COMMON_LED_BLINK_CFG); + fxgmac_write_ephy_reg(pdata, REG_MII_EXT_DATA, + pdata->led.disable_led_setting[4]); + } else { + fxgmac_reset_phy(pdata); + } +} + +static int fxgmac_enable_int(struct fxgmac_channel *channel, + enum fxgmac_int int_id) +{ + u32 dma_ch_ier; + + dma_ch_ier = readreg(channel->pdata->pAdapter, + FXGMAC_DMA_REG(channel, DMA_CH_IER)); + + switch (int_id) { + case FXGMAC_INT_DMA_CH_SR_TI: + dma_ch_ier = FXGMAC_SET_REG_BITS(dma_ch_ier, DMA_CH_IER_TIE_POS, + DMA_CH_IER_TIE_LEN, 1); + break; + case FXGMAC_INT_DMA_CH_SR_TPS: + dma_ch_ier = FXGMAC_SET_REG_BITS(dma_ch_ier, + DMA_CH_IER_TXSE_POS, + DMA_CH_IER_TXSE_LEN, 1); + break; + case FXGMAC_INT_DMA_CH_SR_TBU: + dma_ch_ier = FXGMAC_SET_REG_BITS(dma_ch_ier, + DMA_CH_IER_TBUE_POS, + DMA_CH_IER_TBUE_LEN, 1); + break; + case FXGMAC_INT_DMA_CH_SR_RI: + dma_ch_ier = FXGMAC_SET_REG_BITS(dma_ch_ier, DMA_CH_IER_RIE_POS, + DMA_CH_IER_RIE_LEN, 1); + break; + case FXGMAC_INT_DMA_CH_SR_RBU: + dma_ch_ier = FXGMAC_SET_REG_BITS(dma_ch_ier, + DMA_CH_IER_RBUE_POS, + DMA_CH_IER_RBUE_LEN, 1); + break; + case FXGMAC_INT_DMA_CH_SR_RPS: + dma_ch_ier = FXGMAC_SET_REG_BITS(dma_ch_ier, DMA_CH_IER_RSE_POS, + DMA_CH_IER_RSE_LEN, 1); + break; + case FXGMAC_INT_DMA_CH_SR_TI_RI: + dma_ch_ier = FXGMAC_SET_REG_BITS(dma_ch_ier, DMA_CH_IER_TIE_POS, + DMA_CH_IER_TIE_LEN, 1); + dma_ch_ier = FXGMAC_SET_REG_BITS(dma_ch_ier, DMA_CH_IER_RIE_POS, + DMA_CH_IER_RIE_LEN, 1); + dma_ch_ier = FXGMAC_SET_REG_BITS(dma_ch_ier, DMA_CH_IER_NIE_POS, + DMA_CH_IER_NIE_LEN, 1); + break; + case FXGMAC_INT_DMA_CH_SR_FBE: + dma_ch_ier = FXGMAC_SET_REG_BITS(dma_ch_ier, + DMA_CH_IER_FBEE_POS, + DMA_CH_IER_FBEE_LEN, 1); + break; + case FXGMAC_INT_DMA_ALL: + dma_ch_ier |= channel->saved_ier; + break; + default: + return -1; + } + + writereg(channel->pdata->pAdapter, dma_ch_ier, + FXGMAC_DMA_REG(channel, DMA_CH_IER)); + + return 0; +} + +static int fxgmac_disable_int(struct fxgmac_channel *channel, + enum fxgmac_int int_id) +{ + u32 dma_ch_ier; + + dma_ch_ier = readreg(channel->pdata->pAdapter, + FXGMAC_DMA_REG(channel, DMA_CH_IER)); + + switch (int_id) { + case FXGMAC_INT_DMA_CH_SR_TI: + dma_ch_ier = FXGMAC_SET_REG_BITS(dma_ch_ier, DMA_CH_IER_TIE_POS, + DMA_CH_IER_TIE_LEN, 0); + break; + case FXGMAC_INT_DMA_CH_SR_TPS: + dma_ch_ier = FXGMAC_SET_REG_BITS(dma_ch_ier, + DMA_CH_IER_TXSE_POS, + DMA_CH_IER_TXSE_LEN, 0); + break; + case FXGMAC_INT_DMA_CH_SR_TBU: + dma_ch_ier = FXGMAC_SET_REG_BITS(dma_ch_ier, + DMA_CH_IER_TBUE_POS, + DMA_CH_IER_TBUE_LEN, 0); + break; + case FXGMAC_INT_DMA_CH_SR_RI: + dma_ch_ier = FXGMAC_SET_REG_BITS(dma_ch_ier, DMA_CH_IER_RIE_POS, + DMA_CH_IER_RIE_LEN, 0); + break; + case FXGMAC_INT_DMA_CH_SR_RBU: + dma_ch_ier = FXGMAC_SET_REG_BITS(dma_ch_ier, + DMA_CH_IER_RBUE_POS, + DMA_CH_IER_RBUE_LEN, 0); + break; + case FXGMAC_INT_DMA_CH_SR_RPS: + dma_ch_ier = FXGMAC_SET_REG_BITS(dma_ch_ier, DMA_CH_IER_RSE_POS, + DMA_CH_IER_RSE_LEN, 0); + break; + case FXGMAC_INT_DMA_CH_SR_TI_RI: + dma_ch_ier = FXGMAC_SET_REG_BITS(dma_ch_ier, DMA_CH_IER_TIE_POS, + DMA_CH_IER_TIE_LEN, 0); + dma_ch_ier = FXGMAC_SET_REG_BITS(dma_ch_ier, DMA_CH_IER_RIE_POS, + DMA_CH_IER_RIE_LEN, 0); + dma_ch_ier = FXGMAC_SET_REG_BITS(dma_ch_ier, DMA_CH_IER_NIE_POS, + DMA_CH_IER_NIE_LEN, 0); + break; + case FXGMAC_INT_DMA_CH_SR_FBE: + dma_ch_ier = FXGMAC_SET_REG_BITS(dma_ch_ier, + DMA_CH_IER_FBEE_POS, + DMA_CH_IER_FBEE_LEN, 0); + break; + case FXGMAC_INT_DMA_ALL: + channel->saved_ier = dma_ch_ier & FXGMAC_DMA_INTERRUPT_MASK; + dma_ch_ier &= ~FXGMAC_DMA_INTERRUPT_MASK; + break; + default: + return -1; + } + + writereg(channel->pdata->pAdapter, dma_ch_ier, + FXGMAC_DMA_REG(channel, DMA_CH_IER)); + + return 0; +} + +static int fxgmac_dismiss_DMA_int(struct fxgmac_channel *channel, int int_id) +{ + u32 dma_ch_ier; + + int_id = int_id; + dma_ch_ier = readreg(channel->pdata->pAdapter, + FXGMAC_DMA_REG(channel, DMA_CH_SR /*1160*/)); + writereg(channel->pdata->pAdapter, dma_ch_ier, + FXGMAC_DMA_REG(channel, DMA_CH_SR)); + + return 0; +} + +static void fxgmac_dismiss_MTL_Q_int(struct fxgmac_pdata *pdata) +{ + unsigned int i; + u32 mtl_q_isr, q_count; + + q_count = max(pdata->hw_feat.tx_q_cnt, pdata->hw_feat.rx_q_cnt); + for (i = 0; i < q_count; i++) { + /* Clear all the interrupts which are set */ + mtl_q_isr = readreg(pdata->pAdapter, + FXGMAC_MTL_REG(pdata, i, MTL_Q_ISR)); + writereg(pdata->pAdapter, mtl_q_isr, + FXGMAC_MTL_REG(pdata, i, MTL_Q_ISR)); + } +} + +static int fxgmac_dismiss_MAC_int(struct fxgmac_pdata *pdata) +{ + u32 regval, regErrVal; + + /* all MAC interrupts in 0xb0 */ + regval = readreg(pdata->pAdapter, pdata->mac_regs + MAC_ISR); + /* MAC tx/rx error interrupts in 0xb8 */ + regErrVal = readreg(pdata->pAdapter, pdata->mac_regs + MAC_TX_RX_STA); + regval = regval; + regErrVal = regErrVal; + + return 0; +} + +static int fxgmac_dismiss_MAC_PMT_int(struct fxgmac_pdata *pdata) +{ + u32 regval; + + /* MAC PMT interrupts in 0xc0 */ + regval = readreg(pdata->pAdapter, pdata->mac_regs + MAC_PMT_STA); + regval = regval; + + return 0; +} + +static int fxgmac_dismiss_MAC_LPI_int(struct fxgmac_pdata *pdata) +{ + u32 regval; + + /* MAC PMT interrupts in 0xc0 */ + regval = readreg(pdata->pAdapter, pdata->mac_regs + MAC_LPI_STA); + regval = regval; + + return 0; +} + +static int fxgmac_dismiss_MAC_DBG_int(struct fxgmac_pdata *pdata) +{ + u32 regval; + + /* MAC PMT interrupts in 0xc0 */ + regval = readreg(pdata->pAdapter, pdata->mac_regs + MAC_DBG_STA); + { + writereg(pdata->pAdapter, regval, + pdata->mac_regs + MAC_DBG_STA); + } + + return 0; +} + +static int fxgmac_dismiss_all_int(struct fxgmac_pdata *pdata) +{ + struct fxgmac_channel *channel; + unsigned int i; + + channel = pdata->channel_head; + for (i = 0; i < pdata->channel_count; i++, channel++) { + fxgmac_dismiss_DMA_int(channel, 0); + } + fxgmac_dismiss_MTL_Q_int(pdata); + fxgmac_dismiss_MAC_int(pdata); + fxgmac_dismiss_MAC_PMT_int(pdata); + fxgmac_dismiss_MAC_LPI_int(pdata); + fxgmac_dismiss_MAC_DBG_int(pdata); + + if (netif_msg_drv(pdata)) { + DPRINTK("fxgmac_dismiss_all_int callin %d\n", i); + } + + return 0; +} + +static void fxgmac_set_interrupt_moderation(struct fxgmac_pdata *pdata) +{ + u32 value = 0, time; +#if defined(FXGMAC_INTERRUPT_MODERATION_EXTERN) + + pdata->intr_mod_timer = pdata->intr_mod_timer; +#else + pdata->intr_mod_timer = INT_MOD_IN_US; +#endif + +#if defined(FXGMAC_INTERRUPT_TX_INTERVAL) + time = (pdata->intr_mod) ? pdata->tx_usecs : 0; +#else + time = (pdata->intr_mod) ? pdata->intr_mod_timer : 0; +#endif + value = FXGMAC_SET_REG_BITS(value, INT_MOD_TX_POS, INT_MOD_TX_LEN, + time); + +#if defined(FXGMAC_INTERRUPT_RX_INTERVAL) + time = (pdata->intr_mod) ? pdata->rx_usecs : 0; +#endif + + value = FXGMAC_SET_REG_BITS(value, INT_MOD_RX_POS, INT_MOD_RX_LEN, + time); + writereg(pdata->pAdapter, value, pdata->base_mem + INT_MOD); +} +static void fxgmac_enable_msix_rxtxinterrupt(struct fxgmac_pdata *pdata) +{ + u32 intid; + + for (intid = 0; intid < MSIX_TBL_RXTX_NUM; intid++) { + writereg(pdata->pAdapter, 0, + pdata->base_mem + MSIX_TBL_BASE_ADDR + + MSIX_TBL_MASK_OFFSET + intid * 16); + } +} +static void fxgmac_disable_msix_interrupt(struct fxgmac_pdata *pdata) +{ + u32 intid; + + for (intid = 0; intid < MSIX_TBL_MAX_NUM; intid++) { + writereg(pdata->pAdapter, 0x1, + pdata->base_mem + MSIX_TBL_BASE_ADDR + + MSIX_TBL_MASK_OFFSET + intid * 16); + } +} +static int fxgmac_enable_msix_rxtxphyinterrupt(struct fxgmac_pdata *pdata) +{ + u32 intid, regval = 0; + int ret = 0; +#if !(FXGMAC_EPHY_INTERRUPT_D0_OFF) + struct fxgmac_hw_ops *hw_ops = &pdata->hw_ops; +#endif + + for (intid = 0; intid < MSIX_TBL_RXTX_NUM; intid++) { + writereg(pdata->pAdapter, 0, + pdata->base_mem + MSIX_TBL_BASE_ADDR + + MSIX_TBL_MASK_OFFSET + intid * 16); + } + writereg(pdata->pAdapter, 0, + pdata->base_mem + MSIX_TBL_BASE_ADDR + MSIX_TBL_MASK_OFFSET + + MSI_ID_PHY_OTHER * 16); +#if !(FXGMAC_EPHY_INTERRUPT_D0_OFF) + hw_ops->read_ephy_reg(pdata, REG_MII_INT_STATUS, NULL); + regval = FXGMAC_SET_REG_BITS(0, PHY_INT_MASK_LINK_UP_POS, + PHY_INT_MASK_LINK_UP_LEN, 1); + regval = FXGMAC_SET_REG_BITS(regval, PHY_INT_MASK_LINK_DOWN_POS, + PHY_INT_MASK_LINK_DOWN_LEN, 1); + ret = hw_ops->write_ephy_reg(pdata, REG_MII_INT_MASK, regval); + return ret; +#else + return 0; +#endif +} +static void fxgmac_enable_msix_one_interrupt(struct fxgmac_pdata *pdata, + u32 intid) +{ + writereg(pdata->pAdapter, 0, + pdata->base_mem + MSIX_TBL_BASE_ADDR + MSIX_TBL_MASK_OFFSET + + intid * 16); +} + +static void fxgmac_disable_msix_one_interrupt(struct fxgmac_pdata *pdata, + u32 intid) +{ + writereg(pdata->pAdapter, 0x01, + pdata->base_mem + MSIX_TBL_BASE_ADDR + MSIX_TBL_MASK_OFFSET + + intid * 16); +} + +static bool fxgmac_enable_mgm_interrupt(struct fxgmac_pdata *pdata) +{ + writereg(pdata->pAdapter, 0xf0000000, pdata->base_mem + MGMT_INT_CTRL0); + return true; +} + +static bool fxgmac_disable_mgm_interrupt(struct fxgmac_pdata *pdata) +{ + writereg(pdata->pAdapter, 0xffff0000, pdata->base_mem + MGMT_INT_CTRL0); + return true; +} + +static int fxgmac_flush_tx_queues(struct fxgmac_pdata *pdata) +{ + unsigned int i, count; + u32 regval; + + for (i = 0; i < pdata->tx_q_count; i++) { + regval = readreg(pdata->pAdapter, + FXGMAC_MTL_REG(pdata, i, MTL_Q_TQOMR)); + regval = FXGMAC_SET_REG_BITS(regval, MTL_Q_TQOMR_FTQ_POS, + MTL_Q_TQOMR_FTQ_LEN, 1); + writereg(pdata->pAdapter, regval, + FXGMAC_MTL_REG(pdata, i, MTL_Q_TQOMR)); + DPRINTK("fxgmac_flush_tx_queues, reg=0x%p,", + FXGMAC_MTL_REG(pdata, i, MTL_Q_TQOMR)); + DPRINTK(" val=0x%08x\n", regval); + } + + /* Poll Until Poll Condition */ + /*for (i = 0; i < pdata->tx_q_count; i++) { + count = 2000; + regval = readreg(FXGMAC_MTL_REG(pdata, i, MTL_Q_TQOMR)); + regval = FXGMAC_GET_REG_BITS(regval, MTL_Q_TQOMR_FTQ_POS, + MTL_Q_TQOMR_FTQ_LEN); + while (--count && regval) { + usleep_range(pdata->pAdapter, 500, 600); + } + + DPRINTK("fxgmac_flush_tx_queues wait... reg=0x%p, val=0x%08x\n", FXGMAC_MTL_REG(pdata, i, MTL_Q_TQOMR), regval); + + if (!count) + return -EBUSY; + }*/ + for (i = 0; i < pdata->tx_q_count; i++) { + count = 2000; + + do { + usleep_range_ex(pdata->pAdapter, 40, 50); + regval = readreg(pdata->pAdapter, + FXGMAC_MTL_REG(pdata, i, MTL_Q_TQOMR)); + regval = FXGMAC_GET_REG_BITS(regval, + MTL_Q_TQOMR_FTQ_POS, + MTL_Q_TQOMR_FTQ_LEN); + + } while (--count && regval); + DPRINTK("fxgmac_flush_tx_queues wait... reg=0x%p,", + FXGMAC_MTL_REG(pdata, i, MTL_Q_TQOMR)); + DPRINTK(" ... val=0x%08x\n", regval); + if (regval) { /*(!count)*/ + return -EBUSY; + } + } + + return 0; +} + +static void fxgmac_config_dma_bus(struct fxgmac_pdata *pdata) +{ + u32 regval; + + regval = readreg(pdata->pAdapter, pdata->mac_regs + DMA_SBMR); + /* Set enhanced addressing mode */ + regval = FXGMAC_SET_REG_BITS(regval, DMA_SBMR_EAME_POS, + DMA_SBMR_EAME_LEN, 1); + + /* Out standing read/write requests*/ + regval = FXGMAC_SET_REG_BITS(regval, DMA_SBMR_RD_OSR_LMT_POS, + DMA_SBMR_RD_OSR_LMT_LEN, 0x7); + regval = FXGMAC_SET_REG_BITS(regval, DMA_SBMR_WR_OSR_LMT_POS, + DMA_SBMR_WR_OSR_LMT_LEN, 0x7); + + /* Set the System Bus mode */ + regval = FXGMAC_SET_REG_BITS(regval, DMA_SBMR_FB_POS, DMA_SBMR_FB_LEN, + 0); + regval = FXGMAC_SET_REG_BITS(regval, DMA_SBMR_BLEN_4_POS, + DMA_SBMR_BLEN_4_LEN, 1); + regval = FXGMAC_SET_REG_BITS(regval, DMA_SBMR_BLEN_8_POS, + DMA_SBMR_BLEN_8_LEN, 1); + regval = FXGMAC_SET_REG_BITS(regval, DMA_SBMR_BLEN_16_POS, + DMA_SBMR_BLEN_16_LEN, 1); + regval = FXGMAC_SET_REG_BITS(regval, DMA_SBMR_BLEN_32_POS, + DMA_SBMR_BLEN_32_LEN, 1); + writereg(pdata->pAdapter, regval, pdata->mac_regs + DMA_SBMR); +} + +static void fxgmac_legacy_link_speed_setting(struct fxgmac_pdata *pdata) +{ + unsigned int i = 0; + u32 regval = 0; + + fxgmac_phy_config(pdata); + for (i = 0, regval = fxgmac_get_ephy_state(pdata); + (!(regval & MGMT_EPHY_CTRL_STA_EPHY_RELEASE) || + !(regval & MGMT_EPHY_CTRL_STA_EPHY_LINKUP)) && + (i < PHY_LINK_TIMEOUT); + regval = fxgmac_get_ephy_state(pdata), i++) { + usleep_range_ex(pdata->pAdapter, 2000, 2000); + } + fxgmac_read_ephy_reg(pdata, REG_MII_INT_STATUS, NULL); +} + +#if defined(FXGMAC_FIX_SHUT_DOWN_ISSUE) +static void +fxgmac_link_speed_down_fix_shutdown_issue(struct fxgmac_pdata *pdata) +{ + LONGLONG tick_interval; + ULONG tick_inc; + LARGE_INTEGER tick_count; + unsigned int i = 0; + unsigned int regval = 0; + if ((ULONG)pdata->phy_speed != + ((PMP_ADAPTER)pdata->pAdapter)->usLinkSpeed) { + DbgPrintF(MP_TRACE, "%s change phy speed", __FUNCTION__); + pdata->phy_speed = ((PMP_ADAPTER)pdata->pAdapter)->usLinkSpeed; + + if (((PMP_ADAPTER)pdata->pAdapter)->RegParameter.LinkChgWol) { + fxgmac_phy_config(pdata); + + for (i = 0; i < PHY_LINK_TIMEOUT; i++) { + usleep_range_ex(pdata->pAdapter, 2000, 2000); + } + + fxgmac_read_ephy_reg(pdata, REG_MII_INT_STATUS, NULL); + } else { + regval = fxgmac_get_ephy_state(pdata); + KeQueryTickCount(&tick_count); + tick_inc = KeQueryTimeIncrement(); + tick_interval = tick_count.QuadPart - + ((PMP_ADAPTER)pdata->pAdapter) + ->D0_entry_tick_count.QuadPart; + tick_interval *= tick_inc; + tick_interval /= 10; + + /*DbgPrintF(MP_TRACE, "base tick %lld", ((PMP_ADAPTER)pdata->pAdapter)->D0_entry_tick_count.QuadPart); + DbgPrintF(MP_TRACE, "current tick %lld", tick_count.QuadPart); + DbgPrintF(MP_TRACE, "tick inc is %u", tick_inc); + DbgPrintF(MP_TRACE, "tick_interval is %lld", tick_interval); + DbgPrintF(MP_TRACE, "regval is 0x%x", regval);*/ + if (((regval & MGMT_EPHY_CTRL_STA_EPHY_RELEASE) && + (regval & MGMT_EPHY_CTRL_STA_EPHY_LINKUP)) || + ((regval & MGMT_EPHY_CTRL_STA_EPHY_RELEASE) && + !(regval & MGMT_EPHY_CTRL_STA_EPHY_LINKUP) && + (tick_interval < RESUME_MAX_TIME))) { + fxgmac_legacy_link_speed_setting(pdata); + } + } + } +} +#endif + +static void fxgmac_pre_powerdown(struct fxgmac_pdata *pdata, bool phyloopback) +{ + u32 regval = 0; + int speed = SPEED_10; +#ifdef FXGMAC_LINK_SPEED_CHECK_PHY_LINK + int link; +#endif + speed = speed; + + fxgmac_disable_rx(pdata); + + /* HERE, WE NEED TO CONSIDER PHY CONFIG...TBD */ + DPRINTK("fxgmac_config_powerdown, phy and mac status update speed %d\n", + speed); + + if (!phyloopback) { + fxgmac_read_ephy_reg(pdata, REG_MII_LPA, ®val); + if (!FXGMAC_GET_REG_BITS(regval, + PHY_MII_LINK_PARNTNER_10FULL_POS, + PHY_MII_LINK_PARNTNER_10FULL_LEN) && + !FXGMAC_GET_REG_BITS(regval, + PHY_MII_LINK_PARNTNER_10HALF_POS, + PHY_MII_LINK_PARNTNER_10HALF_LEN)) { +#if defined(FXGMAC_LINK_SPEED_NOT_USE_LOCAL_VARIABLE) + if (SPEED_10 == + ((PMP_ADAPTER)pdata->pAdapter)->usLinkSpeed) { + ((PMP_ADAPTER)pdata->pAdapter)->usLinkSpeed = + SPEED_100; + } +#else + speed = SPEED_100; +#endif + } + +#if defined(FXGMAC_FIX_SHUT_DOWN_ISSUE) + fxgmac_link_speed_down_fix_shutdown_issue(pdata); +#elif defined(FXGMAC_LINK_SPEED_CHECK_PHY_LINK) + /* + When the Linux platform enters the s4 state, it goes through the suspend->resume->suspend process. + The process of suspending again after resume is fast, and PHY auto-negotiation is not yet complete, + so the auto-negotiation of PHY must be carried out again.Windows platforms and UEFI platforms do + not need to auto-negotiate again, as they will not have such a process. + When the Linux platform enters the s4 state, force speed to 10M. + */ + regval = fxgmac_get_ephy_state(pdata); + link = FXGMAC_GET_REG_BITS(regval, + MGMT_EPHY_CTRL_STA_EPHY_LINKUP_POS, + MGMT_EPHY_CTRL_STA_EPHY_LINKUP_LEN); + if (link && (speed != pdata->phy_speed)) { + pdata->phy_speed = speed; + fxgmac_legacy_link_speed_setting(pdata); + } +#else + fxgmac_legacy_link_speed_setting(pdata); +#endif + } + + fxgmac_config_mac_speed(pdata); + + /* After enable OOB_WOL from efuse, mac will loopcheck phy status, and lead to panic sometimes. + So we should disable it from powerup, enable it from power down.*/ + regval = (u32)readreg(pdata->pAdapter, pdata->base_mem + OOB_WOL_CTRL); + regval = FXGMAC_SET_REG_BITS(regval, OOB_WOL_CTRL_DIS_POS, + OOB_WOL_CTRL_DIS_LEN, 0); + writereg(pdata->pAdapter, regval, pdata->base_mem + OOB_WOL_CTRL); + usleep_range_ex(pdata->pAdapter, 2000, 2000); + + fxgmac_set_mac_address(pdata, pdata->mac_addr); +} + +#ifdef FXGMAC_WOL_INTEGRATED_WOL_PARAMETER +static void fxgmac_config_powerdown(struct fxgmac_pdata *pdata, + unsigned int wol) +#else +static void fxgmac_config_powerdown(struct fxgmac_pdata *pdata, + unsigned int offloadcount, bool magic_en, + bool remote_pattern_en) +#endif +{ + u32 regval = 0; + + fxgmac_disable_tx(pdata); + fxgmac_disable_rx(pdata); + + /* performs fxgmac power down sequence + * 1. set led + * 2. check wol. + * 3. check arp offloading + * 4. disable gmac rx + * 5. set gmac power down + */ + + fxgmac_efuse_read_data(pdata, EFUSE_LED_ADDR, ®val); + + regval = FXGMAC_GET_REG_BITS(regval, EFUSE_LED_POS, EFUSE_LED_LEN); + if (EFUSE_LED_COMMON_SOLUTION != regval) { + fxgmac_write_ephy_reg(pdata, REG_MII_EXT_ADDR, + REG_MII_EXT_COMMON_LED0_CFG); + if (EFUSE_LED_SOLUTION3 == regval) { + fxgmac_write_ephy_reg( + pdata, REG_MII_EXT_DATA, + REG_MII_EXT_COMMON_LED0_CFG_VALUE_SLEEP_SOLUTION3); + } else { + fxgmac_write_ephy_reg(pdata, REG_MII_EXT_DATA, 0x00); + } + + fxgmac_write_ephy_reg(pdata, REG_MII_EXT_ADDR, + REG_MII_EXT_COMMON_LED1_CFG); + fxgmac_write_ephy_reg(pdata, REG_MII_EXT_DATA, 0x00); + + fxgmac_write_ephy_reg(pdata, REG_MII_EXT_ADDR, + REG_MII_EXT_COMMON_LED2_CFG); + fxgmac_write_ephy_reg(pdata, REG_MII_EXT_DATA, 0x00); + } + +#if FXGMAC_WOL_FEATURE_ENABLED + fxgmac_config_wol(pdata, wol); +#endif +#if FXGMAC_AOE_FEATURE_ENABLED + /* use default arp offloading feature */ + fxgmac_update_aoe_ipv4addr(pdata, (u8 *)NULL); + fxgmac_enable_arp_offload(pdata); +#endif + +#if FXGMAC_NS_OFFLOAD_ENABLED + /* pls do not change the seq below */ + fxgmac_update_ns_offload_ipv6addr(pdata, FXGMAC_NS_IFA_GLOBAL_UNICAST); + fxgmac_update_ns_offload_ipv6addr(pdata, FXGMAC_NS_IFA_LOCAL_LINK); + fxgmac_enable_ns_offload(pdata); +#endif + +#if FXGMAC_PM_WPI_READ_FEATURE_ENABLED + fxgmac_enable_wake_packet_indication(pdata, 1); +#endif + /* Enable MAC Rx TX */ +#ifdef FXGMAC_WOL_INTEGRATED_WOL_PARAMETER + if (1) { +#else + if (magic_en || remote_pattern_en || offloadcount) { +#endif + regval = readreg(pdata->pAdapter, pdata->mac_regs + MAC_CR); + regval = FXGMAC_SET_REG_BITS(regval, MAC_CR_RE_POS, + MAC_CR_RE_LEN, 1); +#if defined(FXGMAC_AOE_FEATURE_ENABLED) || defined(FXGMAC_NS_OFFLOAD_ENABLED) + if (pdata->hw_feat.aoe) { +#else + if (offloadcount) { +#endif + regval = FXGMAC_SET_REG_BITS(regval, MAC_CR_TE_POS, + MAC_CR_TE_LEN, 1); + } + writereg(pdata->pAdapter, regval, pdata->mac_regs + MAC_CR); + } + + /* Enable fast link mode. - ECO to fix it.*/ + + regval = readreg(pdata->pAdapter, pdata->base_mem + LPW_CTRL); + regval = FXGMAC_SET_REG_BITS(regval, LPW_CTRL_ASPM_LPW_EN_POS, + LPW_CTRL_ASPM_LPW_EN_LEN, 1); + + writereg(pdata->pAdapter, regval, pdata->base_mem + LPW_CTRL); + + /* set gmac power down */ + regval = readreg(pdata->pAdapter, pdata->mac_regs + MAC_PMT_STA); + regval = FXGMAC_SET_REG_BITS(regval, MAC_PMT_STA_PWRDWN_POS, + MAC_PMT_STA_PWRDWN_LEN, 1); + writereg(pdata->pAdapter, regval, pdata->mac_regs + MAC_PMT_STA); + + regval = readreg(pdata->pAdapter, pdata->base_mem + MGMT_SIGDET); + regval = FXGMAC_SET_REG_BITS(regval, MGMT_SIGDET_POS, MGMT_SIGDET_LEN, + MGMT_SIGDET_55MV); + writereg(pdata->pAdapter, regval, pdata->base_mem + MGMT_SIGDET); + + DPRINTK("fxgmac_config_powerdown callout, reg=0x%08x\n", regval); +} + +static void fxgmac_config_powerup(struct fxgmac_pdata *pdata) +{ + u32 regval = 0; + + /* After enable OOB_WOL from efuse, mac will loopcheck phy status, and lead to panic sometimes. + So we should disable it from powerup, enable it from power down.*/ + regval = (u32)readreg(pdata->pAdapter, pdata->base_mem + OOB_WOL_CTRL); + regval = FXGMAC_SET_REG_BITS(regval, OOB_WOL_CTRL_DIS_POS, + OOB_WOL_CTRL_DIS_LEN, 1); + writereg(pdata->pAdapter, regval, pdata->base_mem + OOB_WOL_CTRL); + + /* clear wpi mode whether or not waked by WOL, write reset value */ + regval = + (u32)readreg(pdata->pAdapter, pdata->base_mem + MGMT_WPI_CTRL0); + + regval = FXGMAC_SET_REG_BITS(regval, MGMT_WPI_CTRL0_WPI_MODE_POS, + MGMT_WPI_CTRL0_WPI_MODE_LEN, 0); + writereg(pdata->pAdapter, regval, pdata->base_mem + MGMT_WPI_CTRL0); + /* read pmt_status register to De-assert the pmt_intr_o */ + regval = readreg(pdata->pAdapter, pdata->mac_regs + MAC_PMT_STA); + /* whether or not waked up by WOL, write reset value */ + regval = FXGMAC_SET_REG_BITS(regval, MAC_PMT_STA_PWRDWN_POS, + MAC_PMT_STA_PWRDWN_LEN, 0); + /* write register to synchronized always-on block */ + writereg(pdata->pAdapter, regval, pdata->mac_regs + MAC_PMT_STA); + + /* Disable fast link mode*/ + cfg_r32(pdata, REG_POWER_EIOS, ®val); + regval = FXGMAC_SET_REG_BITS(regval, POWER_EIOS_POS, POWER_EIOS_LEN, 0); + cfg_w32(pdata, REG_POWER_EIOS, regval); + + fxgmac_pwr_clock_gate(pdata); +} + +#if FXGMAC_SANITY_CHECK_ENABLED +/* + * fxgmac_diag_sanity_check + * check if there is any error like tx q hang + * return: 0 normal and other fatal error + */ +static int fxgmac_diag_sanity_check(struct fxgmac_pdata *pdata) +{ + u32 reg_q_val, reg_tail_val; + static u32 reg_tail_pre = 0; + static int cnt = 0; + + reg_q_val = readreg(pdata->pAdapter, + FXGMAC_MTL_REG(pdata, 0 /* tx channe 0 */, + 0x8 /* 0x2d08 */)); + if (!(reg_q_val & 0x10)) { + return 0; + } + reg_tail_val = + readreg(pdata->pAdapter, + FXGMAC_DMA_REG(pdata->channel_head, DMA_CH_TDTR_LO)); + if (reg_tail_pre != reg_tail_val) { + reg_tail_pre = reg_tail_val; + cnt = 0; + } else { + cnt++; + } + + if (cnt > 10) { + reg_q_val = readreg(pdata->pAdapter, + FXGMAC_MTL_REG(pdata, 0 /* tx channe 0 */, + 0x8 /* 0x2d08 */)); + if (reg_q_val & 0x10) { + DPRINTK("fxgmac, WARNing, tx Q status is 0x%x and tail keeps unchanged for %d times, 0x%x\n", + reg_q_val, cnt, reg_tail_val); + return 1; + } + } + + return 0; +} +#endif +static void fxgmac_pwr_clock_gate(struct fxgmac_pdata *pdata) +{ + u32 regval = 0; + + fxgmac_write_ephy_reg(pdata, REG_MII_EXT_ADDR, + REG_MII_EXT_SLEEP_CONTROL1); + fxgmac_read_ephy_reg(pdata, REG_MII_EXT_DATA, ®val); + + regval = FXGMAC_SET_REG_BITS(regval, + MII_EXT_SLEEP_CONTROL1_PLLON_IN_SLP_POS, + MII_EXT_SLEEP_CONTROL1_PLLON_IN_SLP_LEN, + 0); + fxgmac_write_ephy_reg(pdata, REG_MII_EXT_DATA, regval); + + /*regval = readreg(pdata->pAdapter, pdata->base_mem + MGMT_XST_OSC_CTRL); + regval = FXGMAC_SET_REG_BITS(regval, MGMT_XST_OSC_CTRL_XST_OSC_SEL_POS, + MGMT_XST_OSC_CTRL_XST_OSC_SEL_LEN, 0); + writereg(pdata->pAdapter, regval, pdata->base_mem + MGMT_XST_OSC_CTRL); + regval = readreg(pdata->pAdapter, pdata->base_mem + MGMT_XST_OSC_CTRL); + regval = FXGMAC_SET_REG_BITS(regval, MGMT_XST_OSC_CTRL_EN_XST_POS, + MGMT_XST_OSC_CTRL_EN_XST_LEN, 0); + writereg(pdata->pAdapter, regval, pdata->base_mem + MGMT_XST_OSC_CTRL);*/ +} +static void fxgmac_pwr_clock_ungate(struct fxgmac_pdata *pdata) +{ + u32 regval = 0; + + fxgmac_write_ephy_reg(pdata, REG_MII_EXT_ADDR, + REG_MII_EXT_SLEEP_CONTROL1); + fxgmac_read_ephy_reg(pdata, REG_MII_EXT_DATA, ®val); + + regval = FXGMAC_SET_REG_BITS(regval, + MII_EXT_SLEEP_CONTROL1_PLLON_IN_SLP_POS, + MII_EXT_SLEEP_CONTROL1_PLLON_IN_SLP_LEN, + 1); + fxgmac_write_ephy_reg(pdata, REG_MII_EXT_DATA, regval); + + /*regval = readreg(pdata->pAdapter, pdata->base_mem + MGMT_XST_OSC_CTRL); + regval = FXGMAC_SET_REG_BITS(regval, MGMT_XST_OSC_CTRL_EN_XST_POS, + MGMT_XST_OSC_CTRL_EN_XST_LEN, 1); + writereg(pdata->pAdapter, regval, pdata->base_mem + MGMT_XST_OSC_CTRL); + regval = readreg(pdata->pAdapter, pdata->base_mem + MGMT_XST_OSC_CTRL); + regval = FXGMAC_SET_REG_BITS(regval, MGMT_XST_OSC_CTRL_XST_OSC_SEL_POS, + MGMT_XST_OSC_CTRL_XST_OSC_SEL_LEN, 1); + writereg(pdata->pAdapter, regval, pdata->base_mem + MGMT_XST_OSC_CTRL);*/ +} +static unsigned char fxgmac_suspend_int(void *context) + +{ + u32 intid; +#if FXGMAC_EPHY_INTERRUPT_D0_OFF + u32 regval = 0; +#endif + u32 val_mgmt_intcrtl0; + struct fxgmac_pdata *pdata = (struct fxgmac_pdata *)context; + + val_mgmt_intcrtl0 = + (u32)readreg(pdata->pAdapter, pdata->base_mem + MGMT_INT_CTRL0); + + val_mgmt_intcrtl0 = FXGMAC_SET_REG_BITS(val_mgmt_intcrtl0, + MGMT_INT_CTRL0_INT_MASK_POS, + MGMT_INT_CTRL0_INT_MASK_LEN, + MGMT_INT_CTRL0_INT_MASK_EX_PMT); + writereg(pdata->pAdapter, val_mgmt_intcrtl0, + pdata->base_mem + MGMT_INT_CTRL0); + + for (intid = 0; intid < MSIX_TBL_MAX_NUM; intid++) { + writereg(pdata->pAdapter, 0x1, + pdata->base_mem + MSIX_TBL_BASE_ADDR + + MSIX_TBL_MASK_OFFSET + intid * 16); + } + + writereg(pdata->pAdapter, 0x0, + pdata->base_mem + MSIX_TBL_BASE_ADDR + MSIX_TBL_MASK_OFFSET + + MSI_ID_PHY_OTHER * 16); + readreg(pdata->pAdapter, pdata->base_mem + MGMT_WOL_CTRL); + + fxgmac_read_ephy_reg(pdata, REG_MII_INT_STATUS, NULL); +#if FXGMAC_EPHY_INTERRUPT_D0_OFF + regval = FXGMAC_SET_REG_BITS(0, PHY_INT_MASK_LINK_UP_POS, + PHY_INT_MASK_LINK_UP_LEN, 1); + regval = FXGMAC_SET_REG_BITS(regval, PHY_INT_MASK_LINK_DOWN_POS, + PHY_INT_MASK_LINK_DOWN_LEN, 1); + fxgmac_write_ephy_reg(pdata, REG_MII_INT_MASK, regval); +#endif + + return true; +} +static int fxgmac_suspend_txrx(struct fxgmac_pdata *pdata) +{ + struct fxgmac_channel *channel; + unsigned int i; + u32 regval; + int busy = 15; + /* Prepare for Tx DMA channel stop */ + channel = pdata->channel_head; + for (i = 0; i < pdata->channel_count; i++, channel++) { + if (!channel->tx_ring) { + break; + } + fxgmac_prepare_tx_stop(pdata, channel); + } + + /* Disable each Tx DMA channel */ + channel = pdata->channel_head; + for (i = 0; i < pdata->channel_count; i++, channel++) { + if (!channel->tx_ring) { + break; + } + + regval = readreg(pdata->pAdapter, + FXGMAC_DMA_REG(channel, DMA_CH_TCR)); + regval = FXGMAC_SET_REG_BITS(regval, DMA_CH_TCR_ST_POS, + DMA_CH_TCR_ST_LEN, 0); + writereg(pdata->pAdapter, regval, + FXGMAC_DMA_REG(channel, DMA_CH_TCR)); + DBGPRINT(MP_TRACE, (" disable channel %d tx dma", i)); + } + + do { + regval = + readreg(pdata->pAdapter, pdata->mac_regs + MAC_DBG_STA); + busy--; + } while ((regval & MAC_DBG_STA_TX_BUSY) && (busy)); + + if (0 != (regval & MAC_DBG_STA_TX_BUSY)) { + regval = + readreg(pdata->pAdapter, pdata->mac_regs + MAC_DBG_STA); + DbgPrintF( + MP_WARN, + "warning !!!timed out waiting for Tx MAC to stop regval %x\n", + regval); + return -1; + } + /* wait empty Tx queue */ + for (i = 0; i < pdata->tx_q_count; i++) { + do { + regval = readreg(pdata->pAdapter, + FXGMAC_MTL_REG(pdata, i, MTL_TXQ_DEG)); + busy--; + } while ((regval & MTL_TXQ_DEG_TX_BUSY) && (busy)); + if (0 != (regval & MTL_TXQ_DEG_TX_BUSY)) { + regval = readreg(pdata->pAdapter, + pdata->mac_regs + MTL_TXQ_DEG); + DbgPrintF( + MP_WARN, + "warning !!!timed out waiting for tx queue %u to empty\n", + i); + return -1; + } + } + + /* Disable MAC TxRx */ + regval = readreg(pdata->pAdapter, pdata->mac_regs + MAC_CR); + regval = FXGMAC_SET_REG_BITS(regval, MAC_CR_TE_POS, MAC_CR_TE_LEN, 0); + regval = FXGMAC_SET_REG_BITS(regval, MAC_CR_RE_POS, MAC_CR_RE_LEN, 0); + writereg(pdata->pAdapter, regval, pdata->mac_regs + MAC_CR); + + /* Prepare for Rx DMA channel stop */ + for (i = 0; i < pdata->rx_q_count; i++) { + fxgmac_prepare_rx_stop(pdata, i); + } + /* Disable each Rx DMA channel */ + channel = pdata->channel_head; + for (i = 0; i < pdata->channel_count; i++, channel++) { + if (!channel->rx_ring) { + break; + } + + regval = readreg(pdata->pAdapter, + FXGMAC_DMA_REG(channel, DMA_CH_RCR)); + regval = FXGMAC_SET_REG_BITS(regval, DMA_CH_RCR_SR_POS, + DMA_CH_RCR_SR_LEN, 0); + writereg(pdata->pAdapter, regval, + FXGMAC_DMA_REG(channel, DMA_CH_RCR)); + DBGPRINT(MP_TRACE, (" disable channel %d rx dma", i)); + } + return 0; +} +static void fxgmac_resume_int(struct fxgmac_pdata *pdata) +{ + u32 intid, regval = 0; + u32 val_mgmt_intcrtl0; + + val_mgmt_intcrtl0 = + (u32)readreg(pdata->pAdapter, pdata->base_mem + MGMT_INT_CTRL0); + + val_mgmt_intcrtl0 = FXGMAC_SET_REG_BITS( + val_mgmt_intcrtl0, MGMT_INT_CTRL0_INT_MASK_POS, + MGMT_INT_CTRL0_INT_MASK_LEN, MGMT_INT_CTRL0_INT_MASK_DISABLE); + writereg(pdata->pAdapter, val_mgmt_intcrtl0, + pdata->base_mem + MGMT_INT_CTRL0); + + for (intid = 0; intid < MSIX_TBL_RXTX_NUM; intid++) { + writereg(pdata->pAdapter, 0, + pdata->base_mem + MSIX_TBL_BASE_ADDR + + MSIX_TBL_MASK_OFFSET + intid * 16); + } + + for (intid = MSIX_TBL_RXTX_NUM; intid < MSIX_TBL_MAX_NUM; intid++) { + writereg(pdata->pAdapter, 0, + pdata->base_mem + MSIX_TBL_BASE_ADDR + + MSIX_TBL_MASK_OFFSET + intid * 16); + } + +#if FXGMAC_EPHY_INTERRUPT_D0_OFF + fxgmac_write_ephy_reg(pdata, REG_MII_INT_MASK, 0x0); + fxgmac_read_ephy_reg(pdata, REG_MII_INT_STATUS, NULL); +#else + + regval = FXGMAC_SET_REG_BITS(0, PHY_INT_MASK_LINK_UP_POS, + PHY_INT_MASK_LINK_UP_LEN, 1); + regval = FXGMAC_SET_REG_BITS(regval, PHY_INT_MASK_LINK_DOWN_POS, + PHY_INT_MASK_LINK_DOWN_LEN, 1); + fxgmac_write_ephy_reg(pdata, REG_MII_INT_MASK, regval); +#endif +} + +static void fxgmac_config_wol_wait_time(struct fxgmac_pdata *pdata) +{ + u32 regval; + + regval = readreg(pdata->pAdapter, pdata->base_mem + WOL_CTL); + regval = FXGMAC_SET_REG_BITS(regval, WOL_WAIT_TIME_POS, + WOL_WAIT_TIME_LEN, FXGMAC_WOL_WAIT_TIME); + writereg(pdata->pAdapter, regval, pdata->base_mem + WOL_CTL); +} + +static int fxgmac_hw_init(struct fxgmac_pdata *pdata) +{ + struct fxgmac_desc_ops *desc_ops = &pdata->desc_ops; + int ret; + u32 regval = 0; + + if (netif_msg_drv(pdata)) { + DPRINTK("fxgmac hw init call in regval %x\n", regval); + } + + /* Flush Tx queues */ + ret = fxgmac_flush_tx_queues(pdata); + if (ret) { +#ifdef FXGMAC_FLUSH_TX_CHECK_ENABLED + dev_err(pdata->dev, + "fxgmac_hw_init call flush tx queue err.\n"); + return ret; +#endif + } + + /* Initialize DMA related features */ + fxgmac_config_dma_bus(pdata); + fxgmac_config_osp_mode(pdata); + fxgmac_config_pblx8(pdata); + fxgmac_config_tx_pbl_val(pdata); + fxgmac_config_rx_pbl_val(pdata); + fxgmac_config_rx_coalesce(pdata); + fxgmac_config_tx_coalesce(pdata); + fxgmac_config_rx_buffer_size(pdata); + fxgmac_config_tso_mode(pdata); + fxgmac_config_sph_mode(pdata); + fxgmac_config_rss(pdata); + + desc_ops->tx_desc_init(pdata); + desc_ops->rx_desc_init(pdata); + fxgmac_enable_dma_interrupts(pdata); + + /* Initialize MTL related features */ + fxgmac_config_mtl_mode(pdata); + fxgmac_config_queue_mapping(pdata); + fxgmac_config_tsf_mode(pdata, pdata->tx_sf_mode); + fxgmac_config_rsf_mode(pdata, pdata->rx_sf_mode); + fxgmac_config_tx_threshold(pdata, pdata->tx_threshold); + fxgmac_config_rx_threshold(pdata, pdata->rx_threshold); + fxgmac_config_tx_fifo_size(pdata); + fxgmac_config_rx_fifo_size(pdata); + fxgmac_config_flow_control_threshold(pdata); + fxgmac_config_rx_fep_disable(pdata); + fxgmac_config_rx_fup_enable(pdata); + fxgmac_enable_mtl_interrupts(pdata); + + /* Initialize MAC related features */ + fxgmac_config_mac_address(pdata); + fxgmac_config_crc_check(pdata); + fxgmac_config_rx_mode(pdata); + fxgmac_config_jumbo(pdata); + fxgmac_config_flow_control(pdata); + fxgmac_config_mac_speed(pdata); + fxgmac_config_checksum_offload(pdata); + fxgmac_config_vlan_support(pdata); + fxgmac_config_mmc(pdata); + fxgmac_enable_mac_interrupts(pdata); + + fxgmac_config_wol_wait_time(pdata); + + /* enable EPhy link change interrupt */ + fxgmac_read_ephy_reg(pdata, REG_MII_INT_STATUS, NULL); + regval = FXGMAC_SET_REG_BITS(0, PHY_INT_MASK_LINK_UP_POS, + PHY_INT_MASK_LINK_UP_LEN, 1); + regval = FXGMAC_SET_REG_BITS(regval, PHY_INT_MASK_LINK_DOWN_POS, + PHY_INT_MASK_LINK_DOWN_LEN, 1); + fxgmac_write_ephy_reg(pdata, REG_MII_INT_MASK, regval); + + if (netif_msg_drv(pdata)) { + DPRINTK("fxgmac hw init callout %x\n", regval); + } + return 0; +} + +static void fxgmac_save_nonstick_reg(struct fxgmac_pdata *pdata) +{ + u32 i; + for (i = REG_PCIE_TRIGGER; i < MSI_PBA_REG; i += 4) { + pdata->reg_nonstick[(i - REG_PCIE_TRIGGER) >> 2] = + readreg(pdata->pAdapter, pdata->base_mem + i); + } + +#if defined(UBOOT) + /* PCI config space info */ + dm_pci_read_config16(pdata->pdev, PCI_VENDOR_ID, + &pdata->expansion.pci_venid); + dm_pci_read_config16(pdata->pdev, PCI_DEVICE_ID, + &pdata->expansion.pci_devid); + dm_pci_read_config16(pdata->pdev, PCI_SUBSYSTEM_VENDOR_ID, + &pdata->expansion.SubVendorID); + dm_pci_read_config16(pdata->pdev, PCI_SUBSYSTEM_ID, + &pdata->expansion.SubSystemID); + + dm_pci_read_config8(pdata->pdev, PCI_REVISION_ID, + &pdata->expansion.pci_revid); + + DbgPrintF( + MP_TRACE, + "VenId is %x, Devid is %x, SubId is %x, SubSysId is %x, Revid is %x.\n", + pdata->expansion.pci_venid, pdata->expansion.pci_devid, + pdata->expansion.SubVendorID, pdata->expansion.SubSystemID, + pdata->expansion.pci_revid); + +#elif !defined(UEFI) && !defined(PXE) && !defined(DPDK) && !defined(KDNET) + cfg_r32(pdata, REG_PCI_COMMAND, &pdata->expansion.cfg_pci_cmd); + cfg_r32(pdata, REG_CACHE_LINE_SIZE, + &pdata->expansion.cfg_cache_line_size); + cfg_r32(pdata, REG_MEM_BASE, &pdata->expansion.cfg_mem_base); + cfg_r32(pdata, REG_MEM_BASE_HI, &pdata->expansion.cfg_mem_base_hi); + cfg_r32(pdata, REG_IO_BASE, &pdata->expansion.cfg_io_base); + cfg_r32(pdata, REG_INT_LINE, &pdata->expansion.cfg_int_line); + cfg_r32(pdata, REG_DEVICE_CTRL1, &pdata->expansion.cfg_device_ctrl1); + cfg_r32(pdata, REG_PCI_LINK_CTRL, &pdata->expansion.cfg_pci_link_ctrl); + cfg_r32(pdata, REG_DEVICE_CTRL2, &pdata->expansion.cfg_device_ctrl2); + cfg_r32(pdata, REG_MSIX_CAPABILITY, + &pdata->expansion.cfg_msix_capability); + + DbgPrintF( + MP_TRACE, + "%s:\nCFG%02x-%02x\nCFG%02x-%02x\nCFG%02x-%02x\nCFG%02x-%02x\nCFG%02x-%02x\nCFG%02x-%02x\nCFG%02x-%02x\nCFG%02x-%02x\nCFG%02x-%02x\nCFG%02x-%02x\n", + __FUNCTION__, REG_PCI_COMMAND, pdata->expansion.cfg_pci_cmd, + REG_CACHE_LINE_SIZE, pdata->expansion.cfg_cache_line_size, + REG_MEM_BASE, pdata->expansion.cfg_mem_base, REG_MEM_BASE_HI, + pdata->expansion.cfg_mem_base_hi, REG_IO_BASE, + pdata->expansion.cfg_io_base, REG_INT_LINE, + pdata->expansion.cfg_int_line, REG_DEVICE_CTRL1, + pdata->expansion.cfg_device_ctrl1, REG_PCI_LINK_CTRL, + pdata->expansion.cfg_pci_link_ctrl, REG_DEVICE_CTRL2, + pdata->expansion.cfg_device_ctrl2, REG_MSIX_CAPABILITY, + pdata->expansion.cfg_msix_capability); +#endif +} + +static void fxgmac_restore_nonstick_reg(struct fxgmac_pdata *pdata) +{ + u32 i; + for (i = REG_PCIE_TRIGGER; i < MSI_PBA_REG; i += 4) { + writereg(pdata->pAdapter, + pdata->reg_nonstick[(i - REG_PCIE_TRIGGER) >> 2], + pdata->base_mem + i); + } +} + +#if defined(FXGMAC_ESD_RESTORE_PCIE_CFG) +static void fxgmac_esd_restore_pcie_cfg(struct fxgmac_pdata *pdata) +{ + cfg_w32(pdata, REG_PCI_COMMAND, pdata->expansion.cfg_pci_cmd); + cfg_w32(pdata, REG_CACHE_LINE_SIZE, + pdata->expansion.cfg_cache_line_size); + cfg_w32(pdata, REG_MEM_BASE, pdata->expansion.cfg_mem_base); + cfg_w32(pdata, REG_MEM_BASE_HI, pdata->expansion.cfg_mem_base_hi); + cfg_w32(pdata, REG_IO_BASE, pdata->expansion.cfg_io_base); + cfg_w32(pdata, REG_INT_LINE, pdata->expansion.cfg_int_line); + cfg_w32(pdata, REG_DEVICE_CTRL1, pdata->expansion.cfg_device_ctrl1); + cfg_w32(pdata, REG_PCI_LINK_CTRL, pdata->expansion.cfg_pci_link_ctrl); + cfg_w32(pdata, REG_DEVICE_CTRL2, pdata->expansion.cfg_device_ctrl2); + cfg_w32(pdata, REG_MSIX_CAPABILITY, + pdata->expansion.cfg_msix_capability); +} +#endif + +static int fxgmac_hw_exit(struct fxgmac_pdata *pdata) +{ + u32 regval; + u32 value = 0; + + cfg_r32(pdata, REG_PCI_LINK_CTRL, ®val); + pdata->pcie_link_status = + FXGMAC_GET_REG_BITS(regval, PCI_LINK_CTRL_ASPM_CONTROL_POS, + PCI_LINK_CTRL_ASPM_CONTROL_LEN); + if (PCI_LINK_CTRL_L1_STATUS == (pdata->pcie_link_status & 0x02)) { + regval = FXGMAC_SET_REG_BITS(regval, + PCI_LINK_CTRL_ASPM_CONTROL_POS, + PCI_LINK_CTRL_ASPM_CONTROL_LEN, 0); + cfg_w32(pdata, REG_PCI_LINK_CTRL, regval); + } + + /* Issue a CHIP reset */ + regval = readreg(pdata->pAdapter, pdata->base_mem + SYS_RESET_REG); + DPRINTK("CHIP_RESET 0x%x\n", regval); + /* reg152c bit31 1->reset, self-clear, if read it again, it still set 1. */ + regval = FXGMAC_SET_REG_BITS(regval, SYS_RESET_POS, SYS_RESET_LEN, 1); + writereg(pdata->pAdapter, regval, pdata->base_mem + SYS_RESET_REG); + + usleep_range_ex(pdata->pAdapter, 9000, 10000); + + /* reg152c reset will reset trigger circuit and reload efuse patch 0x1004=0x16,need to release ephy reset again */ + value = FXGMAC_SET_REG_BITS(value, MGMT_EPHY_CTRL_RESET_POS, + MGMT_EPHY_CTRL_RESET_LEN, + MGMT_EPHY_CTRL_STA_EPHY_RELEASE); + writereg(pdata->pAdapter, value, pdata->base_mem + MGMT_EPHY_CTRL); + usleep_range_ex(pdata->pAdapter, 100, 150); + + fxgmac_restore_nonstick_reg(pdata); + + return 0; +} + +static int fxgmac_set_gmac_register(struct fxgmac_pdata *pdata, IOMEM address, + unsigned int data) +{ + if (address < pdata->base_mem) { + return -1; + } + writereg(pdata->pAdapter, data, address); + return 0; +} + +static u32 fxgmac_get_gmac_register(struct fxgmac_pdata *pdata, IOMEM address) +{ + u32 regval = 0; + + if (address > pdata->base_mem) { + regval = readreg(pdata->pAdapter, address); + } + return regval; +} + +static int fxgmac_pcie_init(struct fxgmac_pdata *pdata, bool ltr_en, + bool aspm_l1ss_en, bool aspm_l1_en, + bool aspm_l0s_en) +{ + u32 regval = 0; + u32 deviceid = 0; + + cfg_r32(pdata, REG_PCI_LINK_CTRL, ®val); + if (PCI_LINK_CTRL_L1_STATUS == (pdata->pcie_link_status & 0x02) && + 0x00 == FXGMAC_GET_REG_BITS(regval, PCI_LINK_CTRL_ASPM_CONTROL_POS, + PCI_LINK_CTRL_ASPM_CONTROL_LEN)) { + regval = FXGMAC_SET_REG_BITS(regval, + PCI_LINK_CTRL_ASPM_CONTROL_POS, + PCI_LINK_CTRL_ASPM_CONTROL_LEN, + pdata->pcie_link_status); + cfg_w32(pdata, REG_PCI_LINK_CTRL, regval); + } + + regval = FXGMAC_SET_REG_BITS(0, LTR_IDLE_ENTER_REQUIRE_POS, + LTR_IDLE_ENTER_REQUIRE_LEN, + LTR_IDLE_ENTER_REQUIRE); + regval = FXGMAC_SET_REG_BITS(regval, LTR_IDLE_ENTER_SCALE_POS, + LTR_IDLE_ENTER_SCALE_LEN, + LTR_IDLE_ENTER_SCALE); + regval = FXGMAC_SET_REG_BITS(regval, LTR_IDLE_ENTER_POS, + LTR_IDLE_ENTER_LEN, LTR_IDLE_ENTER_USVAL); + regval = (regval << 16) + regval; /* snoopy + non-snoopy */ + writereg(pdata->pAdapter, regval, pdata->base_mem + LTR_IDLE_ENTER); + + regval = 0; + regval = FXGMAC_SET_REG_BITS(0, LTR_IDLE_EXIT_REQUIRE_POS, + LTR_IDLE_EXIT_REQUIRE_LEN, + LTR_IDLE_EXIT_REQUIRE); + regval = FXGMAC_SET_REG_BITS(regval, LTR_IDLE_EXIT_SCALE_POS, + LTR_IDLE_EXIT_SCALE_LEN, + LTR_IDLE_EXIT_SCALE); + regval = FXGMAC_SET_REG_BITS(regval, LTR_IDLE_EXIT_POS, + LTR_IDLE_EXIT_LEN, LTR_IDLE_EXIT_USVAL); + regval = (regval << 16) + regval; /* snoopy + non-snoopy */ + writereg(pdata->pAdapter, regval, pdata->base_mem + LTR_IDLE_EXIT); + + regval = readreg(pdata->pAdapter, pdata->base_mem + LTR_CTRL); + if (ltr_en) { + regval = FXGMAC_SET_REG_BITS(regval, LTR_CTRL_EN_POS, + LTR_CTRL_EN_LEN, 1); + regval = FXGMAC_SET_REG_BITS(regval, + LTR_CTRL_IDLE_THRE_TIMER_POS, + LTR_CTRL_IDLE_THRE_TIMER_LEN, + LTR_CTRL_IDLE_THRE_TIMER_VAL); + } else { + regval = FXGMAC_SET_REG_BITS(regval, LTR_CTRL_EN_POS, + LTR_CTRL_EN_LEN, 0); + } + writereg(pdata->pAdapter, regval, pdata->base_mem + LTR_CTRL); + + regval = readreg(pdata->pAdapter, pdata->base_mem + LPW_CTRL); + regval = FXGMAC_SET_REG_BITS(regval, LPW_CTRL_ASPM_L0S_EN_POS, + LPW_CTRL_ASPM_L0S_EN_LEN, + aspm_l0s_en ? 1 : 0); + regval = FXGMAC_SET_REG_BITS(regval, LPW_CTRL_ASPM_L1_EN_POS, + LPW_CTRL_ASPM_L1_EN_LEN, + aspm_l1_en ? 1 : 0); + regval = FXGMAC_SET_REG_BITS(regval, LPW_CTRL_L1SS_EN_POS, + LPW_CTRL_L1SS_EN_LEN, + aspm_l1ss_en ? 1 : 0); + writereg(pdata->pAdapter, regval, pdata->base_mem + LPW_CTRL); + + cfg_r32(pdata, REG_ASPM_CONTROL, ®val); + regval = FXGMAC_SET_REG_BITS(regval, ASPM_L1_IDLE_THRESHOLD_POS, + ASPM_L1_IDLE_THRESHOLD_LEN, + ASPM_L1_IDLE_THRESHOLD_1US); + cfg_w32(pdata, REG_ASPM_CONTROL, regval); + + regval = 0; + regval = FXGMAC_SET_REG_BITS(regval, PCIE_SERDES_PLL_AUTOOFF_POS, + PCIE_SERDES_PLL_AUTOOFF_LEN, 1); + writereg(pdata->pAdapter, regval, + pdata->base_mem + REG_PCIE_SERDES_PLL); + + /*fuxi nto adjust sigdet threshold*/ + cfg_r8(pdata, REG_PCI_REVID, ®val); + cfg_r16(pdata, REG_PCI_DEVICE_ID, &deviceid); + if (FXGMAC_REV_01 == regval && PCI_DEVICE_ID_FUXI == deviceid) { + regval = + readreg(pdata->pAdapter, pdata->base_mem + MGMT_SIGDET); + regval = FXGMAC_SET_REG_BITS(regval, MGMT_SIGDET_POS, + MGMT_SIGDET_LEN, MGMT_SIGDET_55MV); + writereg(pdata->pAdapter, regval, + pdata->base_mem + MGMT_SIGDET); + + regval = readreg(pdata->pAdapter, + pdata->base_mem + MGMT_SIGDET_DEGLITCH); + regval = FXGMAC_SET_REG_BITS(regval, + MGMT_SIGDET_DEGLITCH_DISABLE_POS, + MGMT_SIGDET_DEGLITCH_DISABLE_LEN, + 1); + writereg(pdata->pAdapter, regval, + pdata->base_mem + MGMT_SIGDET_DEGLITCH); + } + + return 0; +} + +static void fxgmac_clear_misc_int_status(struct fxgmac_pdata *pdata) +{ + struct fxgmac_hw_ops *hw_ops = &pdata->hw_ops; + u32 regval, i, q_count; + + /* clear phy interrupt status */ + hw_ops->read_ephy_reg(pdata, REG_MII_INT_STATUS, NULL); + hw_ops->read_ephy_reg(pdata, REG_MII_INT_STATUS, NULL); + /* clear other interrupt status of misc interrupt */ + regval = pdata->hw_ops.get_gmac_register(pdata, + pdata->mac_regs + MAC_ISR); + if (regval) { + if (regval & (1 << MGMT_MAC_PHYIF_STA_POS)) + pdata->hw_ops.get_gmac_register( + pdata, pdata->mac_regs + MAC_PHYIF_STA); + + if ((regval & (1 << MGMT_MAC_AN_SR0_POS)) || + (regval & (1 << MGMT_MAC_AN_SR0_POS)) || + (regval & (1 << MGMT_MAC_AN_SR0_POS))) + pdata->hw_ops.get_gmac_register( + pdata, pdata->mac_regs + MAC_AN_SR); + + if (regval & (1 << MGMT_MAC_PMT_STA_POS)) + pdata->hw_ops.get_gmac_register( + pdata, pdata->mac_regs + MAC_PMT_STA); + + if (regval & (1 << MGMT_MAC_LPI_STA_POS)) + pdata->hw_ops.get_gmac_register( + pdata, pdata->mac_regs + MAC_LPI_STA); + + if (regval & (1 << MGMT_MAC_MMC_STA_POS)) { + if (regval & (1 << MGMT_MAC_RX_MMC_STA_POS)) + hw_ops->rx_mmc_int(pdata); + + if (regval & (1 << MGMT_MAC_TX_MMC_STA_POS)) + hw_ops->tx_mmc_int(pdata); + + if (regval & (1 << MGMT_MMC_IPCRXINT_POS)) + pdata->hw_ops.get_gmac_register( + pdata, pdata->mac_regs + MMC_IPCRXINT); + } + + if ((regval & (1 << MGMT_MAC_TX_RX_STA0_POS)) || + (regval & (1 << MGMT_MAC_TX_RX_STA1_POS))) + pdata->hw_ops.get_gmac_register( + pdata, pdata->mac_regs + MAC_TX_RX_STA); + + if (regval & (1 << MGMT_MAC_GPIO_SR_POS)) + pdata->hw_ops.get_gmac_register( + pdata, pdata->mac_regs + MAC_GPIO_SR); + } + + /* MTL_Interrupt_Status, write 1 clear */ + regval = pdata->hw_ops.get_gmac_register(pdata, + pdata->mac_regs + MTL_INT_SR); + pdata->hw_ops.set_gmac_register(pdata, pdata->mac_regs + MTL_INT_SR, + regval); + + /* MTL_Q(#i)_Interrupt_Control_Status, write 1 clear */ + q_count = max(pdata->hw_feat.tx_q_cnt, pdata->hw_feat.rx_q_cnt); + for (i = 0; i < q_count; i++) { + /* Clear all the interrupts which are set */ + regval = pdata->hw_ops.get_gmac_register( + pdata, + pdata->mac_regs + MTL_Q_INT_CTL_SR + i * MTL_Q_INC); + pdata->hw_ops.set_gmac_register( + pdata, + pdata->mac_regs + MTL_Q_INT_CTL_SR + i * MTL_Q_INC, + regval); + } + + /* MTL_ECC_Interrupt_Status, write 1 clear */ + regval = pdata->hw_ops.get_gmac_register(pdata, pdata->mac_regs + + MTL_ECC_INT_SR); + pdata->hw_ops.set_gmac_register(pdata, pdata->mac_regs + MTL_ECC_INT_SR, + regval); + + /* DMA_ECC_Interrupt_Status, write 1 clear */ + regval = pdata->hw_ops.get_gmac_register(pdata, pdata->mac_regs + + DMA_ECC_INT_SR); + pdata->hw_ops.set_gmac_register(pdata, pdata->mac_regs + DMA_ECC_INT_SR, + regval); +} + +static void fxgmac_trigger_pcie(struct fxgmac_pdata *pdata, u32 code) +{ + writereg(pdata->pAdapter, code, pdata->base_mem + REG_PCIE_TRIGGER); +} + +void fxgmac_init_hw_ops(struct fxgmac_hw_ops *hw_ops) +{ + hw_ops->init = fxgmac_hw_init; + hw_ops->exit = fxgmac_hw_exit; + hw_ops->save_nonstick_reg = fxgmac_save_nonstick_reg; + hw_ops->restore_nonstick_reg = fxgmac_restore_nonstick_reg; +#if defined(FXGMAC_ESD_RESTORE_PCIE_CFG) + hw_ops->esd_restore_pcie_cfg = fxgmac_esd_restore_pcie_cfg; +#endif + + hw_ops->set_gmac_register = fxgmac_set_gmac_register; + hw_ops->get_gmac_register = fxgmac_get_gmac_register; + + hw_ops->tx_complete = fxgmac_tx_complete; + hw_ops->enable_tx = fxgmac_enable_tx; + hw_ops->disable_tx = fxgmac_disable_tx; + hw_ops->enable_rx = fxgmac_enable_rx; + hw_ops->disable_rx = fxgmac_disable_rx; + hw_ops->enable_channel_rx = fxgmac_enable_channel_rx; + + hw_ops->enable_int = fxgmac_enable_int; + hw_ops->disable_int = fxgmac_disable_int; + hw_ops->set_interrupt_moderation = fxgmac_set_interrupt_moderation; + hw_ops->enable_msix_rxtxinterrupt = fxgmac_enable_msix_rxtxinterrupt; + hw_ops->disable_msix_interrupt = fxgmac_disable_msix_interrupt; + hw_ops->enable_msix_rxtxphyinterrupt = + fxgmac_enable_msix_rxtxphyinterrupt; + hw_ops->enable_msix_one_interrupt = fxgmac_enable_msix_one_interrupt; + hw_ops->disable_msix_one_interrupt = fxgmac_disable_msix_one_interrupt; + hw_ops->enable_mgm_interrupt = fxgmac_enable_mgm_interrupt; + hw_ops->disable_mgm_interrupt = fxgmac_disable_mgm_interrupt; + hw_ops->dismiss_all_int = fxgmac_dismiss_all_int; + hw_ops->clear_misc_int_status = fxgmac_clear_misc_int_status; + + hw_ops->set_mac_address = fxgmac_set_mac_address; + hw_ops->set_mac_hash = fxgmac_set_mc_addresses; + hw_ops->config_rx_mode = fxgmac_config_rx_mode; + hw_ops->enable_rx_csum = fxgmac_enable_rx_csum; + hw_ops->disable_rx_csum = fxgmac_disable_rx_csum; + + /* For MII speed configuration */ + hw_ops->config_mac_speed = fxgmac_config_mac_speed; + hw_ops->get_xlgmii_phy_status = fxgmac_check_phy_link; + + /* For descriptor related operation */ + hw_ops->is_last_desc = fxgmac_is_last_desc; + hw_ops->is_context_desc = fxgmac_is_context_desc; + + hw_ops->config_tso = fxgmac_config_tso_mode; +#if FXGMAC_SANITY_CHECK_ENABLED + hw_ops->diag_sanity_check = fxgmac_diag_sanity_check; +#endif + + /* For Flow Control */ + hw_ops->config_tx_flow_control = fxgmac_config_tx_flow_control; + hw_ops->config_rx_flow_control = fxgmac_config_rx_flow_control; + + /*For Jumbo Frames*/ + hw_ops->enable_jumbo = fxgmac_config_jumbo; + + /* For Vlan related config */ + hw_ops->enable_tx_vlan = fxgmac_enable_tx_vlan; + hw_ops->disable_tx_vlan = fxgmac_disable_tx_vlan; + hw_ops->enable_rx_vlan_stripping = fxgmac_enable_rx_vlan_stripping; + hw_ops->disable_rx_vlan_stripping = fxgmac_disable_rx_vlan_stripping; + hw_ops->enable_rx_vlan_filtering = fxgmac_enable_rx_vlan_filtering; + hw_ops->disable_rx_vlan_filtering = fxgmac_disable_rx_vlan_filtering; + hw_ops->update_vlan_hash_table = fxgmac_update_vlan_hash_table; + + /* For RX coalescing */ + hw_ops->config_rx_coalesce = fxgmac_config_rx_coalesce; + hw_ops->config_tx_coalesce = fxgmac_config_tx_coalesce; + hw_ops->usec_to_riwt = fxgmac_usec_to_riwt; + hw_ops->riwt_to_usec = fxgmac_riwt_to_usec; + + /* For RX and TX threshold config */ + hw_ops->config_rx_threshold = fxgmac_config_rx_threshold; + hw_ops->config_tx_threshold = fxgmac_config_tx_threshold; + + /* For RX and TX Store and Forward Mode config */ + hw_ops->config_rsf_mode = fxgmac_config_rsf_mode; + hw_ops->config_tsf_mode = fxgmac_config_tsf_mode; + + /* For TX DMA Operating on Second Frame config */ + hw_ops->config_osp_mode = fxgmac_config_osp_mode; + + /* For RX and TX PBL config */ + hw_ops->config_rx_pbl_val = fxgmac_config_rx_pbl_val; + hw_ops->get_rx_pbl_val = fxgmac_get_rx_pbl_val; + hw_ops->config_tx_pbl_val = fxgmac_config_tx_pbl_val; + hw_ops->get_tx_pbl_val = fxgmac_get_tx_pbl_val; + hw_ops->config_pblx8 = fxgmac_config_pblx8; + hw_ops->calculate_max_checksum_size = + fxgmac_calculate_max_checksum_size; + + /* For MMC statistics support */ + hw_ops->tx_mmc_int = fxgmac_tx_mmc_int; + hw_ops->rx_mmc_int = fxgmac_rx_mmc_int; + hw_ops->read_mmc_stats = fxgmac_read_mmc_stats; + + /* For Receive Side Scaling */ + hw_ops->enable_rss = fxgmac_enable_rss; + hw_ops->disable_rss = fxgmac_disable_rss; + hw_ops->get_rss_options = fxgmac_read_rss_options; + hw_ops->set_rss_options = fxgmac_write_rss_options; + hw_ops->set_rss_hash_key = fxgmac_set_rss_hash_key; + hw_ops->set_rss_lookup_table = fxgmac_set_rss_lookup_table; + hw_ops->get_rss_hash_key = fxgmac_read_rss_hash_key; + hw_ops->write_rss_lookup_table = fxgmac_write_rss_lookup_table; + + /*For Power Management*/ +#if defined(FXGMAC_POWER_MANAGEMENT) + hw_ops->set_arp_offload = fxgmac_update_aoe_ipv4addr; + hw_ops->enable_arp_offload = fxgmac_enable_arp_offload; + hw_ops->disable_arp_offload = fxgmac_disable_arp_offload; + + hw_ops->set_ns_offload = fxgmac_set_ns_offload; + hw_ops->enable_ns_offload = fxgmac_enable_ns_offload; + hw_ops->disable_ns_offload = fxgmac_disable_ns_offload; + + hw_ops->enable_wake_magic_pattern = fxgmac_enable_wake_magic_pattern; + hw_ops->disable_wake_magic_pattern = fxgmac_disable_wake_magic_pattern; + + hw_ops->enable_wake_link_change = fxgmac_enable_wake_link_change; + hw_ops->disable_wake_link_change = fxgmac_disable_wake_link_change; + + hw_ops->check_wake_pattern_fifo_pointer = + fxgmac_check_wake_pattern_fifo_pointer; + hw_ops->set_wake_pattern = fxgmac_set_wake_pattern; + hw_ops->enable_wake_pattern = fxgmac_enable_wake_pattern; + hw_ops->disable_wake_pattern = fxgmac_disable_wake_pattern; + hw_ops->set_wake_pattern_mask = fxgmac_set_wake_pattern_mask; +#if FXGMAC_PM_WPI_READ_FEATURE_ENABLED + hw_ops->enable_wake_packet_indication = + fxgmac_enable_wake_packet_indication; + hw_ops->get_wake_packet_indication = fxgmac_get_wake_packet_indication; +#endif +#endif + + /*For phy write /read*/ + hw_ops->reset_phy = fxgmac_reset_phy; + hw_ops->release_phy = fxgmac_release_phy; + hw_ops->get_ephy_state = fxgmac_get_ephy_state; + hw_ops->write_ephy_reg = fxgmac_write_ephy_reg; + hw_ops->read_ephy_reg = fxgmac_read_ephy_reg; + hw_ops->set_ephy_autoneg_advertise = fxgmac_set_ephy_autoneg_advertise; + hw_ops->phy_config = fxgmac_phy_config; + hw_ops->close_phy_led = fxgmac_close_phy_led; + hw_ops->led_under_active = fxmgac_config_led_under_active; + hw_ops->led_under_sleep = fxgmac_config_led_under_sleep; + hw_ops->led_under_shutdown = fxgmac_config_led_under_shutdown; + hw_ops->led_under_disable = fxgmac_config_led_under_disable; + hw_ops->enable_phy_check = fxgmac_enable_phy_check; + hw_ops->disable_phy_check = fxgmac_disable_phy_check; + hw_ops->setup_cable_loopback = fxgmac_setup_cable_loopback; + hw_ops->clean_cable_loopback = fxgmac_clean_cable_loopback; + hw_ops->disable_phy_sleep = fxgmac_disable_phy_sleep; + hw_ops->enable_phy_sleep = fxgmac_enable_phy_sleep; + hw_ops->phy_green_ethernet = fxgmac_phy_green_ethernet; + hw_ops->phy_eee_feature = fxgmac_phy_eee_feature; + + /* For power management */ + hw_ops->pre_power_down = fxgmac_pre_powerdown; + hw_ops->config_power_down = fxgmac_config_powerdown; + hw_ops->config_power_up = fxgmac_config_powerup; + hw_ops->set_suspend_int = fxgmac_suspend_int; + hw_ops->set_resume_int = fxgmac_resume_int; + hw_ops->set_suspend_txrx = fxgmac_suspend_txrx; + hw_ops->set_pwr_clock_gate = fxgmac_pwr_clock_gate; + hw_ops->set_pwr_clock_ungate = fxgmac_pwr_clock_ungate; + + hw_ops->set_all_multicast_mode = fxgmac_set_all_multicast_mode; + hw_ops->config_multicast_mac_hash_table = + fxgmac_config_multicast_mac_hash_table; + hw_ops->set_promiscuous_mode = fxgmac_set_promiscuous_mode; + hw_ops->enable_rx_broadcast = fxgmac_enable_rx_broadcast; + + /* efuse relevant operation. */ + hw_ops->read_patch_from_efuse_per_index = + fxgmac_read_patch_from_efuse_per_index; /* read patch per index. */ + hw_ops->read_mac_subsys_from_efuse = fxgmac_read_mac_subsys_from_efuse; + hw_ops->read_efuse_data = fxgmac_efuse_read_data; +#ifndef COMMENT_UNUSED_CODE_TO_REDUCE_SIZE + hw_ops->read_patch_from_efuse = + fxgmac_read_patch_from_efuse; /* read patch per register. */ + hw_ops->read_patch_from_efuse_per_index = + fxgmac_read_patch_from_efuse_per_index; /* read patch per index. */ + hw_ops->write_patch_to_efuse = fxgmac_write_patch_to_efuse; + hw_ops->write_patch_to_efuse_per_index = + fxgmac_write_patch_to_efuse_per_index; + hw_ops->write_mac_subsys_to_efuse = fxgmac_write_mac_subsys_to_efuse; + hw_ops->efuse_load = fxgmac_efuse_load; + hw_ops->write_oob = fxgmac_efuse_write_oob; + hw_ops->write_led = fxgmac_efuse_write_led; + hw_ops->write_led_config = fxgmac_write_led_setting_to_efuse; + hw_ops->read_led_config = fxgmac_read_led_setting_from_efuse; +#endif + + /* */ + hw_ops->pcie_init = fxgmac_pcie_init; + hw_ops->trigger_pcie = fxgmac_trigger_pcie; +} diff --git a/drivers/net/ethernet/motorcomm/yt6801/fuxi-gmac-ioctl.c b/drivers/net/ethernet/motorcomm/yt6801/fuxi-gmac-ioctl.c new file mode 100644 index 000000000000..16bd806f5662 --- /dev/null +++ b/drivers/net/ethernet/motorcomm/yt6801/fuxi-gmac-ioctl.c @@ -0,0 +1,590 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (c) 2021 Motorcomm Corporation. */ + +#include "fuxi-gmac.h" +#include "fuxi-gmac-reg.h" + +static void fxgmac_dbg_tx_pkt(struct fxgmac_pdata *pdata, u8 *pcmd_data) +{ + unsigned int pktLen = 0; + struct sk_buff *skb; + pfxgmac_test_packet pPkt; + u8 *pTx_data = NULL; + u8 *pSkb_data = NULL; + u32 offload_len = 0; + u8 ipHeadLen, tcpHeadLen, headTotalLen; + static u32 lastGsoSize = 806; + + /* get fxgmac_test_packet */ + pPkt = (pfxgmac_test_packet)(pcmd_data + sizeof(struct ext_ioctl_data)); + pktLen = pPkt->length; + + /* get pkt data */ + pTx_data = (u8 *)pPkt + sizeof(fxgmac_test_packet); + + /* alloc sk_buff */ + skb = alloc_skb(pktLen, GFP_ATOMIC); + if (!skb) { + DPRINTK("alloc skb fail\n"); + return; + } + + /* copy data to skb */ + pSkb_data = skb_put(skb, pktLen); + memset(pSkb_data, 0, pktLen); + memcpy(pSkb_data, pTx_data, pktLen); + + /* set skb parameters */ + skb->dev = pdata->netdev; + skb->pkt_type = PACKET_OUTGOING; + skb->protocol = ntohs(ETH_P_IP); + skb->no_fcs = 1; + skb->ip_summed = CHECKSUM_PARTIAL; + if (skb->len > 1514) { + /* TSO packet */ + /* set tso test flag */ + pdata->expansion.fxgmac_test_tso_flag = true; + + /* get protocol head length */ + ipHeadLen = (pSkb_data[TEST_MAC_HEAD] & 0xF) * 4; + tcpHeadLen = (pSkb_data[TEST_MAC_HEAD + ipHeadLen + + TEST_TCP_HEAD_LEN_OFFSET] >> + 4 & + 0xF) * + 4; + headTotalLen = TEST_MAC_HEAD + ipHeadLen + tcpHeadLen; + offload_len = (pSkb_data[TEST_TCP_OFFLOAD_LEN_OFFSET] << 8 | + pSkb_data[TEST_TCP_OFFLOAD_LEN_OFFSET + 1]) & + 0xFFFF; + /* set tso skb parameters */ + + skb->transport_header = ipHeadLen + TEST_MAC_HEAD; + skb->network_header = TEST_MAC_HEAD; + skb->inner_network_header = TEST_MAC_HEAD; + skb->mac_len = TEST_MAC_HEAD; + + /* set skb_shinfo parameters */ + if (tcpHeadLen > TEST_TCP_FIX_HEAD_LEN) { + skb_shinfo(skb)->gso_size = + (pSkb_data[TEST_TCP_MSS_OFFSET] << 8 | + pSkb_data[TEST_TCP_MSS_OFFSET + 1]) & + 0xFFFF; + } else { + skb_shinfo(skb)->gso_size = 0; + } + if (skb_shinfo(skb)->gso_size != 0) { + lastGsoSize = skb_shinfo(skb)->gso_size; + } else { + skb_shinfo(skb)->gso_size = lastGsoSize; + } + + /* get segment size */ + if (offload_len % skb_shinfo(skb)->gso_size == 0) { + skb_shinfo(skb)->gso_segs = + offload_len / skb_shinfo(skb)->gso_size; + pdata->expansion.fxgmac_test_last_tso_len = + skb_shinfo(skb)->gso_size + headTotalLen; + } else { + skb_shinfo(skb)->gso_segs = + offload_len / skb_shinfo(skb)->gso_size + 1; + pdata->expansion.fxgmac_test_last_tso_len = + offload_len % skb_shinfo(skb)->gso_size + + headTotalLen; + } + pdata->expansion.fxgmac_test_tso_seg_num = + skb_shinfo(skb)->gso_segs; + + skb_shinfo(skb)->gso_type = SKB_GSO_TCPV4; + skb_shinfo(skb)->frag_list = NULL; + skb->csum_start = skb_headroom(skb) + TEST_MAC_HEAD + ipHeadLen; + skb->csum_offset = skb->len - TEST_MAC_HEAD - ipHeadLen; + + pdata->expansion.fxgmac_test_packet_len = + skb_shinfo(skb)->gso_size + headTotalLen; + } else { + /* set non-TSO packet parameters */ + pdata->expansion.fxgmac_test_packet_len = skb->len; + } + + /* send data */ + if (dev_queue_xmit(skb) != NET_XMIT_SUCCESS) { + DPRINTK("xmit data fail \n"); + } +} + +static void fxgmac_dbg_rx_pkt(struct fxgmac_pdata *pdata, u8 *pcmd_data) +{ + unsigned int totalLen = 0; + struct sk_buff *rx_skb; + struct ext_ioctl_data *pcmd; + fxgmac_test_packet pkt; + void *addr = 0; + u8 *rx_data = (u8 *)kzalloc(FXGMAC_MAX_DBG_RX_DATA, GFP_KERNEL); + if (!rx_data) + return; + + /* initial dest data region */ + pcmd = (struct ext_ioctl_data *)pcmd_data; + addr = pcmd->cmd_buf.buf; + while (pdata->expansion.fxgmac_test_skb_arr_in_index != + pdata->expansion.fxgmac_test_skb_arr_out_index) { + /* get received skb data */ + rx_skb = + pdata->expansion.fxgmac_test_skb_array + [pdata->expansion.fxgmac_test_skb_arr_out_index]; + + if (rx_skb->len + sizeof(fxgmac_test_packet) + totalLen < + 64000) { + pkt.length = rx_skb->len; + pkt.type = 0x80; + pkt.buf[0].offset = + totalLen + sizeof(fxgmac_test_packet); + pkt.buf[0].length = rx_skb->len; + + /* get data from skb */ + + memcpy(rx_data, rx_skb->data, rx_skb->len); + + /* update next pointer */ + if ((pdata->expansion.fxgmac_test_skb_arr_out_index + + 1) % FXGMAC_MAX_DBG_TEST_PKT == + pdata->expansion.fxgmac_test_skb_arr_in_index) { + pkt.next = NULL; + } else { + pkt.next = + (pfxgmac_test_packet)(addr + totalLen + + sizeof(fxgmac_test_packet) + + pkt.length); + } + + /* copy data to user space */ + if (copy_to_user((void *)(addr + totalLen), + (void *)(&pkt), + sizeof(fxgmac_test_packet))) { + DPRINTK("cppy pkt data to user fail..."); + } + + if (copy_to_user((void *)(addr + totalLen + + sizeof(fxgmac_test_packet)), + (void *)rx_data, rx_skb->len)) { + DPRINTK("cppy data to user fail..."); + } + + /* update total length */ + totalLen += (sizeof(fxgmac_test_packet) + rx_skb->len); + + /* free skb */ + kfree_skb(rx_skb); + pdata->expansion.fxgmac_test_skb_array + [pdata->expansion.fxgmac_test_skb_arr_out_index] = + NULL; + + /* update gCurSkbOutIndex */ + pdata->expansion.fxgmac_test_skb_arr_out_index = + (pdata->expansion.fxgmac_test_skb_arr_out_index + + 1) % + FXGMAC_MAX_DBG_TEST_PKT; + } else { + DPRINTK("receive data more receive buffer... \n"); + break; + } + } + + if (rx_data) + kfree(rx_data); +} + +long fxgmac_netdev_ops_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) +{ + bool ret = true; + int regval = 0; + struct fxgmac_pdata *pdata = file->private_data; + struct fxgmac_hw_ops *hw_ops = &pdata->hw_ops; + FXGMAC_PDATA_OF_PLATFORM *ex = &pdata->expansion; + CMD_DATA ex_data; + struct ext_ioctl_data pcmd; + u8 *data = NULL; + u8 *buf = NULL; + int in_total_size, in_data_size, out_total_size; + int ioctl_cmd_size = sizeof(struct ext_ioctl_data); + u8 mac[ETH_ALEN] = { 0 }; + struct sk_buff *tmpskb; + + if (!arg) { + DPRINTK("[%s] command arg is %lx !\n", __func__, arg); + goto err; + } + + /* check device type */ + if (_IOC_TYPE(cmd) != IOC_MAGIC) { + DPRINTK("[%s] command type [%c] error!\n", __func__, + _IOC_TYPE(cmd)); + goto err; + } + + /* check command number*/ + if (_IOC_NR(cmd) > IOC_MAXNR) { + DPRINTK("[%s] command number [%d] exceeded!\n", __func__, + _IOC_NR(cmd)); + goto err; + } + + if (copy_from_user(&pcmd, (void *)arg, ioctl_cmd_size)) { + DPRINTK("copy data from user fail... \n"); + goto err; + } + + in_total_size = pcmd.cmd_buf.size_in; + in_data_size = in_total_size - ioctl_cmd_size; + out_total_size = pcmd.cmd_buf.size_out; + + buf = (u8 *)kzalloc(in_total_size, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + if (copy_from_user(buf, (void *)arg, in_total_size)) { + DPRINTK("copy data from user fail... \n"); + goto err; + } + data = buf + ioctl_cmd_size; + + if (arg != 0) { + switch (pcmd.cmd_type) { + /* ioctl diag begin */ + case FXGMAC_DFS_IOCTL_DIAG_BEGIN: + DPRINTK("Debugfs received diag begin command.\n"); + if (netif_running(pdata->netdev)) { + fxgmac_restart_dev(pdata); + } + + /* release last loopback test abnormal exit buffer */ + while (ex->fxgmac_test_skb_arr_in_index != + ex->fxgmac_test_skb_arr_out_index) { + tmpskb = + ex->fxgmac_test_skb_array + [ex->fxgmac_test_skb_arr_out_index]; + if (tmpskb) { + kfree_skb(tmpskb); + ex->fxgmac_test_skb_array + [ex->fxgmac_test_skb_arr_out_index] = + NULL; + } + + ex->fxgmac_test_skb_arr_out_index = + (ex->fxgmac_test_skb_arr_out_index + + 1) % + FXGMAC_MAX_DBG_TEST_PKT; + } + + /* init loopback test parameters */ + ex->fxgmac_test_skb_arr_in_index = 0; + ex->fxgmac_test_skb_arr_out_index = 0; + ex->fxgmac_test_tso_flag = false; + ex->fxgmac_test_tso_seg_num = 0; + ex->fxgmac_test_last_tso_len = 0; + ex->fxgmac_test_packet_len = 0; + break; + + /* ioctl diag end */ + case FXGMAC_DFS_IOCTL_DIAG_END: + DPRINTK("Debugfs received diag end command.\n"); + if (netif_running(pdata->netdev)) { + fxgmac_restart_dev(pdata); + } + break; + + /* ioctl diag tx pkt */ + case FXGMAC_DFS_IOCTL_DIAG_TX_PKT: + fxgmac_dbg_tx_pkt(pdata, buf); + break; + + /* ioctl diag rx pkt */ + case FXGMAC_DFS_IOCTL_DIAG_RX_PKT: + fxgmac_dbg_rx_pkt(pdata, buf); + break; + + /* ioctl device reset */ + case FXGMAC_DFS_IOCTL_DEVICE_RESET: + DPRINTK("Debugfs received device reset command.\n"); + if (netif_running(pdata->netdev)) { + fxgmac_restart_dev(pdata); + } + break; + + case FXGMAC_EFUSE_LED_TEST: + DPRINTK("Debugfs received device led test command.\n"); + memcpy(&pdata->led, data, sizeof(struct led_setting)); + fxgmac_restart_dev(pdata); + break; + + case FXGMAC_EFUSE_UPDATE_LED_CFG: + DPRINTK("Debugfs received device led update command.\n"); + memcpy(&pdata->ledconfig, data, + sizeof(struct led_setting)); + ret = hw_ops->write_led_config(pdata); + hw_ops->read_led_config(pdata); + hw_ops->led_under_active(pdata); + break; + + case FXGMAC_EFUSE_WRITE_LED: + memcpy(&ex_data, data, sizeof(CMD_DATA)); + DPRINTK("FXGMAC_EFUSE_WRITE_LED, val = 0x%x\n", + ex_data.val0); + ret = hw_ops->write_led(pdata, ex_data.val0); + break; + + case FXGMAC_EFUSE_WRITE_OOB: + DPRINTK("FXGMAC_EFUSE_WRITE_OOB.\n"); + ret = hw_ops->write_oob(pdata); + break; + + case FXGMAC_EFUSE_READ_REGIONABC: + memcpy(&ex_data, data, sizeof(CMD_DATA)); + ret = hw_ops->read_efuse_data(pdata, ex_data.val0, + &ex_data.val1); + /* + * DPRINTK("FXGMAC_EFUSE_READ_REGIONABC, address = 0x%x, val = 0x%x\n", + * ex_data.val0, + * ex_data.val1); + */ + if (ret) { + memcpy(data, &ex_data, sizeof(CMD_DATA)); + out_total_size = + ioctl_cmd_size + sizeof(CMD_DATA); + if (copy_to_user((void *)arg, (void *)buf, + out_total_size)) + goto err; + } + break; + + case FXGMAC_EFUSE_WRITE_PATCH_REG: + memcpy(&ex_data, data, sizeof(CMD_DATA)); + /* + * DPRINTK("FXGMAC_EFUSE_WRITE_PATCH_REG, address = 0x%x, val = 0x%x\n", + * ex_data.val0, + * ex_data.val1); + */ + ret = hw_ops->write_patch_to_efuse(pdata, ex_data.val0, + ex_data.val1); + break; + + case FXGMAC_EFUSE_READ_PATCH_REG: + memcpy(&ex_data, data, sizeof(CMD_DATA)); + ret = hw_ops->read_patch_from_efuse(pdata, ex_data.val0, + &ex_data.val1); + /* + * DPRINTK("FXGMAC_EFUSE_READ_PATCH_REG, address = 0x%x, val = 0x%x\n", + * ex_data.val0, ex_data.val1); + */ + if (ret) { + memcpy(data, &ex_data, sizeof(CMD_DATA)); + out_total_size = + ioctl_cmd_size + sizeof(CMD_DATA); + if (copy_to_user((void *)arg, (void *)buf, + out_total_size)) + goto err; + } + break; + + case FXGMAC_EFUSE_WRITE_PATCH_PER_INDEX: + memcpy(&ex_data, data, sizeof(CMD_DATA)); + ret = hw_ops->write_patch_to_efuse_per_index( + pdata, ex_data.val0, ex_data.val1, + ex_data.val2); + /* + * DPRINTK("FXGMAC_EFUSE_WRITE_PATCH_PER_INDEX, index = %d, address = 0x%x, val = 0x%x\n", + * ex_data.val0, ex_data.val1, ex_data.val2); + */ + break; + + case FXGMAC_EFUSE_READ_PATCH_PER_INDEX: + memcpy(&ex_data, data, sizeof(CMD_DATA)); + ret = hw_ops->read_patch_from_efuse_per_index( + pdata, ex_data.val0, &ex_data.val1, + &ex_data.val2); + /* + * DPRINTK("FXGMAC_EFUSE_READ_PATCH_PER_INDEX, address = 0x%x, val = 0x%x\n", + * ex_data.val1, ex_data.val2); + */ + if (ret) { + memcpy(data, &ex_data, sizeof(CMD_DATA)); + out_total_size = + ioctl_cmd_size + sizeof(CMD_DATA); + if (copy_to_user((void *)arg, (void *)buf, + out_total_size)) + goto err; + } + break; + + case FXGMAC_EFUSE_LOAD: + DPRINTK("FXGMAC_EFUSE_LOAD.\n"); + ret = hw_ops->efuse_load(pdata); + break; + + case FXGMAC_GET_MAC_DATA: + ret = hw_ops->read_mac_subsys_from_efuse(pdata, mac, + NULL, NULL); + if (ret) { + memcpy(data, mac, ETH_ALEN); + out_total_size = ioctl_cmd_size + ETH_ALEN; + if (copy_to_user((void *)arg, (void *)buf, + out_total_size)) + goto err; + } + break; + + case FXGMAC_SET_MAC_DATA: + if (in_data_size != ETH_ALEN) + goto err; + memcpy(mac, data, ETH_ALEN); + ret = hw_ops->write_mac_subsys_to_efuse(pdata, mac, + NULL, NULL); + if (ret) { + eth_hw_addr_set(pdata->netdev, mac); + memcpy(pdata->mac_addr, mac, ETH_ALEN); + hw_ops->set_mac_address(pdata, mac); + hw_ops->set_mac_hash(pdata); + } + break; + + case FXGMAC_GET_SUBSYS_ID: + memcpy(&ex_data, data, sizeof(CMD_DATA)); + ret = hw_ops->read_mac_subsys_from_efuse( + pdata, NULL, &ex_data.val0, NULL); + if (ret) { + ex_data.val1 = 0xFFFF; + memcpy(data, &ex_data, sizeof(CMD_DATA)); + out_total_size = + ioctl_cmd_size + sizeof(CMD_DATA); + if (copy_to_user((void *)arg, (void *)buf, + out_total_size)) + goto err; + } + break; + + case FXGMAC_SET_SUBSYS_ID: + memcpy(&ex_data, data, sizeof(CMD_DATA)); + ret = hw_ops->write_mac_subsys_to_efuse( + pdata, NULL, &ex_data.val0, NULL); + break; + + case FXGMAC_GET_REG: + memcpy(&ex_data, data, sizeof(CMD_DATA)); + ex_data.val1 = hw_ops->get_gmac_register( + pdata, (u8 *)(pdata->base_mem + ex_data.val0)); + memcpy(data, &ex_data, sizeof(CMD_DATA)); + out_total_size = ioctl_cmd_size + sizeof(CMD_DATA); + if (copy_to_user((void *)arg, (void *)buf, + out_total_size)) + goto err; + break; + + case FXGMAC_SET_REG: + memcpy(&ex_data, data, sizeof(CMD_DATA)); + regval = hw_ops->set_gmac_register( + pdata, (u8 *)(pdata->base_mem + ex_data.val0), + ex_data.val1); + ret = (regval == 0 ? true : false); + break; + + case FXGMAC_GET_PHY_REG: + memcpy(&ex_data, data, sizeof(CMD_DATA)); + regval = hw_ops->read_ephy_reg(pdata, ex_data.val0, + &ex_data.val1); + if (regval != -1) { + memcpy(data, &ex_data, sizeof(CMD_DATA)); + out_total_size = + ioctl_cmd_size + sizeof(CMD_DATA); + if (copy_to_user((void *)arg, (void *)buf, + out_total_size)) + goto err; + } + ret = (regval == -1 ? false : true); + break; + + case FXGMAC_SET_PHY_REG: + memcpy(&ex_data, data, sizeof(CMD_DATA)); + regval = hw_ops->write_ephy_reg(pdata, ex_data.val0, + ex_data.val1); + ret = (regval == 0 ? true : false); + break; + + case FXGMAC_GET_PCIE_LOCATION: + ex_data.val0 = pdata->pdev->bus->number; + ex_data.val1 = PCI_SLOT(pdata->pdev->devfn); + ex_data.val2 = PCI_FUNC(pdata->pdev->devfn); + memcpy(data, &ex_data, sizeof(CMD_DATA)); + out_total_size = ioctl_cmd_size + sizeof(CMD_DATA); + if (copy_to_user((void *)arg, (void *)buf, + out_total_size)) + goto err; + break; + + case FXGMAC_GET_GSO_SIZE: + ex_data.val0 = pdata->netdev->gso_max_size; + memcpy(data, &ex_data, sizeof(CMD_DATA)); + out_total_size = ioctl_cmd_size + sizeof(CMD_DATA); + if (copy_to_user((void *)arg, (void *)buf, + out_total_size)) + goto err; + break; + + case FXGMAC_SET_GSO_SIZE: + memcpy(&ex_data, data, sizeof(CMD_DATA)); + pdata->netdev->gso_max_size = ex_data.val0; + break; + + case FXGMAC_SET_RX_MODERATION: + memcpy(&ex_data, data, sizeof(CMD_DATA)); + regval = readreg(pdata->pAdapter, + pdata->base_mem + INT_MOD); + regval = FXGMAC_SET_REG_BITS(regval, INT_MOD_RX_POS, + INT_MOD_RX_LEN, + ex_data.val0); + writereg(pdata->pAdapter, regval, + pdata->base_mem + INT_MOD); + break; + + case FXGMAC_SET_TX_MODERATION: + memcpy(&ex_data, data, sizeof(CMD_DATA)); + regval = readreg(pdata->pAdapter, + pdata->base_mem + INT_MOD); + regval = FXGMAC_SET_REG_BITS(regval, INT_MOD_TX_POS, + INT_MOD_TX_LEN, + ex_data.val0); + writereg(pdata->pAdapter, regval, + pdata->base_mem + INT_MOD); + break; + + case FXGMAC_GET_TXRX_MODERATION: + regval = readreg(pdata->pAdapter, + pdata->base_mem + INT_MOD); + ex_data.val0 = FXGMAC_GET_REG_BITS( + regval, INT_MOD_RX_POS, INT_MOD_RX_LEN); + ex_data.val1 = FXGMAC_GET_REG_BITS( + regval, INT_MOD_TX_POS, INT_MOD_TX_LEN); + memcpy(data, &ex_data, sizeof(CMD_DATA)); + out_total_size = ioctl_cmd_size + sizeof(CMD_DATA); + if (copy_to_user((void *)arg, (void *)buf, + out_total_size)) + goto err; + break; + + default: + DPRINTK("Debugfs received invalid command: %x.\n", + pcmd.cmd_type); + ret = false; + break; + } + } + + if (buf) + kfree(buf); + return ret ? FXGMAC_SUCCESS : FXGMAC_FAIL; + +err: + if (buf) + kfree(buf); + return FXGMAC_FAIL; +} diff --git a/drivers/net/ethernet/motorcomm/yt6801/fuxi-gmac-net.c b/drivers/net/ethernet/motorcomm/yt6801/fuxi-gmac-net.c new file mode 100644 index 000000000000..d44999072574 --- /dev/null +++ b/drivers/net/ethernet/motorcomm/yt6801/fuxi-gmac-net.c @@ -0,0 +1,3052 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (c) 2021 Motorcomm Corporation. */ + +#include "fuxi-gmac.h" +#include "fuxi-gmac-reg.h" + +static int fxgmac_one_poll_rx(struct napi_struct *, int); +static int fxgmac_one_poll_tx(struct napi_struct *, int); +static int fxgmac_all_poll(struct napi_struct *, int); +static int fxgmac_dev_read(struct fxgmac_channel *channel); + +void fxgmac_lock(struct fxgmac_pdata *pdata) +{ + mutex_lock(&pdata->expansion.mutex); +} + +void fxgmac_unlock(struct fxgmac_pdata *pdata) +{ + mutex_unlock(&pdata->expansion.mutex); +} + +#ifdef FXGMAC_ESD_CHECK_ENABLED +static void fxgmac_schedule_esd_work(struct fxgmac_pdata *pdata) +{ + set_bit(FXGMAC_FLAG_TASK_ESD_CHECK_PENDING, + pdata->expansion.task_flags); + schedule_delayed_work(&pdata->expansion.esd_work, FXGMAC_ESD_INTERVAL); +} + +static void fxgmac_update_esd_stats(struct fxgmac_pdata *pdata) +{ + u32 value; + + value = readreg(pdata->pAdapter, + pdata->mac_regs + MMC_TXEXCESSIVECOLLSIONFRAMES); + pdata->expansion.esd_stats.tx_abort_excess_collisions += value; + + value = readreg(pdata->pAdapter, + pdata->mac_regs + MMC_TXUNDERFLOWERROR_LO); + pdata->expansion.esd_stats.tx_dma_underrun += value; + + value = readreg(pdata->pAdapter, + pdata->mac_regs + MMC_TXCARRIERERRORFRAMES); + pdata->expansion.esd_stats.tx_lost_crs += value; + + value = readreg(pdata->pAdapter, + pdata->mac_regs + MMC_TXLATECOLLISIONFRAMES); + pdata->expansion.esd_stats.tx_late_collisions += value; + + value = readreg(pdata->pAdapter, pdata->mac_regs + MMC_RXCRCERROR_LO); + pdata->expansion.esd_stats.rx_crc_errors += value; + + value = readreg(pdata->pAdapter, pdata->mac_regs + MMC_RXALIGNERROR); + pdata->expansion.esd_stats.rx_align_errors += value; + + value = readreg(pdata->pAdapter, pdata->mac_regs + MMC_RXRUNTERROR); + pdata->expansion.esd_stats.rx_runt_errors += value; + + value = readreg(pdata->pAdapter, + pdata->mac_regs + MMC_TXSINGLECOLLISION_G); + pdata->expansion.esd_stats.single_collisions += value; + + value = readreg(pdata->pAdapter, + pdata->mac_regs + MMC_TXMULTIPLECOLLISION_G); + pdata->expansion.esd_stats.multi_collisions += value; + + value = readreg(pdata->pAdapter, + pdata->mac_regs + MMC_TXDEFERREDFRAMES); + pdata->expansion.esd_stats.tx_deferred_frames += value; +} + +static void fxgmac_check_esd_work(struct fxgmac_pdata *pdata) +{ + FXGMAC_ESD_STATS *stats = &pdata->expansion.esd_stats; + int i = 0; + u32 regval; + + /* ESD test will make recv crc errors more than 4,294,967,xxx in one second. */ + if (stats->rx_crc_errors > FXGMAC_ESD_ERROR_THRESHOLD || + stats->rx_align_errors > FXGMAC_ESD_ERROR_THRESHOLD || + stats->rx_runt_errors > FXGMAC_ESD_ERROR_THRESHOLD || + stats->tx_abort_excess_collisions > FXGMAC_ESD_ERROR_THRESHOLD || + stats->tx_dma_underrun > FXGMAC_ESD_ERROR_THRESHOLD || + stats->tx_lost_crs > FXGMAC_ESD_ERROR_THRESHOLD || + stats->tx_late_collisions > FXGMAC_ESD_ERROR_THRESHOLD || + stats->single_collisions > FXGMAC_ESD_ERROR_THRESHOLD || + stats->multi_collisions > FXGMAC_ESD_ERROR_THRESHOLD || + stats->tx_deferred_frames > FXGMAC_ESD_ERROR_THRESHOLD) { + dev_info(pdata->dev, "%s - Error:\n", __func__); + dev_info(pdata->dev, "rx_crc_errors %ul.\n", + stats->rx_crc_errors); + dev_info(pdata->dev, "rx_align_errors %ul.\n", + stats->rx_align_errors); + dev_info(pdata->dev, "rx_runt_errors %ul.\n", + stats->rx_runt_errors); + dev_info(pdata->dev, "tx_abort_excess_collisions %ul.\n", + stats->tx_abort_excess_collisions); + dev_info(pdata->dev, "tx_dma_underrun %ul.\n", + stats->tx_dma_underrun); + dev_info(pdata->dev, "tx_lost_crs %ul.\n", stats->tx_lost_crs); + dev_info(pdata->dev, "tx_late_collisions %ul.\n", + stats->tx_late_collisions); + dev_info(pdata->dev, "single_collisions %ul.\n", + stats->single_collisions); + dev_info(pdata->dev, "multi_collisions %ul.\n", + stats->multi_collisions); + dev_info(pdata->dev, "tx_deferred_frames %ul.\n", + stats->tx_deferred_frames); + + dev_info(pdata->dev, "esd error triggered, restart NIC...\n"); + cfg_r32(pdata, REG_PCI_COMMAND, ®val); + while ((regval == FXGMAC_PCIE_LINK_DOWN) && + (i++ < FXGMAC_PCIE_RECOVER_TIMES)) { + usleep_range_ex(pdata->pAdapter, 200, 200); + cfg_r32(pdata, REG_PCI_COMMAND, ®val); + dev_info(pdata->dev, + "pcie recovery link cost %d(200us)\n", i); + } + + if (regval == FXGMAC_PCIE_LINK_DOWN) { + dev_info(pdata->dev, + "pcie link down, recovery failed.\n"); + return; + } + + if (regval & FXGMAC_PCIE_IO_MEM_MASTER_ENABLE) { + pdata->hw_ops.esd_restore_pcie_cfg(pdata); + cfg_r32(pdata, REG_PCI_COMMAND, ®val); + dev_info(pdata->dev, + "pci command reg is %x after restoration.\n", + regval); + fxgmac_restart_dev(pdata); + } + } + + memset(stats, 0, sizeof(FXGMAC_ESD_STATS)); +} + +static void fxgmac_esd_work(struct work_struct *work) +{ + struct fxgmac_pdata *pdata = container_of(work, struct fxgmac_pdata, + expansion.esd_work.work); + + rtnl_lock(); + if (!netif_running(pdata->netdev) || + !test_and_clear_bit(FXGMAC_FLAG_TASK_ESD_CHECK_PENDING, + pdata->expansion.task_flags)) + goto out_unlock; + + fxgmac_update_esd_stats(pdata); + fxgmac_check_esd_work(pdata); + fxgmac_schedule_esd_work(pdata); + +out_unlock: + rtnl_unlock(); +} + +static void fxgmac_cancel_esd_work(struct fxgmac_pdata *pdata) +{ + struct work_struct *work = &pdata->expansion.esd_work.work; + + if (!work->func) { + dev_info(pdata->dev, "work func is NULL.\n"); + return; + } + + cancel_delayed_work_sync(&pdata->expansion.esd_work); +} +#endif + +unsigned int fxgmac_get_netdev_ip4addr(struct fxgmac_pdata *pdata) +{ + struct net_device *netdev = pdata->netdev; + struct in_ifaddr *ifa; + unsigned int ipval = 0xc0a801ca; + + rcu_read_lock(); + /* we only get the first IPv4 addr. */ + ifa = rcu_dereference(netdev->ip_ptr->ifa_list); + if (ifa) { + /* binary ipv4 addr with __be */ + ipval = (unsigned int)ifa->ifa_address; + + DPRINTK("%s, netdev %s IPv4 address %pI4, mask: %pI4\n", + __FUNCTION__, ifa->ifa_label, &ifa->ifa_address, + &ifa->ifa_mask); + } + + rcu_read_unlock(); + + return ipval; +} + +unsigned char *fxgmac_get_netdev_ip6addr(struct fxgmac_pdata *pdata, + unsigned char *ipval, + unsigned char *ip6addr_solicited, + unsigned int ifa_flag) +{ + struct net_device *netdev = pdata->netdev; + struct inet6_dev *i6dev; + struct inet6_ifaddr *ifp; + unsigned char local_ipval[16] = { 0 }; + unsigned char solicited_ipval[16] = { 0 }; + struct in6_addr *addr_ip6 = (struct in6_addr *)local_ipval; + struct in6_addr *addr_ip6_solicited = + (struct in6_addr *)solicited_ipval; + int err = -EADDRNOTAVAIL; + + if (ipval) { + addr_ip6 = (struct in6_addr *)ipval; + } + + if (ip6addr_solicited) { + addr_ip6_solicited = (struct in6_addr *)ip6addr_solicited; + } + + in6_pton("fe80::4808:8ffb:d93e:d753", -1, (u8 *)addr_ip6, -1, NULL); + + if (ifa_flag & FXGMAC_NS_IFA_GLOBAL_UNICAST) + DPRINTK("%s FXGMAC_NS_IFA_GLOBAL_UNICAST is set, %x\n", + __FUNCTION__, ifa_flag); + + if (ifa_flag & FXGMAC_NS_IFA_LOCAL_LINK) + DPRINTK("%s FXGMAC_NS_IFA_LOCAL_LINK is set, %x\n", + __FUNCTION__, ifa_flag); + + rcu_read_lock(); + i6dev = __in6_dev_get(netdev); + if (i6dev != NULL) { + read_lock_bh(&i6dev->lock); + list_for_each_entry(ifp, &i6dev->addr_list, if_list) { + /* here we need only the ll addr, use scope to filter out it. */ + if (((ifa_flag & FXGMAC_NS_IFA_GLOBAL_UNICAST) && (ifp->scope != IFA_LINK)) || ((ifa_flag & FXGMAC_NS_IFA_LOCAL_LINK) && (ifp->scope == IFA_LINK)/* && + !(ifp->flags & IFA_F_TENTATIVE)*/)) { + memcpy(addr_ip6, &ifp->addr, 16); + addrconf_addr_solict_mult(addr_ip6, + addr_ip6_solicited); + err = 0; + + break; + } + } + read_unlock_bh(&i6dev->lock); + } + rcu_read_unlock(); + + if (err) + DPRINTK("%s get ipv6 addr failed, use default.\n", + __FUNCTION__); + + return (err ? NULL : ipval); +} + +inline unsigned int fxgmac_tx_avail_desc(struct fxgmac_ring *ring) +{ + unsigned int avail; + + if (ring->dirty > ring->cur) + avail = ring->dirty - ring->cur; + else + avail = ring->dma_desc_count - ring->cur + ring->dirty; + + return avail; +} + +inline unsigned int fxgmac_rx_dirty_desc(struct fxgmac_ring *ring) +{ + unsigned int dirty; + + if (ring->dirty <= ring->cur) + dirty = ring->cur - ring->dirty; + else + dirty = ring->dma_desc_count - ring->dirty + ring->cur; + + return dirty; +} + +static netdev_tx_t fxgmac_maybe_stop_tx_queue(struct fxgmac_channel *channel, + struct fxgmac_ring *ring, + unsigned int count) +{ + struct fxgmac_pdata *pdata = channel->pdata; + + if (count > fxgmac_tx_avail_desc(ring)) { + if (netif_msg_tx_done(pdata)) { + netif_info( + pdata, drv, pdata->netdev, + "Tx queue stopped, not enough descriptors available\n"); + } + + /* Avoid wrongly optimistic queue wake-up: tx poll thread must + * not miss a ring update when it notices a stopped queue. + */ + smp_wmb(); + netif_stop_subqueue(pdata->netdev, channel->queue_index); + ring->tx.queue_stopped = 1; + + /* Sync with tx poll: + * - publish queue status and cur ring index (write barrier) + * - refresh dirty ring index (read barrier). + * May the current thread have a pessimistic view of the ring + * status and forget to wake up queue, a racing tx poll thread + * can't. + */ + smp_mb(); + if (count <= fxgmac_tx_avail_desc(ring)) { + ring->tx.queue_stopped = 0; + netif_start_subqueue(pdata->netdev, + channel->queue_index); + fxgmac_tx_start_xmit(channel, ring); + } else { + /* If we haven't notified the hardware because of xmit_more + * support, tell it now + */ + if (ring->tx.xmit_more) + fxgmac_tx_start_xmit(channel, ring); + if (netif_msg_tx_done(pdata)) + DPRINTK("about stop tx q, ret BUSY\n"); + return NETDEV_TX_BUSY; + } + } + + return NETDEV_TX_OK; +} + +static void fxgmac_prep_vlan(struct sk_buff *skb, + struct fxgmac_pkt_info *pkt_info) +{ + if (skb_vlan_tag_present(skb)) + pkt_info->vlan_ctag = skb_vlan_tag_get(skb); +} + +static int fxgmac_prep_tso(struct fxgmac_pdata *pdata, struct sk_buff *skb, + struct fxgmac_pkt_info *pkt_info) +{ + int ret; + + if (!FXGMAC_GET_REG_BITS(pkt_info->attributes, + TX_PACKET_ATTRIBUTES_TSO_ENABLE_POS, + TX_PACKET_ATTRIBUTES_TSO_ENABLE_LEN)) + return 0; + + ret = skb_cow_head(skb, 0); + if (ret) + return ret; + + pkt_info->header_len = skb_transport_offset(skb) + tcp_hdrlen(skb); + pkt_info->tcp_header_len = tcp_hdrlen(skb); + pkt_info->tcp_payload_len = skb->len - pkt_info->header_len; + pkt_info->mss = skb_shinfo(skb)->gso_size; + + if (netif_msg_tx_done(pdata)) { + DPRINTK("header_len=%u\n", pkt_info->header_len); + DPRINTK("tcp_header_len=%u, tcp_payload_len=%u\n", + pkt_info->tcp_header_len, pkt_info->tcp_payload_len); + DPRINTK("mss=%u\n", pkt_info->mss); + } + /* Update the number of packets that will ultimately be transmitted + * along with the extra bytes for each extra packet + */ + pkt_info->tx_packets = skb_shinfo(skb)->gso_segs; + pkt_info->tx_bytes += (pkt_info->tx_packets - 1) * pkt_info->header_len; + + return 0; +} + +static int fxgmac_is_tso(struct sk_buff *skb) +{ + if (skb->ip_summed != CHECKSUM_PARTIAL) + return 0; + + if (!skb_is_gso(skb)) + return 0; + + return 1; +} + +static void fxgmac_prep_tx_pkt(struct fxgmac_pdata *pdata, + struct fxgmac_ring *ring, struct sk_buff *skb, + struct fxgmac_pkt_info *pkt_info) +{ + skb_frag_t *frag; + unsigned int context_desc; + unsigned int len; + unsigned int i; + + pkt_info->skb = skb; + + context_desc = 0; + pkt_info->desc_count = 0; + + pkt_info->tx_packets = 1; + pkt_info->tx_bytes = skb->len; + if (netif_msg_tx_done(pdata)) + DPRINTK("fxgmac_prep_tx_pkt callin,pkt desc cnt=%d,skb len=%d, skbheadlen=%d\n", + pkt_info->desc_count, skb->len, skb_headlen(skb)); + + if (fxgmac_is_tso(skb)) { + /* TSO requires an extra descriptor if mss is different */ + if (skb_shinfo(skb)->gso_size != ring->tx.cur_mss) { + context_desc = 1; + pkt_info->desc_count++; + } + if (netif_msg_tx_done(pdata)) + DPRINTK("fxgmac_is_tso=%d, ip_summed=%d,skb gso=%d\n", + ((skb->ip_summed == CHECKSUM_PARTIAL) && + (skb_is_gso(skb))) ? + 1 : + 0, + skb->ip_summed, skb_is_gso(skb) ? 1 : 0); + + /* TSO requires an extra descriptor for TSO header */ + pkt_info->desc_count++; + + pkt_info->attributes = FXGMAC_SET_REG_BITS( + pkt_info->attributes, + TX_PACKET_ATTRIBUTES_TSO_ENABLE_POS, + TX_PACKET_ATTRIBUTES_TSO_ENABLE_LEN, 1); + pkt_info->attributes = FXGMAC_SET_REG_BITS( + pkt_info->attributes, + TX_PACKET_ATTRIBUTES_CSUM_ENABLE_POS, + TX_PACKET_ATTRIBUTES_CSUM_ENABLE_LEN, 1); + if (netif_msg_tx_done(pdata)) + DPRINTK("fxgmac_prep_tx_pkt,tso, pkt desc cnt=%d\n", + pkt_info->desc_count); + } else if (skb->ip_summed == CHECKSUM_PARTIAL) + pkt_info->attributes = FXGMAC_SET_REG_BITS( + pkt_info->attributes, + TX_PACKET_ATTRIBUTES_CSUM_ENABLE_POS, + TX_PACKET_ATTRIBUTES_CSUM_ENABLE_LEN, 1); + + if (skb_vlan_tag_present(skb)) { + /* VLAN requires an extra descriptor if tag is different */ + if (skb_vlan_tag_get(skb) != ring->tx.cur_vlan_ctag) + /* We can share with the TSO context descriptor */ + if (!context_desc) { + context_desc = 1; + pkt_info->desc_count++; + } + + pkt_info->attributes = FXGMAC_SET_REG_BITS( + pkt_info->attributes, + TX_PACKET_ATTRIBUTES_VLAN_CTAG_POS, + TX_PACKET_ATTRIBUTES_VLAN_CTAG_LEN, 1); + if (netif_msg_tx_done(pdata)) + DPRINTK("fxgmac_prep_tx_pkt,VLAN, pkt desc cnt=%d,vlan=0x%04x\n", + pkt_info->desc_count, skb_vlan_tag_get(skb)); + } + + for (len = skb_headlen(skb); len;) { + pkt_info->desc_count++; + len -= min_t(unsigned int, len, FXGMAC_TX_MAX_BUF_SIZE); + } + + for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) { + frag = &skb_shinfo(skb)->frags[i]; + for (len = skb_frag_size(frag); len;) { + pkt_info->desc_count++; + len -= min_t(unsigned int, len, FXGMAC_TX_MAX_BUF_SIZE); + } + } + if (netif_msg_tx_done(pdata)) + DPRINTK("fxgmac_prep_tx_pkt callout,pkt desc cnt=%d,skb len=%d, skbheadlen=%d,frags=%d\n", + pkt_info->desc_count, skb->len, skb_headlen(skb), + skb_shinfo(skb)->nr_frags); +} + +static int fxgmac_calc_rx_buf_size(struct net_device *netdev, unsigned int mtu) +{ + unsigned int rx_buf_size; + unsigned int max_mtu; + + /* On the Linux platform, the MTU size does not include the length + * of the MAC address and the length of the Type, but FXGMAC_JUMBO_PACKET_MTU include them. + */ + max_mtu = FXGMAC_JUMBO_PACKET_MTU - ETH_HLEN; + if (mtu > max_mtu) { + netdev_alert(netdev, "MTU exceeds maximum supported value\n"); + return -EINVAL; + } + + rx_buf_size = mtu + ETH_HLEN + ETH_FCS_LEN + VLAN_HLEN; + rx_buf_size = + clamp_val(rx_buf_size, FXGMAC_RX_MIN_BUF_SIZE, + PAGE_SIZE * 4 /* follow yonggang's suggestion */); + + rx_buf_size = (rx_buf_size + FXGMAC_RX_BUF_ALIGN - 1) & + ~(FXGMAC_RX_BUF_ALIGN - 1); + + return rx_buf_size; +} + +static void fxgmac_enable_rx_tx_ints(struct fxgmac_pdata *pdata) +{ + struct fxgmac_hw_ops *hw_ops = &pdata->hw_ops; + struct fxgmac_channel *channel; + enum fxgmac_int int_id; + unsigned int i; + + channel = pdata->channel_head; + for (i = 0; i < pdata->channel_count; i++, channel++) { + if (channel->tx_ring && channel->rx_ring) + int_id = FXGMAC_INT_DMA_CH_SR_TI_RI; + else if (channel->tx_ring) + int_id = FXGMAC_INT_DMA_CH_SR_TI; + else if (channel->rx_ring) + int_id = FXGMAC_INT_DMA_CH_SR_RI; + else + continue; + + hw_ops->enable_int(channel, int_id); + } +} + +static int fxgmac_misc_poll(struct napi_struct *napi, int budget) +{ + struct fxgmac_pdata *pdata = + container_of(napi, struct fxgmac_pdata, expansion.napi_misc); + struct fxgmac_hw_ops *hw_ops = &pdata->hw_ops; + + if (napi_complete_done(napi, 0)) + hw_ops->enable_msix_one_interrupt(pdata, MSI_ID_PHY_OTHER); + return 0; +} + +static irqreturn_t fxgmac_misc_isr(int irq, void *data) +{ + struct fxgmac_pdata *pdata = data; + struct fxgmac_hw_ops *hw_ops = &pdata->hw_ops; + u32 regval; + + regval = readreg(pdata->pAdapter, pdata->base_mem + MGMT_INT_CTRL0); + if (!(regval & MGMT_INT_CTRL0_INT_STATUS_MISC)) + return IRQ_HANDLED; + + hw_ops->disable_msix_one_interrupt(pdata, MSI_ID_PHY_OTHER); + hw_ops->clear_misc_int_status(pdata); + + napi_schedule_irqoff(&pdata->expansion.napi_misc); + + return IRQ_HANDLED; +} + +static irqreturn_t fxgmac_isr(int irq, void *data) +{ + unsigned int dma_ch_isr; + struct fxgmac_pdata *pdata = data; + struct fxgmac_channel *channel; + struct fxgmac_hw_ops *hw_ops = &pdata->hw_ops; + unsigned int i; + u32 val; + + val = readreg(pdata->pAdapter, pdata->base_mem + MGMT_INT_CTRL0); + if (!(val & MGMT_INT_CTRL0_INT_STATUS_RXTXMISC_MASK)) + return IRQ_HANDLED; + + hw_ops->disable_mgm_interrupt(pdata); + pdata->expansion.mgm_intctrl_val = val; + + pdata->stats.mgmt_int_isr++; + + for (i = 0; i < pdata->channel_count; i++) { + channel = pdata->channel_head + i; + + dma_ch_isr = readl(FXGMAC_DMA_REG(channel, DMA_CH_SR)); + + if (FXGMAC_GET_REG_BITS(dma_ch_isr, DMA_CH_SR_TPS_POS, + DMA_CH_SR_TPS_LEN)) + pdata->stats.tx_process_stopped++; + + if (FXGMAC_GET_REG_BITS(dma_ch_isr, DMA_CH_SR_RPS_POS, + DMA_CH_SR_RPS_LEN)) + pdata->stats.rx_process_stopped++; + + if (FXGMAC_GET_REG_BITS(dma_ch_isr, DMA_CH_SR_TBU_POS, + DMA_CH_SR_TBU_LEN)) + pdata->stats.tx_buffer_unavailable++; + + if (FXGMAC_GET_REG_BITS(dma_ch_isr, DMA_CH_SR_RBU_POS, + DMA_CH_SR_RBU_LEN)) + pdata->stats.rx_buffer_unavailable++; + + /* Restart the device on a Fatal Bus Error */ + if (FXGMAC_GET_REG_BITS(dma_ch_isr, DMA_CH_SR_FBE_POS, + DMA_CH_SR_FBE_LEN)) { + pdata->stats.fatal_bus_error++; + schedule_work(&pdata->expansion.restart_work); + } + + /* Clear all interrupt signals */ + writel(dma_ch_isr, FXGMAC_DMA_REG(channel, DMA_CH_SR)); + } + + if (pdata->expansion.mgm_intctrl_val & MGMT_INT_CTRL0_INT_STATUS_MISC) + hw_ops->clear_misc_int_status(pdata); + + if (napi_schedule_prep(&pdata->expansion.napi)) { + pdata->stats.napi_poll_isr++; + /* Turn on polling */ + __napi_schedule_irqoff(&pdata->expansion.napi); + } + + return IRQ_HANDLED; +} + +static irqreturn_t fxgmac_dma_isr(int irq, void *data) +{ + struct fxgmac_channel *channel = data; + struct fxgmac_pdata *pdata = channel->pdata; + struct fxgmac_hw_ops *hw_ops = &pdata->hw_ops; + u32 regval; + int message_id; + + if (irq == channel->expansion.dma_irq_tx) { + message_id = MSI_ID_TXQ0; + hw_ops->disable_msix_one_interrupt(pdata, message_id); + regval = 0; + regval = FXGMAC_SET_REG_BITS(regval, DMA_CH_SR_TI_POS, + DMA_CH_SR_TI_LEN, 1); + writereg(pdata->pAdapter, regval, + FXGMAC_DMA_REG(channel, DMA_CH_SR)); + napi_schedule_irqoff(&channel->expansion.napi_tx); + } else { + message_id = channel->queue_index; + hw_ops->disable_msix_one_interrupt(pdata, message_id); + regval = 0; + regval = readreg(pdata->pAdapter, + FXGMAC_DMA_REG(channel, DMA_CH_SR)); + regval = FXGMAC_SET_REG_BITS(regval, DMA_CH_SR_RI_POS, + DMA_CH_SR_RI_LEN, 1); + writereg(pdata->pAdapter, regval, + FXGMAC_DMA_REG(channel, DMA_CH_SR)); + napi_schedule_irqoff(&channel->expansion.napi_rx); + } + + return IRQ_HANDLED; +} + +#if FXGMAC_TX_HANG_TIMER_ENABLED +static void fxgmac_tx_hang_timer_handler(struct timer_list *t) +{ + struct fxgmac_channel *channel = + from_timer(channel, t, expansion.tx_hang_timer); +#if FXGMAC_TX_HANG_CHECH_DIRTY + struct fxgmac_ring *ring = channel->tx_ring; +#endif + struct fxgmac_pdata *pdata = channel->pdata; + struct net_device *netdev = pdata->netdev; + unsigned int hw_reg_cur; + unsigned int regval; + +#if FXGMAC_TX_HANG_CHECH_DIRTY + hw_reg_cur = ring->dirty; +#else + hw_reg_cur = readl( + FXGMAC_DMA_REG(channel, 0x44 /* tx desc curr pointer reg */)); +#endif + if (hw_reg_cur == channel->expansion.tx_hang_hw_cur) { + /* hw current desc still stucked */ + if (!pdata->tx_hang_restart_queuing) { + pdata->tx_hang_restart_queuing = 1; + DPRINTK("tx_hang_timer_handler: restart scheduled, at desc %u, queuing=%u.\n", + channel->expansion.tx_hang_hw_cur, + pdata->tx_hang_restart_queuing); + + netif_tx_stop_all_queues(netdev); + + /* Disable MAC Rx */ + regval = readl(pdata->mac_regs + MAC_CR); + regval = FXGMAC_SET_REG_BITS(regval, MAC_CR_CST_POS, + MAC_CR_CST_LEN, 0); + regval = FXGMAC_SET_REG_BITS(regval, MAC_CR_ACS_POS, + MAC_CR_ACS_LEN, 0); + regval = FXGMAC_SET_REG_BITS(regval, MAC_CR_RE_POS, + MAC_CR_RE_LEN, 0); + writel(regval, pdata->mac_regs + MAC_CR); + + schedule_work(&pdata->expansion.restart_work); + } + } + + channel->expansion.tx_hang_timer_active = 0; +} + +static void fxgmac_tx_hang_timer_start(struct fxgmac_channel *channel) +{ + struct fxgmac_pdata *pdata = channel->pdata; + + /* Start the Tx hang timer */ + if (1 && !channel->expansion.tx_hang_timer_active) { + channel->expansion.tx_hang_timer_active = 1; + + /* FXGMAC_INIT_DMA_TX_USECS is desc3 polling period, we give 2 more checking period */ + mod_timer(&channel->expansion.tx_hang_timer, + jiffies + usecs_to_jiffies(FXGMAC_INIT_DMA_TX_USECS * + 10)); + } +} +#endif + +static void fxgmac_napi_enable(struct fxgmac_pdata *pdata, unsigned int add) +{ + struct fxgmac_channel *channel; + unsigned int i; + u32 tx_napi = 0, rx_napi = 0, misc_napi = 0; + + misc_napi = FXGMAC_GET_REG_BITS(pdata->expansion.int_flags, + FXGMAC_FLAG_MISC_NAPI_FREE_POS, + FXGMAC_FLAG_MISC_NAPI_FREE_LEN); + tx_napi = FXGMAC_GET_REG_BITS(pdata->expansion.int_flags, + FXGMAC_FLAG_TX_NAPI_FREE_POS, + FXGMAC_FLAG_TX_NAPI_FREE_LEN); + rx_napi = FXGMAC_GET_REG_BITS(pdata->expansion.int_flags, + FXGMAC_FLAG_RX_NAPI_FREE_POS, + FXGMAC_FLAG_RX_NAPI_FREE_LEN); + + if (pdata->per_channel_irq) { + channel = pdata->channel_head; + for (i = 0; i < pdata->channel_count; i++, channel++) { + if (!FXGMAC_GET_REG_BITS( + rx_napi, i, + FXGMAC_FLAG_PER_CHAN_RX_NAPI_FREE_LEN)) { + if (add) { + netif_napi_add_weight( + pdata->netdev, + &channel->expansion.napi_rx, + fxgmac_one_poll_rx, + NAPI_POLL_WEIGHT); + } + napi_enable(&channel->expansion.napi_rx); + pdata->expansion.int_flags = FXGMAC_SET_REG_BITS( + pdata->expansion.int_flags, + FXGMAC_FLAG_RX_NAPI_FREE_POS + i, + FXGMAC_FLAG_PER_CHAN_RX_NAPI_FREE_LEN, + FXGMAC_NAPI_ENABLE); + } + + if (FXGMAC_IS_CHANNEL_WITH_TX_IRQ(i) && !tx_napi) { + netif_napi_add_weight( + pdata->netdev, + &channel->expansion.napi_tx, + fxgmac_one_poll_tx, NAPI_POLL_WEIGHT); + napi_enable(&channel->expansion.napi_tx); + pdata->expansion.int_flags = + FXGMAC_SET_REG_BITS( + pdata->expansion.int_flags, + FXGMAC_FLAG_TX_NAPI_FREE_POS, + FXGMAC_FLAG_TX_NAPI_FREE_LEN, + FXGMAC_NAPI_ENABLE); + } + if (netif_msg_drv(pdata)) + DPRINTK("napi_enable, msix ch%d napi enabled done,add=%d\n", + i, add); + } + + /* for misc */ + if (!misc_napi) { + netif_napi_add_weight(pdata->netdev, + &pdata->expansion.napi_misc, + fxgmac_misc_poll, + NAPI_POLL_WEIGHT); + napi_enable(&pdata->expansion.napi_misc); + pdata->expansion.int_flags = FXGMAC_SET_REG_BITS( + pdata->expansion.int_flags, + FXGMAC_FLAG_MISC_NAPI_FREE_POS, + FXGMAC_FLAG_MISC_NAPI_FREE_LEN, + FXGMAC_NAPI_ENABLE); + } + } else { + i = FXGMAC_GET_REG_BITS(pdata->expansion.int_flags, + FXGMAC_FLAG_LEGACY_NAPI_FREE_POS, + FXGMAC_FLAG_LEGACY_NAPI_FREE_LEN); + if (!i) { + if (add) { + netif_napi_add_weight(pdata->netdev, + &pdata->expansion.napi, + fxgmac_all_poll, + NAPI_POLL_WEIGHT); + } + + napi_enable(&pdata->expansion.napi); + pdata->expansion.int_flags = FXGMAC_SET_REG_BITS( + pdata->expansion.int_flags, + FXGMAC_FLAG_LEGACY_NAPI_FREE_POS, + FXGMAC_FLAG_LEGACY_NAPI_FREE_LEN, + FXGMAC_NAPI_ENABLE); + } + } +} + +static void fxgmac_napi_disable(struct fxgmac_pdata *pdata, unsigned int del) +{ + struct fxgmac_channel *channel; + unsigned int i; + u32 tx_napi = 0, rx_napi = 0, misc_napi = 0; + + if (pdata->per_channel_irq) { + misc_napi = FXGMAC_GET_REG_BITS(pdata->expansion.int_flags, + FXGMAC_FLAG_MISC_NAPI_FREE_POS, + FXGMAC_FLAG_MISC_NAPI_FREE_LEN); + tx_napi = FXGMAC_GET_REG_BITS(pdata->expansion.int_flags, + FXGMAC_FLAG_TX_NAPI_FREE_POS, + FXGMAC_FLAG_TX_NAPI_FREE_LEN); + rx_napi = FXGMAC_GET_REG_BITS(pdata->expansion.int_flags, + FXGMAC_FLAG_RX_NAPI_FREE_POS, + FXGMAC_FLAG_RX_NAPI_FREE_LEN); + channel = pdata->channel_head; + if (channel != NULL) { + for (i = 0; i < pdata->channel_count; i++, channel++) { + if (FXGMAC_GET_REG_BITS( + rx_napi, i, + FXGMAC_FLAG_PER_CHAN_RX_NAPI_FREE_LEN)) { + napi_disable( + &channel->expansion.napi_rx); + + if (del) { + netif_napi_del( + &channel->expansion + .napi_rx); + } + pdata->expansion + .int_flags = FXGMAC_SET_REG_BITS( + pdata->expansion.int_flags, + FXGMAC_FLAG_RX_NAPI_FREE_POS + + i, + FXGMAC_FLAG_PER_CHAN_RX_NAPI_FREE_LEN, + FXGMAC_NAPI_DISABLE); + } + + if (FXGMAC_IS_CHANNEL_WITH_TX_IRQ(i) && + tx_napi) { + napi_disable( + &channel->expansion.napi_tx); + netif_napi_del( + &channel->expansion.napi_tx); + pdata->expansion + .int_flags = FXGMAC_SET_REG_BITS( + pdata->expansion.int_flags, + FXGMAC_FLAG_TX_NAPI_FREE_POS, + FXGMAC_FLAG_TX_NAPI_FREE_LEN, + FXGMAC_NAPI_DISABLE); + } + if (netif_msg_drv(pdata)) + DPRINTK("napi_disable, msix ch%d napi disabled done,del=%d\n", + i, del); + } + + if (misc_napi) { + napi_disable(&pdata->expansion.napi_misc); + netif_napi_del(&pdata->expansion.napi_misc); + pdata->expansion.int_flags = + FXGMAC_SET_REG_BITS( + pdata->expansion.int_flags, + FXGMAC_FLAG_MISC_NAPI_FREE_POS, + FXGMAC_FLAG_MISC_NAPI_FREE_LEN, + FXGMAC_NAPI_DISABLE); + } + } + } else { + i = FXGMAC_GET_REG_BITS(pdata->expansion.int_flags, + FXGMAC_FLAG_LEGACY_NAPI_FREE_POS, + FXGMAC_FLAG_LEGACY_NAPI_FREE_LEN); + if (i) { + napi_disable(&pdata->expansion.napi); + + if (del) + netif_napi_del(&pdata->expansion.napi); + pdata->expansion.int_flags = FXGMAC_SET_REG_BITS( + pdata->expansion.int_flags, + FXGMAC_FLAG_LEGACY_NAPI_FREE_POS, + FXGMAC_FLAG_LEGACY_NAPI_FREE_LEN, + FXGMAC_NAPI_DISABLE); + } + } +} + +static int fxgmac_request_irqs(struct fxgmac_pdata *pdata) +{ + struct net_device *netdev = pdata->netdev; + struct fxgmac_channel *channel; + unsigned int i; + int ret; + u32 msi, msix, need_free; + u32 misc = 0, tx = 0, rx = 0; + + msi = FXGMAC_GET_REG_BITS(pdata->expansion.int_flags, + FXGMAC_FLAG_MSI_POS, FXGMAC_FLAG_MSI_LEN); + + msix = FXGMAC_GET_REG_BITS(pdata->expansion.int_flags, + FXGMAC_FLAG_MSIX_POS, FXGMAC_FLAG_MSIX_LEN); + + need_free = FXGMAC_GET_REG_BITS(pdata->expansion.int_flags, + FXGMAC_FLAG_LEGACY_IRQ_FREE_POS, + FXGMAC_FLAG_LEGACY_IRQ_FREE_LEN); + + if (!msix) { + if (!need_free) { + ret = devm_request_irq(pdata->dev, pdata->dev_irq, + fxgmac_isr, + msi ? 0 : IRQF_SHARED, + netdev->name, pdata); + if (ret) { + netdev_alert( + netdev, + "error requesting irq %d, ret = %d\n", + pdata->dev_irq, ret); + return ret; + } + + pdata->expansion.int_flags = FXGMAC_SET_REG_BITS( + pdata->expansion.int_flags, + FXGMAC_FLAG_LEGACY_IRQ_FREE_POS, + FXGMAC_FLAG_LEGACY_IRQ_FREE_LEN, + FXGMAC_IRQ_ENABLE); + } + } + + if (!pdata->per_channel_irq) { + return 0; + } + + tx = FXGMAC_GET_REG_BITS(pdata->expansion.int_flags, + FXGMAC_FLAG_TX_IRQ_FREE_POS, + FXGMAC_FLAG_TX_IRQ_FREE_LEN); + rx = FXGMAC_GET_REG_BITS(pdata->expansion.int_flags, + FXGMAC_FLAG_RX_IRQ_FREE_POS, + FXGMAC_FLAG_RX_IRQ_FREE_LEN); + channel = pdata->channel_head; + for (i = 0; i < pdata->channel_count; i++, channel++) { + snprintf(channel->expansion.dma_irq_name, + sizeof(channel->expansion.dma_irq_name) - 1, + "%s-ch%d-Rx-%u", netdev_name(netdev), i, + channel->queue_index); + if (FXGMAC_IS_CHANNEL_WITH_TX_IRQ(i) && !tx) { + snprintf(channel->expansion.dma_irq_name_tx, + sizeof(channel->expansion.dma_irq_name_tx) - 1, + "%s-ch%d-Tx-%u", netdev_name(netdev), i, + channel->queue_index); + + ret = devm_request_irq( + pdata->dev, channel->expansion.dma_irq_tx, + fxgmac_dma_isr, 0, + channel->expansion.dma_irq_name_tx, channel); + + if (ret) { + netdev_alert( + netdev, + "fxgmac_req_irqs, err with MSIx irq \ + request for ch %d tx, ret=%d\n", + i, ret); + goto err_irq; + } + + pdata->expansion.int_flags = FXGMAC_SET_REG_BITS( + pdata->expansion.int_flags, + FXGMAC_FLAG_TX_IRQ_FREE_POS, + FXGMAC_FLAG_TX_IRQ_FREE_LEN, FXGMAC_IRQ_ENABLE); + + if (netif_msg_drv(pdata)) + DPRINTK("fxgmac_req_irqs, MSIx irq_tx request ok, ch=%d, irq=%d,%s\n", + i, channel->expansion.dma_irq_tx, + channel->expansion.dma_irq_name_tx); + } + + if (!FXGMAC_GET_REG_BITS( + rx, i, FXGMAC_FLAG_PER_CHAN_RX_IRQ_FREE_LEN)) { + ret = devm_request_irq(pdata->dev, channel->dma_irq, + fxgmac_dma_isr, 0, + channel->expansion.dma_irq_name, + channel); + if (ret) { + netdev_alert(netdev, + "error requesting irq %d\n", + channel->dma_irq); + goto err_irq; + } + pdata->expansion.int_flags = FXGMAC_SET_REG_BITS( + pdata->expansion.int_flags, + FXGMAC_FLAG_RX_IRQ_FREE_POS + i, + FXGMAC_FLAG_PER_CHAN_RX_IRQ_FREE_LEN, + FXGMAC_IRQ_ENABLE); + } + } + + misc = FXGMAC_GET_REG_BITS(pdata->expansion.int_flags, + FXGMAC_FLAG_MISC_IRQ_FREE_POS, + FXGMAC_FLAG_MISC_IRQ_FREE_LEN); + if (!misc) { + snprintf(pdata->expansion.misc_irq_name, + sizeof(pdata->expansion.misc_irq_name) - 1, "%s-misc", + netdev_name(netdev)); + ret = devm_request_irq(pdata->dev, pdata->expansion.misc_irq, + fxgmac_misc_isr, 0, + pdata->expansion.misc_irq_name, pdata); + if (ret) { + netdev_alert(netdev, + "error requesting misc irq %d, ret = %d\n", + pdata->expansion.misc_irq, ret); + goto err_irq; + } + pdata->expansion.int_flags = FXGMAC_SET_REG_BITS( + pdata->expansion.int_flags, + FXGMAC_FLAG_MISC_IRQ_FREE_POS, + FXGMAC_FLAG_MISC_IRQ_FREE_LEN, FXGMAC_IRQ_ENABLE); + } + + if (netif_msg_drv(pdata)) + DPRINTK("fxgmac_req_irqs, MSIx irq request ok, total=%d,%d~%d\n", + i, (pdata->channel_head)[0].dma_irq, + (pdata->channel_head)[i - 1].dma_irq); + return 0; + +err_irq: + netdev_alert(netdev, + "fxgmac_req_irqs, err with MSIx irq request at %d, \ + ret=%d\n", + i, ret); + + if (pdata->per_channel_irq) { + for (i--, channel--; i < pdata->channel_count; i--, channel--) { + if (FXGMAC_IS_CHANNEL_WITH_TX_IRQ(i) && tx) { + pdata->expansion.int_flags = + FXGMAC_SET_REG_BITS( + pdata->expansion.int_flags, + FXGMAC_FLAG_TX_IRQ_FREE_POS, + FXGMAC_FLAG_TX_IRQ_FREE_LEN, + FXGMAC_IRQ_DISABLE); + devm_free_irq(pdata->dev, + channel->expansion.dma_irq_tx, + channel); + } + + if (FXGMAC_GET_REG_BITS( + rx, i, + FXGMAC_FLAG_PER_CHAN_RX_IRQ_FREE_LEN)) { + pdata->expansion.int_flags = FXGMAC_SET_REG_BITS( + pdata->expansion.int_flags, + FXGMAC_FLAG_RX_IRQ_FREE_POS + i, + FXGMAC_FLAG_PER_CHAN_RX_IRQ_FREE_LEN, + FXGMAC_IRQ_DISABLE); + devm_free_irq(pdata->dev, channel->dma_irq, + channel); + } + } + + if (misc) { + pdata->expansion.int_flags = FXGMAC_SET_REG_BITS( + pdata->expansion.int_flags, + FXGMAC_FLAG_MISC_IRQ_FREE_POS, + FXGMAC_FLAG_MISC_IRQ_FREE_LEN, + FXGMAC_IRQ_DISABLE); + devm_free_irq(pdata->dev, pdata->expansion.misc_irq, + pdata); + } + } + return ret; +} + +static void fxgmac_free_irqs(struct fxgmac_pdata *pdata) +{ + struct fxgmac_channel *channel; + unsigned int i = 0; + u32 need_free, msix; + u32 misc = 0, tx = 0, rx = 0; + + msix = FXGMAC_GET_REG_BITS(pdata->expansion.int_flags, + FXGMAC_FLAG_MSIX_POS, FXGMAC_FLAG_MSIX_LEN); + + need_free = FXGMAC_GET_REG_BITS(pdata->expansion.int_flags, + FXGMAC_FLAG_LEGACY_IRQ_FREE_POS, + FXGMAC_FLAG_LEGACY_IRQ_FREE_LEN); + + if (!msix) { + if (need_free) { + devm_free_irq(pdata->dev, pdata->dev_irq, pdata); + pdata->expansion.int_flags = FXGMAC_SET_REG_BITS( + pdata->expansion.int_flags, + FXGMAC_FLAG_LEGACY_IRQ_FREE_POS, + FXGMAC_FLAG_LEGACY_IRQ_FREE_LEN, + FXGMAC_IRQ_DISABLE); + } + } + + if (!pdata->per_channel_irq) + return; + + misc = FXGMAC_GET_REG_BITS(pdata->expansion.int_flags, + FXGMAC_FLAG_MISC_IRQ_FREE_POS, + FXGMAC_FLAG_MISC_IRQ_FREE_LEN); + tx = FXGMAC_GET_REG_BITS(pdata->expansion.int_flags, + FXGMAC_FLAG_TX_IRQ_FREE_POS, + FXGMAC_FLAG_TX_IRQ_FREE_LEN); + rx = FXGMAC_GET_REG_BITS(pdata->expansion.int_flags, + FXGMAC_FLAG_RX_IRQ_FREE_POS, + FXGMAC_FLAG_RX_IRQ_FREE_LEN); + + channel = pdata->channel_head; + if (channel != NULL) { + for (i = 0; i < pdata->channel_count; i++, channel++) { + if (FXGMAC_IS_CHANNEL_WITH_TX_IRQ(i) && tx) { + pdata->expansion.int_flags = + FXGMAC_SET_REG_BITS( + pdata->expansion.int_flags, + FXGMAC_FLAG_TX_IRQ_FREE_POS, + FXGMAC_FLAG_TX_IRQ_FREE_LEN, + FXGMAC_IRQ_DISABLE); + devm_free_irq(pdata->dev, + channel->expansion.dma_irq_tx, + channel); + if (netif_msg_drv(pdata)) + DPRINTK("fxgmac_free_irqs, MSIx irq_tx clear done, ch=%d\n", + i); + } + + if (FXGMAC_GET_REG_BITS( + rx, i, + FXGMAC_FLAG_PER_CHAN_RX_IRQ_FREE_LEN)) { + pdata->expansion.int_flags = FXGMAC_SET_REG_BITS( + pdata->expansion.int_flags, + FXGMAC_FLAG_RX_IRQ_FREE_POS + i, + FXGMAC_FLAG_PER_CHAN_RX_IRQ_FREE_LEN, + FXGMAC_IRQ_DISABLE); + devm_free_irq(pdata->dev, channel->dma_irq, + channel); + } + } + + if (misc) { + pdata->expansion.int_flags = FXGMAC_SET_REG_BITS( + pdata->expansion.int_flags, + FXGMAC_FLAG_MISC_IRQ_FREE_POS, + FXGMAC_FLAG_MISC_IRQ_FREE_LEN, + FXGMAC_IRQ_DISABLE); + devm_free_irq(pdata->dev, pdata->expansion.misc_irq, + pdata); + } + } + if (netif_msg_drv(pdata)) + DPRINTK("fxgmac_free_irqs, MSIx rx irq clear done, total=%d\n", + i); +} + +void fxgmac_free_tx_data(struct fxgmac_pdata *pdata) +{ + struct fxgmac_desc_ops *desc_ops = &pdata->desc_ops; + struct fxgmac_desc_data *desc_data; + struct fxgmac_channel *channel; + struct fxgmac_ring *ring; + unsigned int i, j; + + channel = pdata->channel_head; + if (channel != NULL) { + for (i = 0; i < pdata->channel_count; i++, channel++) { + ring = channel->tx_ring; + if (!ring) + break; + + for (j = 0; j < ring->dma_desc_count; j++) { + desc_data = FXGMAC_GET_DESC_DATA(ring, j); + desc_ops->unmap_desc_data(pdata, desc_data); + } + } + } +} + +void fxgmac_free_rx_data(struct fxgmac_pdata *pdata) +{ + struct fxgmac_desc_ops *desc_ops = &pdata->desc_ops; + struct fxgmac_desc_data *desc_data; + struct fxgmac_channel *channel; + struct fxgmac_ring *ring; + unsigned int i, j; + + channel = pdata->channel_head; + if (channel != NULL) { + for (i = 0; i < pdata->channel_count; i++, channel++) { + ring = channel->rx_ring; + if (!ring) + break; + + for (j = 0; j < ring->dma_desc_count; j++) { + desc_data = FXGMAC_GET_DESC_DATA(ring, j); + desc_ops->unmap_desc_data(pdata, desc_data); + } + } + } +} + +/* + * since kernel does not clear the MSI mask bits and + * this function clear MSI mask bits when MSI is enabled. + */ +static int fxgmac_disable_pci_msi_config(struct pci_dev *pdev) +{ + u16 pcie_cap_offset = 0; + u32 pcie_msi_mask_bits = 0; + int ret = 0; + + pcie_cap_offset = pci_find_capability(pdev, PCI_CAP_ID_MSI); + if (pcie_cap_offset) { + ret = pci_read_config_dword(pdev, pcie_cap_offset, + &pcie_msi_mask_bits); + if (ret) { + DPRINTK(KERN_ERR + "read pci config space MSI cap. failed, %d\n", + ret); + ret = -EFAULT; + } + } + + pcie_msi_mask_bits = FXGMAC_SET_REG_BITS(pcie_msi_mask_bits, + PCI_CAP_ID_MSI_ENABLE_POS, + PCI_CAP_ID_MSI_ENABLE_LEN, 0); + ret = pci_write_config_dword(pdev, pcie_cap_offset, pcie_msi_mask_bits); + if (ret) { + DPRINTK(KERN_ERR "write pci config space MSI mask failed, %d\n", + ret); + ret = -EFAULT; + } + + return ret; +} + +static int fxgmac_disable_pci_msix_config(struct pci_dev *pdev) +{ + u16 pcie_cap_offset = 0; + u32 pcie_msi_mask_bits = 0; + int ret = 0; + + pcie_cap_offset = pci_find_capability(pdev, PCI_CAP_ID_MSIX); + if (pcie_cap_offset) { + ret = pci_read_config_dword(pdev, pcie_cap_offset, + &pcie_msi_mask_bits); + if (ret) { + DPRINTK(KERN_ERR + "read pci config space MSIX cap. failed, %d\n", + ret); + ret = -EFAULT; + } + } + + pcie_msi_mask_bits = FXGMAC_SET_REG_BITS(pcie_msi_mask_bits, + PCI_CAP_ID_MSIX_ENABLE_POS, + PCI_CAP_ID_MSIX_ENABLE_LEN, 0); + ret = pci_write_config_dword(pdev, pcie_cap_offset, pcie_msi_mask_bits); + if (ret) { + DPRINTK(KERN_ERR + "write pci config space MSIX mask failed, %d\n", + ret); + ret = -EFAULT; + } + + return ret; +} + +int fxgmac_start(struct fxgmac_pdata *pdata) +{ + struct fxgmac_hw_ops *hw_ops = &pdata->hw_ops; + struct net_device *netdev = pdata->netdev; + unsigned int pcie_low_power = 0; + u32 regval; + int ret; + + if (netif_msg_drv(pdata)) + DPRINTK("fxgmac start callin here.\n"); + + if (pdata->expansion.dev_state != FXGMAC_DEV_OPEN && + pdata->expansion.dev_state != FXGMAC_DEV_STOP && + pdata->expansion.dev_state != FXGMAC_DEV_RESUME) + return 0; + + /* must reset software again here, to avoid flushing tx queue error + * caused by the system only run probe + * when installing driver on the arm platform. + */ + hw_ops->exit(pdata); + + if (FXGMAC_GET_REG_BITS(pdata->expansion.int_flags, + FXGMAC_FLAG_LEGACY_POS, + FXGMAC_FLAG_LEGACY_LEN)) { + /* + * we should disable msi and msix here when we use legacy interrupt,for two reasons: + * 1. Exit will restore msi and msix config regisiter, that may enable them. + * 2. When the driver that uses the msix interrupt by default is compiled + * into the OS, uninstall the driver through rmmod, and then install the + * driver that uses the legacy interrupt, at which time the msix enable + * will be turned on again by default after waking up from S4 on some platform. + * such as UOS platform. + */ + ret = fxgmac_disable_pci_msi_config(pdata->pdev); + ret |= fxgmac_disable_pci_msix_config(pdata->pdev); + if (ret) + return ret; + } + + hw_ops->reset_phy(pdata); + hw_ops->release_phy(pdata); + hw_ops->pcie_init(pdata, pcie_low_power & PCIE_LP_ASPM_LTR, + pcie_low_power & PCIE_LP_ASPM_L1SS, + pcie_low_power & PCIE_LP_ASPM_L1, + pcie_low_power & PCIE_LP_ASPM_L0S); + if (test_bit(FXGMAC_POWER_STATE_DOWN, &pdata->expansion.powerstate)) { + netdev_err( + pdata->netdev, + "fxgmac powerstate is %lu when config power to up.\n", + pdata->expansion.powerstate); + } + hw_ops->config_power_up(pdata); + + hw_ops->dismiss_all_int(pdata); + + if (netdev->base_addr) { + regval = (unsigned int)(*( + (u32 *)(netdev->base_addr + MGMT_INT_CTRL0))); + } + + ret = hw_ops->init(pdata); + if (ret) { + DPRINTK("fxgmac hw init error.\n"); + return ret; + } + fxgmac_napi_enable(pdata, 1); + + ret = fxgmac_request_irqs(pdata); + if (ret) + goto err_napi; + + hw_ops->enable_tx(pdata); + hw_ops->enable_rx(pdata); + + regval = (u32)readl((const volatile void *)(pdata->mac_regs + DMA_MR)); + regval = FXGMAC_SET_REG_BITS(regval, DMA_MR_INTM_POS, DMA_MR_INTM_LEN, + 1); + regval = FXGMAC_SET_REG_BITS(regval, DMA_MR_QUREAD_POS, + DMA_MR_QUREAD_LEN, 1); + writel(regval, pdata->mac_regs + DMA_MR); + + writel(0xF0000000, + (volatile void *)(netdev->base_addr + MGMT_INT_CTRL0)); + +#if FXGMAC_INT_MODERATION_ENABLED + hw_ops->set_interrupt_moderation(pdata); +#endif + + if (pdata->per_channel_irq) + hw_ops->enable_msix_rxtxphyinterrupt(pdata); + + fxgmac_enable_rx_tx_ints(pdata); + +#ifdef FXGMAC_ESD_CHECK_ENABLED + fxgmac_schedule_esd_work(pdata); +#endif + + fxgmac_set_phy_link_ksettings(pdata); + hw_ops->led_under_active(pdata); + pdata->expansion.dev_state = FXGMAC_DEV_START; + + fxgmac_phy_timer_init(pdata); + return 0; + +err_napi: + fxgmac_phy_timer_destroy(pdata); + fxgmac_napi_disable(pdata, 1); + hw_ops->exit(pdata); + dev_err(pdata->dev, "fxgmac start callout with irq err.\n"); + return ret; +} + +void fxgmac_stop(struct fxgmac_pdata *pdata) +{ + struct fxgmac_hw_ops *hw_ops = &pdata->hw_ops; + struct net_device *netdev = pdata->netdev; + struct fxgmac_channel *channel; + struct netdev_queue *txq; + unsigned int i; + + if (pdata->expansion.dev_state != FXGMAC_DEV_START) + return; + + pdata->expansion.dev_state = FXGMAC_DEV_STOP; + + if (pdata->per_channel_irq) { + hw_ops->disable_msix_interrupt(pdata); + } else { + hw_ops->disable_mgm_interrupt(pdata); + } + + pdata->expansion.phy_link = false; + + netif_carrier_off(netdev); + netif_tx_stop_all_queues(netdev); + hw_ops->disable_tx(pdata); + hw_ops->disable_rx(pdata); + fxgmac_free_irqs(pdata); + fxgmac_napi_disable(pdata, 1); + + channel = pdata->channel_head; + if (channel != NULL) { + for (i = 0; i < pdata->channel_count; i++, channel++) { + if (!channel->tx_ring) + continue; + + txq = netdev_get_tx_queue(netdev, channel->queue_index); + netdev_tx_reset_queue(txq); + } + } + + fxgmac_phy_timer_destroy(pdata); +} + +void fxgmac_restart_dev(struct fxgmac_pdata *pdata) +{ + struct fxgmac_hw_ops *hw_ops = &pdata->hw_ops; + int ret; + + /* If not running, "restart" will happen on open */ + if (!netif_running(pdata->netdev) && + pdata->expansion.dev_state != FXGMAC_DEV_START) + return; + + fxgmac_lock(pdata); + fxgmac_stop(pdata); + hw_ops->led_under_shutdown(pdata); + + fxgmac_free_tx_data(pdata); + fxgmac_free_rx_data(pdata); + + ret = fxgmac_start(pdata); + if (ret) { + DPRINTK("fxgmac_restart_dev: fxgmac_start failed.\n"); + } + + fxgmac_unlock(pdata); +} + +static void fxgmac_restart(struct work_struct *work) +{ + struct fxgmac_pdata *pdata = + container_of(work, struct fxgmac_pdata, expansion.restart_work); + + rtnl_lock(); + + fxgmac_restart_dev(pdata); + + rtnl_unlock(); +} + +void fxgmac_net_powerup(struct fxgmac_pdata *pdata) +{ + int ret; + struct fxgmac_hw_ops *hw_ops = &pdata->hw_ops; + + if (netif_msg_drv(pdata)) + DPRINTK("fxgmac_net_powerup callin\n"); + + /* signal that we are up now */ + pdata->expansion.powerstate = 0; + if (__test_and_set_bit(FXGMAC_POWER_STATE_UP, + &pdata->expansion.powerstate)) { + return; /* do nothing if already up */ + } + + ret = fxgmac_start(pdata); + if (ret) { + DPRINTK("fxgmac_net_powerup: fxgmac_start error\n"); + return; + } + + hw_ops->disable_arp_offload(pdata); + + if (netif_msg_drv(pdata)) { + DPRINTK("fxgmac_net_powerup callout, powerstate=%ld.\n", + pdata->expansion.powerstate); + } +} + +void fxgmac_net_powerdown(struct fxgmac_pdata *pdata, unsigned int wol) +{ + struct net_device *netdev = pdata->netdev; + struct fxgmac_hw_ops *hw_ops = &pdata->hw_ops; + + if (netif_msg_drv(pdata)) + DPRINTK("fxgmac_net_powerdown callin here.\n"); + + /* signal that we are down to the interrupt handler */ + if (__test_and_set_bit(FXGMAC_POWER_STATE_DOWN, + &pdata->expansion.powerstate)) + return; /* do nothing if already down */ + + if (netif_msg_drv(pdata)) + DPRINTK("fxgmac_net_powerdown continue with down process.\n"); + /* phy polling timer should detect the state of fxgmac and stop link status polling accordingly */ + + __clear_bit(FXGMAC_POWER_STATE_UP, &pdata->expansion.powerstate); + + /* Shut off incoming Tx traffic */ + netif_tx_stop_all_queues(netdev); + + /* call carrier off first to avoid false dev_watchdog timeouts */ + netif_carrier_off(netdev); + netif_tx_disable(netdev); + + /* Disable Rx */ + hw_ops->disable_rx(pdata); + + /* synchronize_rcu() needed for pending XDP buffers to drain */ + + synchronize_rcu(); + +#ifdef FXGMAC_ESD_CHECK_ENABLED + fxgmac_cancel_esd_work(pdata); +#endif + + fxgmac_stop(pdata); + + hw_ops->pre_power_down(pdata, false); + + if (!test_bit(FXGMAC_POWER_STATE_DOWN, &pdata->expansion.powerstate)) { + netdev_err( + pdata->netdev, + "fxgmac powerstate is %lu when config power to down.\n", + pdata->expansion.powerstate); + } + + /* set mac to lowpower mode and enable wol accordingly */ + hw_ops->config_power_down(pdata, wol); + + fxgmac_free_tx_data(pdata); + fxgmac_free_rx_data(pdata); + if (netif_msg_drv(pdata)) + DPRINTK("fxgmac_net_powerdown callout, powerstate=%ld.\n", + pdata->expansion.powerstate); +} + +static int fxgmac_open(struct net_device *netdev) +{ + struct fxgmac_pdata *pdata = netdev_priv(netdev); + struct fxgmac_desc_ops *desc_ops; + int ret; + + if (netif_msg_drv(pdata)) + DPRINTK("fxgmac_open callin\n"); + + fxgmac_lock(pdata); + pdata->expansion.dev_state = FXGMAC_DEV_OPEN; + desc_ops = &pdata->desc_ops; + + /* Calculate the Rx buffer size before allocating rings */ + + ret = fxgmac_calc_rx_buf_size(netdev, netdev->mtu); + if (ret < 0) + goto unlock; + pdata->rx_buf_size = ret; + + /* Allocate the channels and rings */ + ret = desc_ops->alloc_channels_and_rings(pdata); + if (ret) + goto unlock; + + INIT_WORK(&pdata->expansion.restart_work, fxgmac_restart); +#ifdef FXGMAC_ESD_CHECK_ENABLED + INIT_DELAYED_WORK(&pdata->expansion.esd_work, fxgmac_esd_work); +#endif + + ret = fxgmac_start(pdata); + if (ret) + goto err_channels_and_rings; + + if (netif_msg_drv(pdata)) + DPRINTK("fxgmac_open callout\n"); + + fxgmac_unlock(pdata); + + return 0; + +err_channels_and_rings: + desc_ops->free_channels_and_rings(pdata); + DPRINTK("fxgmac_open callout with channel alloc err\n"); +unlock: + fxgmac_unlock(pdata); + return ret; +} + +static int fxgmac_close(struct net_device *netdev) +{ + struct fxgmac_pdata *pdata = netdev_priv(netdev); + struct fxgmac_desc_ops *desc_ops = &pdata->desc_ops; + + if (netif_msg_drv(pdata)) + DPRINTK("fxgmac_close callin\n"); + + fxgmac_lock(pdata); + /* Stop the device */ + fxgmac_stop(pdata); + pdata->expansion.dev_state = FXGMAC_DEV_CLOSE; + +#ifdef FXGMAC_ESD_CHECK_ENABLED + fxgmac_cancel_esd_work(pdata); +#endif + + /* Free the channels and rings */ + desc_ops->free_channels_and_rings(pdata); + + pdata->hw_ops.reset_phy(pdata); + + if (netif_msg_drv(pdata)) + DPRINTK("fxgmac_close callout\n"); + + fxgmac_unlock(pdata); + return 0; +} + +static void fxgmac_tx_timeout(struct net_device *netdev, unsigned int unused) +{ + struct fxgmac_pdata *pdata = netdev_priv(netdev); + + netdev_warn(netdev, "tx timeout, device restarting\n"); +#if FXGMAC_TX_HANG_TIMER_ENABLED + if (!pdata->tx_hang_restart_queuing) + schedule_work(&pdata->expansion.restart_work); +#else + schedule_work(&pdata->expansion.restart_work); +#endif +} + +static netdev_tx_t fxgmac_xmit(struct sk_buff *skb, struct net_device *netdev) +{ + struct fxgmac_pdata *pdata = netdev_priv(netdev); + struct fxgmac_pkt_info *tx_pkt_info; + struct fxgmac_desc_ops *desc_ops; + struct fxgmac_channel *channel; + struct netdev_queue *txq; + struct fxgmac_ring *ring; + int ret; + + desc_ops = &pdata->desc_ops; + + if (netif_msg_tx_done(pdata)) + DPRINTK("xmit callin, skb->len=%d,q=%d\n", skb->len, + skb->queue_mapping); + + channel = pdata->channel_head + skb->queue_mapping; + txq = netdev_get_tx_queue(netdev, channel->queue_index); + ring = channel->tx_ring; + tx_pkt_info = &ring->pkt_info; + + if (skb->len == 0) { + netif_err(pdata, tx_err, netdev, + "empty skb received from stack\n"); + dev_kfree_skb_any(skb); + return NETDEV_TX_OK; + } + + /* Prepare preliminary packet info for TX */ + memset(tx_pkt_info, 0, sizeof(*tx_pkt_info)); + fxgmac_prep_tx_pkt(pdata, ring, skb, tx_pkt_info); + + /* Check that there are enough descriptors available */ + ret = fxgmac_maybe_stop_tx_queue(channel, ring, + tx_pkt_info->desc_count); + if (ret) { + return ret; + } + + ret = fxgmac_prep_tso(pdata, skb, tx_pkt_info); + if (ret) { + netif_err(pdata, tx_err, netdev, + "error processing TSO packet\n"); + dev_kfree_skb_any(skb); + return NETDEV_TX_OK; + } + fxgmac_prep_vlan(skb, tx_pkt_info); + + if (!desc_ops->map_tx_skb(channel, skb)) { + dev_kfree_skb_any(skb); + netif_err(pdata, tx_err, netdev, "xmit, map tx skb err\n"); + return NETDEV_TX_OK; + } + + /* Report on the actual number of bytes (to be) sent */ + netdev_tx_sent_queue(txq, tx_pkt_info->tx_bytes); + if (netif_msg_tx_done(pdata)) + DPRINTK("xmit,before hw_xmit, byte len=%d\n", + tx_pkt_info->tx_bytes); + + /* Configure required descriptor fields for transmission */ + fxgmac_dev_xmit(channel); + + if (netif_msg_pktdata(pdata)) + fxgmac_dbg_pkt(netdev, skb, true); + + /* Stop the queue in advance if there may not be enough descriptors */ + fxgmac_maybe_stop_tx_queue(channel, ring, FXGMAC_TX_MAX_DESC_NR); + + return NETDEV_TX_OK; +} + +static void fxgmac_get_stats64(struct net_device *netdev, + struct rtnl_link_stats64 *s) +{ + struct fxgmac_pdata *pdata = netdev_priv(netdev); + struct fxgmac_stats *pstats = &pdata->stats; + +#if FXGMAC_PM_FEATURE_ENABLED + /* 20210709 for net power down */ + if (!test_bit(FXGMAC_POWER_STATE_DOWN, &pdata->expansion.powerstate)) +#endif + { + + pdata->hw_ops.read_mmc_stats(pdata); + } + s->rx_packets = pstats->rxframecount_gb; + s->rx_bytes = pstats->rxoctetcount_gb; + s->rx_errors = pstats->rxframecount_gb - pstats->rxbroadcastframes_g - + pstats->rxmulticastframes_g - pstats->rxunicastframes_g; + s->multicast = pstats->rxmulticastframes_g; + s->rx_length_errors = pstats->rxlengtherror; + s->rx_crc_errors = pstats->rxcrcerror; + s->rx_fifo_errors = pstats->rxfifooverflow; + + s->tx_packets = pstats->txframecount_gb; + s->tx_bytes = pstats->txoctetcount_gb; + s->tx_errors = pstats->txframecount_gb - pstats->txframecount_g; + s->tx_dropped = netdev->stats.tx_dropped; + + return; +} + +static int fxgmac_set_mac_address(struct net_device *netdev, void *addr) +{ + struct fxgmac_pdata *pdata = netdev_priv(netdev); + struct fxgmac_hw_ops *hw_ops = &pdata->hw_ops; + struct sockaddr *saddr = addr; + + if (!is_valid_ether_addr(saddr->sa_data)) + return -EADDRNOTAVAIL; + + eth_hw_addr_set(netdev, saddr->sa_data); + memcpy(pdata->mac_addr, saddr->sa_data, netdev->addr_len); + + hw_ops->set_mac_address(pdata, saddr->sa_data); + hw_ops->set_mac_hash(pdata); + + DPRINTK("fxgmac,set mac addr to %02x:%02x:%02x:%02x:%02x:%02x\n", + netdev->dev_addr[0], netdev->dev_addr[1], netdev->dev_addr[2], + netdev->dev_addr[3], netdev->dev_addr[4], netdev->dev_addr[5]); + return 0; +} + +/* + * cmd = [0x89F0, 0x89FF] + * When using it, we must pay attention to the thread synchronization + * of this interface. Because it's an external call that isn't + * initiated by the OS. + */ +static int fxgmac_ioctl(struct net_device *netdev, struct ifreq *ifr, int cmd) +{ + struct file f; + int ret = FXGMAC_SUCCESS; + struct fxgmac_pdata *pdata = netdev_priv(netdev); + + if (!netif_running(netdev) || + pdata->expansion.dev_state != FXGMAC_DEV_START) + return -ENODEV; + + f.private_data = pdata; + + switch (cmd) { + case FXGMAC_DEV_CMD: + ret = fxgmac_netdev_ops_ioctl(&f, FXGMAC_IOCTL_DFS_COMMAND, + (unsigned long)(ifr->ifr_data)); + break; + default: + ret = -EINVAL; + break; + } + + return ret; +} + +static int fxgmac_siocdevprivate(struct net_device *dev, struct ifreq *ifr, + void __user *data, int cmd) +{ + return fxgmac_ioctl(dev, ifr, cmd); +} + +static int fxgmac_change_mtu(struct net_device *netdev, int mtu) +{ + struct fxgmac_pdata *pdata = netdev_priv(netdev); + int ret, max_mtu; +#ifdef FXGMAC_DEBUG + int old_mtu = netdev->mtu; +#endif + + /* On the Linux platform, the MTU size does not include the length + * of the MAC address and the length of the Type, but FXGMAC_JUMBO_PACKET_MTU include them. + */ + max_mtu = FXGMAC_JUMBO_PACKET_MTU - ETH_HLEN; + if (mtu > max_mtu) { + netdev_alert(netdev, "MTU exceeds maximum supported value\n"); + return -EINVAL; + } + + fxgmac_lock(pdata); + fxgmac_stop(pdata); + fxgmac_free_tx_data(pdata); + + /* + * We must unmap rx desc's dma before we change rx_buf_size. + * Becaues the size of the unmapped DMA is set according to rx_buf_size + */ + fxgmac_free_rx_data(pdata); + + pdata->jumbo = mtu > ETH_DATA_LEN ? 1 : 0; + + ret = fxgmac_calc_rx_buf_size(netdev, mtu); + if (ret < 0) + return ret; + + pdata->rx_buf_size = ret; + netdev->mtu = mtu; + + if (netif_running(netdev)) + fxgmac_start(pdata); + + netdev_update_features(netdev); + + DPRINTK("fxgmac,set MTU from %d to %d. min, max=(%d,%d)\n", old_mtu, + netdev->mtu, netdev->min_mtu, netdev->max_mtu); + + fxgmac_unlock(pdata); + + return 0; +} + +static int fxgmac_vlan_rx_add_vid(struct net_device *netdev, __be16 proto, + u16 vid) +{ + struct fxgmac_pdata *pdata = netdev_priv(netdev); + struct fxgmac_hw_ops *hw_ops = &pdata->hw_ops; + + set_bit(vid, pdata->active_vlans); +#if FXGMAC_FILTER_SINGLE_VLAN_ENABLED + pdata->vlan = vid; + hw_ops->enable_rx_vlan_filtering(pdata); +#else + hw_ops->update_vlan_hash_table(pdata); +#endif + DPRINTK("fxgmac,add rx vlan %d\n", vid); + return 0; +} + +static int fxgmac_vlan_rx_kill_vid(struct net_device *netdev, __be16 proto, + u16 vid) +{ + struct fxgmac_pdata *pdata = netdev_priv(netdev); + struct fxgmac_hw_ops *hw_ops = &pdata->hw_ops; + + clear_bit(vid, pdata->active_vlans); +#if FXGMAC_FILTER_SINGLE_VLAN_ENABLED + pdata->vlan = 0; + hw_ops->disable_rx_vlan_filtering(pdata); +#else + hw_ops->update_vlan_hash_table(pdata); +#endif + + DPRINTK("fxgmac,del rx vlan %d\n", vid); + return 0; +} + +#ifdef CONFIG_NET_POLL_CONTROLLER +static void fxgmac_poll_controller(struct net_device *netdev) +{ + struct fxgmac_pdata *pdata = netdev_priv(netdev); + struct fxgmac_channel *channel; + unsigned int i; + + if (pdata->per_channel_irq) { + channel = pdata->channel_head; + for (i = 0; i < pdata->channel_count; i++, channel++) + fxgmac_dma_isr(channel->dma_irq, channel); + } else { + disable_irq(pdata->dev_irq); + fxgmac_isr(pdata->dev_irq, pdata); + enable_irq(pdata->dev_irq); + } +} +#endif /* CONFIG_NET_POLL_CONTROLLER */ + +static netdev_features_t fxgmac_fix_features(struct net_device *netdev, + netdev_features_t features) +{ + u32 fifo_size; + struct fxgmac_pdata *pdata = netdev_priv(netdev); + struct fxgmac_hw_ops *hw_ops = &pdata->hw_ops; + + fifo_size = hw_ops->calculate_max_checksum_size(pdata); + + if (netdev->mtu > fifo_size) { + features &= ~NETIF_F_IP_CSUM; + features &= ~NETIF_F_IPV6_CSUM; + } + + return features; +} + +static int fxgmac_set_features(struct net_device *netdev, + netdev_features_t features) +{ + netdev_features_t rxhash, rxcsum, rxvlan, rxvlan_filter, tso; + struct fxgmac_pdata *pdata = netdev_priv(netdev); + struct fxgmac_hw_ops *hw_ops = &pdata->hw_ops; + int ret = 0; + + rxhash = pdata->expansion.netdev_features & NETIF_F_RXHASH; + rxcsum = pdata->expansion.netdev_features & NETIF_F_RXCSUM; + rxvlan = pdata->expansion.netdev_features & NETIF_F_HW_VLAN_CTAG_RX; + rxvlan_filter = pdata->expansion.netdev_features & + NETIF_F_HW_VLAN_CTAG_FILTER; + tso = pdata->expansion.netdev_features & (NETIF_F_TSO | NETIF_F_TSO6); + + if ((features & (NETIF_F_TSO | NETIF_F_TSO6)) && !tso) { + DPRINTK("enable tso.\n"); + pdata->hw_feat.tso = 1; + hw_ops->config_tso(pdata); + } else if (!(features & (NETIF_F_TSO | NETIF_F_TSO6)) && tso) { + DPRINTK("disable tso.\n"); + pdata->hw_feat.tso = 0; + hw_ops->config_tso(pdata); + } + + if ((features & NETIF_F_RXHASH) && !rxhash) + ret = hw_ops->enable_rss(pdata); + else if (!(features & NETIF_F_RXHASH) && rxhash) + ret = hw_ops->disable_rss(pdata); + if (ret) + return ret; + + if ((features & NETIF_F_RXCSUM) && !rxcsum) + hw_ops->enable_rx_csum(pdata); + else if (!(features & NETIF_F_RXCSUM) && rxcsum) + hw_ops->disable_rx_csum(pdata); + + if ((features & NETIF_F_HW_VLAN_CTAG_RX) && !rxvlan) + hw_ops->enable_rx_vlan_stripping(pdata); + else if (!(features & NETIF_F_HW_VLAN_CTAG_RX) && rxvlan) + hw_ops->disable_rx_vlan_stripping(pdata); + + if ((features & NETIF_F_HW_VLAN_CTAG_FILTER) && !rxvlan_filter) + hw_ops->enable_rx_vlan_filtering(pdata); + else if (!(features & NETIF_F_HW_VLAN_CTAG_FILTER) && rxvlan_filter) + hw_ops->disable_rx_vlan_filtering(pdata); + + pdata->expansion.netdev_features = features; + + DPRINTK("fxgmac,set features done,%llx\n", (u64)features); + return 0; +} + +static void fxgmac_set_rx_mode(struct net_device *netdev) +{ + struct fxgmac_pdata *pdata = netdev_priv(netdev); + struct fxgmac_hw_ops *hw_ops = &pdata->hw_ops; + + hw_ops->config_rx_mode(pdata); +} + +static const struct net_device_ops fxgmac_netdev_ops = { + .ndo_open = fxgmac_open, + .ndo_stop = fxgmac_close, + .ndo_start_xmit = fxgmac_xmit, + .ndo_tx_timeout = fxgmac_tx_timeout, + .ndo_get_stats64 = fxgmac_get_stats64, + .ndo_change_mtu = fxgmac_change_mtu, + + .ndo_set_mac_address = fxgmac_set_mac_address, + .ndo_validate_addr = eth_validate_addr, + .ndo_do_ioctl = fxgmac_ioctl, + .ndo_siocdevprivate = fxgmac_siocdevprivate, + .ndo_vlan_rx_add_vid = fxgmac_vlan_rx_add_vid, + .ndo_vlan_rx_kill_vid = fxgmac_vlan_rx_kill_vid, +#ifdef CONFIG_NET_POLL_CONTROLLER + .ndo_poll_controller = fxgmac_poll_controller, +#endif + .ndo_set_features = fxgmac_set_features, + .ndo_fix_features = fxgmac_fix_features, + .ndo_set_rx_mode = fxgmac_set_rx_mode, +}; + +const struct net_device_ops *fxgmac_get_netdev_ops(void) +{ + return &fxgmac_netdev_ops; +} + +static void fxgmac_rx_refresh(struct fxgmac_channel *channel) +{ + struct fxgmac_pdata *pdata = channel->pdata; + struct fxgmac_ring *ring = channel->rx_ring; + struct fxgmac_desc_data *desc_data; + struct fxgmac_desc_ops *desc_ops = &pdata->desc_ops; + + while (ring->dirty != ring->cur) { + desc_data = FXGMAC_GET_DESC_DATA(ring, ring->dirty); + /* Reset desc_data values */ + desc_ops->unmap_desc_data(pdata, desc_data); + + if (desc_ops->map_rx_buffer(pdata, ring, desc_data)) + break; + desc_ops->rx_desc_reset(pdata, desc_data, ring->dirty); + ring->dirty = + FXGMAC_GET_ENTRY(ring->dirty, ring->dma_desc_count); + } + + /* Make sure everything is written before the register write */ + wmb(); + + /* Update the Rx Tail Pointer Register with address of + * the last cleaned entry + */ + desc_data = FXGMAC_GET_DESC_DATA( + ring, (ring->dirty - 1) & (ring->dma_desc_count - 1)); + writel(lower_32_bits(desc_data->dma_desc_addr), + FXGMAC_DMA_REG(channel, DMA_CH_RDTR_LO)); +} + +static struct sk_buff *fxgmac_create_skb(struct fxgmac_pdata *pdata, + struct napi_struct *napi, + struct fxgmac_desc_data *desc_data, + unsigned int len) +{ + struct sk_buff *skb; + unsigned int copy_len; + u8 *packet; + + skb = napi_alloc_skb(napi, desc_data->rx.hdr.dma_len); + if (!skb) + return NULL; + + /* Start with the header buffer which may contain just the header + * or the header plus data + */ + dma_sync_single_range_for_cpu(pdata->dev, desc_data->rx.hdr.dma_base, + desc_data->rx.hdr.dma_off, + desc_data->rx.hdr.dma_len, + DMA_FROM_DEVICE); + + packet = page_address(desc_data->rx.hdr.pa.pages) + + desc_data->rx.hdr.pa.pages_offset; + copy_len = len; + copy_len = min(desc_data->rx.hdr.dma_len, copy_len); + skb_copy_to_linear_data(skb, packet, copy_len); + skb_put(skb, copy_len); + return skb; +} + +static int fxgmac_tx_poll(struct fxgmac_channel *channel) +{ + struct fxgmac_pdata *pdata = channel->pdata; + struct fxgmac_ring *ring = channel->tx_ring; + struct net_device *netdev = pdata->netdev; + unsigned int tx_packets = 0, tx_bytes = 0; + struct fxgmac_desc_data *desc_data; + struct fxgmac_dma_desc *dma_desc; + struct fxgmac_desc_ops *desc_ops; + struct fxgmac_hw_ops *hw_ops; + struct netdev_queue *txq; + int processed = 0; + unsigned int cur; + + static int fxgmac_restart_need = 0; + static u32 change_cnt = 0; + static u32 reg_cur_pre = 0xffffffff; + (void)reg_cur_pre; + (void)change_cnt; + (void)fxgmac_restart_need; + +#if FXGMAC_TX_HANG_TIMER_ENABLED + static u32 reg_cur = 0; +#endif + + desc_ops = &pdata->desc_ops; + hw_ops = &pdata->hw_ops; + + /* Nothing to do if there isn't a Tx ring for this channel */ + if (!ring) { + if (netif_msg_tx_done(pdata) && + (channel->queue_index < pdata->tx_q_count)) + DPRINTK("tx_poll, null point to ring %d\n", + channel->queue_index); + return 0; + } + if ((ring->cur != ring->dirty) && (netif_msg_tx_done(pdata))) + DPRINTK("tx_poll callin, ring_cur=%d,ring_dirty=%d,qIdx=%d\n", + ring->cur, ring->dirty, channel->queue_index); + + cur = ring->cur; + + /* Be sure we get ring->cur before accessing descriptor data */ + smp_rmb(); + + txq = netdev_get_tx_queue(netdev, channel->queue_index); + + while (ring->dirty != cur) { + desc_data = FXGMAC_GET_DESC_DATA(ring, ring->dirty); + dma_desc = desc_data->dma_desc; + + if (!hw_ops->tx_complete(dma_desc)) { +#if FXGMAC_TRIGGER_TX_HANG + struct net_device *netdev = pdata->netdev; +#define FXGMAC_HANG_THRESHOLD 1 + + reg_cur = readl(FXGMAC_DMA_REG( + channel, 0x44 /* tx desc curr pointer reg */)); + + if (reg_cur != reg_cur_pre) { + reg_cur_pre = reg_cur; + change_cnt = 0; + } else { + change_cnt++; + } + + if (change_cnt > 2) { + DPRINTK("after complete check, cur=%d, dirty=%d,qIdx=%d, hw desc cur=%#x, pre=%#x\n", + ring->cur, ring->dirty, + channel->queue_index, reg_cur, + reg_cur_pre); + + if ((ring->cur > ring->dirty) && + ((ring->cur - ring->dirty) > + FXGMAC_HANG_THRESHOLD)) { + DPRINTK("after complete check warning..., too many TBD occupied by HW, 0xdbbb, %d.\n", + (ring->cur - ring->dirty)); + (*((u32 *)(netdev->base_addr + + 0x1000))) = 0xdbbb; + + if (!fxgmac_restart_need) { + schedule_work( + &pdata->expansion + .restart_work); + fxgmac_restart_need = 1; + change_cnt = 0; + } + } else if ((ring->cur < ring->dirty) && + ((ring->cur + (ring->dma_desc_count - + ring->dirty)) > + FXGMAC_HANG_THRESHOLD)) { + DPRINTK("after complete check warning..., too many TBD occupied by HW, 0xdb00, %d.\n", + (ring->cur + + (ring->dma_desc_count - + ring->dirty))); + (*((u32 *)(netdev->base_addr + + 0x1000))) = 0xdb00; + + if (!fxgmac_restart_need) { + schedule_work( + &pdata->expansion + .restart_work); + fxgmac_restart_need = 1; + change_cnt = 0; + } + } + } +#endif +#if FXGMAC_TX_HANG_TIMER_ENABLED + if ((!pdata->tx_hang_restart_queuing) && + (!channel->expansion.tx_hang_timer_active)) { + reg_cur = ring->dirty; + if (reg_cur_pre != reg_cur) { + reg_cur_pre = reg_cur; + change_cnt = 0; + } else { + change_cnt++; + } + + if (change_cnt > 4) { +#if FXGMAC_TX_HANG_CHECH_DIRTY + channel->expansion.tx_hang_hw_cur = + ring->dirty; +#else + channel->expansion + .tx_hang_hw_cur = readl(FXGMAC_DMA_REG( + channel, + 0x44 /* tx desc curr pointer reg */)); +#endif + /* double check for race conditione */ + if ((!pdata->tx_hang_restart_queuing) && + (!channel->expansion + .tx_hang_timer_active)) { + DPRINTK("tx_hang polling: start timer at desc %u, timer act=%u, queuing=%u, qidx=%u.\n", + reg_cur, + channel->expansion + .tx_hang_timer_active, + pdata->tx_hang_restart_queuing, + channel->queue_index); + fxgmac_tx_hang_timer_start( + channel); + } + } + } else if (pdata->tx_hang_restart_queuing) { + } +#endif + + break; + } + + reg_cur_pre = 0xffffffff; + fxgmac_restart_need = 0; + change_cnt = 0; + + /* Make sure descriptor fields are read after reading + * the OWN bit + */ + dma_rmb(); + + if (netif_msg_tx_done(pdata)) + fxgmac_dump_tx_desc(pdata, ring, ring->dirty, 1, 0); + + if (hw_ops->is_last_desc(dma_desc)) { + tx_packets += desc_data->tx.packets; + tx_bytes += desc_data->tx.bytes; + } + + /* Free the SKB and reset the descriptor for re-use */ + desc_ops->unmap_desc_data(pdata, desc_data); + desc_ops->tx_desc_reset(desc_data); + + processed++; + + ring->dirty = + FXGMAC_GET_ENTRY(ring->dirty, ring->dma_desc_count); + } + + if (!processed) + return 0; + + netdev_tx_completed_queue(txq, tx_packets, tx_bytes); + + smp_wmb(); + if ((ring->tx.queue_stopped == 1) && + (fxgmac_tx_avail_desc(ring) > FXGMAC_TX_DESC_MIN_FREE)) { + ring->tx.queue_stopped = 0; + netif_tx_wake_queue(txq); + } + + if (netif_msg_tx_done(pdata)) { + DPRINTK("tx_poll callout, processed=%d\n", processed); + } + + return processed; +} + +static int fxgmac_rx_poll(struct fxgmac_channel *channel, int budget) +{ + struct fxgmac_pdata *pdata = channel->pdata; + struct fxgmac_ring *ring = channel->rx_ring; + struct net_device *netdev = pdata->netdev; + unsigned int len, max_len; + unsigned int context_next, context; + struct fxgmac_desc_data *desc_data; + struct fxgmac_pkt_info *pkt_info; + unsigned int incomplete; + struct napi_struct *napi; + struct sk_buff *skb; + int packet_count = 0; + + /* Nothing to do if there isn't a Rx ring for this channel */ + if (!ring) + return 0; + + incomplete = 0; + context_next = 0; + + napi = (pdata->per_channel_irq) ? &channel->expansion.napi_rx : + &pdata->expansion.napi; + + desc_data = FXGMAC_GET_DESC_DATA(ring, ring->cur); + pkt_info = &ring->pkt_info; + + while (packet_count < budget) { + memset(pkt_info, 0, sizeof(*pkt_info)); + skb = NULL; + len = 0; + +read_again: + desc_data = FXGMAC_GET_DESC_DATA(ring, ring->cur); + + if (fxgmac_rx_dirty_desc(ring) > FXGMAC_RX_DESC_MAX_DIRTY) + fxgmac_rx_refresh(channel); + + if (fxgmac_dev_read(channel)) + break; + + ring->cur = FXGMAC_GET_ENTRY(ring->cur, ring->dma_desc_count); + + incomplete = FXGMAC_GET_REG_BITS( + pkt_info->attributes, + RX_PACKET_ATTRIBUTES_INCOMPLETE_POS, + RX_PACKET_ATTRIBUTES_INCOMPLETE_LEN); + context_next = FXGMAC_GET_REG_BITS( + pkt_info->attributes, + RX_PACKET_ATTRIBUTES_CONTEXT_NEXT_POS, + RX_PACKET_ATTRIBUTES_CONTEXT_NEXT_LEN); + context = FXGMAC_GET_REG_BITS(pkt_info->attributes, + RX_PACKET_ATTRIBUTES_CONTEXT_POS, + RX_PACKET_ATTRIBUTES_CONTEXT_LEN); + + if (incomplete || context_next) + goto read_again; + + if (pkt_info->errors) { + netif_err(pdata, rx_err, netdev, + "error in received packet\n"); + dev_kfree_skb(skb); + pdata->netdev->stats.rx_dropped++; + goto next_packet; + } + + if (!context) { + len = desc_data->rx.len; + + if (len == 0) { + if (net_ratelimit()) + netif_err( + pdata, rx_err, netdev, + "A packet of length 0 was received\n"); + pdata->netdev->stats.rx_length_errors++; + pdata->netdev->stats.rx_dropped++; + goto next_packet; + } + + if (len && !skb) { + skb = fxgmac_create_skb(pdata, napi, desc_data, + len); + if (unlikely(!skb)) { + if (net_ratelimit()) + netif_err( + pdata, rx_err, netdev, + "create skb failed\n"); + pdata->netdev->stats.rx_dropped++; + goto next_packet; + } + } + + max_len = netdev->mtu + ETH_HLEN; + if (!(netdev->features & NETIF_F_HW_VLAN_CTAG_RX) && + skb->protocol == htons(ETH_P_8021Q)) + max_len += VLAN_HLEN; + if (len > max_len) { + if (net_ratelimit()) + netif_err( + pdata, rx_err, netdev, + "len %d larger than max size %d\n", + len, max_len); + pdata->netdev->stats.rx_length_errors++; + pdata->netdev->stats.rx_dropped++; + dev_kfree_skb(skb); + goto next_packet; + } + } + + if (!skb) { + pdata->netdev->stats.rx_dropped++; + goto next_packet; + } + + if (netif_msg_pktdata(pdata)) + fxgmac_print_pkt(netdev, skb, false); + + skb_checksum_none_assert(skb); + if (netdev->features & NETIF_F_RXCSUM) + skb->ip_summed = CHECKSUM_UNNECESSARY; + + if (FXGMAC_GET_REG_BITS(pkt_info->attributes, + RX_PACKET_ATTRIBUTES_VLAN_CTAG_POS, + RX_PACKET_ATTRIBUTES_VLAN_CTAG_LEN)) { + __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), + pkt_info->vlan_ctag); + pdata->stats.rx_vlan_packets++; + } + + if (FXGMAC_GET_REG_BITS(pkt_info->attributes, + RX_PACKET_ATTRIBUTES_RSS_HASH_POS, + RX_PACKET_ATTRIBUTES_RSS_HASH_LEN)) + skb_set_hash(skb, pkt_info->rss_hash, + pkt_info->rss_hash_type); + + skb->dev = netdev; + skb->protocol = eth_type_trans(skb, netdev); + skb_record_rx_queue(skb, channel->queue_index); + + if (pdata->expansion.fxgmac_test_tso_flag) { + /* tso test */ + if (pdata->expansion.fxgmac_test_tso_seg_num == 1) { + /* last segment */ + if (pdata->expansion.fxgmac_test_last_tso_len == + skb->len + FXGMAC_TEST_MAC_HEAD_LEN) { + /* receive last segment, reset flag */ + pdata->expansion.fxgmac_test_tso_flag = + false; + pdata->expansion + .fxgmac_test_tso_seg_num = 0; + pdata->expansion.fxgmac_test_packet_len = + 0; + pdata->expansion + .fxgmac_test_last_tso_len = 0; + + /* process packet */ + if ((pdata->expansion + .fxgmac_test_skb_arr_in_index + + 1) % FXGMAC_MAX_DBG_TEST_PKT != + pdata->expansion + .fxgmac_test_skb_arr_out_index) { + struct sk_buff *tmpskb = + skb_copy(skb, + GFP_ATOMIC); + skb_push( + tmpskb, + FXGMAC_TEST_MAC_HEAD_LEN); + + pdata->expansion.fxgmac_test_skb_array + [pdata->expansion + .fxgmac_test_skb_arr_in_index] = + tmpskb; + pdata->expansion + .fxgmac_test_skb_arr_in_index = + (pdata->expansion + .fxgmac_test_skb_arr_in_index + + 1) % + FXGMAC_MAX_DBG_TEST_PKT; + } else { + DPRINTK("loopback test buffer is full."); + } + } + } else /* non last segment */ + { + if (pdata->expansion.fxgmac_test_packet_len == + skb->len + FXGMAC_TEST_MAC_HEAD_LEN) { + /* receive a segment */ + pdata->expansion + .fxgmac_test_tso_seg_num--; + + /* process packet */ + if ((pdata->expansion + .fxgmac_test_skb_arr_in_index + + 1) % FXGMAC_MAX_DBG_TEST_PKT != + pdata->expansion + .fxgmac_test_skb_arr_out_index) { + struct sk_buff *tmpskb = + skb_copy(skb, + GFP_ATOMIC); + skb_push( + tmpskb, + FXGMAC_TEST_MAC_HEAD_LEN); + + pdata->expansion.fxgmac_test_skb_array + [pdata->expansion + .fxgmac_test_skb_arr_in_index] = + tmpskb; + pdata->expansion + .fxgmac_test_skb_arr_in_index = + (pdata->expansion + .fxgmac_test_skb_arr_in_index + + 1) % + FXGMAC_MAX_DBG_TEST_PKT; + } else { + DPRINTK("loopback test buffer is full."); + } + } + } + } else if (pdata->expansion.fxgmac_test_packet_len != 0) { + /* xsum and phy loopback test */ + if (pdata->expansion.fxgmac_test_packet_len == + skb->len + FXGMAC_TEST_MAC_HEAD_LEN) { + /* reset fxg_packet_len */ + pdata->expansion.fxgmac_test_packet_len = 0; + + if ((pdata->expansion + .fxgmac_test_skb_arr_in_index + + 1) % FXGMAC_MAX_DBG_TEST_PKT != + pdata->expansion + .fxgmac_test_skb_arr_out_index) { + struct sk_buff *tmpskb = + skb_copy(skb, GFP_ATOMIC); + skb_push(tmpskb, + FXGMAC_TEST_MAC_HEAD_LEN); + + pdata->expansion.fxgmac_test_skb_array + [pdata->expansion + .fxgmac_test_skb_arr_in_index] = + tmpskb; + pdata->expansion + .fxgmac_test_skb_arr_in_index = + (pdata->expansion + .fxgmac_test_skb_arr_in_index + + 1) % + FXGMAC_MAX_DBG_TEST_PKT; + } else { + DPRINTK("loopback test buffer is full."); + } + } + } + + napi_gro_receive(napi, skb); + +next_packet: + packet_count++; + + pdata->netdev->stats.rx_packets++; + pdata->netdev->stats.rx_bytes += len; + } + + return packet_count; +} + +static int fxgmac_one_poll_tx(struct napi_struct *napi, int budget) +{ + struct fxgmac_channel *channel = + container_of(napi, struct fxgmac_channel, expansion.napi_tx); + struct fxgmac_pdata *pdata = channel->pdata; + struct fxgmac_hw_ops *hw_ops = &pdata->hw_ops; + + fxgmac_tx_poll(channel); + if (napi_complete_done(napi, 0)) { + hw_ops->enable_msix_one_interrupt(pdata, MSI_ID_TXQ0); + } + return 0; +} + +static int fxgmac_one_poll_rx(struct napi_struct *napi, int budget) +{ + struct fxgmac_channel *channel = + container_of(napi, struct fxgmac_channel, expansion.napi_rx); + int processed = 0; + struct fxgmac_pdata *pdata = channel->pdata; + struct fxgmac_hw_ops *hw_ops = &pdata->hw_ops; + + processed = fxgmac_rx_poll(channel, budget); + if (processed < budget) { + /* if there no interrupt occurred when this interrupt running,struct napi's state is NAPIF_STATE_SCHED, + * napi_complete_done return true and we can enable irq,it will not cause unbalanced iqr issure. + * if there more interrupt occurred when this interrupt running,struct napi's state is NAPIF_STATE_SCHED | NAPIF_STATE_MISSED + * because napi_schedule_prep will make it. At this time napi_complete_done will return false and + * schedule poll again because of NAPIF_STATE_MISSED,it will cause unbalanced irq issure. + */ + if (napi_complete_done(napi, processed)) { + hw_ops->enable_msix_one_interrupt(pdata, + channel->queue_index); + } + } + + return processed; +} + +static int fxgmac_all_poll(struct napi_struct *napi, int budget) +{ + struct fxgmac_pdata *pdata = + container_of(napi, struct fxgmac_pdata, expansion.napi); + struct fxgmac_channel *channel; + struct fxgmac_hw_ops *hw_ops = &pdata->hw_ops; + int processed; + unsigned int i; + + if (netif_msg_rx_status(pdata)) { + DPRINTK("rx all_poll callin budget=%d\n", budget); + } + + processed = 0; + do { + channel = pdata->channel_head; + /* Cleanup Tx ring first */ + /*since only 1 tx channel supported in this version, poll ch 0 always. */ + fxgmac_tx_poll(pdata->channel_head + 0); + for (i = 0; i < pdata->channel_count; i++, channel++) { + processed += fxgmac_rx_poll(channel, budget); + } + } while (false); + + /* If we processed everything, we are done */ + if (processed < budget) { + /* Turn off polling */ + if (napi_complete_done(napi, processed)) + hw_ops->enable_mgm_interrupt(pdata); + } + + if ((processed) && (netif_msg_rx_status(pdata))) { + DPRINTK("rx all_poll callout received = %d\n", processed); + } + + return processed; +} + +void fxgmac_tx_start_xmit(struct fxgmac_channel *channel, + struct fxgmac_ring *ring) +{ + struct fxgmac_pdata *pdata = channel->pdata; + struct fxgmac_desc_data *desc_data; + + /* Make sure everything is written before the register write */ + wmb(); + + /* Issue a poll command to Tx DMA by writing address + * of next immediate free descriptor + */ + desc_data = FXGMAC_GET_DESC_DATA(ring, ring->cur); + + writereg(pdata->pAdapter, lower_32_bits(desc_data->dma_desc_addr), + FXGMAC_DMA_REG(channel, DMA_CH_TDTR_LO)); + + if (netif_msg_tx_done(pdata)) { + DPRINTK("tx_start_xmit: dump before wr reg, \ + dma base=0x%016llx,reg=0x%08x, \ + tx timer usecs=%u,tx_timer_active=%u\n", + desc_data->dma_desc_addr, + readreg(pdata->pAdapter, + FXGMAC_DMA_REG(channel, DMA_CH_TDTR_LO)), + pdata->tx_usecs, channel->tx_timer_active); + } + + ring->tx.xmit_more = 0; +} + +void fxgmac_dev_xmit(struct fxgmac_channel *channel) +{ + struct fxgmac_pdata *pdata = channel->pdata; + struct fxgmac_ring *ring = channel->tx_ring; + unsigned int tso_context, vlan_context; + struct fxgmac_desc_data *desc_data; + struct fxgmac_dma_desc *dma_desc; + struct fxgmac_pkt_info *pkt_info; + unsigned int csum, tso, vlan; + int start_index = ring->cur; + int cur_index = ring->cur; + int i; + + if (netif_msg_tx_done(pdata)) + DPRINTK("dev_xmit callin, desc cur=%d\n", cur_index); + + pkt_info = &ring->pkt_info; + csum = FXGMAC_GET_REG_BITS(pkt_info->attributes, + TX_PACKET_ATTRIBUTES_CSUM_ENABLE_POS, + TX_PACKET_ATTRIBUTES_CSUM_ENABLE_LEN); + tso = FXGMAC_GET_REG_BITS(pkt_info->attributes, + TX_PACKET_ATTRIBUTES_TSO_ENABLE_POS, + TX_PACKET_ATTRIBUTES_TSO_ENABLE_LEN); + vlan = FXGMAC_GET_REG_BITS(pkt_info->attributes, + TX_PACKET_ATTRIBUTES_VLAN_CTAG_POS, + TX_PACKET_ATTRIBUTES_VLAN_CTAG_LEN); + + if (tso && (pkt_info->mss != ring->tx.cur_mss)) + tso_context = 1; + else + tso_context = 0; + + if ((tso_context) && (netif_msg_tx_done(pdata))) { + DPRINTK("fxgmac_dev_xmit,tso_%s tso=0x%x,pkt_mss=%d,cur_mss=%d\n", + (pkt_info->mss) ? "start" : "stop", tso, pkt_info->mss, + ring->tx.cur_mss); + } + + if (vlan && (pkt_info->vlan_ctag != ring->tx.cur_vlan_ctag)) + vlan_context = 1; + else + vlan_context = 0; + + if (vlan && (netif_msg_tx_done(pdata))) + DPRINTK("fxgmac_dev_xmi:pkt vlan=%d, ring vlan=%d, vlan_context=%d\n", + pkt_info->vlan_ctag, ring->tx.cur_vlan_ctag, + vlan_context); + + desc_data = FXGMAC_GET_DESC_DATA(ring, cur_index); + dma_desc = desc_data->dma_desc; + + /* Create a context descriptor if this is a TSO pkt_info */ + if (tso_context || vlan_context) { + if (tso_context) { + if (netif_msg_tx_done(pdata)) + DPRINTK("xlgamc dev xmit,construct tso context descriptor, mss=%u\n", + pkt_info->mss); + + /* Set the MSS size */ + dma_desc->desc2 = FXGMAC_SET_REG_BITS_LE( + dma_desc->desc2, TX_CONTEXT_DESC2_MSS_POS, + TX_CONTEXT_DESC2_MSS_LEN, pkt_info->mss); + + /* Mark it as a CONTEXT descriptor */ + dma_desc->desc3 = FXGMAC_SET_REG_BITS_LE( + dma_desc->desc3, TX_CONTEXT_DESC3_CTXT_POS, + TX_CONTEXT_DESC3_CTXT_LEN, 1); + + /* Indicate this descriptor contains the MSS */ + dma_desc->desc3 = FXGMAC_SET_REG_BITS_LE( + dma_desc->desc3, TX_CONTEXT_DESC3_TCMSSV_POS, + TX_CONTEXT_DESC3_TCMSSV_LEN, 1); + + ring->tx.cur_mss = pkt_info->mss; + } + + if (vlan_context) { + netif_dbg(pdata, tx_queued, pdata->netdev, + "VLAN context descriptor, ctag=%u\n", + pkt_info->vlan_ctag); + + /* Mark it as a CONTEXT descriptor */ + dma_desc->desc3 = FXGMAC_SET_REG_BITS_LE( + dma_desc->desc3, TX_CONTEXT_DESC3_CTXT_POS, + TX_CONTEXT_DESC3_CTXT_LEN, 1); + + /* Set the VLAN tag */ + dma_desc->desc3 = FXGMAC_SET_REG_BITS_LE( + dma_desc->desc3, TX_CONTEXT_DESC3_VT_POS, + TX_CONTEXT_DESC3_VT_LEN, pkt_info->vlan_ctag); + + /* Indicate this descriptor contains the VLAN tag */ + dma_desc->desc3 = FXGMAC_SET_REG_BITS_LE( + dma_desc->desc3, TX_CONTEXT_DESC3_VLTV_POS, + TX_CONTEXT_DESC3_VLTV_LEN, 1); + + ring->tx.cur_vlan_ctag = pkt_info->vlan_ctag; + } + + cur_index = FXGMAC_GET_ENTRY(cur_index, ring->dma_desc_count); + desc_data = FXGMAC_GET_DESC_DATA(ring, cur_index); + dma_desc = desc_data->dma_desc; + } + + /* Update buffer address (for TSO this is the header) */ + dma_desc->desc0 = cpu_to_le32(lower_32_bits(desc_data->skb_dma)); + dma_desc->desc1 = cpu_to_le32(upper_32_bits(desc_data->skb_dma)); + + /* Update the buffer length */ + dma_desc->desc2 = FXGMAC_SET_REG_BITS_LE(dma_desc->desc2, + TX_NORMAL_DESC2_HL_B1L_POS, + TX_NORMAL_DESC2_HL_B1L_LEN, + desc_data->skb_dma_len); + + /* VLAN tag insertion check */ + if (vlan) { + dma_desc->desc2 = FXGMAC_SET_REG_BITS_LE( + dma_desc->desc2, TX_NORMAL_DESC2_VTIR_POS, + TX_NORMAL_DESC2_VTIR_LEN, TX_NORMAL_DESC2_VLAN_INSERT); + pdata->stats.tx_vlan_packets++; + } + + /* Timestamp enablement check */ + if (FXGMAC_GET_REG_BITS(pkt_info->attributes, + TX_PACKET_ATTRIBUTES_PTP_POS, + TX_PACKET_ATTRIBUTES_PTP_LEN)) + dma_desc->desc2 = FXGMAC_SET_REG_BITS_LE( + dma_desc->desc2, TX_NORMAL_DESC2_TTSE_POS, + TX_NORMAL_DESC2_TTSE_LEN, 1); + + /* Mark it as First Descriptor */ + dma_desc->desc3 = FXGMAC_SET_REG_BITS_LE(dma_desc->desc3, + TX_NORMAL_DESC3_FD_POS, + TX_NORMAL_DESC3_FD_LEN, 1); + + /* Mark it as a NORMAL descriptor */ + dma_desc->desc3 = FXGMAC_SET_REG_BITS_LE(dma_desc->desc3, + TX_NORMAL_DESC3_CTXT_POS, + TX_NORMAL_DESC3_CTXT_LEN, 0); + + /* Set OWN bit if not the first descriptor */ + if (cur_index != start_index) + dma_desc->desc3 = FXGMAC_SET_REG_BITS_LE( + dma_desc->desc3, TX_NORMAL_DESC3_OWN_POS, + TX_NORMAL_DESC3_OWN_LEN, 1); + + if (tso) { + /* Enable TSO */ + dma_desc->desc3 = FXGMAC_SET_REG_BITS_LE( + dma_desc->desc3, TX_NORMAL_DESC3_TSE_POS, + TX_NORMAL_DESC3_TSE_LEN, 1); + dma_desc->desc3 = FXGMAC_SET_REG_BITS_LE( + dma_desc->desc3, TX_NORMAL_DESC3_TCPPL_POS, + TX_NORMAL_DESC3_TCPPL_LEN, pkt_info->tcp_payload_len); + dma_desc->desc3 = FXGMAC_SET_REG_BITS_LE( + dma_desc->desc3, TX_NORMAL_DESC3_TCPHDRLEN_POS, + TX_NORMAL_DESC3_TCPHDRLEN_LEN, + pkt_info->tcp_header_len / 4); + + pdata->stats.tx_tso_packets++; + } else { + /* Enable CRC and Pad Insertion */ + dma_desc->desc3 = FXGMAC_SET_REG_BITS_LE( + dma_desc->desc3, TX_NORMAL_DESC3_CPC_POS, + TX_NORMAL_DESC3_CPC_LEN, 0); + + /* Enable HW CSUM */ + if (csum) + dma_desc->desc3 = FXGMAC_SET_REG_BITS_LE( + dma_desc->desc3, TX_NORMAL_DESC3_CIC_POS, + TX_NORMAL_DESC3_CIC_LEN, 0x3); + + /* Set the total length to be transmitted */ + dma_desc->desc3 = FXGMAC_SET_REG_BITS_LE(dma_desc->desc3, + TX_NORMAL_DESC3_FL_POS, + TX_NORMAL_DESC3_FL_LEN, + pkt_info->length); + } + if (netif_msg_tx_done(pdata)) + DPRINTK("dev_xmit before more descs, desc cur=%d, start=%d, desc=%#x,%#x,%#x,%#x\n", + cur_index, start_index, dma_desc->desc0, + dma_desc->desc1, dma_desc->desc2, dma_desc->desc3); + + if (start_index <= cur_index) + i = cur_index - start_index + 1; + else + i = ring->dma_desc_count - start_index + cur_index; + + for (; i < pkt_info->desc_count; i++) { + cur_index = FXGMAC_GET_ENTRY(cur_index, ring->dma_desc_count); + + desc_data = FXGMAC_GET_DESC_DATA(ring, cur_index); + dma_desc = desc_data->dma_desc; + + /* Update buffer address */ + dma_desc->desc0 = + cpu_to_le32(lower_32_bits(desc_data->skb_dma)); + dma_desc->desc1 = + cpu_to_le32(upper_32_bits(desc_data->skb_dma)); + + /* Update the buffer length */ + dma_desc->desc2 = FXGMAC_SET_REG_BITS_LE( + dma_desc->desc2, TX_NORMAL_DESC2_HL_B1L_POS, + TX_NORMAL_DESC2_HL_B1L_LEN, desc_data->skb_dma_len); + + /* Set OWN bit */ + dma_desc->desc3 = FXGMAC_SET_REG_BITS_LE( + dma_desc->desc3, TX_NORMAL_DESC3_OWN_POS, + TX_NORMAL_DESC3_OWN_LEN, 1); + + /* Mark it as NORMAL descriptor */ + dma_desc->desc3 = FXGMAC_SET_REG_BITS_LE( + dma_desc->desc3, TX_NORMAL_DESC3_CTXT_POS, + TX_NORMAL_DESC3_CTXT_LEN, 0); + + /* Enable HW CSUM */ + if (csum) + dma_desc->desc3 = FXGMAC_SET_REG_BITS_LE( + dma_desc->desc3, TX_NORMAL_DESC3_CIC_POS, + TX_NORMAL_DESC3_CIC_LEN, 0x3); + } + + /* Set LAST bit for the last descriptor */ + dma_desc->desc3 = FXGMAC_SET_REG_BITS_LE(dma_desc->desc3, + TX_NORMAL_DESC3_LD_POS, + TX_NORMAL_DESC3_LD_LEN, 1); + + dma_desc->desc2 = FXGMAC_SET_REG_BITS_LE(dma_desc->desc2, + TX_NORMAL_DESC2_IC_POS, + TX_NORMAL_DESC2_IC_LEN, 1); + + /* Save the Tx info to report back during cleanup */ + desc_data->tx.packets = pkt_info->tx_packets; + desc_data->tx.bytes = pkt_info->tx_bytes; + + if (netif_msg_tx_done(pdata)) + DPRINTK("dev_xmit last descs, desc cur=%d, desc=%#x,%#x,%#x,%#x\n", + cur_index, dma_desc->desc0, dma_desc->desc1, + dma_desc->desc2, dma_desc->desc3); + + /* In case the Tx DMA engine is running, make sure everything + * is written to the descriptor(s) before setting the OWN bit + * for the first descriptor + */ + dma_wmb(); + + /* Set OWN bit for the first descriptor */ + desc_data = FXGMAC_GET_DESC_DATA(ring, start_index); + dma_desc = desc_data->dma_desc; + dma_desc->desc3 = FXGMAC_SET_REG_BITS_LE(dma_desc->desc3, + TX_NORMAL_DESC3_OWN_POS, + TX_NORMAL_DESC3_OWN_LEN, 1); + + if (netif_msg_tx_done(pdata)) + DPRINTK("dev_xmit first descs, start=%d, desc=%#x,%#x,%#x,%#x\n", + start_index, dma_desc->desc0, dma_desc->desc1, + dma_desc->desc2, dma_desc->desc3); + + if (netif_msg_tx_queued(pdata)) + fxgmac_dump_tx_desc(pdata, ring, start_index, + pkt_info->desc_count, 1); + + /* Make sure ownership is written to the descriptor */ + smp_wmb(); + + ring->cur = FXGMAC_GET_ENTRY(cur_index, ring->dma_desc_count); + + fxgmac_tx_start_xmit(channel, ring); + + /* yzhang for reduce debug output */ + if (netif_msg_tx_done(pdata)) { + DPRINTK("dev_xmit callout %s: descriptors %u to %u written\n", + channel->name, start_index & (ring->dma_desc_count - 1), + (ring->cur - 1) & (ring->dma_desc_count - 1)); + } +} + +extern void fxgmac_diag_get_rx_info(struct fxgmac_channel *channel); + +static void fxgmac_get_rx_tstamp(struct fxgmac_pkt_info *pkt_info, + struct fxgmac_dma_desc *dma_desc) +{ + u64 nsec; + nsec = le32_to_cpu(dma_desc->desc1); + nsec <<= 32; + nsec |= le32_to_cpu(dma_desc->desc0); + if (nsec != 0xffffffffffffffffULL) { + pkt_info->rx_tstamp = nsec; + pkt_info->attributes = FXGMAC_SET_REG_BITS( + pkt_info->attributes, + RX_PACKET_ATTRIBUTES_RX_TSTAMP_POS, + RX_PACKET_ATTRIBUTES_RX_TSTAMP_LEN, 1); + } +} + +static int fxgmac_dev_read(struct fxgmac_channel *channel) +{ + struct fxgmac_pdata *pdata = channel->pdata; + struct fxgmac_ring *ring = channel->rx_ring; + struct net_device *netdev = pdata->netdev; + struct fxgmac_desc_data *desc_data; + struct fxgmac_dma_desc *dma_desc; + struct fxgmac_pkt_info *pkt_info; + u32 ipce, iphe, rxparser; + unsigned int err, etlt; + + static unsigned int cnt_incomplete = 0; + + desc_data = FXGMAC_GET_DESC_DATA(ring, ring->cur); + dma_desc = desc_data->dma_desc; + pkt_info = &ring->pkt_info; + + /* Check for data availability */ + if (FXGMAC_GET_REG_BITS_LE(dma_desc->desc3, RX_NORMAL_DESC3_OWN_POS, + RX_NORMAL_DESC3_OWN_LEN)) + return 1; + + /* Make sure descriptor fields are read after reading the OWN bit */ + dma_rmb(); + + if (netif_msg_rx_status(pdata)) + fxgmac_dump_rx_desc(pdata, ring, ring->cur); + + if (FXGMAC_GET_REG_BITS_LE(dma_desc->desc3, RX_NORMAL_DESC3_CTXT_POS, + RX_NORMAL_DESC3_CTXT_LEN)) { + /* Timestamp Context Descriptor */ + fxgmac_get_rx_tstamp(pkt_info, dma_desc); + + pkt_info->attributes = FXGMAC_SET_REG_BITS( + pkt_info->attributes, RX_PACKET_ATTRIBUTES_CONTEXT_POS, + RX_PACKET_ATTRIBUTES_CONTEXT_LEN, 1); + pkt_info->attributes = FXGMAC_SET_REG_BITS( + pkt_info->attributes, + RX_PACKET_ATTRIBUTES_CONTEXT_NEXT_POS, + RX_PACKET_ATTRIBUTES_CONTEXT_NEXT_LEN, 0); + if (netif_msg_rx_status(pdata)) + DPRINTK("dev_read context desc,ch=%s\n", channel->name); + return 0; + } + + /* Normal Descriptor, be sure Context Descriptor bit is off */ + pkt_info->attributes = FXGMAC_SET_REG_BITS( + pkt_info->attributes, RX_PACKET_ATTRIBUTES_CONTEXT_POS, + RX_PACKET_ATTRIBUTES_CONTEXT_LEN, 0); + + /* Get the header length */ + if (FXGMAC_GET_REG_BITS_LE(dma_desc->desc3, RX_NORMAL_DESC3_FD_POS, + RX_NORMAL_DESC3_FD_LEN)) { + desc_data->rx.hdr_len = FXGMAC_GET_REG_BITS_LE( + dma_desc->desc2, RX_NORMAL_DESC2_HL_POS, + RX_NORMAL_DESC2_HL_LEN); + if (desc_data->rx.hdr_len) + pdata->stats.rx_split_header_packets++; + } + + /* Get the pkt_info length */ + desc_data->rx.len = FXGMAC_GET_REG_BITS_LE(dma_desc->desc3, + RX_NORMAL_DESC3_PL_POS, + RX_NORMAL_DESC3_PL_LEN); + + if (!FXGMAC_GET_REG_BITS_LE(dma_desc->desc3, RX_NORMAL_DESC3_LD_POS, + RX_NORMAL_DESC3_LD_LEN)) { + /* Not all the data has been transferred for this pkt_info */ + pkt_info->attributes = FXGMAC_SET_REG_BITS( + pkt_info->attributes, + RX_PACKET_ATTRIBUTES_INCOMPLETE_POS, + RX_PACKET_ATTRIBUTES_INCOMPLETE_LEN, 1); + cnt_incomplete++; + if ((cnt_incomplete < 2) && netif_msg_rx_status(pdata)) + DPRINTK("dev_read NOT last desc,pkt incomplete yet,%u\n", + cnt_incomplete); + + return 0; + } + if ((cnt_incomplete) && netif_msg_rx_status(pdata)) + DPRINTK("dev_read rx back to normal and incomplete cnt=%u\n", + cnt_incomplete); + cnt_incomplete = 0; + + /* This is the last of the data for this pkt_info */ + pkt_info->attributes = FXGMAC_SET_REG_BITS( + pkt_info->attributes, RX_PACKET_ATTRIBUTES_INCOMPLETE_POS, + RX_PACKET_ATTRIBUTES_INCOMPLETE_LEN, 0); + + /* Set checksum done indicator as appropriate */ + if (netdev->features & NETIF_F_RXCSUM) { + ipce = FXGMAC_GET_REG_BITS_LE(desc_data->dma_desc->desc1, + RX_NORMAL_DESC1_WB_IPCE_POS, + RX_NORMAL_DESC1_WB_IPCE_LEN); + iphe = FXGMAC_GET_REG_BITS_LE(desc_data->dma_desc->desc1, + RX_NORMAL_DESC1_WB_IPHE_POS, + RX_NORMAL_DESC1_WB_IPHE_LEN); + if (!ipce && !iphe) + pkt_info->attributes = FXGMAC_SET_REG_BITS( + pkt_info->attributes, + RX_PACKET_ATTRIBUTES_CSUM_DONE_POS, + RX_PACKET_ATTRIBUTES_CSUM_DONE_LEN, 1); + else + return 0; + } + + /* Check for errors (only valid in last descriptor) */ + err = FXGMAC_GET_REG_BITS_LE(dma_desc->desc3, RX_NORMAL_DESC3_ES_POS, + RX_NORMAL_DESC3_ES_LEN); + /* b111: Incomplete parsing due to ECC error */ + rxparser = FXGMAC_GET_REG_BITS_LE(desc_data->dma_desc->desc2, + RX_NORMAL_DESC2_WB_RAPARSER_POS, + RX_NORMAL_DESC2_WB_RAPARSER_LEN); + if (err || rxparser == 0x7) { + pkt_info->errors = FXGMAC_SET_REG_BITS( + pkt_info->errors, RX_PACKET_ERRORS_FRAME_POS, + RX_PACKET_ERRORS_FRAME_LEN, 1); + return 0; + } + + etlt = FXGMAC_GET_REG_BITS_LE(dma_desc->desc3, RX_NORMAL_DESC3_ETLT_POS, + RX_NORMAL_DESC3_ETLT_LEN); + if ((etlt == 0x4) && (netdev->features & NETIF_F_HW_VLAN_CTAG_RX)) { + pkt_info->attributes = FXGMAC_SET_REG_BITS( + pkt_info->attributes, + RX_PACKET_ATTRIBUTES_VLAN_CTAG_POS, + RX_PACKET_ATTRIBUTES_VLAN_CTAG_LEN, 1); + pkt_info->vlan_ctag = FXGMAC_GET_REG_BITS_LE( + dma_desc->desc0, RX_NORMAL_DESC0_OVT_POS, + RX_NORMAL_DESC0_OVT_LEN); + netif_dbg(pdata, rx_status, netdev, "vlan-ctag=%#06x\n", + pkt_info->vlan_ctag); + } + + return 0; +} diff --git a/drivers/net/ethernet/motorcomm/yt6801/fuxi-gmac-pci.c b/drivers/net/ethernet/motorcomm/yt6801/fuxi-gmac-pci.c new file mode 100644 index 000000000000..672c7e78a5b3 --- /dev/null +++ b/drivers/net/ethernet/motorcomm/yt6801/fuxi-gmac-pci.c @@ -0,0 +1,249 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (c) 2021 Motorcomm Corporation. */ + +#include "fuxi-gmac.h" +#include "fuxi-gmac-reg.h" + +/* declarations */ +static void fxgmac_shutdown(struct pci_dev *pdev); + +static int fxgmac_probe(struct pci_dev *pcidev, const struct pci_device_id *id) +{ + struct device *dev = &pcidev->dev; + struct fxgmac_resources res; + int i, ret; + + ret = pcim_enable_device(pcidev); + if (ret) { + dev_err(dev, "ERROR: fxgmac_probe failed to enable device\n"); + return ret; + } + + for (i = 0; i <= PCI_STD_RESOURCE_END; i++) { + if (pci_resource_len(pcidev, i) == 0) + continue; + + ret = pcim_iomap_regions(pcidev, BIT(i), FXGMAC_DRV_NAME); + if (ret) { + dev_err(dev, + "fxgmac_probe pcim_iomap_regions failed\n"); + return ret; + } + /* DPRINTK(KERN_INFO "fxgmac_probe iomap_region i=%#x,ret=%#x\n",(int)i,(int)ret); */ + break; + } + + pci_set_master(pcidev); + + memset(&res, 0, sizeof(res)); + res.irq = pcidev->irq; + res.addr = pcim_iomap_table(pcidev)[i]; + + return fxgmac_drv_probe(&pcidev->dev, &res); +} + +static void fxgmac_remove(struct pci_dev *pcidev) +{ + struct net_device *netdev; + struct fxgmac_pdata *pdata; + u32 msix; + + netdev = dev_get_drvdata(&pcidev->dev); + pdata = netdev_priv(netdev); + msix = FXGMAC_GET_REG_BITS(pdata->expansion.int_flags, + FXGMAC_FLAG_MSIX_POS, FXGMAC_FLAG_MSIX_LEN); + + fxgmac_drv_remove(&pcidev->dev); + +#ifdef CONFIG_PCI_MSI + if (msix) { + pci_disable_msix(pcidev); + kfree(pdata->expansion.msix_entries); + pdata->expansion.msix_entries = NULL; + } +#endif + + DPRINTK("%s has been removed\n", netdev->name); +} + +/* for Power management, 20210628 */ +static int __fxgmac_shutdown(struct pci_dev *pdev, bool *enable_wake) +{ + struct net_device *netdev = dev_get_drvdata(&pdev->dev); + struct fxgmac_pdata *pdata = netdev_priv(netdev); + u32 wufc = pdata->expansion.wol; +#ifdef CONFIG_PM + int retval = 0; +#endif + + DPRINTK("fxpm,_fxgmac_shutdown, callin\n"); + + rtnl_lock(); + + /* for linux shutdown, we just treat it as power off wol can be ignored + * for suspend, we do need recovery by wol + */ + fxgmac_net_powerdown(pdata, (unsigned int)!!wufc); + netif_device_detach(netdev); + rtnl_unlock(); + +#ifdef CONFIG_PM + retval = pci_save_state(pdev); + if (retval) { + DPRINTK("fxpm,_fxgmac_shutdown, save pci state failed.\n"); + return retval; + } +#endif + + DPRINTK("fxpm,_fxgmac_shutdown, save pci state done.\n"); + + pci_wake_from_d3(pdev, !!wufc); + *enable_wake = !!wufc; + + pci_disable_device(pdev); + + DPRINTK("fxpm,_fxgmac_shutdown callout, enable wake=%d.\n", + *enable_wake); + + return 0; +} + +static void fxgmac_shutdown(struct pci_dev *pdev) +{ + struct net_device *netdev = dev_get_drvdata(&pdev->dev); + struct fxgmac_pdata *pdata = netdev_priv(netdev); + struct fxgmac_hw_ops *hw_ops = &pdata->hw_ops; + bool wake; + + DPRINTK("fxpm, fxgmac_shutdown callin\n"); + + fxgmac_lock(pdata); + __fxgmac_shutdown(pdev, &wake); + hw_ops->led_under_shutdown(pdata); + + if (system_state == SYSTEM_POWER_OFF) { + pci_wake_from_d3(pdev, wake); + pci_set_power_state(pdev, PCI_D3hot); + } + DPRINTK("fxpm, fxgmac_shutdown callout, system power off=%d\n", + (system_state == SYSTEM_POWER_OFF) ? 1 : 0); + fxgmac_unlock(pdata); +} + +#ifdef CONFIG_PM +/* yzhang, 20210628 for PM */ +static int fxgmac_suspend(struct pci_dev *pdev, + pm_message_t __always_unused state) +{ + struct net_device *netdev = dev_get_drvdata(&pdev->dev); + struct fxgmac_pdata *pdata = netdev_priv(netdev); + struct fxgmac_hw_ops *hw_ops = &pdata->hw_ops; + int retval = 0; + bool wake; + + DPRINTK("fxpm, fxgmac_suspend callin\n"); + + fxgmac_lock(pdata); + if (pdata->expansion.dev_state != FXGMAC_DEV_START) + goto unlock; + + if (netif_running(netdev)) { + retval = __fxgmac_shutdown(pdev, &wake); + if (retval) + goto unlock; + } else { + wake = !!(pdata->expansion.wol); + } + hw_ops->led_under_sleep(pdata); + + if (wake) { + pci_prepare_to_sleep(pdev); + } else { + pci_wake_from_d3(pdev, false); + pci_set_power_state(pdev, PCI_D3hot); + } + + pdata->expansion.dev_state = FXGMAC_DEV_SUSPEND; + DPRINTK("fxpm, fxgmac_suspend callout to %s\n", + wake ? "sleep" : "D3hot"); + +unlock: + fxgmac_unlock(pdata); + return retval; +} + +static int fxgmac_resume(struct pci_dev *pdev) +{ + struct net_device *netdev = dev_get_drvdata(&pdev->dev); + struct fxgmac_pdata *pdata = netdev_priv(netdev); + u32 err = 0; + + DPRINTK("fxpm, fxgmac_resume callin\n"); + + fxgmac_lock(pdata); + if (pdata->expansion.dev_state != FXGMAC_DEV_SUSPEND) + goto unlock; + + pdata->expansion.dev_state = FXGMAC_DEV_RESUME; + + pci_set_power_state(pdev, PCI_D0); + pci_restore_state(pdev); + /* + * pci_restore_state clears dev->state_saved so call + * pci_save_state to restore it. + */ + pci_save_state(pdev); + + err = pci_enable_device_mem(pdev); + if (err) { + dev_err(pdata->dev, + "fxgmac_resume, failed to enable PCI device from suspend\n"); + goto unlock; + } + smp_mb__before_atomic(); + __clear_bit(FXGMAC_POWER_STATE_DOWN, &pdata->expansion.powerstate); + pci_set_master(pdev); + + pci_wake_from_d3(pdev, false); + + rtnl_lock(); + err = 0; + if (!err && netif_running(netdev)) + fxgmac_net_powerup(pdata); + + if (!err) + netif_device_attach(netdev); + + rtnl_unlock(); + + DPRINTK("fxpm, fxgmac_resume callout\n"); +unlock: + fxgmac_unlock(pdata); + return err; +} +#endif + +static const struct pci_device_id fxgmac_pci_tbl[] = { { PCI_DEVICE(0x1f0a, + 0x6801) }, + { 0 } }; +MODULE_DEVICE_TABLE(pci, fxgmac_pci_tbl); + +static struct pci_driver fxgmac_pci_driver = { + .name = FXGMAC_DRV_NAME, + .id_table = fxgmac_pci_tbl, + .probe = fxgmac_probe, + .remove = fxgmac_remove, +#ifdef CONFIG_PM + /* currently, we only use USE_LEGACY_PM_SUPPORT */ + .suspend = fxgmac_suspend, + .resume = fxgmac_resume, +#endif + .shutdown = fxgmac_shutdown, +}; + +module_pci_driver(fxgmac_pci_driver); + +MODULE_DESCRIPTION(FXGMAC_DRV_DESC); +MODULE_VERSION(FXGMAC_DRV_VERSION); +MODULE_AUTHOR("Motorcomm Electronic Tech. Co., Ltd."); +MODULE_LICENSE("GPL"); diff --git a/drivers/net/ethernet/motorcomm/yt6801/fuxi-gmac-phy.c b/drivers/net/ethernet/motorcomm/yt6801/fuxi-gmac-phy.c new file mode 100644 index 000000000000..9e921c44756a --- /dev/null +++ b/drivers/net/ethernet/motorcomm/yt6801/fuxi-gmac-phy.c @@ -0,0 +1,417 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (c) 2021 Motorcomm Corporation. */ + +#include "fuxi-gmac.h" +#include "fuxi-gmac-reg.h" + +/* + * When in forced mode, set the speed, duplex, and auto-negotiation of the PHY + * all at once to avoid the problems caused by individual settings + * on some machines + */ +int fxgmac_phy_force_mode(struct fxgmac_pdata *pdata) +{ + struct fxgmac_hw_ops *hw_ops = &pdata->hw_ops; + u32 regval = 0; + unsigned int high_bit = 0, low_bit = 0; + int ret = 0; + + switch (pdata->phy_speed) { + case SPEED_1000: + high_bit = 1, low_bit = 0; + break; + case SPEED_100: + high_bit = 0, low_bit = 1; + break; + case SPEED_10: + high_bit = 0, low_bit = 0; + break; + default: + break; + } + + hw_ops->read_ephy_reg(pdata, REG_MII_BMCR, ®val); + regval = FXGMAC_SET_REG_BITS(regval, PHY_CR_AUTOENG_POS, + PHY_CR_AUTOENG_LEN, pdata->phy_autoeng); + regval = FXGMAC_SET_REG_BITS(regval, PHY_CR_SPEED_SEL_H_POS, + PHY_CR_SPEED_SEL_H_LEN, high_bit); + regval = FXGMAC_SET_REG_BITS(regval, PHY_CR_SPEED_SEL_L_POS, + PHY_CR_SPEED_SEL_L_LEN, low_bit); + regval = FXGMAC_SET_REG_BITS(regval, PHY_CR_DUPLEX_POS, + PHY_CR_DUPLEX_LEN, pdata->phy_duplex); + ret = hw_ops->write_ephy_reg(pdata, REG_MII_BMCR, regval); + return ret; +} + +int fxgmac_phy_force_speed(struct fxgmac_pdata *pdata, int speed) +{ + struct fxgmac_hw_ops *hw_ops = &pdata->hw_ops; + u32 regval = 0; + unsigned int high_bit = 0, low_bit = 0; + int ret = 0; + + switch (speed) { + case SPEED_1000: + high_bit = 1, low_bit = 0; + break; + case SPEED_100: + high_bit = 0, low_bit = 1; + break; + case SPEED_10: + high_bit = 0, low_bit = 0; + break; + default: + break; + } + + hw_ops->read_ephy_reg(pdata, REG_MII_BMCR, ®val); + regval = FXGMAC_SET_REG_BITS(regval, PHY_CR_SPEED_SEL_H_POS, + PHY_CR_SPEED_SEL_H_LEN, high_bit); + regval = FXGMAC_SET_REG_BITS(regval, PHY_CR_SPEED_SEL_L_POS, + PHY_CR_SPEED_SEL_L_LEN, low_bit); + ret = hw_ops->write_ephy_reg(pdata, REG_MII_BMCR, regval); + return ret; +} + +int fxgmac_phy_force_duplex(struct fxgmac_pdata *pdata, int duplex) +{ + struct fxgmac_hw_ops *hw_ops = &pdata->hw_ops; + u32 regval = 0; + int ret = 0; + + hw_ops->read_ephy_reg(pdata, REG_MII_BMCR, ®val); + regval = FXGMAC_SET_REG_BITS(regval, PHY_CR_DUPLEX_POS, + PHY_CR_DUPLEX_LEN, (duplex ? 1 : 0)); + hw_ops->write_ephy_reg(pdata, REG_MII_BMCR, regval); + return ret; +} + +int fxgmac_phy_force_autoneg(struct fxgmac_pdata *pdata, int autoneg) +{ + struct fxgmac_hw_ops *hw_ops = &pdata->hw_ops; + u32 regval = 0; + int ret = 0; + + hw_ops->read_ephy_reg(pdata, REG_MII_BMCR, ®val); + regval = FXGMAC_SET_REG_BITS(regval, PHY_CR_AUTOENG_POS, + PHY_CR_AUTOENG_LEN, (autoneg ? 1 : 0)); + ret = hw_ops->write_ephy_reg(pdata, REG_MII_BMCR, regval); + return ret; +} + +void fxgmac_set_phy_link_ksettings(struct fxgmac_pdata *pdata) +{ + struct fxgmac_hw_ops *hw_ops = &pdata->hw_ops; + pdata->phy_speed = pdata->expansion.pre_phy_speed; + pdata->phy_duplex = pdata->expansion.pre_phy_duplex; + pdata->phy_autoeng = pdata->expansion.pre_phy_autoneg; + + if (pdata->phy_autoeng) + hw_ops->phy_config(pdata); + else + fxgmac_phy_force_mode(pdata); +} + +/* + * input: lport + * output: + * cap_mask, bit definitions: + * pause capbility and 100/10 capbilitys follow the definition of mii reg4. + * for 1000M capability, bit0=1000M half; bit1=1000M full, refer to mii reg9.[9:8]. + */ +int fxgmac_ephy_autoneg_ability_get(struct fxgmac_pdata *pdata, + unsigned int *cap_mask) +{ + struct fxgmac_hw_ops *hw_ops = &pdata->hw_ops; + unsigned int val; + unsigned int reg; + + if ((!hw_ops->read_ephy_reg) || (!hw_ops->write_ephy_reg)) + return -1; + + reg = REG_MII_ADVERTISE; + if (hw_ops->read_ephy_reg(pdata, reg, &val) < 0) + goto busy_exit; + + if (FXGMAC_ADVERTISE_10HALF & val) { + *cap_mask |= FXGMAC_ADVERTISE_10HALF; + } else { + *cap_mask &= ~FXGMAC_ADVERTISE_10HALF; + } + + if (FXGMAC_ADVERTISE_10FULL & val) { + *cap_mask |= FXGMAC_ADVERTISE_10FULL; + } else { + *cap_mask &= ~FXGMAC_ADVERTISE_10FULL; + } + + if (FXGMAC_ADVERTISE_100HALF & val) { + *cap_mask |= FXGMAC_ADVERTISE_100HALF; + } else { + *cap_mask &= ~FXGMAC_ADVERTISE_100HALF; + } + + if (FXGMAC_ADVERTISE_100FULL & val) { + *cap_mask |= FXGMAC_ADVERTISE_100FULL; + } else { + *cap_mask &= ~FXGMAC_ADVERTISE_100FULL; + } + + if (FXGMAC_ADVERTISE_PAUSE_CAP & val) { + *cap_mask |= FXGMAC_ADVERTISE_PAUSE_CAP; + } else { + *cap_mask &= ~FXGMAC_ADVERTISE_PAUSE_CAP; + } + + if (FXGMAC_ADVERTISE_PAUSE_ASYM & val) { + *cap_mask |= FXGMAC_ADVERTISE_PAUSE_ASYM; + } else { + *cap_mask &= ~FXGMAC_ADVERTISE_PAUSE_ASYM; + } + + reg = REG_MII_CTRL1000; + if (hw_ops->read_ephy_reg(pdata, reg, &val) < 0) + goto busy_exit; + + if (REG_BIT_ADVERTISE_1000HALF & val) { + *cap_mask |= FXGMAC_ADVERTISE_1000HALF; + } else { + *cap_mask &= ~FXGMAC_ADVERTISE_1000HALF; + } + + if (REG_BIT_ADVERTISE_1000FULL & val) { + *cap_mask |= FXGMAC_ADVERTISE_1000FULL; + } else { + *cap_mask &= ~FXGMAC_ADVERTISE_1000FULL; + } + + return 0; + +busy_exit: + DPRINTK("fxgmac_ephy_autoneg_ability_get exit due to ephy reg access fail.\n"); + + return -1; +} + +int fxgmac_ephy_soft_reset(struct fxgmac_pdata *pdata) +{ + struct fxgmac_hw_ops *hw_ops = &pdata->hw_ops; + int ret; + volatile unsigned int val; + int busy = 15; + + ret = hw_ops->read_ephy_reg(pdata, REG_MII_BMCR, (unsigned int *)&val); + if (0 > ret) + goto busy_exit; + + ret = hw_ops->write_ephy_reg(pdata, REG_MII_BMCR, (val | 0x8000)); + if (0 > ret) + goto busy_exit; + + do { + ret = hw_ops->read_ephy_reg(pdata, REG_MII_BMCR, + (unsigned int *)&val); + busy--; + + } while ((ret == 0) && (0 != (val & 0x8000)) && (busy)); + + if (0 == (val & 0x8000)) + return 0; + + DPRINTK("fxgmac_ephy_soft_reset, timeout, busy=%d.\n", busy); + return -EBUSY; + +busy_exit: + DPRINTK("fxgmac_ephy_soft_reset exit due to ephy reg access fail.\n"); + + return ret; +} + +/* this function used to double check the speed. for fiber, to correct there is no 10M */ +static int fxgmac_ephy_adjust_status(u32 lport, int val, int is_utp, int *speed, + int *duplex) +{ + int speed_mode; + + *speed = -1; + *duplex = (val & BIT(FXGMAC_EPHY_DUPLEX_BIT)) >> FXGMAC_EPHY_DUPLEX_BIT; + speed_mode = (val & FXGMAC_EPHY_SPEED_MODE) >> + FXGMAC_EPHY_SPEED_MODE_BIT; + switch (speed_mode) { + case 0: + if (is_utp) + *speed = SPEED_10M; + break; + case 1: + *speed = SPEED_100M; + break; + case 2: + *speed = SPEED_1000M; + break; + case 3: + break; + default: + break; + } + + return 0; +} + +/* + * this function for polling to get status of ephy link. + * output: + * speed: SPEED_10M, SPEED_100M, SPEED_1000M or -1; + * duplex: 0 or 1, see reg 0x11, bit YT8614_DUPLEX_BIT. + * ret_link: 0 or 1, link down or up. + * media: only valid when ret_link=1, (YT8614_SMI_SEL_SDS_SGMII + 1) for fiber; (YT8614_SMI_SEL_PHY + 1) for utp. -1 for link down. + */ +int fxgmac_ephy_status_get(struct fxgmac_pdata *pdata, int *speed, int *duplex, + int *ret_link, int *media) +{ + struct fxgmac_hw_ops *hw_ops = &pdata->hw_ops; + int ret; + u16 reg; + volatile unsigned int val; + volatile int link; + int link_utp = 0, link_fiber = 0; + + reg = REG_MII_SPEC_STATUS; + ret = hw_ops->read_ephy_reg(pdata, reg, (unsigned int *)&val); + if (0 > ret) + goto busy_exit; + + link = val & (BIT(FXGMAC_EPHY_LINK_STATUS_BIT)); + if (link) { + link_utp = 1; + fxgmac_ephy_adjust_status(0, val, 1, speed, duplex); + } else { + link_utp = 0; + } + + if (link_utp || link_fiber) { + /* case of fiber of priority */ + if (link_utp) + *media = (FXGMAC_EPHY_SMI_SEL_PHY + 1); + if (link_fiber) + *media = (FXGMAC_EPHY_SMI_SEL_SDS_SGMII + 1); + + *ret_link = 1; + } else { + *ret_link = 0; + *media = -1; + *speed = -1; + *duplex = -1; + } + + return 0; + +busy_exit: + DPRINTK("fxgmac_ephy_status_get exit due to ephy reg access fail.\n"); + + return ret; +} + +/* + * fxgmac_phy_update_link - update the phy link status + * @adapter: pointer to the device adapter structure + */ +static void fxgmac_phy_update_link(struct net_device *netdev) +{ + struct fxgmac_pdata *pdata = netdev_priv(netdev); + struct fxgmac_hw_ops *hw_ops = &pdata->hw_ops; + u32 regval, cur_link, cur_speed; + + regval = hw_ops->get_ephy_state(pdata); + + if (regval & MGMT_EPHY_CTRL_STA_EPHY_RESET) { + pdata->expansion.phy_link = false; + return; + } + + cur_link = FXGMAC_GET_REG_BITS(regval, + MGMT_EPHY_CTRL_STA_EPHY_LINKUP_POS, + MGMT_EPHY_CTRL_STA_EPHY_LINKUP_LEN); + if (pdata->expansion.phy_link != cur_link) { + hw_ops->read_ephy_reg(pdata, REG_MII_INT_STATUS, NULL); + hw_ops->read_ephy_reg(pdata, REG_MII_INT_STATUS, NULL); + + pdata->expansion.phy_link = cur_link; + if (pdata->expansion.phy_link) { + cur_speed = FXGMAC_GET_REG_BITS( + regval, MGMT_EPHY_CTRL_STA_SPEED_POS, + MGMT_EPHY_CTRL_STA_SPEED_LEN); + pdata->phy_speed = (cur_speed == 2) ? SPEED_1000 : + (cur_speed == 1) ? SPEED_100 : + SPEED_10; + pdata->phy_duplex = FXGMAC_GET_REG_BITS( + regval, MGMT_EPHY_CTRL_STA_EPHY_DUPLEX_POS, + MGMT_EPHY_CTRL_STA_EPHY_DUPLEX_LEN); + hw_ops->config_mac_speed(pdata); + + hw_ops->enable_rx(pdata); + hw_ops->enable_tx(pdata); + netif_carrier_on(pdata->netdev); + if (netif_running(pdata->netdev)) { + netif_tx_wake_all_queues(pdata->netdev); + dev_info(pdata->dev, + "%s now is link up, mac_speed=%d.\n", + netdev_name(pdata->netdev), + pdata->phy_speed); + } + } else { + netif_carrier_off(pdata->netdev); + netif_tx_stop_all_queues(pdata->netdev); + pdata->phy_speed = SPEED_UNKNOWN; + pdata->phy_duplex = DUPLEX_UNKNOWN; + hw_ops->disable_rx(pdata); + hw_ops->disable_tx(pdata); + dev_info(pdata->dev, "%s now is link down\n", + netdev_name(pdata->netdev)); + } + } +} + +static void fxgmac_phy_link_poll(struct timer_list *t) +{ + struct fxgmac_pdata *pdata = + from_timer(pdata, t, expansion.phy_poll_tm); + + if (NULL == pdata->netdev) { + DPRINTK("fxgmac_phy_timer polling with NULL netdev %lx\n", + (unsigned long)(pdata->netdev)); + return; + } + + pdata->stats.ephy_poll_timer_cnt++; + +#if FXGMAC_PM_FEATURE_ENABLED + if (!test_bit(FXGMAC_POWER_STATE_DOWN, &pdata->expansion.powerstate)) +#endif + { + mod_timer(&pdata->expansion.phy_poll_tm, jiffies + HZ / 2); + fxgmac_phy_update_link(pdata->netdev); + } else { + DPRINTK("fxgmac_phy_timer polling, powerstate changed, %ld, netdev=%lx, tm=%lx\n", + pdata->expansion.powerstate, + (unsigned long)(pdata->netdev), + (unsigned long)&pdata->expansion.phy_poll_tm); + } +} + +int fxgmac_phy_timer_init(struct fxgmac_pdata *pdata) +{ + init_timer_key(&pdata->expansion.phy_poll_tm, NULL, 0, + "fuxi_phy_link_update_timer", NULL); + pdata->expansion.phy_poll_tm.expires = jiffies + HZ / 2; + pdata->expansion.phy_poll_tm.function = (void *)(fxgmac_phy_link_poll); + add_timer(&pdata->expansion.phy_poll_tm); + + DPRINTK("fxgmac_phy_timer started, %lx\n", jiffies); + return 0; +} + +void fxgmac_phy_timer_destroy(struct fxgmac_pdata *pdata) +{ + del_timer_sync(&pdata->expansion.phy_poll_tm); + DPRINTK("fxgmac_phy_timer removed\n"); +} diff --git a/drivers/net/ethernet/motorcomm/yt6801/fuxi-gmac-reg.h b/drivers/net/ethernet/motorcomm/yt6801/fuxi-gmac-reg.h new file mode 100644 index 000000000000..5685303ae954 --- /dev/null +++ b/drivers/net/ethernet/motorcomm/yt6801/fuxi-gmac-reg.h @@ -0,0 +1,1932 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (c) 2021 Motorcomm Corporation. */ + +#ifndef __FXGMAC_GMAC_REG_H__ +#define __FXGMAC_GMAC_REG_H__ + +#define AISC_MODE + +#define FXGMAC_REV_01 0x01 +#define FXGMAC_REV_03 0x03 + +/* MAC register offsets */ +#define MAC_OFFSET 0x2000 +#define MAC_CR 0x0000 +#define MAC_ECR 0x0004 +#define MAC_PFR 0x0008 +#define MAC_WTR 0x000c +#define MAC_HTR0 0x0010 +#define MAC_VLANTR 0x0050 +#define MAC_VLANHTR 0x0058 +#define MAC_VLANIR 0x0060 +#define MAC_IVLANIR 0x0064 +#define MAC_Q0TFCR 0x0070 +#define MAC_RFCR 0x0090 +#define MAC_RQC0R 0x00a0 +#define MAC_RQC1R 0x00a4 +#define MAC_RQC2R 0x00a8 +#define MAC_RQC3R 0x00ac +#define MAC_ISR 0x00b0 +#define MAC_IER 0x00b4 +#define MAC_TX_RX_STA 0x00b8 +#define MAC_PMT_STA 0x00c0 +#define MAC_RWK_PAC 0x00c4 +#define MAC_LPI_STA 0x00d0 +#define MAC_LPI_CONTROL 0x00d4 +#define MAC_LPI_TIMER 0x00d8 +#define MAC_MS_TIC_COUNTER 0x00dc +#define MAC_AN_CR 0x00e0 +#define MAC_AN_SR 0x00e4 +#define MAC_AN_ADV 0x00e8 +#define MAC_AN_LPA 0x00ec +#define MAC_AN_EXP 0x00f0 +#define MAC_PHYIF_STA 0x00f8 +#define MAC_VR 0x0110 +#define MAC_DBG_STA 0x0114 +#define MAC_HWF0R 0x011c +#define MAC_HWF1R 0x0120 +#define MAC_HWF2R 0x0124 +#define MAC_HWF3R 0x0128 +#define MAC_MDIO_ADDRESS 0x0200 +#define MAC_MDIO_DATA 0x0204 +#define MAC_GPIOCR 0x0208 +#define MAC_GPIO_SR 0x020c +#define MAC_ARP_PROTO_ADDR 0x0210 +#define MAC_CSR_SW_CTRL 0x0230 + +#define MAC_MACA0HR 0x0300 +#define MAC_MACA0LR 0x0304 + +#define MAC_MACA1HR 0x0308 +#define MAC_MACA1HR_AE_POS 31 +#define MAC_MACA1HR_AE_LEN 1 + +#define MAC_MACA1LR 0x030c + +#define MAC_RSSCR 0x3c80 +#define MAC_RSSAR 0x3c88 +#define MAC_RSSDR 0x3c8c + +#define MAC_QTFCR_INC 4 +#define MAC_MACA_INC 4 +#define MAC_HTR_INC 4 +#define MAC_RQC2_INC 4 +#define MAC_RQC2_Q_PER_REG 4 + +/* MAC register entry bit positions and sizes */ +#define MAC_HWF0R_ADDMACADRSEL_POS 18 +#define MAC_HWF0R_ADDMACADRSEL_LEN 5 +#define MAC_HWF0R_ARPOFFSEL_POS 9 +#define MAC_HWF0R_ARPOFFSEL_LEN 1 +#define MAC_HWF0R_EEESEL_POS 13 +#define MAC_HWF0R_EEESEL_LEN 1 +#define MAC_HWF0R_ACTPHYIFSEL_POS 28 +#define MAC_HWF0R_ACTPHYIFSEL_LEN 3 +#define MAC_HWF0R_MGKSEL_POS 7 +#define MAC_HWF0R_MGKSEL_LEN 1 +#define MAC_HWF0R_MMCSEL_POS 8 +#define MAC_HWF0R_MMCSEL_LEN 1 +#define MAC_HWF0R_RWKSEL_POS 6 +#define MAC_HWF0R_RWKSEL_LEN 1 +#define MAC_HWF0R_RXCOESEL_POS 16 +#define MAC_HWF0R_RXCOESEL_LEN 1 +#define MAC_HWF0R_SAVLANINS_POS 27 +#define MAC_HWF0R_SAVLANINS_LEN 1 +#define MAC_HWF0R_SMASEL_POS 5 +#define MAC_HWF0R_SMASEL_LEN 1 +#define MAC_HWF0R_TSSEL_POS 12 +#define MAC_HWF0R_TSSEL_LEN 1 +#define MAC_HWF0R_TSSTSSEL_POS 25 +#define MAC_HWF0R_TSSTSSEL_LEN 2 +#define MAC_HWF0R_TXCOESEL_POS 14 +#define MAC_HWF0R_TXCOESEL_LEN 1 +#define MAC_HWF0R_VLHASH_POS 4 +#define MAC_HWF0R_VLHASH_LEN 1 +#define MAC_HWF1R_ADDR64_POS 14 +#define MAC_HWF1R_ADDR64_LEN 2 +#define MAC_HWF1R_ADVTHWORD_POS 13 +#define MAC_HWF1R_ADVTHWORD_LEN 1 +#define MAC_HWF1R_DBGMEMA_POS 19 +#define MAC_HWF1R_DBGMEMA_LEN 1 +#define MAC_HWF1R_DCBEN_POS 16 +#define MAC_HWF1R_DCBEN_LEN 1 +#define MAC_HWF1R_HASHTBLSZ_POS 24 +#define MAC_HWF1R_HASHTBLSZ_LEN 2 +#define MAC_HWF1R_L3L4FNUM_POS 27 +#define MAC_HWF1R_L3L4FNUM_LEN 4 + +#define MAC_HWF1R_RAVSEL_POS 21 +#define MAC_HWF1R_RAVSEL_LEN 1 +#define MAC_HWF1R_AVSEL_POS 20 +#define MAC_HWF1R_AVSEL_LEN 1 +#define MAC_HWF1R_RXFIFOSIZE_POS 0 +#define MAC_HWF1R_RXFIFOSIZE_LEN 5 +#define MAC_HWF1R_SPHEN_POS 17 +#define MAC_HWF1R_SPHEN_LEN 1 +#define MAC_HWF1R_TSOEN_POS 18 +#define MAC_HWF1R_TSOEN_LEN 1 +#define MAC_HWF1R_TXFIFOSIZE_POS 6 +#define MAC_HWF1R_TXFIFOSIZE_LEN 5 +#define MAC_HWF2R_AUXSNAPNUM_POS 28 +#define MAC_HWF2R_AUXSNAPNUM_LEN 3 +#define MAC_HWF2R_PPSOUTNUM_POS 24 +#define MAC_HWF2R_PPSOUTNUM_LEN 3 +#define MAC_HWF2R_RXCHCNT_POS 12 +#define MAC_HWF2R_RXCHCNT_LEN 4 +#define MAC_HWF2R_RXQCNT_POS 0 +#define MAC_HWF2R_RXQCNT_LEN 4 +#define MAC_HWF2R_TXCHCNT_POS 18 +#define MAC_HWF2R_TXCHCNT_LEN 4 +#define MAC_HWF2R_TXQCNT_POS 6 +#define MAC_HWF2R_TXQCNT_LEN 4 +#define MAC_IER_TSIE_POS 12 +#define MAC_IER_TSIE_LEN 1 +#define MAC_ISR_MMCRXIS_POS 9 +#define MAC_ISR_MMCRXIS_LEN 1 +#define MAC_ISR_MMCTXIS_POS 10 +#define MAC_ISR_MMCTXIS_LEN 1 +#define MAC_ISR_PMTIS_POS 4 +#define MAC_ISR_PMTIS_LEN 1 +#define MAC_ISR_TSIS_POS 12 +#define MAC_ISR_TSIS_LEN 1 +#define MAC_MACA1HR_AE_POS 31 +#define MAC_MACA1HR_AE_LEN 1 +#define MAC_PFR_HMC_POS 2 +#define MAC_PFR_HMC_LEN 1 +#define MAC_PFR_HPF_POS 10 +#define MAC_PFR_HPF_LEN 1 +#define MAC_PFR_PM_POS 4 +#define MAC_PFR_PM_LEN 1 +#define MAC_PFR_DBF_POS 5 +#define MAC_PFR_DBF_LEN 1 +#define MAC_PFR_HUC_POS 1 +#define MAC_PFR_HUC_LEN 1 +#define MAC_PFR_PR_POS 0 +#define MAC_PFR_PR_LEN 1 +#define MAC_PFR_VTFE_POS 16 +#define MAC_PFR_VTFE_LEN 1 +#define MAC_Q0TFCR_PT_POS 16 +#define MAC_Q0TFCR_PT_LEN 16 +#define MAC_Q0TFCR_TFE_POS 1 +#define MAC_Q0TFCR_TFE_LEN 1 +#define MAC_CR_ARPEN_POS 31 +#define MAC_CR_ARPEN_LEN 1 +#define MAC_CR_ACS_POS 20 +#define MAC_CR_ACS_LEN 1 +#define MAC_CR_CST_POS 21 +#define MAC_CR_CST_LEN 1 +#define MAC_CR_IPC_POS 27 +#define MAC_CR_IPC_LEN 1 +#define MAC_CR_JE_POS 16 +#define MAC_CR_JE_LEN 1 +#define MAC_CR_LM_POS 12 +#define MAC_CR_LM_LEN 1 +#define MAC_CR_RE_POS 0 +#define MAC_CR_RE_LEN 1 +#define MAC_CR_PS_POS 15 +#define MAC_CR_PS_LEN 1 +#define MAC_CR_FES_POS 14 +#define MAC_CR_FES_LEN 1 +#define MAC_CR_DM_POS 13 +#define MAC_CR_DM_LEN 1 +#define MAC_CR_TE_POS 1 +#define MAC_CR_TE_LEN 1 +#define MAC_ECR_DCRCC_POS 16 +#define MAC_ECR_DCRCC_LEN 1 +#define MAC_ECR_HDSMS_POS 20 +#define MAC_ECR_HDSMS_LEN 3 +#define MAC_RFCR_PFCE_POS 8 +#define MAC_RFCR_PFCE_LEN 1 +#define MAC_RFCR_RFE_POS 0 +#define MAC_RFCR_RFE_LEN 1 +#define MAC_RFCR_UP_POS 1 +#define MAC_RFCR_UP_LEN 1 +#define MAC_RQC0R_RXQ0EN_POS 0 +#define MAC_RQC0R_RXQ0EN_LEN 2 +#define MAC_LPIIE_POS 5 +#define MAC_LPIIE_LEN 1 +#define MAC_LPIATE_POS 20 +#define MAC_LPIATE_LEN 1 +#define MAC_LPITXA_POS 19 +#define MAC_LPITXA_LEN 1 +#define MAC_PLS_POS 17 +#define MAC_PLS_LEN 1 +#define MAC_LPIEN_POS 16 +#define MAC_LPIEN_LEN 1 +#define MAC_LPI_ENTRY_TIMER 8 +#define MAC_LPIET_POS 3 +#define MAC_LPIET_LEN 17 +#define MAC_TWT_TIMER 0x10 +#define MAC_TWT_POS 0 +#define MAC_TWT_LEN 16 +#define MAC_LST_TIMER 2 +#define MAC_LST_POS 16 +#define MAC_LST_LEN 10 +#define MAC_MS_TIC 24 +#define MAC_MS_TIC_POS 0 +#define MAC_MS_TIC_LEN 12 + +/* RSS table */ +#define MAC_RSSAR_ADDRT_POS 2 +#define MAC_RSSAR_ADDRT_LEN 1 +#define MAC_RSSAR_CT_POS 1 +#define MAC_RSSAR_CT_LEN 1 +#define MAC_RSSAR_OB_POS 0 +#define MAC_RSSAR_OB_LEN 1 +#define MAC_RSSAR_RSSIA_POS 8 +#define MAC_RSSAR_RSSIA_LEN 8 + +/* RSS control and options */ +/* note, below options definitions are used only for pdata->options, + * not for register, so the position is not consistent with register. + * [0] ipv4 + * [1] tcpv4 + * [2] udpv4 + * [3] ipv6 + * [4] tcpv6 + * [5] udpv6 + */ +#define MAC_RSSCR_IP4TE_POS 0 +#define MAC_RSSCR_IP4TE_LEN 1 +#define MAC_RSSCR_IP6TE_POS 3 +#define MAC_RSSCR_IP6TE_LEN 1 +#define MAC_RSSCR_TCP4TE_POS 1 +#define MAC_RSSCR_TCP4TE_LEN 1 +#define MAC_RSSCR_UDP4TE_POS 2 +#define MAC_RSSCR_UDP4TE_LEN 1 +#define MAC_RSSCR_TCP6TE_POS 4 +#define MAC_RSSCR_TCP6TE_LEN 1 +#define MAC_RSSCR_UDP6TE_POS 5 +#define MAC_RSSCR_UDP6TE_LEN 1 + +/* RSS indirection table */ +#define MAC_RSSDR_DMCH_POS 0 +#define MAC_RSSDR_DMCH_LEN 2 + +#define MAC_VLANHTR_VLHT_POS 0 +#define MAC_VLANHTR_VLHT_LEN 16 +#define MAC_VLANIR_VLTI_POS 20 +#define MAC_VLANIR_VLTI_LEN 1 +#define MAC_VLANIR_CSVL_POS 19 +#define MAC_VLANIR_CSVL_LEN 1 +#define MAC_VLANIR_VLP_POS 18 +#define MAC_VLANIR_VLP_LEN 1 +#define MAC_VLANIR_VLC_POS 16 +#define MAC_VLANIR_VLC_LEN 2 +#define MAC_VLANIR_VLT_POS 0 +#define MAC_VLANIR_VLT_LEN 16 +#define MAC_VLANTR_DOVLTC_POS 20 +#define MAC_VLANTR_DOVLTC_LEN 1 +#define MAC_VLANTR_ERSVLM_POS 19 +#define MAC_VLANTR_ERSVLM_LEN 1 +#define MAC_VLANTR_ESVL_POS 18 +#define MAC_VLANTR_ESVL_LEN 1 +#define MAC_VLANTR_ETV_POS 16 +#define MAC_VLANTR_ETV_LEN 1 +#define MAC_VLANTR_EVLS_POS 21 +#define MAC_VLANTR_EVLS_LEN 2 +#define MAC_VLANTR_EVLRXS_POS 24 +#define MAC_VLANTR_EVLRXS_LEN 1 +#define MAC_VLANTR_VL_POS 0 +#define MAC_VLANTR_VL_LEN 16 +#define MAC_VLANTR_VTHM_POS 25 +#define MAC_VLANTR_VTHM_LEN 1 +#define MAC_VLANTR_VTIM_POS 17 +#define MAC_VLANTR_VTIM_LEN 1 +#define MAC_VR_DEVID_POS 16 +#define MAC_VR_DEVID_LEN 16 +#define MAC_VR_SVER_POS 0 +#define MAC_VR_SVER_LEN 8 +#define MAC_VR_USERVER_POS 8 +#define MAC_VR_USERVER_LEN 8 + +#define MAC_DBG_STA_TX_BUSY 0x70000 +#define MTL_TXQ_DEG_TX_BUSY 0x10 + +#define MAC_MDIO_ADDRESS_BUSY 1 + +#define MAC_MDIO_ADDR_GOC_POS 2 +#define MAC_MDIO_ADDR_GOC_LEN 2 +#define MAC_MDIO_ADDR_GB_POS 0 +#define MAC_MDIO_ADDR_GB_LEN 1 + +#define MAC_MDIO_DATA_RA_POS 16 +#define MAC_MDIO_DATA_RA_LEN 16 +#define MAC_MDIO_DATA_GD_POS 0 +#define MAC_MDIO_DATA_GD_LEN 16 + +/* bit definitions for PMT and WOL, 20210622 */ +#define MAC_PMT_STA_PWRDWN_POS 0 +#define MAC_PMT_STA_PWRDWN_LEN 1 +#define MAC_PMT_STA_MGKPKTEN_POS 1 +#define MAC_PMT_STA_MGKPKTEN_LEN 1 +#define MAC_PMT_STA_RWKPKTEN_POS 2 +#define MAC_PMT_STA_RWKPKTEN_LEN 1 +#define MAC_PMT_STA_MGKPRCVD_POS 5 +#define MAC_PMT_STA_MGKPRCVD_LEN 1 +#define MAC_PMT_STA_RWKPRCVD_POS 6 +#define MAC_PMT_STA_RWKPRCVD_LEN 1 +#define MAC_PMT_STA_GLBLUCAST_POS 9 +#define MAC_PMT_STA_GLBLUCAST_LEN 1 +#define MAC_PMT_STA_RWKPTR_POS 24 +#define MAC_PMT_STA_RWKPTR_LEN 4 +#define MAC_PMT_STA_RWKFILTERST_POS 31 +#define MAC_PMT_STA_RWKFILTERST_LEN 1 + +/* MMC register offsets */ +#define MMC_CR 0x0700 +#define MMC_RISR 0x0704 +#define MMC_TISR 0x0708 +#define MMC_RIER 0x070c +#define MMC_TIER 0x0710 +#define MMC_TXOCTETCOUNT_GB_LO 0x0714 +#define MMC_TXFRAMECOUNT_GB_LO 0x0718 +#define MMC_TXBROADCASTFRAMES_G_LO 0x071c +#define MMC_TXMULTICASTFRAMES_G_LO 0x0720 +#define MMC_TX64OCTETS_GB_LO 0x0724 +#define MMC_TX65TO127OCTETS_GB_LO 0x0728 +#define MMC_TX128TO255OCTETS_GB_LO 0x072c +#define MMC_TX256TO511OCTETS_GB_LO 0x0730 +#define MMC_TX512TO1023OCTETS_GB_LO 0x0734 +#define MMC_TX1024TOMAXOCTETS_GB_LO 0x0738 +#define MMC_TXUNICASTFRAMES_GB_LO 0x073c +#define MMC_TXMULTICASTFRAMES_GB_LO 0x0740 +#define MMC_TXBROADCASTFRAMES_GB_LO 0x0744 +#define MMC_TXUNDERFLOWERROR_LO 0x0748 +#define MMC_TXSINGLECOLLISION_G 0x074c +#define MMC_TXMULTIPLECOLLISION_G 0x0750 +#define MMC_TXDEFERREDFRAMES 0x0754 +#define MMC_TXLATECOLLISIONFRAMES 0x0758 +#define MMC_TXEXCESSIVECOLLSIONFRAMES 0x075c +#define MMC_TXCARRIERERRORFRAMES 0x0760 +#define MMC_TXOCTETCOUNT_G_LO 0x0764 +#define MMC_TXFRAMECOUNT_G_LO 0x0768 +#define MMC_TXEXCESSIVEDEFERRALERROR 0x076c +#define MMC_TXPAUSEFRAMES_LO 0x0770 +#define MMC_TXVLANFRAMES_G_LO 0x0774 +#define MMC_TXOVERSIZEFRAMES 0x0778 +#define MMC_RXFRAMECOUNT_GB_LO 0x0780 +#define MMC_RXOCTETCOUNT_GB_LO 0x0784 +#define MMC_RXOCTETCOUNT_G_LO 0x0788 +#define MMC_RXBROADCASTFRAMES_G_LO 0x078c +#define MMC_RXMULTICASTFRAMES_G_LO 0x0790 +#define MMC_RXCRCERROR_LO 0x0794 +#define MMC_RXALIGNERROR 0x0798 +#define MMC_RXRUNTERROR 0x079c +#define MMC_RXJABBERERROR 0x07a0 +#define MMC_RXUNDERSIZE_G 0x07a4 +#define MMC_RXOVERSIZE_G 0x07a8 +#define MMC_RX64OCTETS_GB_LO 0x07ac +#define MMC_RX65TO127OCTETS_GB_LO 0x07b0 +#define MMC_RX128TO255OCTETS_GB_LO 0x07b4 +#define MMC_RX256TO511OCTETS_GB_LO 0x07b8 +#define MMC_RX512TO1023OCTETS_GB_LO 0x07bc +#define MMC_RX1024TOMAXOCTETS_GB_LO 0x07c0 +#define MMC_RXUNICASTFRAMES_G_LO 0x07c4 +#define MMC_RXLENGTHERROR_LO 0x07c8 +#define MMC_RXOUTOFRANGETYPE_LO 0x07cc +#define MMC_RXPAUSEFRAMES_LO 0x07d0 +#define MMC_RXFIFOOVERFLOW_LO 0x07d4 +#define MMC_RXVLANFRAMES_GB_LO 0x07d8 +#define MMC_RXWATCHDOGERROR 0x07dc +#define MMC_RXRECEIVEERRORFRAME 0x07e0 +#define MMC_RXCONTROLFRAME_G 0x07e4 + +#define MMC_IPCRXINTMASK 0x800 +#define MMC_IPCRXINT 0x808 + +/* MMC register entry bit positions and sizes */ +#define MMC_CR_CR_POS 0 +#define MMC_CR_CR_LEN 1 +#define MMC_CR_CSR_POS 1 +#define MMC_CR_CSR_LEN 1 +#define MMC_CR_ROR_POS 2 +#define MMC_CR_ROR_LEN 1 +#define MMC_CR_MCF_POS 3 +#define MMC_CR_MCF_LEN 1 + +#define MMC_RIER_ALL_INTERRUPTS_POS 0 +#define MMC_RIER_ALL_INTERRUPTS_LEN 28 +#define MMC_RISR_RXFRAMECOUNT_GB_POS 0 +#define MMC_RISR_RXFRAMECOUNT_GB_LEN 1 +#define MMC_RISR_RXOCTETCOUNT_GB_POS 1 +#define MMC_RISR_RXOCTETCOUNT_GB_LEN 1 +#define MMC_RISR_RXOCTETCOUNT_G_POS 2 +#define MMC_RISR_RXOCTETCOUNT_G_LEN 1 +#define MMC_RISR_RXBROADCASTFRAMES_G_POS 3 +#define MMC_RISR_RXBROADCASTFRAMES_G_LEN 1 +#define MMC_RISR_RXMULTICASTFRAMES_G_POS 4 +#define MMC_RISR_RXMULTICASTFRAMES_G_LEN 1 +#define MMC_RISR_RXCRCERROR_POS 5 +#define MMC_RISR_RXCRCERROR_LEN 1 +#define MMC_RISR_RXALIGNERROR_POS 6 +#define MMC_RISR_RXALIGNERROR_LEN 1 +#define MMC_RISR_RXRUNTERROR_POS 7 +#define MMC_RISR_RXRUNTERROR_LEN 1 +#define MMC_RISR_RXJABBERERROR_POS 8 +#define MMC_RISR_RXJABBERERROR_LEN 1 +#define MMC_RISR_RXUNDERSIZE_G_POS 9 +#define MMC_RISR_RXUNDERSIZE_G_LEN 1 +#define MMC_RISR_RXOVERSIZE_G_POS 10 +#define MMC_RISR_RXOVERSIZE_G_LEN 1 +#define MMC_RISR_RX64OCTETS_GB_POS 11 +#define MMC_RISR_RX64OCTETS_GB_LEN 1 +#define MMC_RISR_RX65TO127OCTETS_GB_POS 12 +#define MMC_RISR_RX65TO127OCTETS_GB_LEN 1 +#define MMC_RISR_RX128TO255OCTETS_GB_POS 13 +#define MMC_RISR_RX128TO255OCTETS_GB_LEN 1 +#define MMC_RISR_RX256TO511OCTETS_GB_POS 14 +#define MMC_RISR_RX256TO511OCTETS_GB_LEN 1 +#define MMC_RISR_RX512TO1023OCTETS_GB_POS 15 +#define MMC_RISR_RX512TO1023OCTETS_GB_LEN 1 +#define MMC_RISR_RX1024TOMAXOCTETS_GB_POS 16 +#define MMC_RISR_RX1024TOMAXOCTETS_GB_LEN 1 +#define MMC_RISR_RXUNICASTFRAMES_G_POS 17 +#define MMC_RISR_RXUNICASTFRAMES_G_LEN 1 +#define MMC_RISR_RXLENGTHERROR_POS 18 +#define MMC_RISR_RXLENGTHERROR_LEN 1 +#define MMC_RISR_RXOUTOFRANGETYPE_POS 19 +#define MMC_RISR_RXOUTOFRANGETYPE_LEN 1 +#define MMC_RISR_RXPAUSEFRAMES_POS 20 +#define MMC_RISR_RXPAUSEFRAMES_LEN 1 +#define MMC_RISR_RXFIFOOVERFLOW_POS 21 +#define MMC_RISR_RXFIFOOVERFLOW_LEN 1 +#define MMC_RISR_RXVLANFRAMES_GB_POS 22 +#define MMC_RISR_RXVLANFRAMES_GB_LEN 1 +#define MMC_RISR_RXWATCHDOGERROR_POS 23 +#define MMC_RISR_RXWATCHDOGERROR_LEN 1 +#define MMC_RISR_RXERRORFRAMES_POS 24 +#define MMC_RISR_RXERRORFRAMES_LEN 1 +#define MMC_RISR_RXERRORCONTROLFRAMES_POS 25 +#define MMC_RISR_RXERRORCONTROLFRAMES_LEN 1 +#define MMC_RISR_RXLPIMICROSECOND_POS 26 +#define MMC_RISR_RXLPIMICROSECOND_LEN 1 +#define MMC_RISR_RXLPITRANSITION_POS 27 +#define MMC_RISR_RXLPITRANSITION_LEN 1 + +#define MMC_TIER_ALL_INTERRUPTS_POS 0 +#define MMC_TIER_ALL_INTERRUPTS_LEN 28 +#define MMC_TISR_TXOCTETCOUNT_GB_POS 0 +#define MMC_TISR_TXOCTETCOUNT_GB_LEN 1 +#define MMC_TISR_TXFRAMECOUNT_GB_POS 1 +#define MMC_TISR_TXFRAMECOUNT_GB_LEN 1 +#define MMC_TISR_TXBROADCASTFRAMES_G_POS 2 +#define MMC_TISR_TXBROADCASTFRAMES_G_LEN 1 +#define MMC_TISR_TXMULTICASTFRAMES_G_POS 3 +#define MMC_TISR_TXMULTICASTFRAMES_G_LEN 1 +#define MMC_TISR_TX64OCTETS_GB_POS 4 +#define MMC_TISR_TX64OCTETS_GB_LEN 1 +#define MMC_TISR_TX65TO127OCTETS_GB_POS 5 +#define MMC_TISR_TX65TO127OCTETS_GB_LEN 1 +#define MMC_TISR_TX128TO255OCTETS_GB_POS 6 +#define MMC_TISR_TX128TO255OCTETS_GB_LEN 1 +#define MMC_TISR_TX256TO511OCTETS_GB_POS 7 +#define MMC_TISR_TX256TO511OCTETS_GB_LEN 1 +#define MMC_TISR_TX512TO1023OCTETS_GB_POS 8 +#define MMC_TISR_TX512TO1023OCTETS_GB_LEN 1 +#define MMC_TISR_TX1024TOMAXOCTETS_GB_POS 9 +#define MMC_TISR_TX1024TOMAXOCTETS_GB_LEN 1 +#define MMC_TISR_TXUNICASTFRAMES_GB_POS 10 +#define MMC_TISR_TXUNICASTFRAMES_GB_LEN 1 +#define MMC_TISR_TXMULTICASTFRAMES_GB_POS 11 +#define MMC_TISR_TXMULTICASTFRAMES_GB_LEN 1 +#define MMC_TISR_TXBROADCASTFRAMES_GB_POS 12 +#define MMC_TISR_TXBROADCASTFRAMES_GB_LEN 1 +#define MMC_TISR_TXUNDERFLOWERROR_POS 13 +#define MMC_TISR_TXUNDERFLOWERROR_LEN 1 +#define MMC_TISR_TXSINGLECOLLISION_G_POS 14 +#define MMC_TISR_TXSINGLECOLLISION_G_LEN 1 +#define MMC_TISR_TXMULTIPLECOLLISION_G_POS 15 +#define MMC_TISR_TXMULTIPLECOLLISION_G_LEN 1 +#define MMC_TISR_TXDEFERREDFRAMES_POS 16 +#define MMC_TISR_TXDEFERREDFRAMES_LEN 1 +#define MMC_TISR_TXLATECOLLISIONFRAMES_POS 17 +#define MMC_TISR_TXLATECOLLISIONFRAMES_LEN 1 +#define MMC_TISR_TXEXCESSIVECOLLISIONFRAMES_POS 18 +#define MMC_TISR_TXEXCESSIVECOLLISIONFRAMES_LEN 1 +#define MMC_TISR_TXCARRIERERRORFRAMES_POS 19 +#define MMC_TISR_TXCARRIERERRORFRAMES_LEN 1 +#define MMC_TISR_TXOCTETCOUNT_G_POS 20 +#define MMC_TISR_TXOCTETCOUNT_G_LEN 1 +#define MMC_TISR_TXFRAMECOUNT_G_POS 21 +#define MMC_TISR_TXFRAMECOUNT_G_LEN 1 +#define MMC_TISR_TXEXCESSIVEDEFERRALFRAMES_POS 22 +#define MMC_TISR_TXEXCESSIVEDEFERRALFRAMES_LEN 1 +#define MMC_TISR_TXPAUSEFRAMES_POS 23 +#define MMC_TISR_TXPAUSEFRAMES_LEN 1 +#define MMC_TISR_TXVLANFRAMES_G_POS 24 +#define MMC_TISR_TXVLANFRAMES_G_LEN 1 +#define MMC_TISR_TXOVERSIZE_G_POS 25 +#define MMC_TISR_TXOVERSIZE_G_LEN 1 +#define MMC_TISR_TXLPIMICROSECOND_POS 26 +#define MMC_TISR_TXLPIMICROSECOND_LEN 1 +#define MMC_TISR_TXLPITRANSITION_POS 27 +#define MMC_TISR_TXLPITRANSITION_LEN 1 + +/* MTL register offsets */ +#define MTL_OMR 0x0c00 +#define MTL_FDCR 0x0c08 +#define MTL_FDSR 0x0c0c +#define MTL_FDDR 0x0c10 +#define MTL_INT_SR 0x0c20 +#define MTL_RQDCM0R 0x0c30 +#define MTL_ECC_INT_SR 0x0ccc + +#define MTL_RQDCM_INC 4 +#define MTL_RQDCM_Q_PER_REG 4 + +/* MTL register entry bit positions and sizes */ +#define MTL_OMR_ETSALG_POS 5 +#define MTL_OMR_ETSALG_LEN 2 +#define MTL_OMR_RAA_POS 2 +#define MTL_OMR_RAA_LEN 1 + +/* MTL queue register offsets + * Multiple queues can be active. The first queue has registers + * that begin at 0x0d00. Each subsequent queue has registers that + * are accessed using an offset of 0x40 from the previous queue. + */ +#define MTL_Q_BASE 0x0d00 +#define MTL_Q_INC 0x40 +#define MTL_Q_INT_CTL_SR 0x0d2c + +#define MTL_Q_TQOMR 0x00 +#define MTL_Q_TQUR 0x04 +#define MTL_Q_RQOMR 0x30 +#define MTL_Q_RQMPOCR 0x34 +#define MTL_Q_RQDR 0x38 +#define MTL_Q_RQCR 0x3c +#define MTL_Q_IER 0x2c +#define MTL_Q_ISR 0x2c +#define MTL_TXQ_DEG 0x08 + +/* MTL queue register entry bit positions and sizes */ +#define MTL_Q_RQDR_PRXQ_POS 16 +#define MTL_Q_RQDR_PRXQ_LEN 14 +#define MTL_Q_RQDR_RXQSTS_POS 4 +#define MTL_Q_RQDR_RXQSTS_LEN 2 +#define MTL_Q_RQOMR_RFA_POS 8 +#define MTL_Q_RQOMR_RFA_LEN 6 +#define MTL_Q_RQOMR_RFD_POS 14 +#define MTL_Q_RQOMR_RFD_LEN 6 +#define MTL_Q_RQOMR_EHFC_POS 7 +#define MTL_Q_RQOMR_EHFC_LEN 1 +#define MTL_Q_RQOMR_RQS_POS 20 +#define MTL_Q_RQOMR_RQS_LEN 9 +#define MTL_Q_RQOMR_RSF_POS 5 +#define MTL_Q_RQOMR_RSF_LEN 1 +#define MTL_Q_RQOMR_FEP_POS 4 +#define MTL_Q_RQOMR_FEP_LEN 1 +#define MTL_Q_RQOMR_FUP_POS 3 +#define MTL_Q_RQOMR_FUP_LEN 1 +#define MTL_Q_RQOMR_RTC_POS 0 +#define MTL_Q_RQOMR_RTC_LEN 2 +#define MTL_Q_TQOMR_FTQ_POS 0 +#define MTL_Q_TQOMR_FTQ_LEN 1 + +#define MTL_Q_TQOMR_TQS_POS 16 +#define MTL_Q_TQOMR_TQS_LEN 7 +#define MTL_Q_TQOMR_TSF_POS 1 +#define MTL_Q_TQOMR_TSF_LEN 1 +#define MTL_Q_TQOMR_TTC_POS 4 +#define MTL_Q_TQOMR_TTC_LEN 3 +#define MTL_Q_TQOMR_TXQEN_POS 2 +#define MTL_Q_TQOMR_TXQEN_LEN 2 + +/* MTL queue register value */ +#define MTL_RSF_DISABLE 0x00 +#define MTL_RSF_ENABLE 0x01 +#define MTL_TSF_DISABLE 0x00 +#define MTL_TSF_ENABLE 0x01 +#define MTL_FEP_DISABLE 0x00 +#define MTL_FEP_ENABLE 0x01 + +#define MTL_RX_THRESHOLD_64 0x00 +#define MTL_RX_THRESHOLD_32 0x01 +#define MTL_RX_THRESHOLD_96 0x02 +#define MTL_RX_THRESHOLD_128 0x03 +#define MTL_TX_THRESHOLD_32 0x00 +#define MTL_TX_THRESHOLD_64 0x01 +#define MTL_TX_THRESHOLD_96 0x02 +#define MTL_TX_THRESHOLD_128 0x03 +#define MTL_TX_THRESHOLD_192 0x04 +#define MTL_TX_THRESHOLD_256 0x05 +#define MTL_TX_THRESHOLD_384 0x06 +#define MTL_TX_THRESHOLD_512 0x07 + +#define MTL_ETSALG_WRR 0x00 +#define MTL_ETSALG_WFQ 0x01 +#define MTL_ETSALG_DWRR 0x02 +#define MTL_ETSALG_SP 0x03 + +#define MTL_RAA_SP 0x00 +#define MTL_RAA_WSP 0x01 + +#define MTL_Q_DISABLED 0x00 +#define MTL_Q_EN_IF_AV 0x01 +#define MTL_Q_ENABLED 0x02 + +#define MTL_RQDCM0R_Q0MDMACH 0x0 +#define MTL_RQDCM0R_Q1MDMACH 0x00000100 +#define MTL_RQDCM0R_Q2MDMACH 0x00020000 +#define MTL_RQDCM0R_Q3MDMACH 0x03000000 +#define MTL_RQDCM1R_Q4MDMACH 0x00000004 +#define MTL_RQDCM1R_Q5MDMACH 0x00000500 +#define MTL_RQDCM1R_Q6MDMACH 0x00060000 +#define MTL_RQDCM1R_Q7MDMACH 0x07000000 +#define MTL_RQDCM2R_Q8MDMACH 0x00000008 +#define MTL_RQDCM2R_Q9MDMACH 0x00000900 +#define MTL_RQDCM2R_Q10MDMACH 0x000A0000 +#define MTL_RQDCM2R_Q11MDMACH 0x0B000000 + +#define MTL_RQDCM0R_Q0DDMACH 0x10 +#define MTL_RQDCM0R_Q1DDMACH 0x00001000 +#define MTL_RQDCM0R_Q2DDMACH 0x00100000 +#define MTL_RQDCM0R_Q3DDMACH 0x10000000 +#define MTL_RQDCM1R_Q4DDMACH 0x00000010 +#define MTL_RQDCM1R_Q5DDMACH 0x00001000 +#define MTL_RQDCM1R_Q6DDMACH 0x00100000 +#define MTL_RQDCM1R_Q7DDMACH 0x10000000 + +/* MTL traffic class register offsets + * Multiple traffic classes can be active. The first class has registers + * that begin at 0x1100. Each subsequent queue has registers that + * are accessed using an offset of 0x80 from the previous queue. + */ +/* NO TRAFFIC CLASS REGISTER DESCRIPTION */ +#define MTL_TC_BASE MTL_Q_BASE +#define MTL_TC_INC MTL_Q_INC + +#define MTL_TC_TQDR 0x08 +#define MTL_TC_ETSCR 0x10 +#define MTL_TC_ETSSR 0x14 +#define MTL_TC_QWR 0x18 + +/* The Queue 0 Transmit Debug register gives the debug status of various blocks + * related to the Transmit queue + */ +#define MTL_TC_TQDR_TRCSTS_POS 1 +#define MTL_TC_TQDR_TRCSTS_LEN 2 +#define MTL_TC_TQDR_TXQSTS_POS 4 +#define MTL_TC_TQDR_TXQSTS_LEN 1 + +/* MTL traffic class register entry bit positions and sizes */ +#define MTL_TC_ETSCR_TSA_POS 0 +#define MTL_TC_ETSCR_TSA_LEN 2 +#define MTL_TC_QWR_QW_POS 0 +#define MTL_TC_QWR_QW_LEN 21 + +/* MTL traffic class register value */ +#define MTL_TSA_SP 0x00 +#define MTL_TSA_ETS 0x02 + +/* DMA register offsets */ +#define DMA_MR 0x1000 +#define DMA_SBMR 0x1004 +#define DMA_ISR 0x1008 +#define DMA_DSR0 0x100c +#define DMA_DSR1 0x1010 +#define DMA_DSR2 0x1014 +#define DMA_AXIARCR 0x1020 +#define DMA_AXIAWCR 0x1024 +#define DMA_AXIAWRCR 0x1028 +#define DMA_SAFE_ISR 0x1080 +#define DMA_ECC_IE 0x1084 +#define DMA_ECC_INT_SR 0x1088 + +/* DMA register entry bit positions and sizes */ +#define DMA_ISR_MACIS_POS 17 +#define DMA_ISR_MACIS_LEN 1 +#define DMA_ISR_MTLIS_POS 16 +#define DMA_ISR_MTLIS_LEN 1 +#define DMA_MR_SWR_POS 0 +#define DMA_MR_SWR_LEN 1 +#define DMA_MR_TXPR_POS 11 +#define DMA_MR_TXPR_LEN 1 +#define DMA_MR_INTM_POS 16 +#define DMA_MR_INTM_LEN 2 +#define DMA_MR_QUREAD_POS 19 +#define DMA_MR_QUREAD_LEN 1 +#define DMA_MR_TNDF_POS 20 +#define DMA_MR_TNDF_LEN 2 +#define DMA_MR_RNDF_POS 22 +#define DMA_MR_RNDF_LEN 2 + +#define DMA_SBMR_EN_LPI_POS 31 +#define DMA_SBMR_EN_LPI_LEN 1 +#define DMA_SBMR_LPI_XIT_PKT_POS 30 +#define DMA_SBMR_LPI_XIT_PKT_LEN 1 +#define DMA_SBMR_WR_OSR_LMT_POS 24 +#define DMA_SBMR_WR_OSR_LMT_LEN 6 +#define DMA_SBMR_RD_OSR_LMT_POS 16 +#define DMA_SBMR_RD_OSR_LMT_LEN 8 +#define DMA_SBMR_AAL_POS 12 +#define DMA_SBMR_AAL_LEN 1 +#define DMA_SBMR_EAME_POS 11 +#define DMA_SBMR_EAME_LEN 1 +#define DMA_SBMR_AALE_POS 10 +#define DMA_SBMR_AALE_LEN 1 +#define DMA_SBMR_BLEN_4_POS 1 +#define DMA_SBMR_BLEN_4_LEN 1 +#define DMA_SBMR_BLEN_8_POS 2 +#define DMA_SBMR_BLEN_8_LEN 1 +#define DMA_SBMR_BLEN_16_POS 3 +#define DMA_SBMR_BLEN_16_LEN 1 +#define DMA_SBMR_BLEN_32_POS 4 +#define DMA_SBMR_BLEN_32_LEN 1 +#define DMA_SBMR_BLEN_64_POS 5 +#define DMA_SBMR_BLEN_64_LEN 1 +#define DMA_SBMR_BLEN_128_POS 6 +#define DMA_SBMR_BLEN_128_LEN 1 +#define DMA_SBMR_BLEN_256_POS 7 +#define DMA_SBMR_BLEN_256_LEN 1 +#define DMA_SBMR_FB_POS 0 +#define DMA_SBMR_FB_LEN 1 + +/* DMA register values */ +#define DMA_DSR_RPS_LEN 4 +#define DMA_DSR_TPS_LEN 4 +#define DMA_DSR_Q_LEN (DMA_DSR_RPS_LEN + DMA_DSR_TPS_LEN) +#define DMA_DSR0_TPS_START 12 +#define DMA_DSRX_FIRST_QUEUE 3 +#define DMA_DSRX_INC 4 +#define DMA_DSRX_QPR 4 +#define DMA_DSRX_TPS_START 4 +#define DMA_TPS_STOPPED 0x00 +#define DMA_TPS_SUSPENDED 0x06 + +/* DMA channel register offsets + * Multiple channels can be active. The first channel has registers + * that begin at 0x1100. Each subsequent channel has registers that + * are accessed using an offset of 0x80 from the previous channel. + */ +#define DMA_CH_BASE 0x1100 +#define DMA_CH_INC 0x80 + +#define DMA_CH_CR 0x00 +#define DMA_CH_TCR 0x04 +#define DMA_CH_RCR 0x08 +#define DMA_CH_TDLR_HI 0x10 +#define DMA_CH_TDLR_LO 0x14 +#define DMA_CH_RDLR_HI 0x18 +#define DMA_CH_RDLR_LO 0x1c +#define DMA_CH_TDTR_LO 0x20 +#define DMA_CH_RDTR_LO 0x28 +#define DMA_CH_TDRLR 0x2c +#define DMA_CH_RDRLR 0x30 +#define DMA_CH_IER 0x34 +#define DMA_CH_RIWT 0x38 +#define DMA_CH_CATDR_LO 0x44 +#define DMA_CH_CARDR_LO 0x4c +#define DMA_CH_CATBR_HI 0x50 +#define DMA_CH_CATBR_LO 0x54 +#define DMA_CH_CARBR_HI 0x58 +#define DMA_CH_CARBR_LO 0x5c +#define DMA_CH_SR 0x60 + +/* DMA channel register entry bit positions and sizes */ +#define DMA_CH_CR_PBLX8_POS 16 +#define DMA_CH_CR_PBLX8_LEN 1 +#define DMA_CH_CR_SPH_POS 24 +#define DMA_CH_CR_SPH_LEN 1 +#define DMA_CH_IER_AIE_POS 14 +#define DMA_CH_IER_AIE_LEN 1 +#define DMA_CH_IER_FBEE_POS 12 +#define DMA_CH_IER_FBEE_LEN 1 +#define DMA_CH_IER_NIE_POS 15 +#define DMA_CH_IER_NIE_LEN 1 +#define DMA_CH_IER_RBUE_POS 7 +#define DMA_CH_IER_RBUE_LEN 1 +#define DMA_CH_IER_RIE_POS 6 +#define DMA_CH_IER_RIE_LEN 1 +#define DMA_CH_IER_RSE_POS 8 +#define DMA_CH_IER_RSE_LEN 1 +#define DMA_CH_IER_TBUE_POS 2 +#define DMA_CH_IER_TBUE_LEN 1 +#define DMA_CH_IER_TIE_POS 0 +#define DMA_CH_IER_TIE_LEN 1 +#define DMA_CH_IER_TXSE_POS 1 +#define DMA_CH_IER_TXSE_LEN 1 +#define DMA_CH_RCR_PBL_POS 16 +#define DMA_CH_RCR_PBL_LEN 6 +#define DMA_CH_RCR_RBSZ_POS 1 +#define DMA_CH_RCR_RBSZ_LEN 14 +#define DMA_CH_RCR_SR_POS 0 +#define DMA_CH_RCR_SR_LEN 1 +#define DMA_CH_RIWT_RWT_POS 0 +#define DMA_CH_RIWT_RWT_LEN 8 +#define DMA_CH_SR_FBE_POS 12 +#define DMA_CH_SR_FBE_LEN 1 +#define DMA_CH_SR_RBU_POS 7 +#define DMA_CH_SR_RBU_LEN 1 +#define DMA_CH_SR_RI_POS 6 +#define DMA_CH_SR_RI_LEN 1 +#define DMA_CH_SR_RPS_POS 8 +#define DMA_CH_SR_RPS_LEN 1 +#define DMA_CH_SR_TBU_POS 2 +#define DMA_CH_SR_TBU_LEN 1 +#define DMA_CH_SR_TI_POS 0 +#define DMA_CH_SR_TI_LEN 1 +#define DMA_CH_SR_TPS_POS 1 +#define DMA_CH_SR_TPS_LEN 1 +#define DMA_CH_TCR_OSP_POS 4 +#define DMA_CH_TCR_OSP_LEN 1 +#define DMA_CH_TCR_PBL_POS 16 +#define DMA_CH_TCR_PBL_LEN 6 +#define DMA_CH_TCR_ST_POS 0 +#define DMA_CH_TCR_ST_LEN 1 +#define DMA_CH_TCR_TSE_POS 12 +#define DMA_CH_TCR_TSE_LEN 1 + +/* DMA channel register values */ +#define DMA_OSP_DISABLE 0x00 +#define DMA_OSP_ENABLE 0x01 +#define DMA_PBL_1 1 +#define DMA_PBL_2 2 +#define DMA_PBL_4 4 +#define DMA_PBL_8 8 +#define DMA_PBL_16 16 +#define DMA_PBL_32 32 +#define DMA_PBL_64 64 +#define DMA_PBL_128 128 +#define DMA_PBL_256 256 +#define DMA_PBL_X8_DISABLE 0x00 +#define DMA_PBL_X8_ENABLE 0x01 + +/* Descriptor/Packet entry bit positions and sizes */ +#define RX_PACKET_ERRORS_CRC_POS 2 +#define RX_PACKET_ERRORS_CRC_LEN 1 +#define RX_PACKET_ERRORS_FRAME_POS 3 +#define RX_PACKET_ERRORS_FRAME_LEN 1 +#define RX_PACKET_ERRORS_LENGTH_POS 0 +#define RX_PACKET_ERRORS_LENGTH_LEN 1 +#define RX_PACKET_ERRORS_OVERRUN_POS 1 +#define RX_PACKET_ERRORS_OVERRUN_LEN 1 + +#define RX_PACKET_ATTRIBUTES_CSUM_DONE_POS 0 +#define RX_PACKET_ATTRIBUTES_CSUM_DONE_LEN 1 +#define RX_PACKET_ATTRIBUTES_VLAN_CTAG_POS 1 +#define RX_PACKET_ATTRIBUTES_VLAN_CTAG_LEN 1 +#define RX_PACKET_ATTRIBUTES_INCOMPLETE_POS 2 +#define RX_PACKET_ATTRIBUTES_INCOMPLETE_LEN 1 +#define RX_PACKET_ATTRIBUTES_CONTEXT_NEXT_POS 3 +#define RX_PACKET_ATTRIBUTES_CONTEXT_NEXT_LEN 1 +#define RX_PACKET_ATTRIBUTES_CONTEXT_POS 4 +#define RX_PACKET_ATTRIBUTES_CONTEXT_LEN 1 +#define RX_PACKET_ATTRIBUTES_RX_TSTAMP_POS 5 +#define RX_PACKET_ATTRIBUTES_RX_TSTAMP_LEN 1 +#define RX_PACKET_ATTRIBUTES_RSS_HASH_POS 6 +#define RX_PACKET_ATTRIBUTES_RSS_HASH_LEN 1 + +#define RX_NORMAL_DESC0_OVT_POS 0 +#define RX_NORMAL_DESC0_OVT_LEN 16 +#define RX_NORMAL_DESC2_HL_POS 0 +#define RX_NORMAL_DESC2_HL_LEN 10 + +#define RX_NORMAL_DESC3_CDA_LEN 1 +#define RX_NORMAL_DESC3_CTXT_POS 30 +#define RX_NORMAL_DESC3_CTXT_LEN 1 +#define RX_NORMAL_DESC3_ES_POS 15 +#define RX_NORMAL_DESC3_ES_LEN 1 +#define RX_NORMAL_DESC3_ETLT_POS 16 +#define RX_NORMAL_DESC3_ETLT_LEN 3 +#define RX_NORMAL_DESC3_FD_POS 29 +#define RX_NORMAL_DESC3_FD_LEN 1 +#define RX_NORMAL_DESC3_INTE_POS 30 +#define RX_NORMAL_DESC3_INTE_LEN 1 + +#define RX_NORMAL_DESC3_L34T_LEN 4 +#define RX_NORMAL_DESC3_RSV_POS 26 +#define RX_NORMAL_DESC3_RSV_LEN 1 +#define RX_NORMAL_DESC3_LD_POS 28 +#define RX_NORMAL_DESC3_LD_LEN 1 +#define RX_NORMAL_DESC3_OWN_POS 31 +#define RX_NORMAL_DESC3_OWN_LEN 1 +#define RX_NORMAL_DESC3_BUF2V_POS 25 +#define RX_NORMAL_DESC3_BUF2V_LEN 1 +#define RX_NORMAL_DESC3_BUF1V_POS 24 +#define RX_NORMAL_DESC3_BUF1V_LEN 1 +#define RX_NORMAL_DESC3_PL_POS 0 +#define RX_NORMAL_DESC3_PL_LEN 15 + +#define RX_NORMAL_DESC0_WB_IVT_POS 16 +#define RX_NORMAL_DESC0_WB_IVT_LEN 16 +#define RX_NORMAL_DESC0_WB_OVT_POS 0 +#define RX_NORMAL_DESC0_WB_OVT_LEN 16 +#define RX_NORMAL_DESC0_WB_OVT_VLANID_POS 0 +#define RX_NORMAL_DESC0_WB_OVT_VLANID_LEN 12 +#define RX_NORMAL_DESC0_WB_OVT_CFI_POS 12 +#define RX_NORMAL_DESC0_WB_OVT_CFI_LEN 1 +#define RX_NORMAL_DESC0_WB_OVT_PRIO_POS 13 +#define RX_NORMAL_DESC0_WB_OVT_PRIO_LEN 3 + +#define RX_NORMAL_DESC1_WB_IPCE_POS 7 +#define RX_NORMAL_DESC1_WB_IPCE_LEN 1 +#define RX_NORMAL_DESC1_WB_IPV6_POS 5 +#define RX_NORMAL_DESC1_WB_IPV6_LEN 1 +#define RX_NORMAL_DESC1_WB_IPV4_POS 4 +#define RX_NORMAL_DESC1_WB_IPV4_LEN 1 +#define RX_NORMAL_DESC1_WB_IPHE_POS 3 +#define RX_NORMAL_DESC1_WB_IPHE_LEN 1 +#define RX_NORMAL_DESC1_WB_PT_POS 0 +#define RX_NORMAL_DESC1_WB_PT_LEN 3 + +#define RX_NORMAL_DESC2_WB_HF_POS 18 +#define RX_NORMAL_DESC2_WB_HF_LEN 1 + +/* Destination Address Filter Fail. When Flexible RX Parser is disabled, + * and this bit is set, it indicates that the packet failed + * the DA Filter in the MAC. + */ +#define RX_NORMAL_DESC2_WB_DAF_POS 17 +#define RX_NORMAL_DESC2_WB_DAF_LEN 1 +#define RX_NORMAL_DESC2_WB_RAPARSER_POS 11 +#define RX_NORMAL_DESC2_WB_RAPARSER_LEN 3 + +#define RX_NORMAL_DESC3_WB_LD_POS 28 +#define RX_NORMAL_DESC3_WB_LD_LEN 1 +#define RX_NORMAL_DESC3_WB_RS0V_POS 25 +#define RX_NORMAL_DESC3_WB_RS0V_LEN 1 + +/* When this bit is set, it indicates that a Cyclic Redundancy Check (CRC) + * Error occurred on the received packet.This field is valid only when the + * LD bit of RDES3 is set. + */ +#define RX_NORMAL_DESC3_WB_CE_POS 24 +#define RX_NORMAL_DESC3_WB_CE_LEN 1 + +#define RX_DESC3_L34T_IPV4_TCP 1 +#define RX_DESC3_L34T_IPV4_UDP 2 +#define RX_DESC3_L34T_IPV4_ICMP 3 +#define RX_DESC3_L34T_IPV6_TCP 9 +#define RX_DESC3_L34T_IPV6_UDP 10 +#define RX_DESC3_L34T_IPV6_ICMP 11 + +#define RX_DESC1_PT_UDP 1 +#define RX_DESC1_PT_TCP 2 +#define RX_DESC1_PT_ICMP 3 +#define RX_DESC1_PT_AV_TAG_DATA 6 +#define RX_DESC1_PT_AV_TAG_CTRL 7 +#define RX_DESC1_PT_AV_NOTAG_CTRL 5 + +#define RX_CONTEXT_DESC3_TSA_LEN 1 + +#define RX_CONTEXT_DESC3_TSD_LEN 1 + +#define TX_PACKET_ATTRIBUTES_CSUM_ENABLE_POS 0 +#define TX_PACKET_ATTRIBUTES_CSUM_ENABLE_LEN 1 +#define TX_PACKET_ATTRIBUTES_TSO_ENABLE_POS 1 +#define TX_PACKET_ATTRIBUTES_TSO_ENABLE_LEN 1 +#define TX_PACKET_ATTRIBUTES_VLAN_CTAG_POS 2 +#define TX_PACKET_ATTRIBUTES_VLAN_CTAG_LEN 1 +#define TX_PACKET_ATTRIBUTES_PTP_POS 3 +#define TX_PACKET_ATTRIBUTES_PTP_LEN 1 + +#define TX_CONTEXT_DESC2_MSS_POS 0 +#define TX_CONTEXT_DESC2_MSS_LEN 14 +#define TX_CONTEXT_DESC2_IVLTV_POS 16 +#define TX_CONTEXT_DESC2_IVLTV_LEN 16 + +#define TX_CONTEXT_DESC3_CTXT_POS 30 +#define TX_CONTEXT_DESC3_CTXT_LEN 1 +#define TX_CONTEXT_DESC3_TCMSSV_POS 26 +#define TX_CONTEXT_DESC3_TCMSSV_LEN 1 +#define TX_CONTEXT_DESC3_IVTIR_POS 18 +#define TX_CONTEXT_DESC3_IVTIR_LEN 2 +#define TX_CONTEXT_DESC3_IVTIR_INSERT 2 +#define TX_CONTEXT_DESC3_IVLTV_POS 17 +#define TX_CONTEXT_DESC3_IVLTV_LEN 1 +#define TX_CONTEXT_DESC3_VLTV_POS 16 +#define TX_CONTEXT_DESC3_VLTV_LEN 1 +#define TX_CONTEXT_DESC3_VT_POS 0 +#define TX_CONTEXT_DESC3_VT_LEN 16 + +#define TX_NORMAL_DESC2_HL_B1L_POS 0 +#define TX_NORMAL_DESC2_HL_B1L_LEN 14 +#define TX_NORMAL_DESC2_IC_POS 31 +#define TX_NORMAL_DESC2_IC_LEN 1 +#define TX_NORMAL_DESC2_TTSE_POS 30 +#define TX_NORMAL_DESC2_TTSE_LEN 1 +#define TX_NORMAL_DESC2_VTIR_POS 14 +#define TX_NORMAL_DESC2_VTIR_LEN 2 +#define TX_NORMAL_DESC2_VLAN_INSERT 0x2 + +#define TX_NORMAL_DESC3_TCPPL_POS 0 +#define TX_NORMAL_DESC3_TCPPL_LEN 18 +#define TX_NORMAL_DESC3_FL_POS 0 +#define TX_NORMAL_DESC3_FL_LEN 15 +#define TX_NORMAL_DESC3_CIC_POS \ + 16 /* Checksum Insertion Control or TCP Payload Length. + 2'b00: Checksum Insertion Disabled. + 2'b01: Only IP header checksum calculation and insertion are enabled. + 2'b10: IP header checksum and payload checksum calculation and insertion are + enabled, but pseudo-header checksum is not calculated in hardware. + 2'b11: IP Header checksum and payload checksum calculation and insertion are + enabled, and pseudo - header checksum is calculated in hardware. */ +#define TX_NORMAL_DESC3_CIC_LEN 2 +#define TX_NORMAL_DESC3_TSE_POS 18 +#define TX_NORMAL_DESC3_TSE_LEN 1 +#define TX_NORMAL_DESC3_TCPHDRLEN_POS \ + 19 /* THL: TCP/UDP Header Length.If the TSE bit is set, this field contains + the length of the TCP / UDP header.The minimum value of this field must + be 5 for TCP header.The value must be equal to 2 for UDP header. This + field is valid only for the first descriptor.*/ +#define TX_NORMAL_DESC3_TCPHDRLEN_LEN 4 +#define TX_NORMAL_DESC3_CPC_POS 26 +#define TX_NORMAL_DESC3_CPC_LEN 2 +#define TX_NORMAL_DESC3_LD_POS 28 +#define TX_NORMAL_DESC3_LD_LEN 1 +#define TX_NORMAL_DESC3_FD_POS 29 +#define TX_NORMAL_DESC3_FD_LEN 1 +#define TX_NORMAL_DESC3_CTXT_POS 30 +#define TX_NORMAL_DESC3_CTXT_LEN 1 +#define TX_NORMAL_DESC3_OWN_POS 31 +#define TX_NORMAL_DESC3_OWN_LEN 1 + +/* for ephy generic register definitions */ +#define FXGMAC_EPHY_REGS_LEN 32 + +#define REG_MII_BMCR 0x00 +#define PHY_CR_RESET_POS 15 +#define PHY_CR_RESET_LEN 1 +#define PHY_CR_SPEED_SEL_H_POS 6 +#define PHY_CR_SPEED_SEL_H_LEN 1 +#define PHY_CR_SPEED_SEL_L_POS 13 +#define PHY_CR_SPEED_SEL_L_LEN 1 +#define PHY_CR_AUTOENG_POS 12 +#define PHY_CR_AUTOENG_LEN 1 +#define PHY_CR_POWER_POS 11 +#define PHY_CR_POWER_LEN 1 +#define PHY_CR_RE_AUTOENG_POS 9 +#define PHY_CR_RE_AUTOENG_LEN 1 +#define PHY_CR_DUPLEX_POS 8 +#define PHY_CR_DUPLEX_LEN 1 +#define REG_MII_BMCR_ENABLE_LOOPBACK 0x8140 +#define REG_MII_BMCR_DISABLE_LOOPBACK 0x9140 +#define REG_MII_BMSR 0x01 +#define REG_MII_PHYSID1 0x02 +#define REG_MII_PHYSID2 0x03 +#define REG_MII_ADVERTISE 0x04 +#define PHY_MII_ADVERTISE_ASYPAUSE_POS 11 +#define PHY_MII_ADVERTISE_ASYPAUSE_LEN 1 +#define PHY_MII_ADVERTISE_PAUSE_POS 10 +#define PHY_MII_ADVERTISE_PAUSE_LEN 1 +#define PHY_MII_ADVERTISE_100FULL_POS 8 +#define PHY_MII_ADVERTISE_100FULL_LEN 1 +#define PHY_MII_ADVERTISE_100HALF_POS 7 +#define PHY_MII_ADVERTISE_100HALF_LEN 1 +#define PHY_MII_ADVERTISE_10FULL_POS 6 +#define PHY_MII_ADVERTISE_10FULL_LEN 1 +#define PHY_MII_ADVERTISE_10HALF_POS 5 +#define PHY_MII_ADVERTISE_10HALF_LEN 1 +#define REG_MII_LPA 0x05 +#define PHY_MII_LINK_PARNTNER_10FULL_POS 6 +#define PHY_MII_LINK_PARNTNER_10FULL_LEN 1 +#define PHY_MII_LINK_PARNTNER_10HALF_POS 5 +#define PHY_MII_LINK_PARNTNER_10HALF_LEN 1 +#define REG_MII_EXPANSION 0x06 +#define REG_MII_NEXT_PAGE 0x07 +#define REG_MII_LPR_NEXT_PAGE 0x08 +#define REG_MII_CTRL1000 0x09 +#define PHY_MII_CTRL1000_1000FULL_POS 9 +#define PHY_MII_CTRL1000_1000FULL_LEN 1 +#define PHY_MII_CTRL1000_1000HALF_POS 8 +#define PHY_MII_CTRL1000_1000HALF_LEN 1 +#define REG_MII_STAT1000 0x0a +#define PHY_MII_STAT1000_CFG_ERROR_POS 15 +#define PHY_MII_STAT1000_CFG_ERROR_LEN 1 + +#define REG_MII_MMD_CTRL 0x0d +#define REG_MII_MMD_DATA 0x0e + +#define REG_MII_ESTATUS 0x0f + +#define REG_MII_SPEC_CTRL 0x10 +#define PHY_MII_SPEC_CTRL_CRS_ON_POS 3 +#define PHY_MII_SPEC_CTRL_CRS_ON_LEN 1 +#define REG_MII_SPEC_STATUS 0x11 +#define PHY_MII_SPEC_DUPLEX_POS 13 +#define PHY_MII_SPEC_DUPLEX_LEN 1 +#define REG_MII_INT_MASK 0x12 + +#ifdef AISC_MODE +#define PHY_INT_MASK_LINK_UP_POS 10 +#define PHY_INT_MASK_LINK_UP_LEN 1 +#define PHY_INT_MASK_LINK_DOWN_POS 11 +#define PHY_INT_MASK_LINK_DOWN_LEN 1 +#else +#define PHY_INT_MASK_LINK_UP_POS 1 +#define PHY_INT_MASK_LINK_UP_LEN 1 +#define PHY_INT_MASK_LINK_DOWN_POS 0 +#define PHY_INT_MASK_LINK_DOWN_LEN 1 +#endif +#define REG_MII_INT_STATUS 0x13 +#define PHY_INT_STAT_LINK_UP_POS 1 +#define PHY_INT_STAT_LINK_UP_LEN 1 +#define PHY_INT_STAT_LINK_DOWN_POS 0 +#define PHY_INT_STAT_LINK_DOWN_LEN 1 +#define REG_MII_DOWNG_CTRL 0x14 +#define REG_MII_RERRCOUNTER 0x15 + +#define REG_MII_EXT_ADDR 0x1e +#define REG_MII_EXT_DATA 0x1f + +#define FXGMAC_EPHY_ID_MASK 0x0000ffff + +/* for ephy link capability + * Advertisement control register(0x04) + */ +#define FXGMAC_ADVERTISE_SLCT 0x001f +#define FXGMAC_ADVERTISE_CSMA 0x0001 +#define FXGMAC_ADVERTISE_1000FULL 0x0004 +#define FXGMAC_ADVERTISE_1000HALF 0x0008 +#define FXGMAC_ADVERTISE_10HALF 0x0020 +#define FXGMAC_ADVERTISE_10FULL 0x0040 +#define FXGMAC_ADVERTISE_100HALF 0x0080 +#define FXGMAC_ADVERTISE_100FULL 0x0100 +#define FXGMAC_ADVERTISE_100BASE4 0x0200 +#define FXGMAC_ADVERTISE_PAUSE_CAP 0x0400 +#define FXGMAC_ADVERTISE_PAUSE_ASYM 0x0800 +#define FXGMAC_ADVERTISE_RESV 0x1000 +#define FXGMAC_ADVERTISE_RFAULT 0x2000 +#define FXGMAC_ADVERTISE_LPACK 0x4000 +#define FXGMAC_ADVERTISE_NPAGE 0x8000 + +/* 1000BASE-T Control register(0x09) */ +#define REG_BIT_ADVERTISE_1000FULL 0x0200 +#define REG_BIT_ADVERTISE_1000HALF 0x0100 + +#define REG_BIT_ADVERTISE_1000_CAP \ + (REG_BIT_ADVERTISE_1000FULL | REG_BIT_ADVERTISE_1000HALF) +#define REG_BIT_ADVERTISE_100_10_CAP \ + (FXGMAC_ADVERTISE_100FULL | FXGMAC_ADVERTISE_100HALF | \ + FXGMAC_ADVERTISE_10FULL | FXGMAC_ADVERTISE_10HALF) + +#ifndef SPEED_1000M +#define SPEED_1000M 1000 +#endif +#ifndef SPEED_100M +#define SPEED_100M 100 +#endif +#ifndef SPEED_10M +#define SPEED_10M 10 +#endif + +#ifndef SPEED_UNKNOWN +#define SPEED_UNKNOWN 0xffff +#endif + +#ifndef DUPLEX_FULL +#define DUPLEX_FULL 1 +#endif +#ifndef DUPLEX_HALF +#define DUPLEX_HALF 0 +#endif + +#ifndef BIT +#define BIT(n) (0x1 << (n)) +#endif + +#ifndef FXGMAC_EPHY_SPEED_MODE_BIT +#define FXGMAC_EPHY_SPEED_MODE 0xc000 +#define FXGMAC_EPHY_DUPLEX 0x2000 +#define FXGMAC_EPHY_SPEED_MODE_BIT 14 +#define FXGMAC_EPHY_DUPLEX_BIT 13 +#define FXGMAC_EPHY_LINK_STATUS_BIT 10 + +#endif + +#define FXGMAC_EPHY_SMI_SEL_PHY 0x0 +#define FXGMAC_EPHY_SMI_SEL_SDS_QSGMII 0x02 +#define FXGMAC_EPHY_SMI_SEL_SDS_SGMII 0x03 + +#define REG_MII_EXT_ANALOG_CFG3 0x52 +#define MII_EXT_ANALOG_CFG3_ADC_START_CFG_POS 14 +#define MII_EXT_ANALOG_CFG3_ADC_START_CFG_LEN 2 + +#define MII_EXT_ANALOG_CFG3_ADC_START_CFG_DEFAULT 0x0 +#define MII_EXT_ANALOG_CFG3_ON_TIME_CFG_POS 12 +#define MII_EXT_ANALOG_CFG3_ON_TIME_CFG_LEN 2 +#define MII_EXT_ANALOG_CFG3_VGA_AMP_GAIN_CFG_POS 8 +#define MII_EXT_ANALOG_CFG3_VGA_AMP_GAIN_CFG_LEN 4 +#define MII_EXT_ANALOG_CFG3_VGA_IBIAS_CFG_POS 4 +#define MII_EXT_ANALOG_CFG3_VGA_IBIAS_CFG_LEN 3 +#define MII_EXT_ANALOG_CFG3_OCP_CFG_POS 2 +#define MII_EXT_ANALOG_CFG3_OCP_CFG_LEN 2 +#define MII_EXT_ANALOG_CFG3_VGA_LPF_CFG_POS 0 +#define MII_EXT_ANALOG_CFG3_VGA_LPF_CFG_LEN 2 + +#define REG_MII_EXT_PMA_DEBUG_KCOEF 0x78 +#define MII_EXT_PMA_DEBUG_KCOEF_IPR_KCOEF_GE_LNG_POS 8 +#define MII_EXT_PMA_DEBUG_KCOEF_IPR_KCOEF_GE_LNG_LEN 6 + +#define MII_EXT_PMA_DEBUG_KCOEF_IPR_KCOEF_GE_LNG_DEFAULT 0x20 +#define MII_EXT_PMA_DEBUG_KCOEF_IPR_KCOEF_DEFAULT_POS 0 +#define MII_EXT_PMA_DEBUG_KCOEF_IPR_KCOEF_DEFAULT_LEN 6 + +#define REG_MII_EXT_LPBK_REG 0x0a +#define REG_MII_EXT_LPBK_REG_ENABLE_LOOPBACK 0x3a18 +#define REG_MII_EXT_LPBK_REG_CLEAN_LOOPBACK 0x3a08 +#define REG_MII_EXT_SLEEP_CONTROL_REG 0x27 +#define REG_MII_EXT_SLEEP_REG_ENABLE_LOOPBACK 0x6812 +#define REG_MII_EXT_SLEEP_REG_CLEAN_LOOPBACK 0xe812 + +#define REG_MII_EXT_ANALOG_CFG2 0x51 +#define REG_MII_EXT_ANALOG_CFG2_VALUE 0x4a9 +#define REG_MII_EXT_ANALOG_CFG8 0x57 +#define REG_MII_EXT_ANALOG_CFG8_VALUE 0x274c +#define REG_MII_EXT_ANALOG_CFG8_137D1D05_VALUE 0x264c + +#define REG_MII_EXT_COMMON_LED_CFG 0xa00b +#define REG_MII_EXT_COMMON_LED0_CFG 0xa00c +#define REG_MII_EXT_COMMON_LED0_CFG_VALUE_SOLUTION0 0x2600 +#define REG_MII_EXT_COMMON_LED0_CFG_VALUE_SOLUTION1 0x00 +#define REG_MII_EXT_COMMON_LED0_CFG_VALUE_SOLUTION2 0x20 +#define REG_MII_EXT_COMMON_LED0_CFG_VALUE_SOLUTION3 0x2600 +#define REG_MII_EXT_COMMON_LED1_CFG 0xa00d +#define REG_MII_EXT_COMMON_LED1_CFG_VALUE_SOLUTION0 0x1800 +#define REG_MII_EXT_COMMON_LED1_CFG_VALUE_SOLUTION1 0x00 +#define REG_MII_EXT_COMMON_LED1_CFG_VALUE_SOLUTION2 0x40 +#define REG_MII_EXT_COMMON_LED2_CFG 0xa00e +#define REG_MII_EXT_COMMON_LED2_CFG_VALUE_SOLUTION0 0x00 +#define REG_MII_EXT_COMMON_LED2_CFG_VALUE_SOLUTION2 0x07 +#define REG_MII_EXT_COMMON_LED2_CFG_VALUE_SOLUTION3 0x20 +#define REG_MII_EXT_COMMON_LED2_CFG_VALUE_SOLUTION4 0x1800 +#define REG_MII_EXT_COMMON_LED_BLINK_CFG 0xa00f +#define REG_MII_EXT_COMMON_LED_BLINK_CFG_SOLUTION2 0x0f + +#define REG_MII_EXT_COMMON_LED0_CFG_VALUE_SLEEP_SOLUTION3 0x2600 + +#define REG_MII_EXT_PKG_CFG0 0xa0 +#define REG_MII_EXT_PKG_CHECK_POS 14 +#define REG_MII_EXT_PKG_CHECK_LEN 2 +#define REG_MII_EXT_PKG_ENABLE_CHECK 0x2 +#define REG_MII_EXT_PKG_DISABLE_CHECK 0x1 +#define REG_MII_EXT_SLEEP_CONTROL1 0x27 +#define MII_EXT_SLEEP_CONTROL1_EN_POS 15 +#define MII_EXT_SLEEP_CONTROL1_EN_LEN 1 +#define MII_EXT_SLEEP_CONTROL1_PLLON_IN_SLP_POS 14 +#define MII_EXT_SLEEP_CONTROL1_PLLON_IN_SLP_LEN 1 +#define REG_MII_EXT_PKG_RX_VALID0 0xa3 +#define REG_MII_EXT_REG_RX_VALID1 0xa4 +#define REG_MII_EXT_REG_RX_OS0 0xa5 +#define REG_MII_EXT_REG_RX_OS1 0xa6 +#define REG_MII_EXT_REG_RX_US0 0xa7 +#define REG_MII_EXT_REG_RX_US1 0xa8 +#define REG_MII_EXT_REG_RX_ERR 0xa9 +#define REG_MII_EXT_REG_RX_0S_BAD 0xaa +#define REG_MII_EXT_REG_RX_FRAGMENT 0xab +#define REG_MII_EXT_REG_RX_NOSFD 0xac +#define REG_MII_EXT_REG_TX_VALID0 0xad +#define REG_MII_EXT_REG_TX_VALID1 0xae +#define REG_MII_EXT_REG_TX_OS0 0xaf +#define REG_MII_EXT_REG_TX_OS1 0xb0 +#define REG_MII_EXT_REG_TX_US0 0xb1 +#define REG_MII_EXT_REG_TX_US1 0xb2 +#define REG_MII_EXT_REG_TX_ERR 0xb3 +#define REG_MII_EXT_REG_TX_OS_BAD 0xb4 +#define REG_MII_EXT_REG_TX_FRAGMENT 0xb5 +#define REG_MII_EXT_REG_TX_NOSFD 0xb6 +#define REG_MII_EXT_REG_PMA_DBG0_ADC 0x13 +#define REG_MII_EXT_ENABLE_GIGA_POWER_SAVING_FOR_SHORT_CABLE 0x3538 +#define REG_MII_EXT_REG_CLD_REG0 0x3a0 +#define REG_MII_EXT_ENABLE_CLD_NP_WP 0xeb24 +#define REG_MII_EXT_REG_CLD_REG1 0x3cc +#define REG_MII_EXT_ENABLE_CLD_GT_HT_BT 0x7001 +#define REG_MMD_EEE_ABILITY_REG 0x3c +#define REG_MMD_EEE_ABILITY_VALUE 0x06 + +/* Below registers don't belong to GMAC, it has zero offset, not 0x2000 offset. mem_base + REG_XXX. */ +/* When issue happens, driver write this register to trigger pcie sniffer. */ +#define REG_PCIE_TRIGGER 0x1000 +#define PCIE_TRIGGER_CODE_TX_HANG 0x00000002 +#define PCIE_TRIGGER_CODE_LINKDOWN 0x00000003 + +#define MGMT_EPHY_CTRL 0x1004 +/* check register address 0x1004 +* b[6:5] ephy_pause +* b[4:3] ephy_speed 0b10 1000m 0b01 100m +* b[2] ephy_duplex +* b[1] ephy_link +* b[0] ephy_reset.0-reset, 1-unreset. Should be set to 1 before use phy. +*/ +#define MGMT_EPHY_CTRL_RESET_POS 0 +#define MGMT_EPHY_CTRL_RESET_LEN 1 +#define MGMT_EPHY_CTRL_STA_EPHY_RESET 0 +#define MGMT_EPHY_CTRL_STA_EPHY_RELEASE 1 +#define MGMT_EPHY_CTRL_STA_EPHY_LINKUP 2 +#define MGMT_EPHY_CTRL_STA_EPHY_LINKUP_POS 1 +#define MGMT_EPHY_CTRL_STA_EPHY_LINKUP_LEN 1 +#define MGMT_EPHY_CTRL_STA_EPHY_DUPLEX_POS 2 +#define MGMT_EPHY_CTRL_STA_EPHY_DUPLEX_LEN 1 + +#define MGMT_EPHY_CTRL_STA_SPEED_POS 3 +#define MGMT_EPHY_CTRL_STA_SPEED_LEN 2 +#define MGMT_EPHY_CTRL_STA_SPEED_MASK 0x18 + +#define MGMT_EPHY_CTRL_ERROR_VALUE 0xffffffff + +#define MGMT_PCIE_EP_CTRL 0x1008 + +#define MGMT_PCIE_EP_CTRL_DBI_CS_EN_POS 0 +#define MGMT_PCIE_EP_CTRL_DBI_CS_EN_LEN 1 + +#define MGMT_PCIE_CFG_CTRL 0x8bc +#define PCIE_CFG_CTRL_DEFAULT_VAL 0x7ff40 + +#define MGMT_PCIE_CFG_CTRL_CS_EN_POS 0 +#define MGMT_PCIE_CFG_CTRL_CS_EN_LEN 1 + +/* power management */ +#define WOL_CTL 0x100c +#define WOL_PKT_EN_POS 1 +#define WOL_PKT_EN_LEN 1 +#define WOL_LINKCHG_EN_POS 0 +#define WOL_LINKCHG_EN_LEN 1 +#define WOL_WAIT_TIME_POS 2 +#define WOL_WAIT_TIME_LEN 13 + +#define OOB_WOL_CTRL 0x1010 +#define OOB_WOL_CTRL_DIS_POS 0 +#define OOB_WOL_CTRL_DIS_LEN 1 + +#define MGMT_INT_CTRL0 0x1100 +/* b3:0 per rx ch interrupt + * b7:4 per tx ch interrupt + * b8 Safety interrupt signal for un-correctable error + * b9 Safety interrupt signal for correctable error + * b10 Interrupt signal to host system + * b11 Magic Packet Received or Remote Wake-up Packet Received + * b12 ethernet phy interrupt + */ + +/* MAC management registers bit positions and sizes */ +#define MGMT_INT_CTRL0_INT_MASK_POS 16 +#define MGMT_INT_CTRL0_INT_MASK_LEN 16 +#define MGMT_INT_CTRL0_INT_MASK_MASK 0xffff +#define MGMT_INT_CTRL0_INT_MASK_RXCH 0xf +#define MGMT_INT_CTRL0_INT_MASK_TXCH 0x10 +#define MGMT_INT_CTRL0_INT_MASK_EX_PMT 0xf7ff +#define MGMT_INT_CTRL0_INT_MASK_DISABLE 0xf000 + +#define MGMT_INT_CTRL0_INT_STATUS_POS 0 +#define MGMT_INT_CTRL0_INT_STATUS_LEN 16 +#define MGMT_INT_CTRL0_INT_STATUS_MASK 0xffff +#define MGMT_INT_CTRL0_INT_STATUS_RX 0x0001 +#define MGMT_INT_CTRL0_INT_STATUS_TX 0x0010 +#define MGMT_INT_CTRL0_INT_STATUS_TX_INVERSE 0xffef +#define MGMT_INT_CTRL0_INT_STATUS_MISC_INVERSE 0xffdf +#define MGMT_INT_CTRL0_INT_STATUS_MISC 0x0020 + +#define MGMT_INT_CTRL0_INT_MASK_RXCH_POS 16 +#define MGMT_INT_CTRL0_INT_STATUS_RXCH_POS 0 +#define MGMT_INT_CTRL0_INT_STATUS_RXCH_LEN 4 +#define MGMT_INT_CTRL0_INT_STATUS_RXCH_MASK 0xf +#define MGMT_INT_CTRL0_INT_STATUS_RXTX_LEN 5 +#define MGMT_INT_CTRL0_INT_STATUS_RXTX_MASK 0x1f +#define MGMT_INT_CTRL0_INT_STATUS_RXTXMISC_MASK 0x3f + +#define MGMT_INT_CTRL0_INT_MASK_TXCH_POS 20 +#define MGMT_INT_CTRL0_INT_STATUS_TXCH_POS 4 +#define MGMT_INT_CTRL0_INT_STATUS_TXCH_LEN 1 +#define MGMT_INT_CTRL0_INT_STATUS_TXCH_MASK 0x1 + +#define MGMT_MAC_PHYIF_STA_POS 0 +#define MGMT_MAC_AN_SR0_POS 1 +#define MGMT_MAC_AN_SR1_POS 2 +#define MGMT_MAC_AN_SR2_POS 3 +#define MGMT_MAC_PMT_STA_POS 4 +#define MGMT_MAC_LPI_STA_POS 5 +#define MGMT_MAC_MMC_STA_POS 8 +#define MGMT_MAC_RX_MMC_STA_POS 9 +#define MGMT_MAC_TX_MMC_STA_POS 10 +#define MGMT_MMC_IPCRXINT_POS 11 +#define MGMT_MAC_TX_RX_STA0_POS 13 +#define MGMT_MAC_TX_RX_STA1_POS 14 +#define MGMT_MAC_GPIO_SR_POS 15 + +/* Interrupt Ctrl1 */ +#define INT_CTRL1 0x1104 +#define INT_CTRL1_TMR_CNT_CFG_MAX_POS \ + 0 /* Timer counter cfg max. Default 0x19, 1us. */ +#define INT_CTRL1_TMR_CNT_CFG_MAX_LEN 10 +#define INT_CTRL1_TMR_CNT_CFG_DEF_VAL 0x19 +#define INT_CTRL1_MSI_AIO_EN_POS 16 +#define INT_CTRL1_MSI_AIO_EN_LEN 1 + +/* Interrupt Moderation */ +#define INT_MOD 0x1108 +#define INT_MOD_TX_POS 16 +#define INT_MOD_TX_LEN 12 +#define INT_MOD_RX_POS 0 +#define INT_MOD_RX_LEN 12 +#define INT_MOD_IN_US 200 + +/* PCIE LTR 2 working modes: + * Two working mode: + * 1. SW trigger + * LTR idle threshold timer set as 0, enable LTR enable will trigger one LTR message + * Note: PCIe cfg enable should set in initialization before enable LTR. + * 2. HW auto trigger + * LTR idle threshold timer set as one non-zero value, HW monitor system status, + * when system idle timer over threshold, HW send out LTR message + * system exit idle state, send out one LTR exit message. + */ +#define LTR_CTRL 0x1130 +#define LTR_CTRL_IDLE_THRE_TIMER_POS 16 +#define LTR_CTRL_IDLE_THRE_TIMER_LEN 14 +#define LTR_CTRL_IDLE_THRE_TIMER_VAL 0x3fff +#define LTR_CTRL_EN_POS 0 +#define LTR_CTRL_EN_LEN 1 + +#define LTR_CTRL1 0x1134 +#define LTR_CTRL1_LTR_MSG_POS 0 +#define LTR_CTRL1_LTR_MSG_LEN 32 + +#define LTR_CTRL2 0x1138 +#define LTR_CTRL2_DBG_DATA_POS 0 +#define LTR_CTRL2_DBG_DATA_LEN 32 + +#define LTR_IDLE_ENTER \ + 0x113c /* LTR_CTRL3, LTR latency message, only for System IDLE Start. */ +#define LTR_IDLE_ENTER_POS 0 +#define LTR_IDLE_ENTER_LEN 10 +#define LTR_IDLE_ENTER_USVAL 900 +#define LTR_IDLE_ENTER_SCALE_POS 10 +#define LTR_IDLE_ENTER_SCALE_LEN 5 +#define LTR_IDLE_ENTER_SCALE \ + 2 /* 0-1ns, 1-32ns, 2-1024ns, 3-32,768ns, 4-1,048,576ns, 5-33,554,432ns, 110-111-Not Permitted.*/ +#define LTR_IDLE_ENTER_REQUIRE_POS 15 +#define LTR_IDLE_ENTER_REQUIRE_LEN 1 +#define LTR_IDLE_ENTER_REQUIRE 1 + +#define LTR_IDLE_EXIT \ + 0x1140 /* LTR_CTRL4, LTR latency message, only for System IDLE End. */ +#define LTR_IDLE_EXIT_POS 0 +#define LTR_IDLE_EXIT_LEN 10 +#define LTR_IDLE_EXIT_USVAL 171 +#define LTR_IDLE_EXIT_SCALE_POS 10 +#define LTR_IDLE_EXIT_SCALE_LEN 5 +#define LTR_IDLE_EXIT_SCALE 2 +#define LTR_IDLE_EXIT_REQUIRE_POS 15 +#define LTR_IDLE_EXIT_REQUIRE_LEN 1 +#define LTR_IDLE_EXIT_REQUIRE 1 + +#define LPW_CTRL 0x1188 +#define LPW_CTRL_L1SS_EN_POS 22 +#define LPW_CTRL_L1SS_EN_LEN 1 +#define LPW_CTRL_L1SS_SEL_POS \ + 21 /* 0 - up to both CFG0x158 and reg1188 L1ss setting. 1 - up to CFG0x158 L1ss setting. */ +#define LPW_CTRL_L1SS_SEL_LEN 1 +#define LPW_CTRL_L1SS_SEL_CFG 1 +#define LPW_CTRL_ASPM_L1_CPM_POS \ + 19 /*L1.CPM mode enable bit. Default 0,set as 1 enable this mode. clkreq pin need to connect RC*/ +#define LPW_CTRL_ASPM_L1_CPM_LEN 1 +#define LPW_CTRL_ASPM_L0S_EN_POS 17 +#define LPW_CTRL_ASPM_L0S_EN_LEN 1 +#define LPW_CTRL_ASPM_L1_EN_POS 16 +#define LPW_CTRL_ASPM_L1_EN_LEN 1 +#define LPW_CTRL_ASPM_LPW_EN_POS 9 +#define LPW_CTRL_ASPM_LPW_EN_LEN 1 +#define LPW_CTRL_SYS_CLK_125_SEL_POS \ + 8 /* system 125M select: 125M or 62.5MHz. Default: 125MHz.*/ +#define LPW_CTRL_SYS_CLK_125_SEL_LEN 1 +#define LPW_CTRL_PCIE_RADM_CG_EN_POS \ + 5 /* clock gating enable bit of PCIe Radm clock. Default 1; set as 1, enable gating.*/ +#define LPW_CTRL_PCIE_RADM_CG_EN_LEN 1 +#define LPW_CTRL_PCIE_CORE_CG_EN_POS \ + 4 /* clock gating enable bit of PCIe Core clock. Default 1; set as 1, enable gating.*/ +#define LPW_CTRL_PCIE_CORE_CG_EN_LEN 1 +#define LPW_CTRL_PCIE_AXI_CG_EN_POS \ + 3 /* clock gating enable bit of PCIe AXI clock.Default 1; set as 1, enable gating.*/ +#define LPW_CTRL_PCIE_AXI_CG_EN_LEN 1 +#define LPW_CTRL_GMAC_AXI_CG_EN_POS \ + 2 /* clock gating enable bit of GMAC AXI clock. Default 1; set as 1, enable gating.*/ +#define LPW_CTRL_GMAC_AXI_CG_EN_LEN 1 +#define LPW_CTRL_MDIO2APB_CG_EN_POS \ + 1 /* clock gating enable bit of MDIO2APB, default 1. Set as 1, enable clock gating feature. */ +#define LPW_CTRL_MDIO2APB_CG_EN_LEN 1 +#define LPW_CTRL_OTP_CLK_ON_POS \ + 0 /* Turn on before SW OTP operation, default 1. */ +#define LPW_CTRL_OTP_CLK_ON_LEN 1 + +#define MSI_PBA_REG 0x1300 +#define SYS_RESET_REG 0x152c +#define SYS_RESET_POS 31 +#define SYS_RESET_LEN 1 + +#define REG_PCIE_PSM_STATE 0x1994 +#define PCIE_PSM_STATE_POS 0 +#define PCIE_PSM_STATE_LEN 4 +#define PCIE_PSM_STATE_P0 2 +#define PCIE_PSM_STATE_P0s 3 +#define PCIE_PSM_STATE_P1 4 +#define PCIE_PSM_STATE_P1_CPM 5 +#define PCIE_PSM_STATE_P1_1 6 +#define PCIE_PSM_STATE_P1_2 7 +#define PCIE_PSM_STATE_P2 8 + +#define REG_PCIE_SERDES_STATUS 0x1998 +#define PCIE_SERDES_STATUS_DRV_ON_POS 11 +#define PCIE_SERDES_STATUS_DRV_ON_LEN 1 +#define PCIE_SERDES_STATUS_RX_PD_POS 10 +#define PCIE_SERDES_STATUS_RX_PD_LEN 1 +#define PCIE_SERDES_STATUS_PI_PD_POS 9 +#define PCIE_SERDES_STATUS_PI_PD_LEN 1 +#define PCIE_SERDES_STATUS_SIGDET_ON_POS 8 +#define PCIE_SERDES_STATUS_SIGDET_ON_LEN 1 +#define PCIE_SERDES_STATUS_TX_VCM_POS 7 +#define PCIE_SERDES_STATUS_TX_VCM_LEN 1 +#define PCIE_SERDES_STATUS_RX_RT50_POS 6 +#define PCIE_SERDES_STATUS_RX_RT50_LEN 1 +#define PCIE_SERDES_STATUS_BEACON_ON_POS 5 +#define PCIE_SERDES_STATUS_BEACON_ON_LEN 1 +#define PCIE_SERDES_STATUS_PLL_ON_POS 4 +#define PCIE_SERDES_STATUS_PLL_ON_LEN 1 +#define PCIE_SERDES_STATUS_REFCLK_ON_POS 3 +#define PCIE_SERDES_STATUS_REFCLK_ON_LEN 1 +#define PCIE_SERDES_STATUS_LDO_ON_POS 2 +#define PCIE_SERDES_STATUS_LDO_ON_LEN 1 +#define PCIE_SERDES_STATUS_HW_EN_SDS_BIAS_POS 1 +#define PCIE_SERDES_STATUS_HW_EN_SDS_BIAS_LEN 1 +#define PCIE_SERDES_STATUS_HW_BIAS_ON_POS 0 +#define PCIE_SERDES_STATUS_HW_BIAS_ON_LEN 1 + +#define REG_PCIE_SERDES_PLL 0x199c +#define PCIE_SERDES_PLL_AUTOOFF_POS 0 +#define PCIE_SERDES_PLL_AUTOOFF_LEN 1 + +#define NS_OF_GLB_CTL 0x1B00 +#define NS_TPID_PRO 0x1B04 +#define NS_LUT_ROMOTE0 0x1B08 +#define NS_LUT_ROMOTE1 0X1B0C +#define NS_LUT_ROMOTE2 0X1B10 +#define NS_LUT_ROMOTE3 0X1B14 +#define NS_LUT_TARGET0 0X1B18 +#define NS_LUT_TARGET1 0X1B1C +#define NS_LUT_TARGET2 0X1B20 +#define NS_LUT_TARGET3 0X1B24 +#define NS_LUT_SOLICITED0 0X1B28 +#define NS_LUT_SOLICITED1 0X1B2C +#define NS_LUT_SOLICITED2 0X1B30 +#define NS_LUT_SOLICITED3 0X1B34 +#define NS_LUT_MAC_ADDR 0X1B38 +#define NS_LUT_MAC_ADDR_CTL 0X1B3C +#define NS_LUT_TARGET4 0X1B78 +#define NS_LUT_TARGET5 0X1B7c +#define NS_LUT_TARGET6 0X1B80 +#define NS_LUT_TARGET7 0X1B84 + +#define NS_OF_GLB_CTL_TX_CLK_EN_POS 2 +#define NS_OF_GLB_CTL_TX_CLK_EN_LEN 1 +#define NS_OF_GLB_CTL_RX_CLK_EN_POS 1 +#define NS_OF_GLB_CTL_RX_CLK_EN_LEN 1 +#define NS_OF_GLB_CTL_EN_POS 0 +#define NS_OF_GLB_CTL_EN_ELN 1 +#define NS_TPID_PRO_STPID_POS 16 +#define NS_TPID_PRO_STPID_LEN 16 +#define NS_TPID_PRO_CTPID_POS 0 +#define NS_TPID_PRO_CTPID_LEN 16 +#define NS_LUT_DST_CMP_TYPE_POS 19 +#define NS_LUT_DST_CMP_TYPE_LEN 1 +#define NS_LUT_DST_IGNORED_POS 18 +#define NS_LUT_DST_IGNORED_LEN 1 +#define NS_LUT_REMOTE_AWARED_POS 17 +#define NS_LUT_REMOTE_AWARED_LEN 1 +#define NS_LUT_TARGET_ISANY_POS 16 +#define NS_LUT_TARGET_ISANY_LEN 1 +#define NS_LUT_MAC_ADDR_LOW_POS 0 +#define NS_LUT_MAC_ADDR_LOW_LEN 16 + +/* RSS implementation registers, 20210817 */ + +/* 10 RSS key registers */ +#define MGMT_RSS_KEY0 0x1020 +#define MGMT_RSS_KEY9 0x1044 +#define MGMT_RSS_KEY_REG_INC 0x4 + +/* RSS control register */ +#define MGMT_RSS_CTRL 0x1048 +/* b31 enable + * b12:10 indirection table size. 2^(val+1) + * b9:8 default Queue NO. + * b7:0 hash type or options + */ + +/* RSS ctrl register bit definitions. + * [0] ipv4 + * [1] tcpv4 + * [2] udpv4 + * [3] ipv6 + * [4] tcpv6 + * [5] udpv6 + * [6] only ipv4 udp check IP hash + * [7] only ipv6 udp check IP hash + */ +#define MGMT_RSS_CTRL_OPT_POS 0 +#define MGMT_RSS_CTRL_OPT_LEN 8 +#define MGMT_RSS_CTRL_OPT_MASK 0xff +#define MGMT_RSS_CTRL_IPV4_EN 0x01 +#define MGMT_RSS_CTRL_TCPV4_EN 0x02 +#define MGMT_RSS_CTRL_UDPV4_EN 0x04 +#define MGMT_RSS_CTRL_IPV6_EN 0x08 +#define MGMT_RSS_CTRL_TCPV6_EN 0x10 +#define MGMT_RSS_CTRL_UDPV6_EN 0x20 +#define MGMT_RSS_CTRL_IPV4 0x0 +#define MGMT_RSS_CTRL_IPV4 0x0 + +#define MGMT_RSS_CTRL_DEFAULT_Q_POS 8 +#define MGMT_RSS_CTRL_DEFAULT_Q_LEN 2 +#define MGMT_RSS_CTRL_DEFAULT_Q_MASK 0x3 + +#define MGMT_RSS_CTRL_TBL_SIZE_POS 10 +#define MGMT_RSS_CTRL_TBL_SIZE_LEN 3 +#define MGMT_RSS_CTRL_TBL_SIZE_MASK 0x7 + +#define MAC_RSSCR_IP2TE_POS 1 +#define MAC_RSSCR_IP2TE_LEN 1 +#define MAC_RSSCR_RSSE_POS 31 +#define MAC_RSSCR_RSSE_LEN 1 + +/* rss indirection table (IDT) */ +#define MGMT_RSS_IDT 0x1050 +/* b0:1 entry0 + * b2:3 entry1 + * ... + */ +#define MGMT_RSS_IDT_REG_INC 4 +#define MGMT_RSS_IDT_ENTRY_PER_REG 16 +#define MGMT_RSS_IDT_ENTRY_MASK 0x3 +#define MAC_CRC_LENGTH 4 + +/* osc_ctrl */ +#define MGMT_XST_OSC_CTRL 0x1158 +#define MGMT_XST_OSC_CTRL_XST_OSC_SEL_POS 2 +#define MGMT_XST_OSC_CTRL_XST_OSC_SEL_LEN 1 +#define MGMT_XST_OSC_CTRL_EN_OSC_POS 1 +#define MGMT_XST_OSC_CTRL_EN_OSC_LEN 1 +#define MGMT_XST_OSC_CTRL_EN_XST_POS 0 +#define MGMT_XST_OSC_CTRL_EN_XST_LEN 1 + +#define MGMT_WPI_CTRL0 0x1160 +/* b1:0 wpi_mode "2b00: normal working mode; 2b01: WPI write mode, work in sleep mode; 2b10: WPI read mode, work after sleep before normal working mode;" + * b2 ram_op_done Each row ram read done, SW can start read after done; + * b3 wpi_op_done WPI read done for the total packet; + * b17:4 wpi_pkt_len WOL packet length, unit byte; + * b31 wpi_fail Error status in Sleep mode; + */ +#define MGMT_WPI_CTRL0_WPI_MODE_POS 0 +#define MGMT_WPI_CTRL0_WPI_MODE_LEN 2 +#define MGMT_WPI_CTRL0_WPI_MODE_NORMAL 0x00 +#define MGMT_WPI_CTRL0_WPI_MODE_WR 0x01 +#define MGMT_WPI_CTRL0_WPI_MODE_RD 0x02 +#define MGMT_WPI_CTRL0_RAM_OP_DONE 0x4 +#define MGMT_WPI_CTRL0_WPI_OP_DONE 0x8 +#define MGMT_WPI_CTRL0_WPI_PKT_LEN_POS 4 +#define MGMT_WPI_CTRL0_WPI_PKT_LEN_LEN 14 +#define MGMT_WPI_CTRL0_WPI_FAIL 0x80000000 + +#define MGMT_WPI_CTRL1_DATA 0x1164 + +#define MGMT_WOL_CTRL 0x1530 +/* b0 link_chg_status 1: waken by link-change + * b1 mgk_pkt_status 1: waken by magic-packet + * b2 rwk_pkt_status 1: waken by remote patten packet + */ +#define MGMT_WOL_CTRL_WPI_LINK_CHG 1 +#define MGMT_WOL_CTRL_WPI_MGC_PKT 2 +#define MGMT_WOL_CTRL_WPI_RWK_PKT 4 +#define MGMT_WOL_CTRL_WPI_RWK_PKT_NUMBER 0x010000 + +#define MGMT_RMK_CTRL 0x1400 + +#define MGMT_SIGDET_DEGLITCH 0x17f0 +#define MGMT_SIGDET_DEGLITCH_DISABLE_POS 2 +#define MGMT_SIGDET_DEGLITCH_DISABLE_LEN 1 +#define MGMT_SIGDET_DEGLITCH_TIME_WIN_POS 3 +#define MGMT_SIGDET_DEGLITCH_TIME_WIN_LEN 2 +#define MGMT_SIGDET_DEGLITCH_TIME_WIN_10ns 0 +#define MGMT_SIGDET_DEGLITCH_TIME_WIN_20ns 1 +#define MGMT_SIGDET_DEGLITCH_TIME_WIN_30ns 2 +#define MGMT_SIGDET_DEGLITCH_TIME_WIN_40ns 3 + +#define MGMT_SIGDET 0x17f8 +#define MGMT_SIGDET_POS 13 +#define MGMT_SIGDET_LEN 3 +#define MGMT_SIGDET_55MV 7 +#define MGMT_SIGDET_50MV 6 +#define MGMT_SIGDET_45MV 5 +#define MGMT_SIGDET_40MV 4 +#define MGMT_SIGDET_35MV 3 +#define MGMT_SIGDET_30MV 2 +#define MGMT_SIGDET_25MV 1 +#define MGMT_SIGDET_20MV 0 + +#define FXGMAC_MTL_REG(pdata, n, reg) \ + ((pdata)->mac_regs + MTL_Q_BASE + ((n) * MTL_Q_INC) + (reg)) + +#define FXGMAC_DMA_REG(channel, reg) ((channel)->dma_regs + (reg)) + +#define MSI_ID_RXQ0 0 +#define MSI_ID_RXQ1 1 +#define MSI_ID_RXQ2 2 +#define MSI_ID_RXQ3 3 +#define MSI_ID_TXQ0 4 + +#define MSI_ID_PHY_OTHER 5 +#define MSIX_TBL_MAX_NUM 6 +#define MSIX_TBL_RXTX_NUM 5 +#define MSIX_TBL_BASE_ADDR 0x1200 +#define MSIX_TBL_MASK_OFFSET 0xc +#define MSIX_TBL_DATA_OFFSET 0x8 +#define MSIX_TBL_ADDR_OFFSET 0x0 + +/******************************************************************* + efuse entry. val31:0 -> offset15:0 + offset7:0 + offset15:8 + val7:0 + val15:8 + val23:16 + val31:24 +*******************************************************************/ +#define EFUSE_OP_CTRL_0 0x1500 +#define EFUSE_OP_WR_DATA_POS 16 +#define EFUSE_OP_WR_DATA_LEN 8 +#define EFUSE_OP_ADDR_POS 8 +#define EFUSE_OP_ADDR_LEN 8 +#define EFUSE_OP_START_POS 2 +#define EFUSE_OP_START_LEN 1 +#define EFUSE_OP_MODE_POS 0 +#define EFUSE_OP_MODE_LEN 2 +#define EFUSE_OP_MODE_ROW_WRITE 0x0 +#define EFUSE_OP_MODE_ROW_READ 0x1 +#define EFUSE_OP_MODE_AUTO_LOAD 0x2 +#define EFUSE_OP_MODE_READ_BLANK 0x3 + +#define EFUSE_OP_CTRL_1 0x1504 +#define EFUSE_OP_RD_DATA_POS 24 +#define EFUSE_OP_RD_DATA_LEN 8 +#define EFUSE_OP_BIST_ERR_ADDR_POS 16 +#define EFUSE_OP_BIST_ERR_ADDR_LEN 8 +#define EFUSE_OP_BIST_ERR_CNT_POS 8 +#define EFUSE_OP_BIST_ERR_CNT_LEN 8 +#define EFUSE_OP_PGM_PASS_POS 2 +#define EFUSE_OP_PGM_PASS_LEN 1 +#define EFUSE_OP_DONE_POS 1 +#define EFUSE_OP_DONE_LEN 1 + +#define EFUSE_FISRT_UPDATE_ADDR 255 +#define EFUSE_SECOND_UPDATE_ADDR 209 +#define FXGMAC_EFUSE_MAX_ENTRY 39 +#define FXGMAC_EFUSE_MAX_ENTRY_UNDER_LED_COMMON 24 +#define EFUSE_PATCH_ADDR_START_BYTE 0 +#define EFUSE_PATCH_DATA_START_BYTE 2 +#define EFUSE_REGION_A_B_LENGTH 18 +#define EFUSE_EACH_PATH_SIZE 6 + +#define EFUSE_REVID_REGISTER 0x0008 +#define EFUSE_SUBSYS_REGISTER 0x002c +#define MACA0LR_FROM_EFUSE 0x1520 +#define MACA0HR_FROM_EFUSE 0x1524 + +#define EFUSE_LED_ADDR 0x00 +#define EFUSE_LED_POS 0 +#define EFUSE_LED_LEN 5 +#define EFUSE_OOB_ADDR 0x07 +#define EFUSE_OOB_POS 2 +#define EFUSE_OOB_LEN 1 +#define EFUSE_LED_SOLUTION0 0 +#define EFUSE_LED_SOLUTION1 1 +#define EFUSE_LED_SOLUTION2 2 +#define EFUSE_LED_SOLUTION3 3 +#define EFUSE_LED_SOLUTION4 4 +#define EFUSE_LED_COMMON_SOLUTION 0x1f + +/******************** Below for pcie configuration register. *********************/ +#define REG_PCI_VENDOR_ID 0x0 +#define REG_PCI_DEVICE_ID 0x2 +#define PCI_DEVICE_ID_FUXI 0x6801 + +#define REG_PCI_COMMAND 0x4 +#define PCI_COMMAND_IO_SPACE_POS 0 +#define PCI_COMMAND_IO_SPACE_LEN 1 +#define PCI_COMAMND_MEM_SPACE_POS 1 +#define PCI_COMAMND_MEM_SPACE_LEN 1 +#define PCI_COMMAND_MASTER_POS 2 +#define PCI_COMMAND_MASTER_LEN 1 +#define PCI_COMMAND_DIS_INT_POS 10 +#define PCI_COMMAND_DIS_INT_LEN 1 +#define PCI_COMMAND_INTX_STATUS_POS 19 +#define PCI_COMMAND_INTX_STATUS_LEN 1 + +#define REG_PCI_REVID 0x8 +#define REG_PCI_PROGRAM_INTF 0x9 +#define REG_PCI_SUB_CLASS 0xa +#define REG_PCI_BASE_CLASS 0xb +#define REG_CACHE_LINE_SIZE 0xc + +#define REG_MEM_BASE 0x10 +#define REG_MEM_BASE_HI 0x14 + +#define REG_IO_BASE 0x20 + +#define REG_PCI_SUB_VENDOR_ID 0x2c +#define REG_PCI_SUB_DEVICE_ID 0x2e + +#define REG_INT_LINE 0x3c + +#define REG_PM_STATCTRL 0x44 +#define PM_STATCTRL_PWR_STAT_POS 0 +#define PM_STATCTRL_PWR_STAT_LEN 2 +#define PM_STATCTRL_PWR_STAT_D3 3 +#define PM_STATCTRL_PWR_STAT_D0 0 +#define PM_CTRLSTAT_PME_EN_POS 8 +#define PM_CTRLSTAT_PME_EN_LEN 1 +#define PM_CTRLSTAT_DATA_SEL_POS 9 +#define PM_CTRLSTAT_DATA_SEL_LEN 4 +#define PM_CTRLSTAT_DATA_SCAL_POS 13 +#define PM_CTRLSTAT_DATA_SCAL_LEN 2 +#define PM_CTRLSTAT_PME_STAT_POS 15 +#define PM_CTRLSTAT_PME_STAT_LEN 1 + +#define REG_DEVICE_CTRL1 0x78 +#define DEVICE_CTRL1_CONTROL_POS 0 +#define DEVICE_CTRL1_CONTROL_LEN 16 +#define DEVICE_CTRL1_STATUS_POS 16 +#define DEVICE_CTRL1_STATUS_LEN 16 + +#define REG_PCI_LINK_CTRL 0x80 +#define PCI_LINK_CTRL_CONTROL_POS 0 +#define PCI_LINK_CTRL_CONTROL_LEN 16 +#define PCI_LINK_CTRL_ASPM_CONTROL_POS 0 +#define PCI_LINK_CTRL_ASPM_CONTROL_LEN 2 +#define PCI_LINK_CTRL_L1_STATUS 2 +#define PCI_LINK_CTRL_CONTROL_CPM_POS \ + 8 /*L1.CPM mode enable bit. Default 0,set as 1 enable this mode. clkreq pin need to connect RC*/ +#define PCI_LINK_CTRL_CONTROL_CPM_LEN 1 +#define PCI_LINK_CTRL_STATUS_POS 16 +#define PCI_LINK_CTRL_STATUS_LEN 16 + +#define REG_DEVICE_CTRL2 0x98 +#define DEVICE_CTRL2_LTR_EN_POS 10 +#define DEVICE_CTRL2_LTR_EN_LEN 1 + +#define REG_MSIX_CAPABILITY 0xb0 + +/* ASPM L1ss PM Substates */ +#define REG_ASPM_L1SS_CAP 0x154 +#define ASPM_L1SS_CAP_PCIPM_L1_2_POS 0 +#define ASPM_L1SS_CAP_PCIPM_L1_2_LEN 1 +#define ASPM_L1SS_CAP_PCIPM_L1_1_POS 1 +#define ASPM_L1SS_CAP_PCIPM_L1_1_LEN 1 +#define ASPM_L1SS_CAP_ASPM_L1_2_POS 2 +#define ASPM_L1SS_CAP_ASPM_L1_2_LEN 1 +#define ASPM_L1SS_CAP_ASPM_L1_1_POS 3 +#define ASPM_L1SS_CAP_ASPM_L1_1_LEN 1 +#define ASPM_L1SS_CAP_L1_PM_SS_POS 4 +#define ASPM_L1SS_CAP_L1_PM_SS_LEN 1 +#define ASPM_L1SS_CAP_CM_RESTORE_TIME_POS 8 +#define ASPM_L1SS_CAP_CM_RESTORE_TIME_LEN 8 +#define ASPM_L1SS_CAP_P_PWR_ON_SCALE_POS 16 +#define ASPM_L1SS_CAP_P_PWR_ON_SCALE_LEN 2 +#define ASPM_L1SS_CAP_P_PWR_ON_VALUE_POS 19 +#define ASPM_L1SS_CAP_P_PWR_ON_VALUE_LEN 5 + +#define REG_ASPM_L1SS_CTRL1 0x158 +#define REG_ASPM_L1SS_CTRL1_VALUE 0x405e000f +#define ASPM_L1SS_CTRL1_L12_PCIPM_EN_POS 0 +#define ASPM_L1SS_CTRL1_L12_PCIPM_EN_LEN 1 +#define ASPM_L1SS_CTRL1_L11_PCIPM_EN_POS 1 +#define ASPM_L1SS_CTRL1_L11_PCIPM_EN_LEN 1 +#define ASPM_L1SS_CTRL1_L12_EN_POS 2 +#define ASPM_L1SS_CTRL1_L12_EN_LEN 1 +#define ASPM_L1SS_CTRL1_L11_EN_POS 3 +#define ASPM_L1SS_CTRL1_L11_EN_LEN 1 +#define ASPM_L1SS_CTRL1_CM_RESTORE_TIME_POS 8 +#define ASPM_L1SS_CTRL1_CM_RESTORE_TIME_LEN 8 +#define ASPM_L1SS_CTRL1_LTR_L12_TH_VALUE_POS 16 +#define ASPM_L1SS_CTRL1_LTR_L12_TH_VALUE_LEN 10 +#define ASPM_L1SS_CTRL1_L12_TH_SCALE_POS 29 +#define ASPM_L1SS_CTRL1_L12_TH_SCALE_LEN 3 + +#define REG_ASPM_L1SS_CTL2 0x15c + +#define REG_ASPM_CONTROL 0x70c +#define ASPM_L1_IDLE_THRESHOLD_POS 27 +#define ASPM_L1_IDLE_THRESHOLD_LEN 3 +#define ASPM_L1_IDLE_THRESHOLD_1US 0 +#define ASPM_L1_IDLE_THRESHOLD_2US 1 +#define ASPM_L1_IDLE_THRESHOLD_4US 2 +#define ASPM_L1_IDLE_THRESHOLD_8US 3 +#define ASPM_L1_IDLE_THRESHOLD_16US 4 +#define ASPM_L1_IDLE_THRESHOLD_32US 5 +#define ASPM_L1_IDLE_THRESHOLD_64US 6 + +#define REG_POWER_EIOS 0x710 +#define POWER_EIOS_POS 7 +#define POWER_EIOS_LEN 1 + +#define AISTONEID_137D1D05_ADJUST_SI 0x137d1d05 + +#endif /* __FXGMAC_GMAC_REG_H__ */ diff --git a/drivers/net/ethernet/motorcomm/yt6801/fuxi-gmac.h b/drivers/net/ethernet/motorcomm/yt6801/fuxi-gmac.h new file mode 100644 index 000000000000..4c26d4de842a --- /dev/null +++ b/drivers/net/ethernet/motorcomm/yt6801/fuxi-gmac.h @@ -0,0 +1,938 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (c) 2021 Motorcomm Corporation. */ + +#ifndef __FXGMAC_GMAC_H__ +#define __FXGMAC_GMAC_H__ + +#include "fuxi-os.h" + +#define FXGMAC_FPGA_VER_B4_0507 0 +#define FXGMAC_FPGA_VER_20210507 1 + +#define FXGMAC_DRV_NAME "yt6801" + +#define FXGMAC_DRV_DESC "Motorcomm YT6801 Gigabit Ethernet Driver" + +#define FXGMAC_MAC_REGS_OFFSET 0x2000 + +#define FXGMAC_EPHY_INTERRUPT_D0_OFF 0 +#define FXGMAC_ALLOC_NEW_RECBUFFER 0 + +#define RESUME_MAX_TIME 3000000 +#define PHY_LINK_TIMEOUT 3000 +#define ESD_RESET_MAXIMUM 0 + +#define REGWR_RETRY_MAXIMUM 2600 +#define PCIE_LINKDOWN_VALUE 0xFFFFFFFF + +#define FXGMAC_MSIX_Q_VECTORS 4 + +#define FXGMAC_IS_CHANNEL_WITH_TX_IRQ(chId) (0 == (chId) ? 1 : 0) + +/* flags for ipv6 NS offload address, local link or Global unicast */ +#define FXGMAC_NS_IFA_LOCAL_LINK 1 +#define FXGMAC_NS_IFA_GLOBAL_UNICAST 2 + +#define FXGMAX_ASPM_WAR_EN +/* Descriptor related parameters */ +#if FXGMAC_TX_HANG_TIMER_ENABLED +#define FXGMAC_TX_DESC_CNT 1024 +#else +#define FXGMAC_TX_DESC_CNT 256 +#endif +#define FXGMAC_TX_DESC_MIN_FREE (FXGMAC_TX_DESC_CNT >> 3) +#define FXGMAC_TX_DESC_MAX_PROC (FXGMAC_TX_DESC_CNT >> 1) +#define FXGMAC_RX_DESC_CNT 1024 +#define FXGMAC_RX_DESC_MAX_DIRTY (FXGMAC_RX_DESC_CNT >> 3) + +/* Descriptors required for maximum contiguous TSO/GSO packet */ +#define FXGMAC_TX_MAX_SPLIT ((GSO_MAX_SIZE / FXGMAC_TX_MAX_BUF_SIZE) + 1) + +/* Maximum possible descriptors needed for a SKB */ +#define FXGMAC_TX_MAX_DESC_NR (MAX_SKB_FRAGS + FXGMAC_TX_MAX_SPLIT + 2) + +#define FXGMAC_TX_MAX_BUF_SIZE (0x3fff & ~(64 - 1)) +#define FXGMAC_RX_MIN_BUF_SIZE (ETH_FRAME_LEN + ETH_FCS_LEN + VLAN_HLEN) +#define FXGMAC_RX_BUF_ALIGN 64 + +/* Maximum Size for Splitting the Header Data + * Keep in sync with SKB_ALLOC_SIZE + * 3'b000: 64 bytes, 3'b001: 128 bytes + * 3'b010: 256 bytes, 3'b011: 512 bytes + * 3'b100: 1023 bytes , 3'b101'3'b111: Reserved + */ +#define FXGMAC_SPH_HDSMS_SIZE 3 +#define FXGMAC_SKB_ALLOC_SIZE 512 + +#define FXGMAC_MAX_FIFO 81920 + +#define FXGMAC_MAX_DMA_CHANNELS FXGMAC_MSIX_Q_VECTORS +#define FXGMAC_DMA_STOP_TIMEOUT 5 +#define FXGMAC_DMA_INTERRUPT_MASK 0x31c7 +#define FXGMAC_MAX_DMA_CHANNELS_PLUS_1TX (FXGMAC_MAX_DMA_CHANNELS + 1) + +/* Default coalescing parameters */ +#define FXGMAC_INIT_DMA_TX_USECS INT_MOD_IN_US +#define FXGMAC_INIT_DMA_TX_FRAMES 25 +#define FXGMAC_INIT_DMA_RX_USECS INT_MOD_IN_US +#define FXGMAC_INIT_DMA_RX_FRAMES 25 +#define FXGMAC_MAX_DMA_RIWT 0xff +#define FXGMAC_MIN_DMA_RIWT 0x01 + +/* Flow control queue count */ +#define FXGMAC_MAX_FLOW_CONTROL_QUEUES 8 + +/* System clock is 125 MHz */ +#define FXGMAC_SYSCLOCK 125000000 + +/* Maximum MAC address hash table size (256 bits = 8 bytes) */ +#define FXGMAC_MAC_HASH_TABLE_SIZE 8 + +/* wol pattern settings */ +#define MAX_PATTERN_SIZE 128 +#define MAX_PATTERN_COUNT 16 +#define MAX_LPP_ARP_OFFLOAD_COUNT 1 +#define MAX_LPP_NS_OFFLOAD_COUNT 2 + +#define MAX_WPI_LENGTH_SIZE 1536 +#define PM_WAKE_PKT_ALIGN 8 + +/* Receive Side Scaling */ +#define FXGMAC_RSS_HASH_KEY_SIZE 40 +#define FXGMAC_RSS_MAX_TABLE_SIZE 128 +#define FXGMAC_RSS_LOOKUP_TABLE_TYPE 0 +#define FXGMAC_RSS_HASH_KEY_TYPE 1 +#define MAX_MSI_COUNT 16 + +#define FXGMAC_STD_PACKET_MTU 1500 +#define FXGMAC_JUMBO_PACKET_MTU 9014 + +#define NIC_MAX_TCP_OFFLOAD_SIZE 7300 +#define NIC_MIN_LSO_SEGMENT_COUNT 2 + +/* power management */ +#define FXGMAC_POWER_STATE_DOWN 0 +#define FXGMAC_POWER_STATE_UP 1 + +#define FXGMAC_DATA_WIDTH 128 + +#define FXGMAC_WOL_WAIT_TIME 2 + +struct wol_bitmap_pattern { + u32 flags; + u32 pattern_size; + u32 mask_size; + u8 mask_info[MAX_PATTERN_SIZE / 8]; + u8 pattern_info[MAX_PATTERN_SIZE]; + u8 pattern_offset; + u16 pattern_crc; +}; + +struct led_setting { + u32 s0_led_setting[5]; + u32 s3_led_setting[5]; + u32 s5_led_setting[5]; + u32 disable_led_setting[5]; +}; + +typedef struct led_setting LED_SETTING; +typedef struct wol_bitmap_pattern WOL_BITMAP_PATTERN; + +typedef enum { + WAKE_REASON_NONE = 0, + WAKE_REASON_MAGIC, + WAKE_REASON_PATTERNMATCH, + WAKE_REASON_LINK, + WAKE_REASON_TCPSYNV4, + WAKE_REASON_TCPSYNV6, + WAKE_REASON_TBD, + WAKE_REASON_HW_ERR, +} WAKE_REASON; + +/* Helper macro for descriptor handling + * Always use FXGMAC_GET_DESC_DATA to access the descriptor data + */ + +#define FXGMAC_GET_DESC_DATA(ring, idx) ((ring)->desc_data_head + (idx)) +#define FXGMAC_GET_ENTRY(x, size) ((x + 1) & (size - 1)) + +struct fxgmac_pdata; + +enum fxgmac_int { + FXGMAC_INT_DMA_CH_SR_TI, + FXGMAC_INT_DMA_CH_SR_TPS, + FXGMAC_INT_DMA_CH_SR_TBU, + FXGMAC_INT_DMA_CH_SR_RI, + FXGMAC_INT_DMA_CH_SR_RBU, + FXGMAC_INT_DMA_CH_SR_RPS, + FXGMAC_INT_DMA_CH_SR_TI_RI, + FXGMAC_INT_DMA_CH_SR_FBE, + FXGMAC_INT_DMA_ALL, +}; + +struct fxgmac_stats { + /* MMC TX counters */ + u64 txoctetcount_gb; + u64 txframecount_gb; + u64 txbroadcastframes_g; + u64 txmulticastframes_g; + u64 tx64octets_gb; + u64 tx65to127octets_gb; + u64 tx128to255octets_gb; + u64 tx256to511octets_gb; + u64 tx512to1023octets_gb; + u64 tx1024tomaxoctets_gb; + u64 txunicastframes_gb; + u64 txmulticastframes_gb; + u64 txbroadcastframes_gb; + u64 txunderflowerror; + u64 txsinglecollision_g; + u64 txmultiplecollision_g; + u64 txdeferredframes; + u64 txlatecollisionframes; + u64 txexcessivecollisionframes; + u64 txcarriererrorframes; + u64 txoctetcount_g; + u64 txframecount_g; + u64 txexcessivedeferralerror; + u64 txpauseframes; + u64 txvlanframes_g; + u64 txoversize_g; + + /* MMC RX counters */ + u64 rxframecount_gb; + u64 rxoctetcount_gb; + u64 rxoctetcount_g; + u64 rxbroadcastframes_g; + u64 rxmulticastframes_g; + u64 rxcrcerror; + u64 rxalignerror; + u64 rxrunterror; + u64 rxjabbererror; + u64 rxundersize_g; + u64 rxoversize_g; + u64 rx64octets_gb; + u64 rx65to127octets_gb; + u64 rx128to255octets_gb; + u64 rx256to511octets_gb; + u64 rx512to1023octets_gb; + u64 rx1024tomaxoctets_gb; + u64 rxunicastframes_g; + u64 rxlengtherror; + u64 rxoutofrangetype; + u64 rxpauseframes; + u64 rxfifooverflow; + u64 rxvlanframes_gb; + u64 rxwatchdogerror; + u64 rxreceiveerrorframe; + u64 rxcontrolframe_g; + + /* Extra counters */ + u64 tx_tso_packets; + u64 rx_split_header_packets; + u64 tx_process_stopped; + u64 rx_process_stopped; + u64 tx_buffer_unavailable; + u64 rx_buffer_unavailable; + u64 fatal_bus_error; + u64 tx_vlan_packets; + u64 rx_vlan_packets; + u64 napi_poll_isr; + u64 napi_poll_txtimer; + u64 cnt_alive_txtimer; + + u64 ephy_poll_timer_cnt; + u64 mgmt_int_isr; +}; + +struct fxgmac_ring_buf { + struct sk_buff *skb; + DMA_ADDR_T skb_dma; + unsigned int skb_len; +}; + +/* Common Tx and Rx DMA hardware descriptor */ +struct fxgmac_dma_desc { + __le32 desc0; + __le32 desc1; + __le32 desc2; + __le32 desc3; +}; + +/* Page allocation related values */ +struct fxgmac_page_alloc { + struct page *pages; + unsigned int pages_len; + unsigned int pages_offset; + DMA_ADDR_T pages_dma; +}; + +/* Ring entry buffer data */ +struct fxgmac_buffer_data { + struct fxgmac_page_alloc pa; + struct fxgmac_page_alloc pa_unmap; + + DMA_ADDR_T dma_base; + unsigned long dma_off; + unsigned int dma_len; +}; + +/* Tx-related desc data */ +struct fxgmac_tx_desc_data { + unsigned int packets; /* BQL packet count */ + unsigned int bytes; /* BQL byte count */ +}; + +/* Rx-related desc data */ +struct fxgmac_rx_desc_data { + struct fxgmac_buffer_data hdr; /* Header locations */ + struct fxgmac_buffer_data buf; /* Payload locations */ + + unsigned short hdr_len; /* Length of received header */ + unsigned short len; /* Length of received packet */ +}; + +struct fxgmac_pkt_info { + struct sk_buff *skb; + + unsigned int attributes; + + unsigned int errors; + + /* descriptors needed for this packet */ + unsigned int desc_count; + unsigned int length; + + unsigned int tx_packets; + unsigned int tx_bytes; + + unsigned int header_len; + unsigned int tcp_header_len; + unsigned int tcp_payload_len; + unsigned short mss; + + unsigned short vlan_ctag; + + u64 rx_tstamp; + + u32 rss_hash; + RSS_HASH_TYPE rss_hash_type; +}; + +struct fxgmac_desc_data { + /* dma_desc: Virtual address of descriptor + * dma_desc_addr: DMA address of descriptor + */ + struct fxgmac_dma_desc *dma_desc; + DMA_ADDR_T dma_desc_addr; + + /* skb: Virtual address of SKB + * skb_dma: DMA address of SKB data + * skb_dma_len: Length of SKB DMA area + */ + struct sk_buff *skb; + DMA_ADDR_T skb_dma; + unsigned int skb_dma_len; + + /* Tx/Rx -related data */ + struct fxgmac_tx_desc_data tx; + struct fxgmac_rx_desc_data rx; + + unsigned int mapped_as_page; + + /* Incomplete receive save location. If the budget is exhausted + * or the last descriptor (last normal descriptor or a following + * context descriptor) has not been DMA'd yet the current state + * of the receive processing needs to be saved. + */ + unsigned int state_saved; + struct { + struct sk_buff *skb; + unsigned int len; + unsigned int error; + } state; +}; + +struct fxgmac_ring { + /* Per packet related information */ + struct fxgmac_pkt_info pkt_info; + + /* Virtual/DMA addresses of DMA descriptor list and the total count */ + struct fxgmac_dma_desc *dma_desc_head; + DMA_ADDR_T dma_desc_head_addr; + unsigned int dma_desc_count; + + /* Array of descriptor data corresponding the DMA descriptor + * (always use the FXGMAC_GET_DESC_DATA macro to access this data) + */ + struct fxgmac_desc_data *desc_data_head; + + /* Page allocation for RX buffers */ + struct fxgmac_page_alloc rx_hdr_pa; + struct fxgmac_page_alloc rx_buf_pa; + + /* Ring index values + * cur - Tx: index of descriptor to be used for current transfer + * Rx: index of descriptor to check for packet availability + * dirty - Tx: index of descriptor to check for transfer complete + * Rx: index of descriptor to check for buffer reallocation + */ + unsigned int cur; + unsigned int dirty; + + /* Coalesce frame count used for interrupt bit setting */ + unsigned int coalesce_count; + + struct { + unsigned int xmit_more; + unsigned int queue_stopped; + unsigned short cur_mss; + unsigned short cur_vlan_ctag; + } tx; +} ____cacheline_aligned; + +struct fxgmac_channel { + char name[16]; + + /* Address of private data area for device */ + struct fxgmac_pdata *pdata; + + /* Queue index and base address of queue's DMA registers */ + unsigned int queue_index; + + IOMEM dma_regs; + + /* Per channel interrupt irq number */ + u32 dma_irq; + FXGMAC_CHANNEL_OF_PLATFORM expansion; + + u32 saved_ier; + + unsigned int tx_timer_active; + + struct fxgmac_ring *tx_ring; + struct fxgmac_ring *rx_ring; +} ____cacheline_aligned; + +struct fxphy_ag_adv { + u8 auto_neg_en : 1; + u8 full_1000m : 1; + u8 half_1000m : 1; + u8 full_100m : 1; + u8 half_100m : 1; + u8 full_10m : 1; + u8 half_10m : 1; +}; + +struct fxgmac_desc_ops { + int (*alloc_channels_and_rings)(struct fxgmac_pdata *pdata); + void (*free_channels_and_rings)(struct fxgmac_pdata *pdata); + int (*map_tx_skb)(struct fxgmac_channel *channel, struct sk_buff *skb); + int (*map_rx_buffer)(struct fxgmac_pdata *pdata, + struct fxgmac_ring *ring, + struct fxgmac_desc_data *desc_data); + void (*unmap_desc_data)(struct fxgmac_pdata *pdata, + struct fxgmac_desc_data *desc_data); + void (*tx_desc_init)(struct fxgmac_pdata *pdata); + void (*rx_desc_init)(struct fxgmac_pdata *pdata); + /* For descriptor related operation */ + void (*tx_desc_init_channel)(struct fxgmac_channel *channel); + void (*rx_desc_init_channel)(struct fxgmac_channel *channel); + void (*tx_desc_reset)(struct fxgmac_desc_data *desc_data); + void (*rx_desc_reset)(struct fxgmac_pdata *pdata, + struct fxgmac_desc_data *desc_data, + unsigned int index); +}; + +struct fxgmac_hw_ops { + int (*init)(struct fxgmac_pdata *pdata); + int (*exit)(struct fxgmac_pdata *pdata); + void (*save_nonstick_reg)(struct fxgmac_pdata *pdata); + void (*restore_nonstick_reg)(struct fxgmac_pdata *pdata); + int (*set_gmac_register)(struct fxgmac_pdata *pdata, IOMEM address, + unsigned int data); + u32 (*get_gmac_register)(struct fxgmac_pdata *pdata, IOMEM address); + void (*esd_restore_pcie_cfg)(struct fxgmac_pdata *pdata); + + int (*tx_complete)(struct fxgmac_dma_desc *dma_desc); + + void (*enable_tx)(struct fxgmac_pdata *pdata); + void (*disable_tx)(struct fxgmac_pdata *pdata); + void (*enable_rx)(struct fxgmac_pdata *pdata); + void (*disable_rx)(struct fxgmac_pdata *pdata); + void (*enable_channel_rx)(struct fxgmac_pdata *pdata, + unsigned int queue); + + int (*enable_int)(struct fxgmac_channel *channel, + enum fxgmac_int int_id); + int (*disable_int)(struct fxgmac_channel *channel, + enum fxgmac_int int_id); + void (*set_interrupt_moderation)(struct fxgmac_pdata *pdata); + void (*enable_msix_rxtxinterrupt)(struct fxgmac_pdata *pdata); + void (*disable_msix_interrupt)(struct fxgmac_pdata *pdata); + int (*enable_msix_rxtxphyinterrupt)(struct fxgmac_pdata *pdata); + void (*enable_msix_one_interrupt)(struct fxgmac_pdata *pdata, + u32 intid); + void (*disable_msix_one_interrupt)(struct fxgmac_pdata *pdata, + u32 intid); + bool (*enable_mgm_interrupt)(struct fxgmac_pdata *pdata); + bool (*disable_mgm_interrupt)(struct fxgmac_pdata *pdata); + int (*dismiss_all_int)(struct fxgmac_pdata *pdata); + void (*clear_misc_int_status)(struct fxgmac_pdata *pdata); + + void (*dev_xmit)(struct fxgmac_channel *channel); + int (*dev_read)(struct fxgmac_channel *channel); + + int (*set_mac_address)(struct fxgmac_pdata *pdata, u8 *addr); + int (*set_mac_hash)(struct fxgmac_pdata *pdata); + int (*config_rx_mode)(struct fxgmac_pdata *pdata); + int (*enable_rx_csum)(struct fxgmac_pdata *pdata); + int (*disable_rx_csum)(struct fxgmac_pdata *pdata); + void (*config_tso)(struct fxgmac_pdata *pdata); + + /* For MII speed configuration */ + int (*config_mac_speed)(struct fxgmac_pdata *pdata); + int (*get_xlgmii_phy_status)(struct fxgmac_pdata *pdata, u32 *speed, + bool *link_up, + bool link_up_wait_to_complete); + + /* For descriptor related operation */ + + int (*is_last_desc)(struct fxgmac_dma_desc *dma_desc); + int (*is_context_desc)(struct fxgmac_dma_desc *dma_desc); + + /* For Flow Control */ + int (*config_tx_flow_control)(struct fxgmac_pdata *pdata); + int (*config_rx_flow_control)(struct fxgmac_pdata *pdata); + + /* For Jumbo Frames */ + int (*config_mtu)(struct fxgmac_pdata *pdata); + int (*enable_jumbo)(struct fxgmac_pdata *pdata); + + /* For Vlan related config */ + int (*enable_tx_vlan)(struct fxgmac_pdata *pdata); + int (*disable_tx_vlan)(struct fxgmac_pdata *pdata); + int (*enable_rx_vlan_stripping)(struct fxgmac_pdata *pdata); + int (*disable_rx_vlan_stripping)(struct fxgmac_pdata *pdata); + int (*enable_rx_vlan_filtering)(struct fxgmac_pdata *pdata); + int (*disable_rx_vlan_filtering)(struct fxgmac_pdata *pdata); + int (*update_vlan_hash_table)(struct fxgmac_pdata *pdata); + + /* For RX coalescing */ + int (*config_rx_coalesce)(struct fxgmac_pdata *pdata); + int (*config_tx_coalesce)(struct fxgmac_pdata *pdata); + unsigned long (*usec_to_riwt)(struct fxgmac_pdata *pdata, + unsigned int usec); + unsigned long (*riwt_to_usec)(struct fxgmac_pdata *pdata, + unsigned int riwt); + + /* For RX and TX threshold config */ + int (*config_rx_threshold)(struct fxgmac_pdata *pdata, + unsigned int val); + int (*config_tx_threshold)(struct fxgmac_pdata *pdata, + unsigned int val); + + /* For RX and TX Store and Forward Mode config */ + int (*config_rsf_mode)(struct fxgmac_pdata *pdata, unsigned int val); + int (*config_tsf_mode)(struct fxgmac_pdata *pdata, unsigned int val); + + /* For TX DMA Operate on Second Frame config */ + int (*config_osp_mode)(struct fxgmac_pdata *pdata); + + /* For RX and TX PBL config */ + u32 (*calculate_max_checksum_size)(struct fxgmac_pdata *pdata); + int (*config_rx_pbl_val)(struct fxgmac_pdata *pdata); + u32 (*get_rx_pbl_val)(struct fxgmac_pdata *pdata); + int (*config_tx_pbl_val)(struct fxgmac_pdata *pdata); + u32 (*get_tx_pbl_val)(struct fxgmac_pdata *pdata); + int (*config_pblx8)(struct fxgmac_pdata *pdata); + + /* For MMC statistics */ + void (*rx_mmc_int)(struct fxgmac_pdata *pdata); + void (*tx_mmc_int)(struct fxgmac_pdata *pdata); + void (*read_mmc_stats)(struct fxgmac_pdata *pdata); + bool (*update_stats_counters)(struct fxgmac_pdata *pdata, + bool ephy_check_en); + + /* For Receive Side Scaling */ + int (*enable_rss)(struct fxgmac_pdata *pdata); + int (*disable_rss)(struct fxgmac_pdata *pdata); + u32 (*get_rss_options)(struct fxgmac_pdata *pdata); + int (*set_rss_options)(struct fxgmac_pdata *pdata); + int (*set_rss_hash_key)(struct fxgmac_pdata *pdata, const u8 *key); + int (*set_rss_lookup_table)(struct fxgmac_pdata *pdata, + const u32 *table); + + /*For Offload*/ +#ifdef FXGMAC_POWER_MANAGEMENT + void (*set_arp_offload)(struct fxgmac_pdata *pdata, + unsigned char *ip_addr); + int (*enable_arp_offload)(struct fxgmac_pdata *pdata); + int (*disable_arp_offload)(struct fxgmac_pdata *pdata); + + /*NS offload*/ + int (*set_ns_offload)(struct fxgmac_pdata *pdata, unsigned int index, + unsigned char *remote_addr, + unsigned char *solicited_addr, + unsigned char *target_addr1, + unsigned char *target_addr2, + unsigned char *mac_addr); + int (*enable_ns_offload)(struct fxgmac_pdata *pdata); + int (*disable_ns_offload)(struct fxgmac_pdata *pdata); + + int (*enable_wake_magic_pattern)(struct fxgmac_pdata *pdata); + int (*disable_wake_magic_pattern)(struct fxgmac_pdata *pdata); + + int (*enable_wake_link_change)(struct fxgmac_pdata *pdata); + int (*disable_wake_link_change)(struct fxgmac_pdata *pdata); + + int (*check_wake_pattern_fifo_pointer)(struct fxgmac_pdata *pdata); + int (*set_wake_pattern)(struct fxgmac_pdata *pdata, + struct wol_bitmap_pattern *wol_pattern, + u32 pattern_cnt); + int (*enable_wake_pattern)(struct fxgmac_pdata *pdata); + int (*disable_wake_pattern)(struct fxgmac_pdata *pdata); + int (*set_wake_pattern_mask)(struct fxgmac_pdata *pdata, + u32 filter_index, u8 register_index, + u32 Data); +#if FXGMAC_PM_WPI_READ_FEATURE_ENABLED + void (*get_wake_packet_indication)(struct fxgmac_pdata *pdata, + int *wake_reason, + u32 *wake_pattern_number, + u8 *wpi_buf, u32 buf_size, + u32 *packet_size); + void (*enable_wake_packet_indication)(struct fxgmac_pdata *pdata, + int en); +#endif +#endif + + void (*reset_phy)(struct fxgmac_pdata *pdata); + /*for release phy,phy write and read, and provide clock to GMAC. */ + void (*release_phy)(struct fxgmac_pdata *pdata); + void (*enable_phy_check)(struct fxgmac_pdata *pdata); + void (*disable_phy_check)(struct fxgmac_pdata *pdata); + void (*setup_cable_loopback)(struct fxgmac_pdata *pdata); + void (*clean_cable_loopback)(struct fxgmac_pdata *pdata); + void (*disable_phy_sleep)(struct fxgmac_pdata *pdata); + void (*enable_phy_sleep)(struct fxgmac_pdata *pdata); + void (*phy_green_ethernet)(struct fxgmac_pdata *pdata); + void (*phy_eee_feature)(struct fxgmac_pdata *pdata); + u32 (*get_ephy_state)(struct fxgmac_pdata *pdata); + int (*write_ephy_reg)(struct fxgmac_pdata *pdata, u32 val, u32 data); + int (*read_ephy_reg)(struct fxgmac_pdata *pdata, u32 val, + u32 __far *data); + int (*set_ephy_autoneg_advertise)(struct fxgmac_pdata *pdata, + struct fxphy_ag_adv phy_ag_adv); + int (*phy_config)(struct fxgmac_pdata *pdata); + void (*close_phy_led)(struct fxgmac_pdata *pdata); + void (*led_under_active)(struct fxgmac_pdata *pdata); + void (*led_under_sleep)(struct fxgmac_pdata *pdata); + void (*led_under_shutdown)(struct fxgmac_pdata *pdata); + void (*led_under_disable)(struct fxgmac_pdata *pdata); + + /* For power management */ + void (*pre_power_down)(struct fxgmac_pdata *pdata, bool phyloopback); + int (*diag_sanity_check)(struct fxgmac_pdata *pdata); + int (*write_rss_lookup_table)(struct fxgmac_pdata *pdata); + int (*get_rss_hash_key)(struct fxgmac_pdata *pdata, u8 *key_buf); +#ifdef FXGMAC_WOL_INTEGRATED_WOL_PARAMETER + void (*config_power_down)(struct fxgmac_pdata *pdata, unsigned int wol); +#else + void (*config_power_down)(struct fxgmac_pdata *pdata, + unsigned int offloadcount, bool magic_en, + bool remote_pattern_en); +#endif + void (*config_power_up)(struct fxgmac_pdata *pdata); + unsigned char (*set_suspend_int)(void *pdata); + void (*set_resume_int)(struct fxgmac_pdata *pdata); + int (*set_suspend_txrx)(struct fxgmac_pdata *pdata); + void (*set_pwr_clock_gate)(struct fxgmac_pdata *pdata); + void (*set_pwr_clock_ungate)(struct fxgmac_pdata *pdata); + + /* for multicast address list */ + int (*set_all_multicast_mode)(struct fxgmac_pdata *pdata, + unsigned int enable); + void (*config_multicast_mac_hash_table)(struct fxgmac_pdata *pdata, + unsigned char *pmc_mac, + int b_add); + + /* for packet filter-promiscuous and broadcast */ + int (*set_promiscuous_mode)(struct fxgmac_pdata *pdata, + unsigned int enable); + int (*enable_rx_broadcast)(struct fxgmac_pdata *pdata, + unsigned int enable); + + /* efuse relevant operation. */ + bool (*read_patch_from_efuse_per_index)( + struct fxgmac_pdata *pdata, u8 index, u32 __far *offset, + u32 __far *value); /* read patch per index. */ + bool (*read_mac_subsys_from_efuse)(struct fxgmac_pdata *pdata, + u8 *mac_addr, u32 *subsys, + u32 *revid); + bool (*read_efuse_data)(struct fxgmac_pdata *pdata, u32 offset, + u32 __far *value); +#ifndef COMMENT_UNUSED_CODE_TO_REDUCE_SIZE + bool (*read_patch_from_efuse)(struct fxgmac_pdata *pdata, u32 offset, + u32 *value); /* read patch per index. */ + bool (*write_patch_to_efuse)(struct fxgmac_pdata *pdata, u32 offset, + u32 value); + bool (*write_patch_to_efuse_per_index)(struct fxgmac_pdata *pdata, + u8 index, u32 offset, u32 value); + bool (*write_mac_subsys_to_efuse)(struct fxgmac_pdata *pdata, + u8 *mac_addr, u32 *subsys, + u32 *revid); + bool (*read_mac_addr_from_efuse)(struct fxgmac_pdata *pdata, + u8 *mac_addr); + bool (*write_mac_addr_to_efuse)(struct fxgmac_pdata *pdata, + u8 *mac_addr); + bool (*efuse_load)(struct fxgmac_pdata *pdata); + bool (*write_oob)(struct fxgmac_pdata *pdata); + bool (*write_led)(struct fxgmac_pdata *pdata, u32 value); + bool (*read_led_config)(struct fxgmac_pdata *pdata); + bool (*write_led_config)(struct fxgmac_pdata *pdata); +#endif + + int (*pcie_init)(struct fxgmac_pdata *pdata, bool ltr_en, + bool aspm_l1ss_en, bool aspm_l1_en, bool aspm_l0s_en); + void (*trigger_pcie)(struct fxgmac_pdata *pdata, u32 code); +}; + +/* This structure contains flags that indicate what hardware features + * or configurations are present in the device. + */ +struct fxgmac_hw_features { + /* HW Version */ + u32 version; + + /* HW Feature Register0 */ + u32 phyifsel; /* PHY interface support */ + u32 vlhash; /* VLAN Hash Filter */ + u32 sma; /* SMA(MDIO) Interface */ + u32 rwk; /* PMT remote wake-up packet */ + u32 mgk; /* PMT magic packet */ + u32 mmc; /* RMON module */ + u32 aoe; /* ARP Offload */ + u32 ts; /* IEEE 1588-2008 Advanced Timestamp */ + u32 eee; /* Energy Efficient Ethernet */ + u32 tx_coe; /* Tx Checksum Offload */ + u32 rx_coe; /* Rx Checksum Offload */ + u32 addn_mac; /* Additional MAC Addresses */ + u32 ts_src; /* Timestamp Source */ + u32 sa_vlan_ins; /* Source Address or VLAN Insertion */ + + /* HW Feature Register1 */ + u32 rx_fifo_size; /* MTL Receive FIFO Size */ + u32 tx_fifo_size; /* MTL Transmit FIFO Size */ + u32 adv_ts_hi; /* Advance Timestamping High Word */ + u32 dma_width; /* DMA width */ + u32 dcb; /* DCB Feature */ + u32 sph; /* Split Header Feature */ + u32 tso; /* TCP Segmentation Offload */ + u32 dma_debug; /* DMA Debug Registers */ + u32 rss; /* Receive Side Scaling */ + u32 tc_cnt; /* Number of Traffic Classes */ + u32 avsel; /* AV Feature Enable */ + u32 ravsel; /* Rx Side Only AV Feature Enable */ + u32 hash_table_size; /* Hash Table Size */ + u32 l3l4_filter_num; /* Number of L3-L4 Filters */ + + /* HW Feature Register2 */ + u32 rx_q_cnt; /* Number of MTL Receive Queues */ + u32 tx_q_cnt; /* Number of MTL Transmit Queues */ + u32 rx_ch_cnt; /* Number of DMA Receive Channels */ + u32 tx_ch_cnt; /* Number of DMA Transmit Channels */ + u32 pps_out_num; /* Number of PPS outputs */ + u32 aux_snap_num; /* Number of Aux snapshot inputs */ + + /* HW Feature Register3 */ + u32 hwfr3; +}; + +struct fxgmac_resources { + IOMEM addr; + int irq; +}; + +struct fxgmac_pdata { + struct net_device *netdev; + struct device *dev; + PCI_DEV *pdev; + void *pAdapter; + + struct fxgmac_hw_ops hw_ops; + struct fxgmac_desc_ops desc_ops; + + /* Device statistics */ + struct fxgmac_stats stats; + + u32 msg_enable; + u32 reg_nonstick[0x300 >> 2]; + + /* MAC registers base */ + IOMEM mac_regs; + IOMEM base_mem; + + /* Hardware features of the device */ + struct fxgmac_hw_features hw_feat; + + /* Rings for Tx/Rx on a DMA channel */ + struct fxgmac_channel *channel_head; + unsigned int channel_count; + unsigned int tx_ring_count; + unsigned int rx_ring_count; + unsigned int tx_desc_count; + unsigned int rx_desc_count; + unsigned int tx_q_count; + unsigned int rx_q_count; + + /* Tx/Rx common settings */ + unsigned int pblx8; + + /* Tx settings */ + unsigned int tx_sf_mode; + unsigned int tx_threshold; + unsigned int tx_pbl; + unsigned int tx_osp_mode; +#if FXGMAC_TX_HANG_TIMER_ENABLED + /* for tx hang checking. 20211227 */ + unsigned int tx_hang_restart_queuing; +#endif + + /* Rx settings */ + unsigned int rx_sf_mode; + unsigned int rx_threshold; + unsigned int rx_pbl; + + /* Tx coalescing settings */ + unsigned int tx_usecs; + unsigned int tx_frames; + + /* Rx coalescing settings */ + unsigned long rx_riwt; + unsigned int rx_usecs; + unsigned int rx_frames; + + /* Current Rx buffer size */ + unsigned int rx_buf_size; + + /* Flow control settings */ + unsigned int tx_pause; + unsigned int rx_pause; + + /* Jumbo frames */ + unsigned int mtu; + unsigned int jumbo; + + /* CRC checking */ + unsigned int crc_check; + + /* MSIX */ + unsigned int msix; + + /* RSS */ + unsigned int rss; + + /* VlanID */ + unsigned int vlan; + unsigned int vlan_exist; + unsigned int vlan_filter; + unsigned int vlan_strip; + + /* Interrupt Moderation */ + unsigned int intr_mod; + unsigned int intr_mod_timer; + + /* Device interrupt number */ + int dev_irq; + unsigned int per_channel_irq; + u32 channel_irq[FXGMAC_MAX_DMA_CHANNELS_PLUS_1TX]; + + /* Netdev related settings */ + unsigned char mac_addr[ETH_ALEN]; + + /* Filtering support */ +#if FXGMAC_FILTER_MULTIPLE_VLAN_ENABLED + unsigned long active_vlans[BITS_TO_LONGS(VLAN_N_VID)]; +#endif + + /* Device clocks */ + unsigned long sysclk_rate; + + /* Receive Side Scaling settings */ + u8 rss_key[FXGMAC_RSS_HASH_KEY_SIZE]; + u32 rss_table[FXGMAC_RSS_MAX_TABLE_SIZE]; + u32 rss_options; + + int phy_speed; + int phy_duplex; + int phy_autoeng; + +#ifndef COMMENT_UNUSED_CODE_TO_REDUCE_SIZE + char drv_name[32]; + char drv_ver[32]; + + struct wol_bitmap_pattern pattern[MAX_PATTERN_COUNT]; +#endif + + struct led_setting led; + struct led_setting ledconfig; + + FXGMAC_PDATA_OF_PLATFORM expansion; + + u32 pcie_link_status; +}; + +#define FXGMAC_FLAG_MSI_CAPABLE (u32)(1 << 0) +#define FXGMAC_FLAG_MSI_ENABLED (u32)(1 << 1) +#define FXGMAC_FLAG_MSIX_CAPABLE (u32)(1 << 2) +#define FXGMAC_FLAG_MSIX_ENABLED (u32)(1 << 3) +#define FXGMAC_FLAG_LEGACY_ENABLED (u32)(1 << 4) + +#define FXGMAC_FLAG_INTERRUPT_POS 0 +#define FXGMAC_FLAG_INTERRUPT_LEN 5 + +#define FXGMAC_FLAG_MSI_POS 1 +#define FXGMAC_FLAG_MSI_LEN 1 +#define FXGMAC_FLAG_MSIX_POS 3 +#define FXGMAC_FLAG_MSIX_LEN 1 +#define FXGMAC_FLAG_LEGACY_POS 4 +#define FXGMAC_FLAG_LEGACY_LEN 1 +#define FXGMAC_FLAG_LEGACY_IRQ_FREE_POS 31 +#define FXGMAC_FLAG_LEGACY_IRQ_FREE_LEN 1 +#define FXGMAC_FLAG_LEGACY_NAPI_FREE_POS 30 +#define FXGMAC_FLAG_LEGACY_NAPI_FREE_LEN 1 +#define FXGMAC_FLAG_MISC_IRQ_FREE_POS 29 +#define FXGMAC_FLAG_MISC_IRQ_FREE_LEN 1 +#define FXGMAC_FLAG_MISC_NAPI_FREE_POS 28 +#define FXGMAC_FLAG_MISC_NAPI_FREE_LEN 1 +#define FXGMAC_FLAG_TX_IRQ_FREE_POS 27 +#define FXGMAC_FLAG_TX_IRQ_FREE_LEN 1 +#define FXGMAC_FLAG_TX_NAPI_FREE_POS 26 +#define FXGMAC_FLAG_TX_NAPI_FREE_LEN 1 +#define FXGMAC_FLAG_RX_IRQ_FREE_POS 22 +#define FXGMAC_FLAG_RX_IRQ_FREE_LEN 4 +#define FXGMAC_FLAG_PER_CHAN_RX_IRQ_FREE_LEN 1 +#define FXGMAC_FLAG_RX_NAPI_FREE_POS 18 +#define FXGMAC_FLAG_RX_NAPI_FREE_LEN 4 +#define FXGMAC_FLAG_PER_CHAN_RX_NAPI_FREE_LEN 1 + +#ifndef FXGMAC_FAKE_4_TX_QUEUE_ENABLED +#define FXGMAC_FAKE_4_TX_QUEUE_ENABLED 0 +#endif + +void fxgmac_init_desc_ops(struct fxgmac_desc_ops *desc_ops); +void fxgmac_init_hw_ops(struct fxgmac_hw_ops *hw_ops); +const struct net_device_ops *fxgmac_get_netdev_ops(void); +const struct ethtool_ops *fxgmac_get_ethtool_ops(void); +void fxgmac_dump_tx_desc(struct fxgmac_pdata *pdata, struct fxgmac_ring *ring, + unsigned int idx, unsigned int count, + unsigned int flag); +void fxgmac_dump_rx_desc(struct fxgmac_pdata *pdata, struct fxgmac_ring *ring, + unsigned int idx); +void fxgmac_dbg_pkt(struct net_device *netdev, struct sk_buff *skb, bool tx_rx); +void fxgmac_get_all_hw_features(struct fxgmac_pdata *pdata); +void fxgmac_print_all_hw_features(struct fxgmac_pdata *pdata); +int fxgmac_drv_probe(struct device *dev, struct fxgmac_resources *res); +int fxgmac_drv_remove(struct device *dev); + +#endif /* __FXGMAC_GMAC_H__ */ diff --git a/drivers/net/ethernet/motorcomm/yt6801/fuxi-os.h b/drivers/net/ethernet/motorcomm/yt6801/fuxi-os.h new file mode 100644 index 000000000000..babd5cacf499 --- /dev/null +++ b/drivers/net/ethernet/motorcomm/yt6801/fuxi-os.h @@ -0,0 +1,722 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (c) 2021 Motorcomm Corporation. */ + +#ifndef __FXGMAC_OS_H__ +#define __FXGMAC_OS_H__ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef CONFIG_PCI_MSI +#include +#endif + +#include +#include "fuxi-dbg.h" + +struct fxgmac_ring; +struct fxgmac_pdata; +struct fxgmac_channel; + +#define FXGMAC_DRV_VERSION "1.0.29" + +#ifdef CONFIG_PCI_MSI +/* undefined for legacy interrupt mode */ +/* #undef CONFIG_PCI_MSI */ +#endif + +#define FXGMAC_INT_MODERATION_ENABLED 1 + +#define PCIE_LP_ASPM_L0S 1 +#define PCIE_LP_ASPM_L1 2 +#define PCIE_LP_ASPM_L1SS 4 +#define PCIE_LP_ASPM_LTR 8 + +#define FXGMAC_FAIL -1 +#define FXGMAC_SUCCESS 0 +#define FXGMAC_DEV_CMD (SIOCDEVPRIVATE + 1) +#define FXGMAC_IOCTL_DFS_COMMAND _IOWR('M', 0x80, struct ext_ioctl_data) + +#define FXGMAC_MAX_DBG_TEST_PKT 150 +#define FXGMAC_MAX_DBG_BUF_LEN 64000 +#define FXGMAC_MAX_DBG_RX_DATA 1600 +#define FXGMAC_NETDEV_OPS_BUF_LEN 256 + +#define FXGMAC_TEST_MAC_HEAD_LEN 14 + +#define FXGMAC_PM_WPI_READ_FEATURE_ENABLED 1 + +#define RSS_Q_COUNT 4 + +/* #define FXGMAC_TX_INTERRUPT_ENABLED 1 */ +#define FXGMAC_TX_HANG_TIMER_ENABLED 0 +/* 1 to trigger(write reg 0x1000) for sniffer stop */ +#define FXGMAC_TRIGGER_TX_HANG 0 + +/* driver feature configuration */ +#if FXGMAC_TX_HANG_TIMER_ENABLED +/* 0: check hw current desc; 1: check software dirty */ +#define FXGMAC_TX_HANG_CHECH_DIRTY 0 +#endif + +#ifdef CONFIG_ARM64 +/* + * when you want to run this driver on 64bit arm,you should open this,otherwise + * dma's mask cannot be set successfully. + */ +#define FXGMAC_DMA_BIT_MASK 64 +#endif + +#ifdef CONFIG_PCI_MSI +/* should be same as FXGMAC_MAX_DMA_CHANNELS + 1 tx_irq */ +#define FXGMAC_MAX_MSIX_Q_VECTORS (FXGMAC_MSIX_Q_VECTORS + 1) +#define FXGMAC_MSIX_CH0RXDIS_ENABLED 0 +#define FXGMAC_MSIX_INTCTRL_EN 1 + +#define FXGMAC_PHY_INT_NUM 1 +#define FXGMAC_MSIX_INT_NUMS (FXGMAC_MAX_MSIX_Q_VECTORS + FXGMAC_PHY_INT_NUM) +#else +#define FXGMAC_MSIX_CH0RXDIS_ENABLED \ + 0 /* NO modification needed! for non-MSI, set to 0 always */ +#define FXGMAC_MSIX_INTCTRL_EN 0 +#endif + +/* RSS features */ +#ifdef FXGMAC_ONE_CHANNEL +#define FXGMAC_RSS_FEATURE_ENABLED 0 +#else +#define FXGMAC_RSS_FEATURE_ENABLED 1 +#endif +#define FXGMAC_RSS_HASH_KEY_LINUX \ + 1 /* 0:hard to default rss key ;1: normal hash key process from Linux. */ + +/* WOL features */ +#define FXGMAC_WOL_FEATURE_ENABLED 1 +/* since wol upon link will cause issue, disabled it always. */ +#define FXGMAC_WOL_UPON_EPHY_LINK \ + 1 /* 1:enable ephy link change wol ; 0: ephy link change wol is not supported. */ + +/* Pause features */ +#define FXGMAC_PAUSE_FEATURE_ENABLED \ + 1 /* 1:enable flow control/pause framce ; 0: flow control/pause frame not included. */ + +/* ARP offload engine (AOE) */ +#define FXGMAC_AOE_FEATURE_ENABLED \ + 1 /* 1:enable arp offload engine ; 0: aoe is not included. */ + +/* NS offload engine */ +#define FXGMAC_NS_OFFLOAD_ENABLED \ + 1 /* 1:enable NS offload for IPv6 ; 0: NS is not included. */ + +/* for fpga ver after, which needs release phy before set of MAC tx/rx */ +#define FXGMAC_TXRX_EN_AFTER_PHY_RELEASE \ + 1 /* 1:release ephy before mac tx/rx bits are set. */ + +/* power management features */ +#define FXGMAC_PM_FEATURE_ENABLED 1 + +/* sanity check */ +#define FXGMAC_SANITY_CHECK_ENABLED 0 + +/* vlan id filter */ +#define FXGMAC_FILTER_SINGLE_VLAN_ENABLED 0 + +/* Linux driver implement VLAN HASH Table feature to support mutliple VLAN feautre */ +#define FXGMAC_FILTER_MULTIPLE_VLAN_ENABLED 1 + +/* Linux driver implement MAC HASH Table feature */ +#define FXGMAC_MAC_HASH_TABLE 1 + +/* Linux driver implement write multiple mac addr */ +#define FXGMAC_FILTER_MULTIPLE_MAC_ADDR_ENABLED 1 + +/* Linux driver disable MISC Interrupt */ +#define FXGMAC_MISC_INT_HANDLE_FEATURE_ENABLED 1 + +#define FXGMAC_ESD_RESTORE_PCIE_CFG + +#define FXGMAC_WOL_INTEGRATED_WOL_PARAMETER + +#define FXGMAC_LINK_SPEED_CHECK_PHY_LINK + +#define FXGMAC_FLUSH_TX_CHECK_ENABLED + +#define FXGMAC_POWER_MANAGEMENT + +#define FXGMAC_INTERRUPT_TX_INTERVAL + +#define FXGMAC_INTERRUPT_RX_INTERVAL + +#define FXGMAC_WAIT_TX_STOP + +#define FXGMAC_WAIT_RX_STOP_BY_PRXQ_RXQSTS + +#define FXGMAC_USE_DEFAULT_RSS_KEY_TBALE + +#define FXGMAC_RX_VLAN_FILTERING_ENABLED \ + (pdata->netdev->features & NETIF_F_HW_VLAN_CTAG_FILTER) + +#define FXGMAC_NETDEV_PR_MODE_ENABLED \ + ((pdata->netdev->flags & IFF_PROMISC) != 0) +#define FXGMAC_NETDEV_AM_MODE_ENABLED \ + ((pdata->netdev->flags & IFF_ALLMULTI) != 0) +#define FXGMAC_NETDEV_MU_MODE_ENABLED \ + ((pdata->netdev->flags & IFF_MULTICAST) != 0) +#define FXGMAC_NETDEV_BD_MODE_ENABLED \ + ((pdata->netdev->flags & IFF_BROADCAST) != 0) + +#define FXGMAC_RX_CHECKSUM_ENABLED (pdata->netdev->features & NETIF_F_RXCSUM) + +#define TEST_MAC_HEAD 14 +#define TEST_TCP_HEAD_LEN_OFFSET 12 +#define TEST_TCP_OFFLOAD_LEN_OFFSET 48 +#define TEST_TCP_FIX_HEAD_LEN 24 +#define TEST_TCP_MSS_OFFSET 56 + +#define DF_MAX_NIC_NUM 16 + +#ifndef offsetof +#define offsetof(TYPE, MEMBER) ((size_t) & (((TYPE *)0)->MEMBER)) +#endif + +#define ETH_IS_ZEROADDRESS(Address) \ + ((((u8 *)(Address))[0] == ((u8)0x00)) && \ + (((u8 *)(Address))[1] == ((u8)0x00)) && \ + (((u8 *)(Address))[2] == ((u8)0x00)) && \ + (((u8 *)(Address))[3] == ((u8)0x00)) && \ + (((u8 *)(Address))[4] == ((u8)0x00)) && \ + (((u8 *)(Address))[5] == ((u8)0x00))) + +/* centos 7 define start */ +#ifndef dma_rmb +#define dma_rmb() barrier() +#endif + +#ifndef dma_wmb +#define dma_wmb() barrier() +#endif + +#ifndef smp_mb__before_atomic +#define smp_mb__before_atomic() barrier() +#endif + +#ifndef smp_mb__after_atomic +#define smp_mb__after_atomic() barrier() +#endif + +#ifndef skb_vlan_tag_present +#define skb_vlan_tag_present(__skb) ((__skb)->vlan_tci & VLAN_TAG_PRESENT) +#endif + +#ifndef skb_vlan_tag_get +#define skb_vlan_tag_get(__skb) ((__skb)->vlan_tci & ~VLAN_TAG_PRESENT) +#endif + +#ifndef GENMASK +/* Create a contiguous bitmask starting at bit position @l and ending at + * position @h. For example + * GENMASK_ULL(39, 21) gives us the 64bit vector 0x000000ffffe00000. + */ +#define GENMASK(h, l) (((~0UL) << (l)) & (~0UL >> (BITS_PER_LONG - 1 - (h)))) +#endif + +#ifndef ETH_RSS_HASH_TOP +#define __ETH_RSS_HASH_BIT(bit) ((u32)1 << (bit)) +#define __ETH_RSS_HASH(name) __ETH_RSS_HASH_BIT(ETH_RSS_HASH_##name##_BIT) + +#define ETH_RSS_HASH_TOP __ETH_RSS_HASH(TOP) +#endif + +/* read from 8bit register via pci config space */ +#define cfg_r8(_pdata, reg, pdat) \ + pci_read_config_byte((_pdata)->pdev, (reg), (u8 *)(pdat)) + +/* read from 16bit register via pci config space */ +#define cfg_r16(_pdata, reg, pdat) \ + pci_read_config_word((_pdata)->pdev, (reg), (u16 *)(pdat)) + +/* read from 32bit register via pci config space */ +#define cfg_r32(_pdata, reg, pdat) \ + pci_read_config_dword((_pdata)->pdev, (reg), (u32 *)(pdat)) + +/* write to 8bit register via pci config space */ +#define cfg_w8(_pdata, reg, val) \ + pci_write_config_byte((_pdata)->pdev, (reg), (u8)(val)) + +/* write to 16bit register via pci config space */ +#define cfg_w16(_pdata, reg, val) \ + pci_write_config_word((_pdata)->pdev, (reg), (u16)(val)) + +/* write to 32bit register via pci config space */ +#define cfg_w32(_pdata, reg, val) \ + pci_write_config_dword((_pdata)->pdev, (reg), (u32)(val)) + +#define readreg(pAdapter, addr) (readl(addr)) +#define writereg(pAdapter, val, addr) (writel(val, addr)) +#define usleep_range_ex(pAdapter, a, b) (usleep_range(a, b)) +#define _CR(Record, TYPE, Field) \ + ((TYPE *)((char *)(Record) - (char *)&(((TYPE *)0)->Field))) + +#define FXGMAC_GET_REG_BITS(var, pos, len) \ + ({ \ + typeof(pos) _pos = (pos); \ + typeof(len) _len = (len); \ + ((var) & GENMASK(_pos + _len - 1, _pos)) >> (_pos); \ + }) + +#define FXGMAC_GET_REG_BITS_LE(var, pos, len) \ + ({ \ + typeof(pos) _pos = (pos); \ + typeof(len) _len = (len); \ + typeof(var) _var = le32_to_cpu((var)); \ + ((_var) & GENMASK(_pos + _len - 1, _pos)) >> (_pos); \ + }) + +#define FXGMAC_SET_REG_BITS(var, pos, len, val) \ + ({ \ + typeof(var) _var = (var); \ + typeof(pos) _pos = (pos); \ + typeof(len) _len = (len); \ + typeof(val) _val = (val); \ + _val = (_val << _pos) & GENMASK(_pos + _len - 1, _pos); \ + _var = (_var & ~GENMASK(_pos + _len - 1, _pos)) | _val; \ + }) + +#define FXGMAC_SET_REG_BITS_LE(var, pos, len, val) \ + ({ \ + typeof(var) _var = (var); \ + typeof(pos) _pos = (pos); \ + typeof(len) _len = (len); \ + typeof(val) _val = (val); \ + _val = (_val << _pos) & GENMASK(_pos + _len - 1, _pos); \ + _var = (_var & ~GENMASK(_pos + _len - 1, _pos)) | _val; \ + cpu_to_le32(_var); \ + }) + +#define STR_FORMAT "%s" + +#define DbgPrintF(level, fmt, ...) +#define DBGPRINT(Level, Fmt) +#define DBGPRINT_RAW(Level, Fmt) +#define DBGPRINT_S(Status, Fmt) +#define DBGPRINT_UNICODE(Level, UString) +#define Dump(p, cb, fAddress, ulGroup) + +#undef ASSERT +#define ASSERT(x) + +#define DbgPrintOidName(_Oid) +#define DbgPrintAddress(_pAddress) + +#define fxgmac_dump_buffer(_skb, _len, _tx_rx) +#define DumpLine(_p, _cbLine, _fAddress, _ulGroup) + +#ifndef __far +#define __far +#endif + +#ifndef FXGMAC_DEBUG +/* #define FXGMAC_DEBUG */ +#endif + +/* For debug prints */ +#ifdef FXGMAC_DEBUG +#define FXGMAC_PR(fmt, args...) \ + pr_alert("[%s,%d]:" fmt, __func__, __LINE__, ##args) + +/* + * If you want to continue a line, you NEED to use KERN_CONT. + * That has always been true. It hasn't always been enforced, though. + * If you do two printk's and the second one doesn't say "I'm a continuation", + * the printk logic assumes you're just confused and wanted two lines. + */ +#define DPRINTK(fmt, args...) printk(KERN_CONT fmt, ##args) + +#else +#define FXGMAC_PR(x...) \ + do { \ + } while (0) +#define DPRINTK(x...) +#endif + +#define IOC_MAGIC 'M' +#define IOC_MAXNR (0x80 + 5) + +#define FXGMAC_DFS_IOCTL_DEVICE_INACTIVE 0x10001 +#define FXGMAC_DFS_IOCTL_DEVICE_RESET 0x10002 +#define FXGMAC_DFS_IOCTL_DIAG_BEGIN 0x10003 +#define FXGMAC_DFS_IOCTL_DIAG_END 0x10004 +#define FXGMAC_DFS_IOCTL_DIAG_TX_PKT 0x10005 +#define FXGMAC_DFS_IOCTL_DIAG_RX_PKT 0x10006 + +#define FXGMAC_EFUSE_UPDATE_LED_CFG 0x10007 +#define FXGMAC_EFUSE_WRITE_LED 0x10008 +#define FXGMAC_EFUSE_WRITE_PATCH_REG 0x10009 +#define FXGMAC_EFUSE_WRITE_PATCH_PER_INDEX 0x1000A +#define FXGMAC_EFUSE_WRITE_OOB 0x1000B +#define FXGMAC_EFUSE_LOAD 0x1000C +#define FXGMAC_EFUSE_READ_REGIONABC 0x1000D +#define FXGMAC_EFUSE_READ_PATCH_REG 0x1000E +#define FXGMAC_EFUSE_READ_PATCH_PER_INDEX 0x1000F +#define FXGMAC_EFUSE_LED_TEST 0x10010 + +#define FXGMAC_GET_MAC_DATA 0x10011 +#define FXGMAC_SET_MAC_DATA 0x10012 +#define FXGMAC_GET_SUBSYS_ID 0x10013 +#define FXGMAC_SET_SUBSYS_ID 0x10014 +#define FXGMAC_GET_REG 0x10015 +#define FXGMAC_SET_REG 0x10016 +#define FXGMAC_GET_PHY_REG 0x10017 +#define FXGMAC_SET_PHY_REG 0x10018 +#define FXGMAC_EPHY_STATISTICS 0x10019 +#define FXGMAC_GET_STATISTICS 0x1001A +#define FXGMAC_GET_PCIE_LOCATION 0x1001B + +#define FXGMAC_GET_GSO_SIZE 0x1001C +#define FXGMAC_SET_GSO_SIZE 0x1001D +#define FXGMAC_SET_RX_MODERATION 0x1001E +#define FXGMAC_SET_TX_MODERATION 0x1001F +#define FXGMAC_GET_TXRX_MODERATION 0x10020 + +#define MAX_PKT_BUF 1 +#define FXGAMC_MAX_DATA_SIZE (1024 * 4 + 16) + +#ifndef PCI_CAP_ID_MSI +#define PCI_CAP_ID_MSI 0x05 +#endif + +#ifndef PCI_CAP_ID_MSIX +#define PCI_CAP_ID_MSIX 0x11 +#endif + +#define PCI_CAP_ID_MSI_ENABLE_POS 0x10 +#define PCI_CAP_ID_MSI_ENABLE_LEN 0x1 +#define PCI_CAP_ID_MSIX_ENABLE_POS 0x1F +#define PCI_CAP_ID_MSIX_ENABLE_LEN 0x1 + +#define FXGMAC_IRQ_ENABLE 0x1 +#define FXGMAC_IRQ_DISABLE 0x0 +#define FXGMAC_NAPI_ENABLE 0x1 +#define FXGMAC_NAPI_DISABLE 0x0 + +#ifndef fallthrough + +#ifdef __has_attribute +#if __has_attribute(__fallthrough__) +#define fallthrough __attribute__((__fallthrough__)) +#else +#define fallthrough \ + do { \ + } while (0) /* fallthrough */ +#endif + +#else +#define fallthrough \ + do { \ + } while (0) /* fallthrough */ +#endif + +#endif + +#define PHY_POWER_DOWN 1 +#define PHY_POWER_UP 0 + +#define FXGMAC_MMC_IER_ALL_DEFAULT 0 + +/* #define FXGMAC_ESD_CHECK_ENABLED */ +#ifdef FXGMAC_ESD_CHECK_ENABLED +#define FXGMAC_ESD_INTERVAL (5 * HZ) +#define FXGMAC_ESD_ERROR_THRESHOLD (u64)4000000000 +#define FXGMAC_PCIE_LINK_DOWN 0xFFFFFFFF +#define FXGMAC_PCIE_RECOVER_TIMES 5000 +#define FXGMAC_PCIE_IO_MEM_MASTER_ENABLE 0x7 +#endif + +#ifndef BIT +#define BIT(n) (0x1 << (n)) +#endif + +#define UDP_RSS_FLAGS (BIT(MAC_RSSCR_UDP4TE_POS) | BIT(MAC_RSSCR_UDP6TE_POS)) + +#define MF90_SUB_VENTOR_ID 0x17aa +#define MF90_SUB_DEVICE_ID 0x3509 + +#pragma pack(1) +/* it's better to make this struct's size to 128byte. */ +struct pattern_packet { + u8 ether_daddr[ETH_ALEN]; + u8 ether_saddr[ETH_ALEN]; + u16 ether_type; + + __be16 ar_hrd; /* format of hardware address */ + __be16 ar_pro; /* format of protocol */ + unsigned char ar_hln; /* length of hardware address */ + unsigned char ar_pln; /* length of protocol address */ + __be16 ar_op; /* ARP opcode (command) */ + unsigned char ar_sha[ETH_ALEN]; /* sender hardware address */ + unsigned char ar_sip[4]; /* sender IP address */ + unsigned char ar_tha[ETH_ALEN]; /* target hardware address */ + unsigned char ar_tip[4]; /* target IP address */ + + u8 reverse[86]; +}; +#pragma pack() + +typedef dma_addr_t DMA_ADDR_T; +typedef enum pkt_hash_types RSS_HASH_TYPE; +typedef void __iomem *IOMEM; +typedef struct pci_dev PCI_DEV; + +struct ext_command_buf { + void *buf; + u32 size_in; + u32 size_out; +}; + +struct ext_command_mac { + u32 num; + union { + u32 val32; + u16 val16; + u8 val8; + }; +}; + +struct ext_command_mii { + u16 dev; + u16 num; + u16 val; +}; + +struct ext_ioctl_data { + u32 cmd_type; + struct ext_command_buf cmd_buf; +}; + +typedef struct _fxgmac_test_buf { + u8 *addr; + u32 offset; + u32 length; +} fxgmac_test_buf, *pfxgmac_test_buf; + +typedef struct _fxgmac_test_packet { + struct _fxgmac_test_packet *next; + u32 length; /* total length of the packet(buffers) */ + u32 type; /* packet type, vlan, ip checksum, TSO, etc. */ + + fxgmac_test_buf buf[MAX_PKT_BUF]; + fxgmac_test_buf sGList[MAX_PKT_BUF]; + u16 vlanID; + u16 mss; + u32 hash; + u16 cpuNum; + u16 xsum; /* rx, ip-payload checksum */ + u16 csumStart; /* custom checksum offset to the mac-header */ + u16 csumPos; /* custom checksom position (to the mac_header) */ + void *upLevelReserved[4]; + void *lowLevelReserved[4]; +} fxgmac_test_packet, *pfxgmac_test_packet; + +typedef struct fxgmac_channel_of_platform { + char dma_irq_name[IFNAMSIZ + 32]; + + u32 dma_irq_tx; + char dma_irq_name_tx[IFNAMSIZ + 32]; + + /* Netdev related settings */ + struct napi_struct napi_tx; + + /* Netdev related settings */ + struct napi_struct napi_rx; + struct timer_list tx_timer; + +#if FXGMAC_TX_HANG_TIMER_ENABLED + unsigned int tx_hang_timer_active; + struct timer_list tx_hang_timer; + unsigned int tx_hang_hw_cur; +#endif +} FXGMAC_CHANNEL_OF_PLATFORM; + +typedef struct per_regisiter_info { + unsigned int size; + unsigned int address; + unsigned int value; + unsigned char data[FXGAMC_MAX_DATA_SIZE]; +} PER_REG_INFO; + +/* + * for FXGMAC_EFUSE_WRITE_PATCH_PER_INDEX,val0 is index, val1 is offset, + * val2 is value + */ +typedef struct ext_command_data { + u32 val0; + u32 val1; + u32 val2; +} CMD_DATA; + +enum fxgmac_task_flag { + FXGMAC_FLAG_TASK_DOWN = 0, + FXGMAC_FLAG_TASK_RESET_PENDING, + FXGMAC_FLAG_TASK_ESD_CHECK_PENDING, + FXGMAC_FLAG_TASK_LINKCHG_CHECK_PENDING, + FXGMAC_FLAG_TASK_MAX +}; + +typedef struct fxgmac_esd_stats { + u32 tx_abort_excess_collisions; + u32 tx_dma_underrun; + u32 tx_lost_crs; + u32 tx_late_collisions; + u32 rx_crc_errors; + u32 rx_align_errors; + u32 rx_runt_errors; + u32 single_collisions; + u32 multi_collisions; + u32 tx_deferred_frames; +} FXGMAC_ESD_STATS; + +typedef enum fxgmac_dev_state { + FXGMAC_DEV_OPEN = 0x0, + FXGMAC_DEV_CLOSE = 0x1, + FXGMAC_DEV_STOP = 0x2, + FXGMAC_DEV_START = 0x3, + FXGMAC_DEV_SUSPEND = 0x4, + FXGMAC_DEV_RESUME = 0x5, + FXGMAC_DEV_PROBE = 0xFF, +} DEV_STATE; + +typedef struct fxgmac_pdata_of_platform { + u32 cfg_pci_cmd; + u32 cfg_cache_line_size; + u32 cfg_mem_base; + u32 cfg_mem_base_hi; + u32 cfg_io_base; + u32 cfg_int_line; + u32 cfg_device_ctrl1; + u32 cfg_pci_link_ctrl; + u32 cfg_device_ctrl2; + u32 cfg_msix_capability; + + int pre_phy_speed; + int pre_phy_duplex; + int pre_phy_autoneg; + + struct work_struct restart_work; +#ifdef FXGMAC_ESD_CHECK_ENABLED + struct delayed_work esd_work; + FXGMAC_ESD_STATS esd_stats; + DECLARE_BITMAP(task_flags, FXGMAC_FLAG_TASK_MAX); +#endif + u32 int_flags; /* legacy, msi or msix */ + int misc_irq; +#ifdef CONFIG_PCI_MSI + struct msix_entry *msix_entries; +#endif + + /* power management and wol*/ + u32 wol; + unsigned long powerstate; + /*for ns-offload table. 2 entries supported. */ + unsigned int ns_offload_tab_idx; + netdev_features_t netdev_features; + struct napi_struct napi; + struct napi_struct napi_misc; + char misc_irq_name[IFNAMSIZ + 32]; + u32 mgm_intctrl_val; + bool phy_link; + bool fxgmac_test_tso_flag; + u32 fxgmac_test_tso_seg_num; + u32 fxgmac_test_last_tso_len; + u32 fxgmac_test_packet_len; + volatile u32 fxgmac_test_skb_arr_in_index; + volatile u32 fxgmac_test_skb_arr_out_index; + /* CMD_DATA ex_cmd_data; */ + /* PER_REG_INFO per_reg_data; */ + struct sk_buff *fxgmac_test_skb_array[FXGMAC_MAX_DBG_TEST_PKT]; + DEV_STATE dev_state; + struct mutex mutex; + struct timer_list phy_poll_tm; +} FXGMAC_PDATA_OF_PLATFORM; + +void fxgmac_restart_dev(struct fxgmac_pdata *pdata); +long fxgmac_netdev_ops_ioctl(struct file *file, unsigned int cmd, + unsigned long arg); + +int fxgmac_init(struct fxgmac_pdata *pdata, bool save_private_reg); +/* for phy interface */ +int fxgmac_ephy_autoneg_ability_get(struct fxgmac_pdata *pdata, + unsigned int *cap_mask); +int fxgmac_ephy_status_get(struct fxgmac_pdata *pdata, int *speed, int *duplex, + int *ret_link, int *media); +int fxgmac_ephy_soft_reset(struct fxgmac_pdata *pdata); +int fxgmac_phy_force_mode(struct fxgmac_pdata *pdata); +int fxgmac_phy_force_speed(struct fxgmac_pdata *pdata, int speed); +int fxgmac_phy_force_duplex(struct fxgmac_pdata *pdata, int duplex); +int fxgmac_phy_force_autoneg(struct fxgmac_pdata *pdata, int autoneg); + +int fxgmac_phy_timer_init(struct fxgmac_pdata *pdata); +void fxgmac_phy_timer_destroy(struct fxgmac_pdata *pdata); + +unsigned int fxgmac_get_netdev_ip4addr(struct fxgmac_pdata *pdata); +unsigned char *fxgmac_get_netdev_ip6addr(struct fxgmac_pdata *pdata, + unsigned char *ipval, + unsigned char *ip6addr_solicited, + unsigned int ifa_flag); + +#if FXGMAC_PM_FEATURE_ENABLED +void fxgmac_net_powerdown(struct fxgmac_pdata *pdata, unsigned int wol); +void fxgmac_net_powerup(struct fxgmac_pdata *pdata); +#endif + +inline unsigned int fxgmac_tx_avail_desc(struct fxgmac_ring *ring); +inline unsigned int fxgmac_rx_dirty_desc(struct fxgmac_ring *ring); +int fxgmac_start(struct fxgmac_pdata *pdata); +void fxgmac_stop(struct fxgmac_pdata *pdata); +void fxgmac_free_rx_data(struct fxgmac_pdata *pdata); +void fxgmac_free_tx_data(struct fxgmac_pdata *pdata); + +void fxgmac_tx_start_xmit(struct fxgmac_channel *channel, + struct fxgmac_ring *ring); +void fxgmac_dev_xmit(struct fxgmac_channel *channel); +void fxgmac_config_wol(struct fxgmac_pdata *pdata, int en); +void fxgmac_print_pkt(struct net_device *netdev, struct sk_buff *skb, + bool tx_rx); + +void fxgmac_lock(struct fxgmac_pdata *pdata); +void fxgmac_unlock(struct fxgmac_pdata *pdata); + +void fxgmac_set_phy_link_ksettings(struct fxgmac_pdata *pdata); + +#endif /* __FXGMAC_OS_H__ */ -- Gitee