[][Add initial mtk feed for OpenWRT v21.02]
[Description]
Add initial mtk feed for OpenWRT v21.02
[Release-log]
N/A
Change-Id: I8051c6ba87f1ccf26c02fdd88a17d66f63c0b101
Reviewed-on: https://gerrit.mediatek.inc/c/openwrt/feeds/mtk_openwrt_feeds/+/4495320
diff --git a/target/linux/mediatek/files-5.4/drivers/net/ethernet/raeth/raether_qdma.c b/target/linux/mediatek/files-5.4/drivers/net/ethernet/raeth/raether_qdma.c
new file mode 100644
index 0000000..a2414c4
--- /dev/null
+++ b/target/linux/mediatek/files-5.4/drivers/net/ethernet/raeth/raether_qdma.c
@@ -0,0 +1,1509 @@
+/* Copyright 2016 MediaTek Inc.
+ * Author: Carlos Huang <carlos.huang@mediatek.com>
+ * Author: Harry Huang <harry.huang@mediatek.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#include "raether.h"
+#include "ra_ioctl.h"
+#include "raether_qdma.h"
+
+/* skb->mark to queue mapping table */
+struct QDMA_txdesc *free_head;
+
+/* ioctl */
+unsigned int M2Q_table[64] = { 0 };
+EXPORT_SYMBOL(M2Q_table);
+unsigned int lan_wan_separate;
+EXPORT_SYMBOL(lan_wan_separate);
+struct sk_buff *magic_id = (struct sk_buff *)0xFFFFFFFF;
+
+/* CONFIG_HW_SFQ */
+unsigned int web_sfq_enable;
+#define HW_SFQ_UP 3
+#define HW_SFQ_DL 1
+
+#define sfq_debug 0
+struct SFQ_table *sfq0;
+struct SFQ_table *sfq1;
+struct SFQ_table *sfq2;
+struct SFQ_table *sfq3;
+
+#define KSEG1 0xa0000000
+#define PHYS_TO_VIRT(x) phys_to_virt(x)
+#define VIRT_TO_PHYS(x) virt_to_phys(x)
+/* extern void set_fe_dma_glo_cfg(void); */
+struct parse_result sfq_parse_result;
+
+/**
+ *
+ * @brief: get the TXD index from its address
+ *
+ * @param: cpu_ptr
+ *
+ * @return: TXD index
+*/
+
+/**
+ * @brief cal txd number for a page
+ *
+ * @parm size
+ *
+ * @return frag_txd_num
+ */
+
+static inline unsigned int cal_frag_txd_num(unsigned int size)
+{
+ unsigned int frag_txd_num = 0;
+
+ if (size == 0)
+ return 0;
+ while (size > 0) {
+ if (size > MAX_QTXD_LEN) {
+ frag_txd_num++;
+ size -= MAX_QTXD_LEN;
+ } else {
+ frag_txd_num++;
+ size = 0;
+ }
+ }
+ return frag_txd_num;
+}
+
+/**
+ * @brief get free TXD from TXD queue
+ *
+ * @param free_txd
+ *
+ * @return
+ */
+static inline int get_free_txd(struct END_DEVICE *ei_local, int ring_no)
+{
+ unsigned int tmp_idx;
+
+ tmp_idx = ei_local->free_txd_head[ring_no];
+ ei_local->free_txd_head[ring_no] = ei_local->txd_pool_info[tmp_idx];
+ atomic_sub(1, &ei_local->free_txd_num[ring_no]);
+ return tmp_idx;
+}
+
+static inline unsigned int get_phy_addr(struct END_DEVICE *ei_local,
+ unsigned int idx)
+{
+ return ei_local->phy_txd_pool + (idx * QTXD_LEN);
+}
+
+/**
+ * @brief add free TXD into TXD queue
+ *
+ * @param free_txd
+ *
+ * @return
+ */
+static inline void put_free_txd(struct END_DEVICE *ei_local, int free_txd_idx)
+{
+ ei_local->txd_pool_info[ei_local->free_txd_tail[0]] = free_txd_idx;
+ ei_local->free_txd_tail[0] = free_txd_idx;
+}
+
+void init_pseudo_link_list(struct END_DEVICE *ei_local)
+{
+ int i;
+
+ for (i = 0; i < gmac1_txq_num; i++) {
+ atomic_set(&ei_local->free_txd_num[i], gmac1_txq_txd_num);
+ ei_local->free_txd_head[i] = gmac1_txq_txd_num * i;
+ ei_local->free_txd_tail[i] = gmac1_txq_txd_num * (i + 1) - 1;
+ }
+ for (i = 0; i < gmac2_txq_num; i++) {
+ atomic_set(&ei_local->free_txd_num[i + gmac1_txq_num],
+ gmac2_txq_txd_num);
+ ei_local->free_txd_head[i + gmac1_txq_num] =
+ gmac1_txd_num + gmac2_txq_txd_num * i;
+ ei_local->free_txd_tail[i + gmac1_txq_num] =
+ gmac1_txd_num + gmac2_txq_txd_num * (i + 1) - 1;
+ }
+}
+
+static inline int ring_no_mapping(int txd_idx)
+{
+ int i;
+
+ if (txd_idx < gmac1_txd_num) {
+ for (i = 0; i < gmac1_txq_num; i++) {
+ if (txd_idx < (gmac1_txq_txd_num * (i + 1)))
+ return i;
+ }
+ }
+
+ txd_idx -= gmac1_txd_num;
+ for (i = 0; i < gmac2_txq_num; i++) {
+ if (txd_idx < (gmac2_txq_txd_num * (i + 1)))
+ return (i + gmac1_txq_num);
+ }
+ pr_err("txd index out of range\n");
+ return 0;
+}
+
+/*define qdma initial alloc*/
+/**
+ * @brief
+ *
+ * @param net_dev
+ *
+ * @return 0: fail
+ * 1: success
+ */
+bool qdma_tx_desc_alloc(void)
+{
+ struct net_device *dev = dev_raether;
+ struct END_DEVICE *ei_local = netdev_priv(dev);
+ unsigned int txd_idx;
+ int i = 0;
+
+ ei_local->txd_pool =
+ dma_alloc_coherent(&ei_local->qdma_pdev->dev,
+ QTXD_LEN * num_tx_desc,
+ &ei_local->phy_txd_pool, GFP_KERNEL);
+ pr_err("txd_pool=%p phy_txd_pool=%p\n", ei_local->txd_pool,
+ (void *)ei_local->phy_txd_pool);
+
+ if (!ei_local->txd_pool) {
+ pr_err("adapter->txd_pool allocation failed!\n");
+ return 0;
+ }
+ pr_err("ei_local->skb_free start address is 0x%p.\n",
+ ei_local->skb_free);
+ /* set all txd_pool_info to 0. */
+ for (i = 0; i < num_tx_desc; i++) {
+ ei_local->skb_free[i] = 0;
+ ei_local->txd_pool_info[i] = i + 1;
+ ei_local->txd_pool[i].txd_info3.LS = 1;
+ ei_local->txd_pool[i].txd_info3.DDONE = 1;
+ }
+
+ init_pseudo_link_list(ei_local);
+
+ /* get free txd from txd pool */
+ txd_idx = get_free_txd(ei_local, 0);
+ ei_local->tx_cpu_idx = txd_idx;
+ /* add null TXD for transmit */
+ sys_reg_write(QTX_CTX_PTR, get_phy_addr(ei_local, txd_idx));
+ sys_reg_write(QTX_DTX_PTR, get_phy_addr(ei_local, txd_idx));
+
+ /* get free txd from txd pool */
+ txd_idx = get_free_txd(ei_local, 0);
+ ei_local->rls_cpu_idx = txd_idx;
+ /* add null TXD for release */
+ sys_reg_write(QTX_CRX_PTR, get_phy_addr(ei_local, txd_idx));
+ sys_reg_write(QTX_DRX_PTR, get_phy_addr(ei_local, txd_idx));
+
+ /*Reserve 4 TXD for each physical queue */
+ if (ei_local->chip_name == MT7623_FE || ei_local->chip_name == MT7621_FE ||
+ ei_local->chip_name == LEOPARD_FE) {
+ //for (i = 0; i < NUM_PQ; i++)
+ for (i = 0; i < 16; i++)
+ sys_reg_write(QTX_CFG_0 + QUEUE_OFFSET * i,
+ (NUM_PQ_RESV | (NUM_PQ_RESV << 8)));
+ }
+
+ sys_reg_write(QTX_SCH_1, 0x80000000);
+#if 0
+ if (ei_local->chip_name == MT7622_FE) {
+ for (i = 0; i < NUM_PQ; i++) {
+ if (i <= 15) {
+ sys_reg_write(QDMA_PAGE, 0);
+ sys_reg_write(QTX_CFG_0 + QUEUE_OFFSET * i,
+ (NUM_PQ_RESV |
+ (NUM_PQ_RESV << 8)));
+ } else if (i > 15 && i <= 31) {
+ sys_reg_write(QDMA_PAGE, 1);
+ sys_reg_write(QTX_CFG_0 +
+ QUEUE_OFFSET * (i - 16),
+ (NUM_PQ_RESV |
+ (NUM_PQ_RESV << 8)));
+ } else if (i > 31 && i <= 47) {
+ sys_reg_write(QDMA_PAGE, 2);
+ sys_reg_write(QTX_CFG_0 +
+ QUEUE_OFFSET * (i - 32),
+ (NUM_PQ_RESV |
+ (NUM_PQ_RESV << 8)));
+ } else if (i > 47 && i <= 63) {
+ sys_reg_write(QDMA_PAGE, 3);
+ sys_reg_write(QTX_CFG_0 +
+ QUEUE_OFFSET * (i - 48),
+ (NUM_PQ_RESV |
+ (NUM_PQ_RESV << 8)));
+ }
+ }
+ sys_reg_write(QDMA_PAGE, 0);
+ }
+#endif
+
+ return 1;
+}
+
+bool sfq_init(struct net_device *dev)
+{
+ struct END_DEVICE *ei_local = netdev_priv(dev_raether);
+ unsigned int reg_val;
+ dma_addr_t sfq_phy0;
+ dma_addr_t sfq_phy1;
+ dma_addr_t sfq_phy2;
+ dma_addr_t sfq_phy3;
+ struct SFQ_table *sfq0 = NULL;
+ struct SFQ_table *sfq1 = NULL;
+ struct SFQ_table *sfq2 = NULL;
+ struct SFQ_table *sfq3 = NULL;
+
+ dma_addr_t sfq_phy4;
+ dma_addr_t sfq_phy5;
+ dma_addr_t sfq_phy6;
+ dma_addr_t sfq_phy7;
+ struct SFQ_table *sfq4 = NULL;
+ struct SFQ_table *sfq5 = NULL;
+ struct SFQ_table *sfq6 = NULL;
+ struct SFQ_table *sfq7 = NULL;
+
+ int i = 0;
+
+ reg_val = sys_reg_read(VQTX_GLO);
+ reg_val = reg_val | VQTX_MIB_EN;
+ /* Virtual table extends to 32bytes */
+ sys_reg_write(VQTX_GLO, reg_val);
+ reg_val = sys_reg_read(VQTX_GLO);
+ if (ei_local->chip_name == MT7622_FE || ei_local->chip_name == LEOPARD_FE) {
+ sys_reg_write(VQTX_NUM,
+ (VQTX_NUM_0) | (VQTX_NUM_1) | (VQTX_NUM_2) |
+ (VQTX_NUM_3) | (VQTX_NUM_4) | (VQTX_NUM_5) |
+ (VQTX_NUM_6) | (VQTX_NUM_7));
+ } else {
+ sys_reg_write(VQTX_NUM,
+ (VQTX_NUM_0) | (VQTX_NUM_1) | (VQTX_NUM_2) |
+ (VQTX_NUM_3));
+ }
+
+ /* 10 s change hash algorithm */
+ sys_reg_write(VQTX_HASH_CFG, 0xF002710);
+
+ if (ei_local->chip_name == MT7622_FE || ei_local->chip_name == LEOPARD_FE)
+ sys_reg_write(VQTX_VLD_CFG, 0xeca86420);
+ else
+ sys_reg_write(VQTX_VLD_CFG, 0xc840);
+ sys_reg_write(VQTX_HASH_SD, 0x0D);
+ sys_reg_write(QDMA_FC_THRES, 0x9b9b4444);
+ sys_reg_write(QDMA_HRED1, 0);
+ sys_reg_write(QDMA_HRED2, 0);
+ sys_reg_write(QDMA_SRED1, 0);
+ sys_reg_write(QDMA_SRED2, 0);
+ if (ei_local->chip_name == MT7622_FE || ei_local->chip_name == LEOPARD_FE) {
+ sys_reg_write(VQTX_0_3_BIND_QID,
+ (VQTX_0_BIND_QID) | (VQTX_1_BIND_QID) |
+ (VQTX_2_BIND_QID) | (VQTX_3_BIND_QID));
+ sys_reg_write(VQTX_4_7_BIND_QID,
+ (VQTX_4_BIND_QID) | (VQTX_5_BIND_QID) |
+ (VQTX_6_BIND_QID) | (VQTX_7_BIND_QID));
+ pr_err("VQTX_0_3_BIND_QID =%x\n",
+ sys_reg_read(VQTX_0_3_BIND_QID));
+ pr_err("VQTX_4_7_BIND_QID =%x\n",
+ sys_reg_read(VQTX_4_7_BIND_QID));
+ }
+
+ sfq0 = dma_alloc_coherent(&ei_local->qdma_pdev->dev,
+ VQ_NUM0 * sizeof(struct SFQ_table), &sfq_phy0,
+ GFP_KERNEL);
+
+ memset(sfq0, 0x0, VQ_NUM0 * sizeof(struct SFQ_table));
+ for (i = 0; i < VQ_NUM0; i++) {
+ sfq0[i].sfq_info1.VQHPTR = 0xdeadbeef;
+ sfq0[i].sfq_info2.VQTPTR = 0xdeadbeef;
+ }
+ sfq1 = dma_alloc_coherent(&ei_local->qdma_pdev->dev,
+ VQ_NUM1 * sizeof(struct SFQ_table), &sfq_phy1,
+ GFP_KERNEL);
+ memset(sfq1, 0x0, VQ_NUM1 * sizeof(struct SFQ_table));
+ for (i = 0; i < VQ_NUM1; i++) {
+ sfq1[i].sfq_info1.VQHPTR = 0xdeadbeef;
+ sfq1[i].sfq_info2.VQTPTR = 0xdeadbeef;
+ }
+
+ sfq2 = dma_alloc_coherent(&ei_local->qdma_pdev->dev,
+ VQ_NUM2 * sizeof(struct SFQ_table), &sfq_phy2,
+ GFP_KERNEL);
+ memset(sfq2, 0x0, VQ_NUM2 * sizeof(struct SFQ_table));
+ for (i = 0; i < VQ_NUM2; i++) {
+ sfq2[i].sfq_info1.VQHPTR = 0xdeadbeef;
+ sfq2[i].sfq_info2.VQTPTR = 0xdeadbeef;
+ }
+
+ sfq3 = dma_alloc_coherent(&ei_local->qdma_pdev->dev,
+ VQ_NUM3 * sizeof(struct SFQ_table), &sfq_phy3,
+ GFP_KERNEL);
+ memset(sfq3, 0x0, VQ_NUM3 * sizeof(struct SFQ_table));
+ for (i = 0; i < VQ_NUM3; i++) {
+ sfq3[i].sfq_info1.VQHPTR = 0xdeadbeef;
+ sfq3[i].sfq_info2.VQTPTR = 0xdeadbeef;
+ }
+ if (unlikely((!sfq0)) || unlikely((!sfq1)) ||
+ unlikely((!sfq2)) || unlikely((!sfq3))) {
+ pr_err("QDMA SFQ0~3 VQ not available...\n");
+ return 1;
+ }
+ if (ei_local->chip_name == MT7622_FE || ei_local->chip_name == LEOPARD_FE) {
+ sfq4 =
+ dma_alloc_coherent(&ei_local->qdma_pdev->dev,
+ VQ_NUM4 * sizeof(struct SFQ_table),
+ &sfq_phy4, GFP_KERNEL);
+ memset(sfq4, 0x0, VQ_NUM4 * sizeof(struct SFQ_table));
+ for (i = 0; i < VQ_NUM4; i++) {
+ sfq4[i].sfq_info1.VQHPTR = 0xdeadbeef;
+ sfq4[i].sfq_info2.VQTPTR = 0xdeadbeef;
+ }
+ sfq5 =
+ dma_alloc_coherent(&ei_local->qdma_pdev->dev,
+ VQ_NUM5 * sizeof(struct SFQ_table),
+ &sfq_phy5, GFP_KERNEL);
+ memset(sfq5, 0x0, VQ_NUM5 * sizeof(struct SFQ_table));
+ for (i = 0; i < VQ_NUM5; i++) {
+ sfq5[i].sfq_info1.VQHPTR = 0xdeadbeef;
+ sfq5[i].sfq_info2.VQTPTR = 0xdeadbeef;
+ }
+ sfq6 =
+ dma_alloc_coherent(&ei_local->qdma_pdev->dev,
+ VQ_NUM6 * sizeof(struct SFQ_table),
+ &sfq_phy6, GFP_KERNEL);
+ memset(sfq6, 0x0, VQ_NUM6 * sizeof(struct SFQ_table));
+ for (i = 0; i < VQ_NUM6; i++) {
+ sfq6[i].sfq_info1.VQHPTR = 0xdeadbeef;
+ sfq6[i].sfq_info2.VQTPTR = 0xdeadbeef;
+ }
+ sfq7 =
+ dma_alloc_coherent(&ei_local->qdma_pdev->dev,
+ VQ_NUM7 * sizeof(struct SFQ_table),
+ &sfq_phy7, GFP_KERNEL);
+ memset(sfq7, 0x0, VQ_NUM7 * sizeof(struct SFQ_table));
+ for (i = 0; i < VQ_NUM7; i++) {
+ sfq7[i].sfq_info1.VQHPTR = 0xdeadbeef;
+ sfq7[i].sfq_info2.VQTPTR = 0xdeadbeef;
+ }
+ if (unlikely((!sfq4)) || unlikely((!sfq5)) ||
+ unlikely((!sfq6)) || unlikely((!sfq7))) {
+ pr_err("QDMA SFQ4~7 VQ not available...\n");
+ return 1;
+ }
+ }
+
+ pr_err("*****sfq_phy0 is 0x%p!!!*******\n", (void *)sfq_phy0);
+ pr_err("*****sfq_phy1 is 0x%p!!!*******\n", (void *)sfq_phy1);
+ pr_err("*****sfq_phy2 is 0x%p!!!*******\n", (void *)sfq_phy2);
+ pr_err("*****sfq_phy3 is 0x%p!!!*******\n", (void *)sfq_phy3);
+ pr_err("*****sfq_virt0 is 0x%p!!!*******\n", sfq0);
+ pr_err("*****sfq_virt1 is 0x%p!!!*******\n", sfq1);
+ pr_err("*****sfq_virt2 is 0x%p!!!*******\n", sfq2);
+ pr_err("*****sfq_virt3 is 0x%p!!!*******\n", sfq3);
+ if (ei_local->chip_name == MT7622_FE || ei_local->chip_name == LEOPARD_FE) {
+ pr_err("*****sfq_phy4 is 0x%p!!!*******\n", (void *)sfq_phy4);
+ pr_err("*****sfq_phy5 is 0x%p!!!*******\n", (void *)sfq_phy5);
+ pr_err("*****sfq_phy6 is 0x%p!!!*******\n", (void *)sfq_phy6);
+ pr_err("*****sfq_phy7 is 0x%p!!!*******\n", (void *)sfq_phy7);
+ pr_err("*****sfq_virt4 is 0x%p!!!*******\n", sfq4);
+ pr_err("*****sfq_virt5 is 0x%p!!!*******\n", sfq5);
+ pr_err("*****sfq_virt6 is 0x%p!!!*******\n", sfq6);
+ pr_err("*****sfq_virt7 is 0x%p!!!*******\n", sfq7);
+ }
+
+ sys_reg_write(VQTX_TB_BASE0, (u32)sfq_phy0);
+ sys_reg_write(VQTX_TB_BASE1, (u32)sfq_phy1);
+ sys_reg_write(VQTX_TB_BASE2, (u32)sfq_phy2);
+ sys_reg_write(VQTX_TB_BASE3, (u32)sfq_phy3);
+ if (ei_local->chip_name == MT7622_FE || ei_local->chip_name == LEOPARD_FE) {
+ sys_reg_write(VQTX_TB_BASE4, (u32)sfq_phy4);
+ sys_reg_write(VQTX_TB_BASE5, (u32)sfq_phy5);
+ sys_reg_write(VQTX_TB_BASE6, (u32)sfq_phy6);
+ sys_reg_write(VQTX_TB_BASE7, (u32)sfq_phy7);
+ }
+
+ return 0;
+}
+
+bool fq_qdma_init(struct net_device *dev)
+{
+ struct END_DEVICE *ei_local = netdev_priv(dev);
+ /* struct QDMA_txdesc *free_head = NULL; */
+ dma_addr_t phy_free_head;
+ dma_addr_t phy_free_tail;
+ unsigned int *free_page_head = NULL;
+ dma_addr_t phy_free_page_head;
+ int i;
+
+ free_head = dma_alloc_coherent(&ei_local->qdma_pdev->dev,
+ NUM_QDMA_PAGE *
+ QTXD_LEN, &phy_free_head, GFP_KERNEL);
+
+ if (unlikely(!free_head)) {
+ pr_err("QDMA FQ decriptor not available...\n");
+ return 0;
+ }
+ memset(free_head, 0x0, QTXD_LEN * NUM_QDMA_PAGE);
+
+ free_page_head =
+ dma_alloc_coherent(&ei_local->qdma_pdev->dev,
+ NUM_QDMA_PAGE * QDMA_PAGE_SIZE,
+ &phy_free_page_head, GFP_KERNEL);
+
+ if (unlikely(!free_page_head)) {
+ pr_err("QDMA FQ page not available...\n");
+ return 0;
+ }
+ for (i = 0; i < NUM_QDMA_PAGE; i++) {
+ free_head[i].txd_info1.SDP =
+ (phy_free_page_head + (i * QDMA_PAGE_SIZE));
+ if (i < (NUM_QDMA_PAGE - 1)) {
+ free_head[i].txd_info2.NDP =
+ (phy_free_head + ((i + 1) * QTXD_LEN));
+ }
+ free_head[i].txd_info3.SDL = QDMA_PAGE_SIZE;
+ }
+ phy_free_tail =
+ (phy_free_head + (u32)((NUM_QDMA_PAGE - 1) * QTXD_LEN));
+
+ pr_err("phy_free_head is 0x%p!!!\n", (void *)phy_free_head);
+ pr_err("phy_free_tail_phy is 0x%p!!!\n", (void *)phy_free_tail);
+ sys_reg_write(QDMA_FQ_HEAD, (u32)phy_free_head);
+ sys_reg_write(QDMA_FQ_TAIL, (u32)phy_free_tail);
+ sys_reg_write(QDMA_FQ_CNT, ((num_tx_desc << 16) | NUM_QDMA_PAGE));
+ sys_reg_write(QDMA_FQ_BLEN, QDMA_PAGE_SIZE << 16);
+ pr_info("gmac1_txd_num:%d; gmac2_txd_num:%d; num_tx_desc:%d\n",
+ gmac1_txd_num, gmac2_txd_num, num_tx_desc);
+ ei_local->free_head = free_head;
+ ei_local->phy_free_head = phy_free_head;
+ ei_local->free_page_head = free_page_head;
+ ei_local->phy_free_page_head = phy_free_page_head;
+ ei_local->tx_ring_full = 0;
+ return 1;
+}
+
+int sfq_prot;
+
+#if (sfq_debug)
+int udp_source_port;
+int tcp_source_port;
+int ack_packt;
+#endif
+int sfq_parse_layer_info(struct sk_buff *skb)
+{
+ struct vlan_hdr *vh_sfq = NULL;
+ struct ethhdr *eth_sfq = NULL;
+ struct iphdr *iph_sfq = NULL;
+ struct ipv6hdr *ip6h_sfq = NULL;
+ struct tcphdr *th_sfq = NULL;
+ struct udphdr *uh_sfq = NULL;
+
+ memset(&sfq_parse_result, 0, sizeof(sfq_parse_result));
+ eth_sfq = (struct ethhdr *)skb->data;
+ ether_addr_copy(sfq_parse_result.dmac, eth_sfq->h_dest);
+ ether_addr_copy(sfq_parse_result.smac, eth_sfq->h_source);
+ /* memcpy(sfq_parse_result.dmac, eth_sfq->h_dest, ETH_ALEN); */
+ /* memcpy(sfq_parse_result.smac, eth_sfq->h_source, ETH_ALEN); */
+ sfq_parse_result.eth_type = eth_sfq->h_proto;
+
+ if (sfq_parse_result.eth_type == htons(ETH_P_8021Q)) {
+ sfq_parse_result.vlan1_gap = VLAN_HLEN;
+ vh_sfq = (struct vlan_hdr *)(skb->data + ETH_HLEN);
+ sfq_parse_result.eth_type = vh_sfq->h_vlan_encapsulated_proto;
+ } else {
+ sfq_parse_result.vlan1_gap = 0;
+ }
+
+ /* set layer4 start addr */
+ if ((sfq_parse_result.eth_type == htons(ETH_P_IP)) ||
+ (sfq_parse_result.eth_type == htons(ETH_P_PPP_SES) &&
+ sfq_parse_result.ppp_tag == htons(PPP_IP))) {
+ iph_sfq =
+ (struct iphdr *)(skb->data + ETH_HLEN +
+ (sfq_parse_result.vlan1_gap));
+
+ /* prepare layer3/layer4 info */
+ memcpy(&sfq_parse_result.iph, iph_sfq, sizeof(struct iphdr));
+ if (iph_sfq->protocol == IPPROTO_TCP) {
+ th_sfq =
+ (struct tcphdr *)(skb->data + ETH_HLEN +
+ (sfq_parse_result.vlan1_gap) +
+ (iph_sfq->ihl * 4));
+ memcpy(&sfq_parse_result.th, th_sfq,
+ sizeof(struct tcphdr));
+#if (sfq_debug)
+ tcp_source_port = ntohs(sfq_parse_result.th.source);
+ udp_source_port = 0;
+ /* tcp ack packet */
+ if (ntohl(sfq_parse_result.iph.saddr) == 0xa0a0a04)
+ ack_packt = 1;
+ else
+ ack_packt = 0;
+#endif
+ sfq_prot = 2; /* IPV4_HNAPT */
+ if (iph_sfq->frag_off & htons(IP_MF | IP_OFFSET))
+ return 1;
+ } else if (iph_sfq->protocol == IPPROTO_UDP) {
+ uh_sfq =
+ (struct udphdr *)(skb->data + ETH_HLEN +
+ (sfq_parse_result.vlan1_gap) +
+ iph_sfq->ihl * 4);
+ memcpy(&sfq_parse_result.uh, uh_sfq,
+ sizeof(struct udphdr));
+#if (sfq_debug)
+ udp_source_port = ntohs(sfq_parse_result.uh.source);
+ tcp_source_port = 0;
+ ack_packt = 0;
+#endif
+ sfq_prot = 2; /* IPV4_HNAPT */
+ if (iph_sfq->frag_off & htons(IP_MF | IP_OFFSET))
+ return 1;
+ } else {
+ sfq_prot = 1;
+ }
+ } else if (sfq_parse_result.eth_type == htons(ETH_P_IPV6) ||
+ (sfq_parse_result.eth_type == htons(ETH_P_PPP_SES) &&
+ sfq_parse_result.ppp_tag == htons(PPP_IPV6))) {
+ ip6h_sfq =
+ (struct ipv6hdr *)(skb->data + ETH_HLEN +
+ (sfq_parse_result.vlan1_gap));
+ if (ip6h_sfq->nexthdr == NEXTHDR_TCP) {
+ sfq_prot = 4; /* IPV6_5T */
+#if (sfq_debug)
+ if (ntohl(sfq_parse_result.ip6h.saddr.s6_addr32[3]) ==
+ 8)
+ ack_packt = 1;
+ else
+ ack_packt = 0;
+#endif
+ } else if (ip6h_sfq->nexthdr == NEXTHDR_UDP) {
+#if (sfq_debug)
+ ack_packt = 0;
+#endif
+ sfq_prot = 4; /* IPV6_5T */
+
+ } else {
+ sfq_prot = 3; /* IPV6_3T */
+ }
+ }
+ return 0;
+}
+
+int rt2880_qdma_eth_send(struct END_DEVICE *ei_local, struct net_device *dev,
+ struct sk_buff *skb, int gmac_no, int ring_no)
+{
+ unsigned int length = skb->len;
+ struct QDMA_txdesc *cpu_ptr, *prev_cpu_ptr;
+ struct QDMA_txdesc dummy_desc;
+ struct PSEUDO_ADAPTER *p_ad;
+ unsigned long flags;
+ unsigned int next_txd_idx, qidx;
+
+ cpu_ptr = &dummy_desc;
+ /* 2. prepare data */
+ dma_sync_single_for_device(&ei_local->qdma_pdev->dev,
+ virt_to_phys(skb->data),
+ skb->len, DMA_TO_DEVICE);
+ /* cpu_ptr->txd_info1.SDP = VIRT_TO_PHYS(skb->data); */
+ cpu_ptr->txd_info1.SDP = virt_to_phys(skb->data);
+ cpu_ptr->txd_info3.SDL = skb->len;
+ if (ei_local->features & FE_HW_SFQ) {
+ sfq_parse_layer_info(skb);
+ cpu_ptr->txd_info5.VQID0 = 1; /* 1:HW hash 0:CPU */
+ cpu_ptr->txd_info5.PROT = sfq_prot;
+ /* no vlan */
+ cpu_ptr->txd_info5.IPOFST = 14 + (sfq_parse_result.vlan1_gap);
+ }
+ cpu_ptr->txd_info4.FPORT = gmac_no;
+
+ if (ei_local->features & FE_CSUM_OFFLOAD) {
+ if (skb->ip_summed == CHECKSUM_PARTIAL)
+ cpu_ptr->txd_info5.TUI_CO = 7;
+ else
+ cpu_ptr->txd_info5.TUI_CO = 0;
+ }
+
+ if (ei_local->features & FE_HW_VLAN_TX) {
+ if (skb_vlan_tag_present(skb)) {
+ cpu_ptr->txd_info6.INSV_1 = 1;
+ cpu_ptr->txd_info6.VLAN_TAG_1 = skb_vlan_tag_get(skb);
+ cpu_ptr->txd_info4.QID = skb_vlan_tag_get(skb);
+ } else {
+ cpu_ptr->txd_info4.QID = ring_no;
+ cpu_ptr->txd_info6.INSV_1 = 0;
+ cpu_ptr->txd_info6.VLAN_TAG_1 = 0;
+ }
+ } else {
+ cpu_ptr->txd_info6.INSV_1 = 0;
+ cpu_ptr->txd_info6.VLAN_TAG_1 = 0;
+ }
+ cpu_ptr->txd_info4.QID = 0;
+ /* cpu_ptr->txd_info4.QID = ring_no; */
+
+ if ((ei_local->features & QDMA_QOS_MARK) && (skb->mark != 0)) {
+ if (skb->mark < 64) {
+ qidx = M2Q_table[skb->mark];
+ cpu_ptr->txd_info4.QID = ((qidx & 0x30) >> 4);
+ cpu_ptr->txd_info4.QID = (qidx & 0x0f);
+ } else {
+ pr_debug("skb->mark out of range\n");
+ cpu_ptr->txd_info4.QID = 0;
+ cpu_ptr->txd_info4.QID = 0;
+ }
+ }
+ /* QoS Web UI used */
+ if ((ei_local->features & QDMA_QOS_WEB) && (lan_wan_separate == 1)) {
+ if (web_sfq_enable == 1 && (skb->mark == 2)) {
+ if (gmac_no == 1)
+ cpu_ptr->txd_info4.QID = HW_SFQ_DL;
+ else
+ cpu_ptr->txd_info4.QID = HW_SFQ_UP;
+ } else if (gmac_no == 2) {
+ cpu_ptr->txd_info4.QID += 8;
+ }
+ }
+#if defined(CONFIG_HW_NAT) || defined(CONFIG_RA_HW_NAT_MODULE)
+ if (IS_MAGIC_TAG_PROTECT_VALID_HEAD(skb)) {
+ if (FOE_MAGIC_TAG_HEAD(skb) == FOE_MAGIC_PPE) {
+ if (ppe_hook_rx_eth) {
+ cpu_ptr->txd_info4.FPORT = 3; /* PPE */
+ FOE_MAGIC_TAG(skb) = 0;
+ }
+ }
+ } else if (IS_MAGIC_TAG_PROTECT_VALID_TAIL(skb)) {
+ if (FOE_MAGIC_TAG_TAIL(skb) == FOE_MAGIC_PPE) {
+ if (ppe_hook_rx_eth) {
+ cpu_ptr->txd_info4.FPORT = 3; /* PPE */
+ FOE_MAGIC_TAG(skb) = 0;
+ }
+ }
+ }
+#endif
+
+ /* dma_sync_single_for_device(NULL, virt_to_phys(skb->data), */
+ /* skb->len, DMA_TO_DEVICE); */
+ cpu_ptr->txd_info4.SWC = 1;
+
+ /* 5. move CPU_PTR to new TXD */
+ cpu_ptr->txd_info5.TSO = 0;
+ cpu_ptr->txd_info3.LS = 1;
+ cpu_ptr->txd_info3.DDONE = 0;
+ next_txd_idx = get_free_txd(ei_local, ring_no);
+ cpu_ptr->txd_info2.NDP = get_phy_addr(ei_local, next_txd_idx);
+ spin_lock_irqsave(&ei_local->page_lock, flags);
+ prev_cpu_ptr = ei_local->txd_pool + ei_local->tx_cpu_idx;
+ /* update skb_free */
+ ei_local->skb_free[ei_local->tx_cpu_idx] = skb;
+ /* update tx cpu idx */
+ ei_local->tx_cpu_idx = next_txd_idx;
+ /* update txd info */
+ prev_cpu_ptr->txd_info1 = dummy_desc.txd_info1;
+ prev_cpu_ptr->txd_info2 = dummy_desc.txd_info2;
+ prev_cpu_ptr->txd_info4 = dummy_desc.txd_info4;
+ prev_cpu_ptr->txd_info5 = dummy_desc.txd_info5;
+ prev_cpu_ptr->txd_info6 = dummy_desc.txd_info6;
+ prev_cpu_ptr->txd_info7 = dummy_desc.txd_info7;
+ prev_cpu_ptr->txd_info3 = dummy_desc.txd_info3;
+ /* NOTE: add memory barrier to avoid
+ * DMA access memory earlier than memory written
+ */
+ wmb();
+ /* update CPU pointer */
+ sys_reg_write(QTX_CTX_PTR,
+ get_phy_addr(ei_local, ei_local->tx_cpu_idx));
+ spin_unlock_irqrestore(&ei_local->page_lock, flags);
+
+ if (ei_local->features & FE_GE2_SUPPORT) {
+ if (gmac_no == 2) {
+ if (ei_local->pseudo_dev) {
+ p_ad = netdev_priv(ei_local->pseudo_dev);
+ p_ad->stat.tx_packets++;
+
+ p_ad->stat.tx_bytes += length;
+ }
+ } else {
+ ei_local->stat.tx_packets++;
+ ei_local->stat.tx_bytes += skb->len;
+ }
+ } else {
+ ei_local->stat.tx_packets++;
+ ei_local->stat.tx_bytes += skb->len;
+ }
+ if (ei_local->features & FE_INT_NAPI) {
+ if (ei_local->tx_full == 1) {
+ ei_local->tx_full = 0;
+ netif_wake_queue(dev);
+ }
+ }
+
+ return length;
+}
+
+int rt2880_qdma_eth_send_tso(struct END_DEVICE *ei_local,
+ struct net_device *dev, struct sk_buff *skb,
+ int gmac_no, int ring_no)
+{
+ unsigned int length = skb->len;
+ struct QDMA_txdesc *cpu_ptr, *prev_cpu_ptr;
+ struct QDMA_txdesc dummy_desc;
+ struct QDMA_txdesc init_dummy_desc;
+ int ctx_idx;
+ struct iphdr *iph = NULL;
+ struct QDMA_txdesc *init_cpu_ptr;
+ struct tcphdr *th = NULL;
+ skb_frag_t * frag;
+ unsigned int nr_frags = skb_shinfo(skb)->nr_frags;
+ unsigned int len, size, frag_txd_num, qidx;
+ dma_addr_t offset;
+ unsigned long flags;
+ int i;
+ int init_qid, init_qid1;
+ struct ipv6hdr *ip6h = NULL;
+ struct PSEUDO_ADAPTER *p_ad;
+
+ init_cpu_ptr = &init_dummy_desc;
+ cpu_ptr = &init_dummy_desc;
+
+ len = length - skb->data_len;
+ dma_sync_single_for_device(&ei_local->qdma_pdev->dev,
+ virt_to_phys(skb->data),
+ len,
+ DMA_TO_DEVICE);
+ offset = virt_to_phys(skb->data);
+ cpu_ptr->txd_info1.SDP = offset;
+ if (len > MAX_QTXD_LEN) {
+ cpu_ptr->txd_info3.SDL = MAX_QTXD_LEN;
+ cpu_ptr->txd_info3.LS = 0;
+ len -= MAX_QTXD_LEN;
+ offset += MAX_QTXD_LEN;
+ } else {
+ cpu_ptr->txd_info3.SDL = len;
+ cpu_ptr->txd_info3.LS = nr_frags ? 0 : 1;
+ len = 0;
+ }
+ if (ei_local->features & FE_HW_SFQ) {
+ sfq_parse_layer_info(skb);
+
+ cpu_ptr->txd_info5.VQID0 = 1;
+ cpu_ptr->txd_info5.PROT = sfq_prot;
+ /* no vlan */
+ cpu_ptr->txd_info5.IPOFST = 14 + (sfq_parse_result.vlan1_gap);
+ }
+ if (gmac_no == 1)
+ cpu_ptr->txd_info4.FPORT = 1;
+ else
+ cpu_ptr->txd_info4.FPORT = 2;
+
+ cpu_ptr->txd_info5.TSO = 0;
+ cpu_ptr->txd_info4.QID = 0;
+ /* cpu_ptr->txd_info4.QID = ring_no; */
+ if ((ei_local->features & QDMA_QOS_MARK) && (skb->mark != 0)) {
+ if (skb->mark < 64) {
+ qidx = M2Q_table[skb->mark];
+ cpu_ptr->txd_info4.QID = qidx;
+
+ } else {
+ pr_debug("skb->mark out of range\n");
+ cpu_ptr->txd_info4.QID = 0;
+
+ }
+ }
+ if (ei_local->features & FE_CSUM_OFFLOAD) {
+ if (skb->ip_summed == CHECKSUM_PARTIAL)
+ cpu_ptr->txd_info5.TUI_CO = 7;
+ else
+ cpu_ptr->txd_info5.TUI_CO = 0;
+ }
+
+ if (ei_local->features & FE_HW_VLAN_TX) {
+ if (skb_vlan_tag_present(skb)) {
+ cpu_ptr->txd_info6.INSV_1 = 1;
+ cpu_ptr->txd_info6.VLAN_TAG_1 = skb_vlan_tag_get(skb);
+ cpu_ptr->txd_info4.QID = skb_vlan_tag_get(skb);
+ } else {
+ cpu_ptr->txd_info4.QID = ring_no;
+ cpu_ptr->txd_info6.INSV_1 = 0;
+ cpu_ptr->txd_info6.VLAN_TAG_1 = 0;
+ }
+ } else {
+ cpu_ptr->txd_info6.INSV_1 = 0;
+ cpu_ptr->txd_info6.VLAN_TAG_1 = 0;
+ }
+
+ if ((ei_local->features & FE_GE2_SUPPORT) && (lan_wan_separate == 1)) {
+ if (web_sfq_enable == 1 && (skb->mark == 2)) {
+ if (gmac_no == 1)
+ cpu_ptr->txd_info4.QID = HW_SFQ_DL;
+ else
+ cpu_ptr->txd_info4.QID = HW_SFQ_UP;
+ } else if (gmac_no == 2) {
+ cpu_ptr->txd_info4.QID += 8;
+ }
+ }
+ /*debug multi tx queue */
+ init_qid = cpu_ptr->txd_info4.QID;
+ init_qid1 = cpu_ptr->txd_info4.QID;
+#if defined(CONFIG_HW_NAT) || defined(CONFIG_RA_HW_NAT_MODULE)
+ if (IS_MAGIC_TAG_PROTECT_VALID_HEAD(skb)) {
+ if (FOE_MAGIC_TAG_HEAD(skb) == FOE_MAGIC_PPE) {
+ if (ppe_hook_rx_eth) {
+ cpu_ptr->txd_info4.FPORT = 3; /* PPE */
+ FOE_MAGIC_TAG(skb) = 0;
+ }
+ }
+ } else if (IS_MAGIC_TAG_PROTECT_VALID_TAIL(skb)) {
+ if (FOE_MAGIC_TAG_TAIL(skb) == FOE_MAGIC_PPE) {
+ if (ppe_hook_rx_eth) {
+ cpu_ptr->txd_info4.FPORT = 3; /* PPE */
+ FOE_MAGIC_TAG(skb) = 0;
+ }
+ }
+ }
+#endif
+
+ cpu_ptr->txd_info4.SWC = 1;
+
+ ctx_idx = get_free_txd(ei_local, ring_no);
+ cpu_ptr->txd_info2.NDP = get_phy_addr(ei_local, ctx_idx);
+ /*prev_cpu_ptr->txd_info1 = dummy_desc.txd_info1;
+ *prev_cpu_ptr->txd_info2 = dummy_desc.txd_info2;
+ *prev_cpu_ptr->txd_info3 = dummy_desc.txd_info3;
+ *prev_cpu_ptr->txd_info4 = dummy_desc.txd_info4;
+ */
+ if (len > 0) {
+ frag_txd_num = cal_frag_txd_num(len);
+ for (frag_txd_num = frag_txd_num; frag_txd_num > 0;
+ frag_txd_num--) {
+ if (len < MAX_QTXD_LEN)
+ size = len;
+ else
+ size = MAX_QTXD_LEN;
+
+ cpu_ptr = (ei_local->txd_pool + (ctx_idx));
+ dummy_desc.txd_info1 = cpu_ptr->txd_info1;
+ dummy_desc.txd_info2 = cpu_ptr->txd_info2;
+ dummy_desc.txd_info3 = cpu_ptr->txd_info3;
+ dummy_desc.txd_info4 = cpu_ptr->txd_info4;
+ dummy_desc.txd_info5 = cpu_ptr->txd_info5;
+ dummy_desc.txd_info6 = cpu_ptr->txd_info6;
+ dummy_desc.txd_info7 = cpu_ptr->txd_info7;
+ prev_cpu_ptr = cpu_ptr;
+ cpu_ptr = &dummy_desc;
+ cpu_ptr->txd_info4.QID = init_qid;
+ cpu_ptr->txd_info4.QID = init_qid1;
+ cpu_ptr->txd_info1.SDP = offset;
+ cpu_ptr->txd_info3.SDL = size;
+ if ((nr_frags == 0) && (frag_txd_num == 1))
+ cpu_ptr->txd_info3.LS = 1;
+ else
+ cpu_ptr->txd_info3.LS = 0;
+ cpu_ptr->txd_info3.DDONE = 0;
+ cpu_ptr->txd_info4.SWC = 1;
+ if (cpu_ptr->txd_info3.LS == 1)
+ ei_local->skb_free[ctx_idx] = skb;
+ else
+ ei_local->skb_free[ctx_idx] = magic_id;
+ ctx_idx = get_free_txd(ei_local, ring_no);
+ cpu_ptr->txd_info2.NDP =
+ get_phy_addr(ei_local, ctx_idx);
+ prev_cpu_ptr->txd_info1 = dummy_desc.txd_info1;
+ prev_cpu_ptr->txd_info2 = dummy_desc.txd_info2;
+ prev_cpu_ptr->txd_info3 = dummy_desc.txd_info3;
+ prev_cpu_ptr->txd_info4 = dummy_desc.txd_info4;
+ prev_cpu_ptr->txd_info5 = dummy_desc.txd_info5;
+ prev_cpu_ptr->txd_info6 = dummy_desc.txd_info6;
+ prev_cpu_ptr->txd_info7 = dummy_desc.txd_info7;
+ offset += size;
+ len -= size;
+ }
+ }
+
+ for (i = 0; i < nr_frags; i++) {
+ /* 1. set or get init value for current fragment */
+ offset = 0;
+ frag = &skb_shinfo(skb)->frags[i];
+ len = skb_frag_size(frag);
+ frag_txd_num = cal_frag_txd_num(len);
+ for (frag_txd_num = frag_txd_num;
+ frag_txd_num > 0; frag_txd_num--) {
+ /* 2. size will be assigned to SDL
+ * and can't be larger than MAX_TXD_LEN
+ */
+ if (len < MAX_QTXD_LEN)
+ size = len;
+ else
+ size = MAX_QTXD_LEN;
+
+ /* 3. Update TXD info */
+ cpu_ptr = (ei_local->txd_pool + (ctx_idx));
+ dummy_desc.txd_info1 = cpu_ptr->txd_info1;
+ dummy_desc.txd_info2 = cpu_ptr->txd_info2;
+ dummy_desc.txd_info3 = cpu_ptr->txd_info3;
+ dummy_desc.txd_info4 = cpu_ptr->txd_info4;
+ dummy_desc.txd_info5 = cpu_ptr->txd_info5;
+ dummy_desc.txd_info6 = cpu_ptr->txd_info6;
+ dummy_desc.txd_info7 = cpu_ptr->txd_info7;
+ prev_cpu_ptr = cpu_ptr;
+ cpu_ptr = &dummy_desc;
+ cpu_ptr->txd_info4.QID = init_qid;
+ cpu_ptr->txd_info4.QID = init_qid1;
+ cpu_ptr->txd_info1.SDP = skb_frag_dma_map(&ei_local->qdma_pdev->dev, frag, offset, size, DMA_TO_DEVICE);
+ if (unlikely(dma_mapping_error
+ (&ei_local->qdma_pdev->dev,
+ cpu_ptr->txd_info1.SDP)))
+ pr_err("[%s]dma_map_page() failed...\n",
+ __func__);
+
+ cpu_ptr->txd_info3.SDL = size;
+
+ if ((i == (nr_frags - 1)) && (frag_txd_num == 1))
+ cpu_ptr->txd_info3.LS = 1;
+ else
+ cpu_ptr->txd_info3.LS = 0;
+ cpu_ptr->txd_info3.DDONE = 0;
+ cpu_ptr->txd_info4.SWC = 1;
+ /* 4. Update skb_free for housekeeping */
+ if (cpu_ptr->txd_info3.LS == 1)
+ ei_local->skb_free[ctx_idx] = skb;
+ else
+ ei_local->skb_free[ctx_idx] = magic_id;
+
+ /* 5. Get next TXD */
+ ctx_idx = get_free_txd(ei_local, ring_no);
+ cpu_ptr->txd_info2.NDP =
+ get_phy_addr(ei_local, ctx_idx);
+ prev_cpu_ptr->txd_info1 = dummy_desc.txd_info1;
+ prev_cpu_ptr->txd_info2 = dummy_desc.txd_info2;
+ prev_cpu_ptr->txd_info3 = dummy_desc.txd_info3;
+ prev_cpu_ptr->txd_info4 = dummy_desc.txd_info4;
+ prev_cpu_ptr->txd_info5 = dummy_desc.txd_info5;
+ prev_cpu_ptr->txd_info6 = dummy_desc.txd_info6;
+ prev_cpu_ptr->txd_info7 = dummy_desc.txd_info7;
+ /* 6. Update offset and len. */
+ offset += size;
+ len -= size;
+ }
+ }
+
+ if (skb_shinfo(skb)->gso_segs > 1) {
+ /* TsoLenUpdate(skb->len); */
+
+ /* TCP over IPv4 */
+ iph = (struct iphdr *)skb_network_header(skb);
+ if ((iph->version == 4) && (iph->protocol == IPPROTO_TCP)) {
+ th = (struct tcphdr *)skb_transport_header(skb);
+
+ init_cpu_ptr->txd_info5.TSO = 1;
+
+ th->check = htons(skb_shinfo(skb)->gso_size);
+
+ dma_sync_single_for_device(&ei_local->qdma_pdev->dev,
+ virt_to_phys(th),
+ sizeof(struct
+ tcphdr),
+ DMA_TO_DEVICE);
+ }
+ if (ei_local->features & FE_TSO_V6) {
+ ip6h = (struct ipv6hdr *)skb_network_header(skb);
+ if ((ip6h->nexthdr == NEXTHDR_TCP) &&
+ (ip6h->version == 6)) {
+ th = (struct tcphdr *)skb_transport_header(skb);
+ init_cpu_ptr->txd_info5.TSO = 1;
+ th->check = htons(skb_shinfo(skb)->gso_size);
+ dma_sync_single_for_device(&ei_local->qdma_pdev->dev,
+ virt_to_phys(th),
+ sizeof(struct
+ tcphdr),
+ DMA_TO_DEVICE);
+ }
+ }
+
+ if (ei_local->features & FE_HW_SFQ) {
+ init_cpu_ptr->txd_info5.VQID0 = 1;
+ init_cpu_ptr->txd_info5.PROT = sfq_prot;
+ /* no vlan */
+ init_cpu_ptr->txd_info5.IPOFST =
+ 14 + (sfq_parse_result.vlan1_gap);
+ }
+ }
+ /* dma_cache_sync(NULL, skb->data, skb->len, DMA_TO_DEVICE); */
+
+ init_cpu_ptr->txd_info3.DDONE = 0;
+ spin_lock_irqsave(&ei_local->page_lock, flags);
+ prev_cpu_ptr = ei_local->txd_pool + ei_local->tx_cpu_idx;
+ ei_local->skb_free[ei_local->tx_cpu_idx] = magic_id;
+ ei_local->tx_cpu_idx = ctx_idx;
+ prev_cpu_ptr->txd_info1 = init_dummy_desc.txd_info1;
+ prev_cpu_ptr->txd_info2 = init_dummy_desc.txd_info2;
+ prev_cpu_ptr->txd_info4 = init_dummy_desc.txd_info4;
+ prev_cpu_ptr->txd_info3 = init_dummy_desc.txd_info3;
+ prev_cpu_ptr->txd_info5 = init_dummy_desc.txd_info5;
+ prev_cpu_ptr->txd_info6 = init_dummy_desc.txd_info6;
+ prev_cpu_ptr->txd_info7 = init_dummy_desc.txd_info7;
+
+ /* NOTE: add memory barrier to avoid
+ * DMA access memory earlier than memory written
+ */
+ wmb();
+ sys_reg_write(QTX_CTX_PTR,
+ get_phy_addr(ei_local, ei_local->tx_cpu_idx));
+ spin_unlock_irqrestore(&ei_local->page_lock, flags);
+
+ if (ei_local->features & FE_GE2_SUPPORT) {
+ if (gmac_no == 2) {
+ if (ei_local->pseudo_dev) {
+ p_ad = netdev_priv(ei_local->pseudo_dev);
+ p_ad->stat.tx_packets++;
+ p_ad->stat.tx_bytes += length;
+ }
+ } else {
+ ei_local->stat.tx_packets++;
+ ei_local->stat.tx_bytes += skb->len;
+ }
+ } else {
+ ei_local->stat.tx_packets++;
+ ei_local->stat.tx_bytes += skb->len;
+ }
+ if (ei_local->features & FE_INT_NAPI) {
+ if (ei_local->tx_full == 1) {
+ ei_local->tx_full = 0;
+ netif_wake_queue(dev);
+ }
+ }
+
+ return length;
+}
+
+/* QDMA functions */
+int fe_qdma_wait_dma_idle(void)
+{
+ unsigned int reg_val;
+
+ while (1) {
+ reg_val = sys_reg_read(QDMA_GLO_CFG);
+ if ((reg_val & RX_DMA_BUSY)) {
+ pr_err("\n RX_DMA_BUSY !!! ");
+ continue;
+ }
+ if ((reg_val & TX_DMA_BUSY)) {
+ pr_err("\n TX_DMA_BUSY !!! ");
+ continue;
+ }
+ return 0;
+ }
+
+ return -1;
+}
+
+int fe_qdma_rx_dma_init(struct net_device *dev)
+{
+ int i;
+ struct END_DEVICE *ei_local = netdev_priv(dev);
+ unsigned int skb_size;
+ /* Initial QDMA RX Ring */
+
+ skb_size = SKB_DATA_ALIGN(MAX_RX_LENGTH + NET_IP_ALIGN + NET_SKB_PAD) +
+ SKB_DATA_ALIGN(sizeof(struct skb_shared_info));
+
+ ei_local->qrx_ring =
+ dma_alloc_coherent(&ei_local->qdma_pdev->dev,
+ NUM_QRX_DESC * sizeof(struct PDMA_rxdesc),
+ &ei_local->phy_qrx_ring,
+ GFP_ATOMIC | __GFP_ZERO);
+ for (i = 0; i < NUM_QRX_DESC; i++) {
+ ei_local->netrx0_skb_data[i] =
+ raeth_alloc_skb_data(skb_size, GFP_KERNEL);
+ if (!ei_local->netrx0_skb_data[i]) {
+ pr_err("rx skbuff buffer allocation failed!");
+ goto no_rx_mem;
+ }
+
+ memset(&ei_local->qrx_ring[i], 0, sizeof(struct PDMA_rxdesc));
+ ei_local->qrx_ring[i].rxd_info2.DDONE_bit = 0;
+ ei_local->qrx_ring[i].rxd_info2.LS0 = 0;
+ ei_local->qrx_ring[i].rxd_info2.PLEN0 = MAX_RX_LENGTH;
+ ei_local->qrx_ring[i].rxd_info1.PDP0 =
+ dma_map_single(&ei_local->qdma_pdev->dev,
+ ei_local->netrx0_skb_data[i] +
+ NET_SKB_PAD,
+ MAX_RX_LENGTH,
+ DMA_FROM_DEVICE);
+ if (unlikely
+ (dma_mapping_error
+ (&ei_local->qdma_pdev->dev,
+ ei_local->qrx_ring[i].rxd_info1.PDP0))) {
+ pr_err("[%s]dma_map_single() failed...\n", __func__);
+ goto no_rx_mem;
+ }
+ }
+ pr_err("\nphy_qrx_ring = 0x%p, qrx_ring = 0x%p\n",
+ (void *)ei_local->phy_qrx_ring, ei_local->qrx_ring);
+
+ /* Tell the adapter where the RX rings are located. */
+ sys_reg_write(QRX_BASE_PTR_0,
+ phys_to_bus((u32)ei_local->phy_qrx_ring));
+ sys_reg_write(QRX_MAX_CNT_0, cpu_to_le32((u32)NUM_QRX_DESC));
+ sys_reg_write(QRX_CRX_IDX_0, cpu_to_le32((u32)(NUM_QRX_DESC - 1)));
+
+ sys_reg_write(QDMA_RST_CFG, PST_DRX_IDX0);
+ ei_local->rx_ring[0] = ei_local->qrx_ring;
+
+ return 0;
+
+no_rx_mem:
+ return -ENOMEM;
+}
+
+int fe_qdma_tx_dma_init(struct net_device *dev)
+{
+ bool pass;
+ struct END_DEVICE *ei_local = netdev_priv(dev_raether);
+
+ if (ei_local->features & FE_HW_SFQ)
+ sfq_init(dev);
+ /*tx desc alloc, add a NULL TXD to HW */
+ pass = qdma_tx_desc_alloc();
+ if (!pass)
+ return -1;
+
+ pass = fq_qdma_init(dev);
+ if (!pass)
+ return -1;
+
+ return 0;
+}
+
+void fe_qdma_rx_dma_deinit(struct net_device *dev)
+{
+ struct END_DEVICE *ei_local = netdev_priv(dev);
+ int i;
+
+ /* free RX Ring */
+ dma_free_coherent(&ei_local->qdma_pdev->dev,
+ NUM_QRX_DESC * sizeof(struct PDMA_rxdesc),
+ ei_local->qrx_ring, ei_local->phy_qrx_ring);
+
+ /* free RX skb */
+ for (i = 0; i < NUM_QRX_DESC; i++) {
+ raeth_free_skb_data(ei_local->netrx0_skb_data[i]);
+ ei_local->netrx0_skb_data[i] = NULL;
+ }
+}
+
+void fe_qdma_tx_dma_deinit(struct net_device *dev)
+{
+ struct END_DEVICE *ei_local = netdev_priv(dev);
+ int i;
+
+ /* free TX Ring */
+ if (ei_local->txd_pool)
+ dma_free_coherent(&ei_local->qdma_pdev->dev,
+ num_tx_desc * QTXD_LEN,
+ ei_local->txd_pool, ei_local->phy_txd_pool);
+ if (ei_local->free_head)
+ dma_free_coherent(&ei_local->qdma_pdev->dev,
+ NUM_QDMA_PAGE * QTXD_LEN,
+ ei_local->free_head, ei_local->phy_free_head);
+ if (ei_local->free_page_head)
+ dma_free_coherent(&ei_local->qdma_pdev->dev,
+ NUM_QDMA_PAGE * QDMA_PAGE_SIZE,
+ ei_local->free_page_head,
+ ei_local->phy_free_page_head);
+
+ /* free TX data */
+ for (i = 0; i < num_tx_desc; i++) {
+ if ((ei_local->skb_free[i] != (struct sk_buff *)0xFFFFFFFF) &&
+ (ei_local->skb_free[i] != 0))
+ dev_kfree_skb_any(ei_local->skb_free[i]);
+ }
+}
+
+void set_fe_qdma_glo_cfg(void)
+{
+ unsigned int reg_val;
+ unsigned int dma_glo_cfg = 0;
+ struct END_DEVICE *ei_local = netdev_priv(dev_raether);
+
+ reg_val = sys_reg_read(QDMA_GLO_CFG);
+ reg_val &= 0x000000FF;
+
+ sys_reg_write(QDMA_GLO_CFG, reg_val);
+ reg_val = sys_reg_read(QDMA_GLO_CFG);
+
+ /* Enable randon early drop and set drop threshold automatically */
+ if (!(ei_local->features & FE_HW_SFQ))
+ sys_reg_write(QDMA_FC_THRES, 0x4444);
+ sys_reg_write(QDMA_HRED2, 0x0);
+
+ dma_glo_cfg =
+ (TX_WB_DDONE | RX_DMA_EN | TX_DMA_EN | PDMA_BT_SIZE_16DWORDS | PDMA_DESC_32B_E);
+ dma_glo_cfg |= (RX_2B_OFFSET);
+ sys_reg_write(QDMA_GLO_CFG, dma_glo_cfg);
+
+ pr_err("Enable QDMA TX NDP coherence check and re-read mechanism\n");
+ reg_val = sys_reg_read(QDMA_GLO_CFG);
+ reg_val = reg_val | 0x400 | 0x100000;
+ sys_reg_write(QDMA_GLO_CFG, reg_val);
+ //sys_reg_write(QDMA_GLO_CFG, 0x95404575);
+ sys_reg_write(QDMA_GLO_CFG, 0x95404475);
+ pr_err("***********QDMA_GLO_CFG=%x\n", sys_reg_read(QDMA_GLO_CFG));
+}
+
+int ei_qdma_start_xmit(struct sk_buff *skb, struct net_device *dev, int gmac_no)
+{
+ struct END_DEVICE *ei_local = netdev_priv(dev);
+ unsigned int num_of_txd = 0;
+ unsigned int nr_frags = skb_shinfo(skb)->nr_frags, i;
+ skb_frag_t * frag;
+ struct PSEUDO_ADAPTER *p_ad;
+ int ring_no;
+
+ ring_no = skb->queue_mapping + (gmac_no - 1) * gmac1_txq_num;
+
+#if defined(CONFIG_HW_NAT) || defined(CONFIG_RA_HW_NAT_MODULE)
+ if (ppe_hook_tx_eth) {
+ if (ppe_hook_tx_eth(skb, gmac_no) != 1) {
+ dev_kfree_skb_any(skb);
+ return 0;
+ }
+ }
+#endif
+
+// dev->trans_start = jiffies; /* save the timestamp */
+ netif_trans_update(dev);
+ /*spin_lock_irqsave(&ei_local->page_lock, flags); */
+
+ /* check free_txd_num before calling rt288_eth_send() */
+
+ if (ei_local->features & FE_TSO) {
+ num_of_txd += cal_frag_txd_num(skb->len - skb->data_len);
+ if (nr_frags != 0) {
+ for (i = 0; i < nr_frags; i++) {
+ frag = &skb_shinfo(skb)->frags[i];
+ num_of_txd += cal_frag_txd_num(skb_frag_size(frag));
+ }
+ }
+ } else {
+ num_of_txd = 1;
+ }
+
+/* if ((ei_local->free_txd_num > num_of_txd + 1)) { */
+ if (likely(atomic_read(&ei_local->free_txd_num[ring_no]) >
+ (num_of_txd + 1))) {
+ if (num_of_txd == 1)
+ rt2880_qdma_eth_send(ei_local, dev, skb,
+ gmac_no, ring_no);
+ else
+ rt2880_qdma_eth_send_tso(ei_local, dev, skb,
+ gmac_no, ring_no);
+ } else {
+ if (ei_local->features & FE_GE2_SUPPORT) {
+ if (gmac_no == 2) {
+ if (ei_local->pseudo_dev) {
+ p_ad =
+ netdev_priv(ei_local->pseudo_dev);
+ p_ad->stat.tx_dropped++;
+ }
+ } else {
+ ei_local->stat.tx_dropped++;
+ }
+ } else {
+ ei_local->stat.tx_dropped++;
+ }
+ /* kfree_skb(skb); */
+ dev_kfree_skb_any(skb);
+ /* spin_unlock_irqrestore(&ei_local->page_lock, flags); */
+ return 0;
+ }
+ /* spin_unlock_irqrestore(&ei_local->page_lock, flags); */
+ return 0;
+}
+
+int ei_qdma_xmit_housekeeping(struct net_device *netdev, int budget)
+{
+ struct END_DEVICE *ei_local = netdev_priv(netdev);
+
+ dma_addr_t dma_ptr;
+ struct QDMA_txdesc *cpu_ptr = NULL;
+ dma_addr_t tmp_ptr;
+ unsigned int ctx_offset = 0;
+ unsigned int dtx_offset = 0;
+ unsigned int rls_cnt[TOTAL_TXQ_NUM] = { 0 };
+ int ring_no;
+ int i;
+
+ dma_ptr = (dma_addr_t)sys_reg_read(QTX_DRX_PTR);
+ ctx_offset = ei_local->rls_cpu_idx;
+ dtx_offset = (dma_ptr - ei_local->phy_txd_pool) / QTXD_LEN;
+ cpu_ptr = (ei_local->txd_pool + (ctx_offset));
+ while (ctx_offset != dtx_offset) {
+ /* 1. keep cpu next TXD */
+ tmp_ptr = (dma_addr_t)cpu_ptr->txd_info2.NDP;
+ ring_no = ring_no_mapping(ctx_offset);
+ rls_cnt[ring_no]++;
+ /* 2. release TXD */
+ ei_local->txd_pool_info[ei_local->free_txd_tail[ring_no]] =
+ ctx_offset;
+ ei_local->free_txd_tail[ring_no] = ctx_offset;
+ /* atomic_add(1, &ei_local->free_txd_num[ring_no]); */
+ /* 3. update ctx_offset and free skb memory */
+ ctx_offset = (tmp_ptr - ei_local->phy_txd_pool) / QTXD_LEN;
+ if (ei_local->features & FE_TSO) {
+ if (ei_local->skb_free[ctx_offset] != magic_id) {
+ dev_kfree_skb_any(ei_local->skb_free
+ [ctx_offset]);
+ }
+ } else {
+ dev_kfree_skb_any(ei_local->skb_free[ctx_offset]);
+ }
+ ei_local->skb_free[ctx_offset] = 0;
+ /* 4. update cpu_ptr */
+ cpu_ptr = (ei_local->txd_pool + ctx_offset);
+ }
+ for (i = 0; i < TOTAL_TXQ_NUM; i++) {
+ if (rls_cnt[i] > 0)
+ atomic_add(rls_cnt[i], &ei_local->free_txd_num[i]);
+ }
+ /* atomic_add(rls_cnt, &ei_local->free_txd_num[0]); */
+ ei_local->rls_cpu_idx = ctx_offset;
+ netif_wake_queue(netdev);
+ if (ei_local->features & FE_GE2_SUPPORT)
+ netif_wake_queue(ei_local->pseudo_dev);
+ ei_local->tx_ring_full = 0;
+ sys_reg_write(QTX_CRX_PTR,
+ (ei_local->phy_txd_pool + (ctx_offset * QTXD_LEN)));
+
+ return 0;
+}
+
+int ei_qdma_ioctl(struct net_device *dev, struct ifreq *ifr,
+ struct qdma_ioctl_data *data)
+{
+ int ret = 0;
+ struct END_DEVICE *ei_local = netdev_priv(dev);
+ unsigned int cmd;
+
+ cmd = data->cmd;
+
+ switch (cmd) {
+ case RAETH_QDMA_REG_READ:
+
+ if (data->off > REG_HQOS_MAX) {
+ ret = -EINVAL;
+ break;
+ }
+
+ if (ei_local->chip_name == MT7622_FE) { /* harry */
+ unsigned int page = 0;
+
+ /* q16~q31: 0x100 <= data->off < 0x200
+ * q32~q47: 0x200 <= data->off < 0x300
+ * q48~q63: 0x300 <= data->off < 0x400
+ */
+ if (data->off >= 0x100 && data->off < 0x200) {
+ page = 1;
+ data->off = data->off - 0x100;
+ } else if (data->off >= 0x200 && data->off < 0x300) {
+ page = 2;
+ data->off = data->off - 0x200;
+ } else if (data->off >= 0x300 && data->off < 0x400) {
+ page = 3;
+ data->off = data->off - 0x300;
+ } else {
+ page = 0;
+ }
+ /*magic number for ioctl identify CR 0x1b101a14*/
+ if (data->off == 0x777) {
+ page = 0;
+ data->off = 0x214;
+ }
+
+ sys_reg_write(QDMA_PAGE, page);
+ /* pr_debug("page=%d, data->off =%x\n", page, data->off); */
+ }
+
+ data->val = sys_reg_read(QTX_CFG_0 + data->off);
+ pr_info("read reg off:%x val:%x\n", data->off, data->val);
+ ret = copy_to_user(ifr->ifr_data, data, sizeof(*data));
+ sys_reg_write(QDMA_PAGE, 0);
+ if (ret) {
+ pr_info("ret=%d\n", ret);
+ ret = -EFAULT;
+ }
+ break;
+ case RAETH_QDMA_REG_WRITE:
+
+ if (data->off > REG_HQOS_MAX) {
+ ret = -EINVAL;
+ break;
+ }
+
+ if (ei_local->chip_name == MT7622_FE) { /* harry */
+ unsigned int page = 0;
+ /*QoS must enable QDMA drop packet policy*/
+ sys_reg_write(QDMA_FC_THRES, 0x83834444);
+ /* q16~q31: 0x100 <= data->off < 0x200
+ * q32~q47: 0x200 <= data->off < 0x300
+ * q48~q63: 0x300 <= data->off < 0x400
+ */
+ if (data->off >= 0x100 && data->off < 0x200) {
+ page = 1;
+ data->off = data->off - 0x100;
+ } else if (data->off >= 0x200 && data->off < 0x300) {
+ page = 2;
+ data->off = data->off - 0x200;
+ } else if (data->off >= 0x300 && data->off < 0x400) {
+ page = 3;
+ data->off = data->off - 0x300;
+ } else {
+ page = 0;
+ }
+ /*magic number for ioctl identify CR 0x1b101a14*/
+ if (data->off == 0x777) {
+ page = 0;
+ data->off = 0x214;
+ }
+ sys_reg_write(QDMA_PAGE, page);
+ /*pr_info("data->val =%x\n", data->val);*/
+ sys_reg_write(QTX_CFG_0 + data->off, data->val);
+ sys_reg_write(QDMA_PAGE, 0);
+ } else {
+ sys_reg_write(QTX_CFG_0 + data->off, data->val);
+ }
+ /* pr_ino("write reg off:%x val:%x\n", data->off, data->val); */
+ break;
+ case RAETH_QDMA_QUEUE_MAPPING:
+ if ((data->off & 0x100) == 0x100) {
+ lan_wan_separate = 1;
+ data->off &= 0xff;
+ } else {
+ lan_wan_separate = 0;
+ data->off &= 0xff;
+ }
+ M2Q_table[data->off] = data->val;
+ break;
+ case RAETH_QDMA_SFQ_WEB_ENABLE:
+ if (ei_local->features & FE_HW_SFQ) {
+ if ((data->val) == 0x1)
+ web_sfq_enable = 1;
+ else
+ web_sfq_enable = 0;
+ } else {
+ ret = -EINVAL;
+ }
+ break;
+ default:
+ ret = 1;
+ break;
+ }
+
+ return ret;
+}