[][Add Ethernet HW LRO support depending on hw_capability]
[Description]
Add Ethernet HW LRO support and related debug interface
depending on hw_capability.
---------Debug method 1 - Auto learn table---------
- Dump aggregated flows:
$ cat /proc/mtketh/hw_lro_auto_tlb
- Modify MAX_AGGREGATED_CNT:
$ echo 0 [setting] > /proc/mtketh/hw_lro_auto_tlb
- Modify MAX_AGG_TIME:
$ echo 1 [setting] > /proc/mtketh/hw_lro_auto_tlb
- Modify AGE_TIME:
$ echo 2 [setting] > /proc/mtketh/hw_lro_auto_tlb
- Modify AUTO_LEARN_LRO_ELIGIBLE_THRESHOLD:
$ echo 3 [setting] > /proc/mtketh/hw_lro_auto_tlb
- Enable/Disable LRO:
$ echo 4 [1/0] > /proc/mtketh/hw_lro_auto_tlb
---------Debug method 2 - per LRO ring statistics--------
- Dump each LRO rings' statistics, which includes packet count,
packet length, flush reason, etc.
$ cat /proc/mtketh/hw_lro_stats
[Release-log]
N/A
Change-Id: I2359ac177b81077bb7c95ee5fae2f32aadda521f
Reviewed-on: https://gerrit.mediatek.inc/c/openwrt/feeds/mtk_openwrt_feeds/+/4588327
diff --git a/target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_eth_dbg.c b/target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_eth_dbg.c
index 7f4a7b5..979bc9b 100755
--- a/target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_eth_dbg.c
+++ b/target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_eth_dbg.c
@@ -28,6 +28,19 @@
#include "mtk_eth_soc.h"
#include "mtk_eth_dbg.h"
+u32 hw_lro_agg_num_cnt[MTK_HW_LRO_RING_NUM][MTK_HW_LRO_MAX_AGG_CNT + 1];
+u32 hw_lro_agg_size_cnt[MTK_HW_LRO_RING_NUM][16];
+u32 hw_lro_tot_agg_cnt[MTK_HW_LRO_RING_NUM];
+u32 hw_lro_tot_flush_cnt[MTK_HW_LRO_RING_NUM];
+u32 hw_lro_agg_flush_cnt[MTK_HW_LRO_RING_NUM];
+u32 hw_lro_age_flush_cnt[MTK_HW_LRO_RING_NUM];
+u32 hw_lro_seq_flush_cnt[MTK_HW_LRO_RING_NUM];
+u32 hw_lro_timestamp_flush_cnt[MTK_HW_LRO_RING_NUM];
+u32 hw_lro_norule_flush_cnt[MTK_HW_LRO_RING_NUM];
+u32 mtk_hwlro_stats_ebl;
+static struct proc_dir_entry *proc_hw_lro_stats, *proc_hw_lro_auto_tlb;
+typedef int (*mtk_lro_dbg_func) (int par);
+
struct mtk_eth_debug {
struct dentry *root;
};
@@ -78,8 +91,7 @@
{
struct mtk_eth *eth = m->private;
struct mtk_mac *mac = 0;
- u32 d;
- int i, j = 0;
+ int i = 0;
for (i = 0 ; i < MTK_MAX_DEVS ; i++) {
if (!eth->mac[i] ||
@@ -787,14 +799,610 @@
.open = dbg_regs_open,
.read = seq_read,
.llseek = seq_lseek,
+ .release = single_release
+};
+
+void hw_lro_stats_update(u32 ring_no, struct mtk_rx_dma *rxd)
+{
+ u32 idx, agg_cnt, agg_size;
+
+#if defined(CONFIG_MEDIATEK_NETSYS_V2)
+ idx = ring_no - 4;
+ agg_cnt = RX_DMA_GET_AGG_CNT_V2(rxd->rxd6);
+#else
+ idx = ring_no - 1;
+ agg_cnt = RX_DMA_GET_AGG_CNT(rxd->rxd2);
+#endif
+
+ agg_size = RX_DMA_GET_PLEN0(rxd->rxd2);
+
+ hw_lro_agg_size_cnt[idx][agg_size / 5000]++;
+ hw_lro_agg_num_cnt[idx][agg_cnt]++;
+ hw_lro_tot_flush_cnt[idx]++;
+ hw_lro_tot_agg_cnt[idx] += agg_cnt;
+}
+
+void hw_lro_flush_stats_update(u32 ring_no, struct mtk_rx_dma *rxd)
+{
+ u32 idx, flush_reason;
+
+#if defined(CONFIG_MEDIATEK_NETSYS_V2)
+ idx = ring_no - 4;
+ flush_reason = RX_DMA_GET_FLUSH_RSN_V2(rxd->rxd6);
+#else
+ idx = ring_no - 1;
+ flush_reason = RX_DMA_GET_REV(rxd->rxd2);
+#endif
+
+ if ((flush_reason & 0x7) == MTK_HW_LRO_AGG_FLUSH)
+ hw_lro_agg_flush_cnt[idx]++;
+ else if ((flush_reason & 0x7) == MTK_HW_LRO_AGE_FLUSH)
+ hw_lro_age_flush_cnt[idx]++;
+ else if ((flush_reason & 0x7) == MTK_HW_LRO_NOT_IN_SEQ_FLUSH)
+ hw_lro_seq_flush_cnt[idx]++;
+ else if ((flush_reason & 0x7) == MTK_HW_LRO_TIMESTAMP_FLUSH)
+ hw_lro_timestamp_flush_cnt[idx]++;
+ else if ((flush_reason & 0x7) == MTK_HW_LRO_NON_RULE_FLUSH)
+ hw_lro_norule_flush_cnt[idx]++;
+}
+
+ssize_t hw_lro_stats_write(struct file *file, const char __user *buffer,
+ size_t count, loff_t *data)
+{
+ memset(hw_lro_agg_num_cnt, 0, sizeof(hw_lro_agg_num_cnt));
+ memset(hw_lro_agg_size_cnt, 0, sizeof(hw_lro_agg_size_cnt));
+ memset(hw_lro_tot_agg_cnt, 0, sizeof(hw_lro_tot_agg_cnt));
+ memset(hw_lro_tot_flush_cnt, 0, sizeof(hw_lro_tot_flush_cnt));
+ memset(hw_lro_agg_flush_cnt, 0, sizeof(hw_lro_agg_flush_cnt));
+ memset(hw_lro_age_flush_cnt, 0, sizeof(hw_lro_age_flush_cnt));
+ memset(hw_lro_seq_flush_cnt, 0, sizeof(hw_lro_seq_flush_cnt));
+ memset(hw_lro_timestamp_flush_cnt, 0,
+ sizeof(hw_lro_timestamp_flush_cnt));
+ memset(hw_lro_norule_flush_cnt, 0, sizeof(hw_lro_norule_flush_cnt));
+
+ pr_info("clear hw lro cnt table\n");
+
+ return count;
+}
+
+int hw_lro_stats_read_v1(struct seq_file *seq, void *v)
+{
+ int i;
+
+ seq_puts(seq, "HW LRO statistic dump:\n");
+
+ /* Agg number count */
+ seq_puts(seq, "Cnt: RING1 | RING2 | RING3 | Total\n");
+ for (i = 0; i <= MTK_HW_LRO_MAX_AGG_CNT; i++) {
+ seq_printf(seq, " %d : %d %d %d %d\n",
+ i, hw_lro_agg_num_cnt[0][i],
+ hw_lro_agg_num_cnt[1][i], hw_lro_agg_num_cnt[2][i],
+ hw_lro_agg_num_cnt[0][i] + hw_lro_agg_num_cnt[1][i] +
+ hw_lro_agg_num_cnt[2][i]);
+ }
+
+ /* Total agg count */
+ seq_puts(seq, "Total agg: RING1 | RING2 | RING3 | Total\n");
+ seq_printf(seq, " %d %d %d %d\n",
+ hw_lro_tot_agg_cnt[0], hw_lro_tot_agg_cnt[1],
+ hw_lro_tot_agg_cnt[2],
+ hw_lro_tot_agg_cnt[0] + hw_lro_tot_agg_cnt[1] +
+ hw_lro_tot_agg_cnt[2]);
+
+ /* Total flush count */
+ seq_puts(seq, "Total flush: RING1 | RING2 | RING3 | Total\n");
+ seq_printf(seq, " %d %d %d %d\n",
+ hw_lro_tot_flush_cnt[0], hw_lro_tot_flush_cnt[1],
+ hw_lro_tot_flush_cnt[2],
+ hw_lro_tot_flush_cnt[0] + hw_lro_tot_flush_cnt[1] +
+ hw_lro_tot_flush_cnt[2]);
+
+ /* Avg agg count */
+ seq_puts(seq, "Avg agg: RING1 | RING2 | RING3 | Total\n");
+ seq_printf(seq, " %d %d %d %d\n",
+ (hw_lro_tot_flush_cnt[0]) ?
+ hw_lro_tot_agg_cnt[0] / hw_lro_tot_flush_cnt[0] : 0,
+ (hw_lro_tot_flush_cnt[1]) ?
+ hw_lro_tot_agg_cnt[1] / hw_lro_tot_flush_cnt[1] : 0,
+ (hw_lro_tot_flush_cnt[2]) ?
+ hw_lro_tot_agg_cnt[2] / hw_lro_tot_flush_cnt[2] : 0,
+ (hw_lro_tot_flush_cnt[0] + hw_lro_tot_flush_cnt[1] +
+ hw_lro_tot_flush_cnt[2]) ?
+ ((hw_lro_tot_agg_cnt[0] + hw_lro_tot_agg_cnt[1] +
+ hw_lro_tot_agg_cnt[2]) / (hw_lro_tot_flush_cnt[0] +
+ hw_lro_tot_flush_cnt[1] + hw_lro_tot_flush_cnt[2])) : 0);
+
+ /* Statistics of aggregation size counts */
+ seq_puts(seq, "HW LRO flush pkt len:\n");
+ seq_puts(seq, " Length | RING1 | RING2 | RING3 | Total\n");
+ for (i = 0; i < 15; i++) {
+ seq_printf(seq, "%d~%d: %d %d %d %d\n", i * 5000,
+ (i + 1) * 5000, hw_lro_agg_size_cnt[0][i],
+ hw_lro_agg_size_cnt[1][i], hw_lro_agg_size_cnt[2][i],
+ hw_lro_agg_size_cnt[0][i] +
+ hw_lro_agg_size_cnt[1][i] +
+ hw_lro_agg_size_cnt[2][i]);
+ }
+
+ seq_puts(seq, "Flush reason: RING1 | RING2 | RING3 | Total\n");
+ seq_printf(seq, "AGG timeout: %d %d %d %d\n",
+ hw_lro_agg_flush_cnt[0], hw_lro_agg_flush_cnt[1],
+ hw_lro_agg_flush_cnt[2],
+ (hw_lro_agg_flush_cnt[0] + hw_lro_agg_flush_cnt[1] +
+ hw_lro_agg_flush_cnt[2]));
+
+ seq_printf(seq, "AGE timeout: %d %d %d %d\n",
+ hw_lro_age_flush_cnt[0], hw_lro_age_flush_cnt[1],
+ hw_lro_age_flush_cnt[2],
+ (hw_lro_age_flush_cnt[0] + hw_lro_age_flush_cnt[1] +
+ hw_lro_age_flush_cnt[2]));
+
+ seq_printf(seq, "Not in-sequence: %d %d %d %d\n",
+ hw_lro_seq_flush_cnt[0], hw_lro_seq_flush_cnt[1],
+ hw_lro_seq_flush_cnt[2],
+ (hw_lro_seq_flush_cnt[0] + hw_lro_seq_flush_cnt[1] +
+ hw_lro_seq_flush_cnt[2]));
+
+ seq_printf(seq, "Timestamp: %d %d %d %d\n",
+ hw_lro_timestamp_flush_cnt[0],
+ hw_lro_timestamp_flush_cnt[1],
+ hw_lro_timestamp_flush_cnt[2],
+ (hw_lro_timestamp_flush_cnt[0] +
+ hw_lro_timestamp_flush_cnt[1] +
+ hw_lro_timestamp_flush_cnt[2]));
+
+ seq_printf(seq, "No LRO rule: %d %d %d %d\n",
+ hw_lro_norule_flush_cnt[0],
+ hw_lro_norule_flush_cnt[1],
+ hw_lro_norule_flush_cnt[2],
+ (hw_lro_norule_flush_cnt[0] +
+ hw_lro_norule_flush_cnt[1] +
+ hw_lro_norule_flush_cnt[2]));
+
+ return 0;
+}
+
+int hw_lro_stats_read_v2(struct seq_file *seq, void *v)
+{
+ int i;
+
+ seq_puts(seq, "HW LRO statistic dump:\n");
+
+ /* Agg number count */
+ seq_puts(seq, "Cnt: RING4 | RING5 | RING6 | RING7 Total\n");
+ for (i = 0; i <= MTK_HW_LRO_MAX_AGG_CNT; i++) {
+ seq_printf(seq,
+ " %d : %d %d %d %d %d\n",
+ i, hw_lro_agg_num_cnt[0][i], hw_lro_agg_num_cnt[1][i],
+ hw_lro_agg_num_cnt[2][i], hw_lro_agg_num_cnt[3][i],
+ hw_lro_agg_num_cnt[0][i] + hw_lro_agg_num_cnt[1][i] +
+ hw_lro_agg_num_cnt[2][i] + hw_lro_agg_num_cnt[3][i]);
+ }
+
+ /* Total agg count */
+ seq_puts(seq, "Total agg: RING4 | RING5 | RING6 | RING7 Total\n");
+ seq_printf(seq, " %d %d %d %d %d\n",
+ hw_lro_tot_agg_cnt[0], hw_lro_tot_agg_cnt[1],
+ hw_lro_tot_agg_cnt[2], hw_lro_tot_agg_cnt[3],
+ hw_lro_tot_agg_cnt[0] + hw_lro_tot_agg_cnt[1] +
+ hw_lro_tot_agg_cnt[2] + hw_lro_tot_agg_cnt[3]);
+
+ /* Total flush count */
+ seq_puts(seq, "Total flush: RING4 | RING5 | RING6 | RING7 Total\n");
+ seq_printf(seq, " %d %d %d %d %d\n",
+ hw_lro_tot_flush_cnt[0], hw_lro_tot_flush_cnt[1],
+ hw_lro_tot_flush_cnt[2], hw_lro_tot_flush_cnt[3],
+ hw_lro_tot_flush_cnt[0] + hw_lro_tot_flush_cnt[1] +
+ hw_lro_tot_flush_cnt[2] + hw_lro_tot_flush_cnt[3]);
+
+ /* Avg agg count */
+ seq_puts(seq, "Avg agg: RING4 | RING5 | RING6 | RING7 Total\n");
+ seq_printf(seq, " %d %d %d %d %d\n",
+ (hw_lro_tot_flush_cnt[0]) ?
+ hw_lro_tot_agg_cnt[0] / hw_lro_tot_flush_cnt[0] : 0,
+ (hw_lro_tot_flush_cnt[1]) ?
+ hw_lro_tot_agg_cnt[1] / hw_lro_tot_flush_cnt[1] : 0,
+ (hw_lro_tot_flush_cnt[2]) ?
+ hw_lro_tot_agg_cnt[2] / hw_lro_tot_flush_cnt[2] : 0,
+ (hw_lro_tot_flush_cnt[3]) ?
+ hw_lro_tot_agg_cnt[3] / hw_lro_tot_flush_cnt[3] : 0,
+ (hw_lro_tot_flush_cnt[0] + hw_lro_tot_flush_cnt[1] +
+ hw_lro_tot_flush_cnt[2] + hw_lro_tot_flush_cnt[3]) ?
+ ((hw_lro_tot_agg_cnt[0] + hw_lro_tot_agg_cnt[1] +
+ hw_lro_tot_agg_cnt[2] + hw_lro_tot_agg_cnt[3]) /
+ (hw_lro_tot_flush_cnt[0] + hw_lro_tot_flush_cnt[1] +
+ hw_lro_tot_flush_cnt[2] + hw_lro_tot_flush_cnt[3])) : 0);
+
+ /* Statistics of aggregation size counts */
+ seq_puts(seq, "HW LRO flush pkt len:\n");
+ seq_puts(seq, " Length | RING4 | RING5 | RING6 | RING7 Total\n");
+ for (i = 0; i < 15; i++) {
+ seq_printf(seq, "%d~%d: %d %d %d %d %d\n",
+ i * 5000, (i + 1) * 5000,
+ hw_lro_agg_size_cnt[0][i], hw_lro_agg_size_cnt[1][i],
+ hw_lro_agg_size_cnt[2][i], hw_lro_agg_size_cnt[3][i],
+ hw_lro_agg_size_cnt[0][i] +
+ hw_lro_agg_size_cnt[1][i] +
+ hw_lro_agg_size_cnt[2][i] +
+ hw_lro_agg_size_cnt[3][i]);
+ }
+
+ seq_puts(seq, "Flush reason: RING4 | RING5 | RING6 | RING7 Total\n");
+ seq_printf(seq, "AGG timeout: %d %d %d %d %d\n",
+ hw_lro_agg_flush_cnt[0], hw_lro_agg_flush_cnt[1],
+ hw_lro_agg_flush_cnt[2], hw_lro_agg_flush_cnt[3],
+ (hw_lro_agg_flush_cnt[0] + hw_lro_agg_flush_cnt[1] +
+ hw_lro_agg_flush_cnt[2] + hw_lro_agg_flush_cnt[3]));
+
+ seq_printf(seq, "AGE timeout: %d %d %d %d %d\n",
+ hw_lro_age_flush_cnt[0], hw_lro_age_flush_cnt[1],
+ hw_lro_age_flush_cnt[2], hw_lro_age_flush_cnt[3],
+ (hw_lro_age_flush_cnt[0] + hw_lro_age_flush_cnt[1] +
+ hw_lro_age_flush_cnt[2] + hw_lro_age_flush_cnt[3]));
+
+ seq_printf(seq, "Not in-sequence: %d %d %d %d %d\n",
+ hw_lro_seq_flush_cnt[0], hw_lro_seq_flush_cnt[1],
+ hw_lro_seq_flush_cnt[2], hw_lro_seq_flush_cnt[3],
+ (hw_lro_seq_flush_cnt[0] + hw_lro_seq_flush_cnt[1] +
+ hw_lro_seq_flush_cnt[2] + hw_lro_seq_flush_cnt[3]));
+
+ seq_printf(seq, "Timestamp: %d %d %d %d %d\n",
+ hw_lro_timestamp_flush_cnt[0],
+ hw_lro_timestamp_flush_cnt[1],
+ hw_lro_timestamp_flush_cnt[2],
+ hw_lro_timestamp_flush_cnt[3],
+ (hw_lro_timestamp_flush_cnt[0] +
+ hw_lro_timestamp_flush_cnt[1] +
+ hw_lro_timestamp_flush_cnt[2] +
+ hw_lro_timestamp_flush_cnt[3]));
+
+ seq_printf(seq, "No LRO rule: %d %d %d %d %d\n",
+ hw_lro_norule_flush_cnt[0],
+ hw_lro_norule_flush_cnt[1],
+ hw_lro_norule_flush_cnt[2],
+ hw_lro_norule_flush_cnt[3],
+ (hw_lro_norule_flush_cnt[0] +
+ hw_lro_norule_flush_cnt[1] +
+ hw_lro_norule_flush_cnt[2] +
+ hw_lro_norule_flush_cnt[3]));
+
+ return 0;
+}
+
+int hw_lro_stats_read_wrapper(struct seq_file *seq, void *v)
+{
+ struct mtk_eth *eth = g_eth;
+
+ if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2))
+ hw_lro_stats_read_v2(seq, v);
+ else
+ hw_lro_stats_read_v1(seq, v);
+
+ return 0;
+}
+
+static int hw_lro_stats_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, hw_lro_stats_read_wrapper, NULL);
+}
+
+static const struct file_operations hw_lro_stats_fops = {
+ .owner = THIS_MODULE,
+ .open = hw_lro_stats_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .write = hw_lro_stats_write,
.release = single_release
};
+int hwlro_agg_cnt_ctrl(int cnt)
+{
+ int i;
+
+ for (i = 1; i <= MTK_HW_LRO_RING_NUM; i++)
+ SET_PDMA_RXRING_MAX_AGG_CNT(g_eth, i, cnt);
+
+ return 0;
+}
+
+int hwlro_agg_time_ctrl(int time)
+{
+ int i;
+
+ for (i = 1; i <= MTK_HW_LRO_RING_NUM; i++)
+ SET_PDMA_RXRING_AGG_TIME(g_eth, i, time);
+
+ return 0;
+}
+
+int hwlro_age_time_ctrl(int time)
+{
+ int i;
+
+ for (i = 1; i <= MTK_HW_LRO_RING_NUM; i++)
+ SET_PDMA_RXRING_AGE_TIME(g_eth, i, time);
+
+ return 0;
+}
+
+int hwlro_threshold_ctrl(int bandwidth)
+{
+ SET_PDMA_LRO_BW_THRESHOLD(g_eth, bandwidth);
+
+ return 0;
+}
+
+int hwlro_ring_enable_ctrl(int enable)
+{
+ int i;
+
+ pr_info("[%s] %s HW LRO rings\n", __func__, (enable) ? "Enable" : "Disable");
+
+ for (i = 1; i <= MTK_HW_LRO_RING_NUM; i++)
+ SET_PDMA_RXRING_VALID(g_eth, i, enable);
+
+ return 0;
+}
+
+int hwlro_stats_enable_ctrl(int enable)
+{
+ pr_info("[%s] %s HW LRO statistics\n", __func__, (enable) ? "Enable" : "Disable");
+ mtk_hwlro_stats_ebl = enable;
+
+ return 0;
+}
+
+static const mtk_lro_dbg_func lro_dbg_func[] = {
+ [0] = hwlro_agg_cnt_ctrl,
+ [1] = hwlro_agg_time_ctrl,
+ [2] = hwlro_age_time_ctrl,
+ [3] = hwlro_threshold_ctrl,
+ [4] = hwlro_ring_enable_ctrl,
+ [5] = hwlro_stats_enable_ctrl,
+};
+
+ssize_t hw_lro_auto_tlb_write(struct file *file, const char __user *buffer,
+ size_t count, loff_t *data)
+{
+ char buf[32];
+ char *p_buf;
+ char *p_token = NULL;
+ char *p_delimiter = " \t";
+ long x = 0, y = 0;
+ int len = count;
+ int ret;
+
+ if (len >= sizeof(buf)) {
+ pr_info("Input handling fail!\n");
+ len = sizeof(buf) - 1;
+ return -1;
+ }
+
+ if (copy_from_user(buf, buffer, len))
+ return -EFAULT;
+
+ buf[len] = '\0';
+
+ p_buf = buf;
+ p_token = strsep(&p_buf, p_delimiter);
+ if (!p_token)
+ x = 0;
+ else
+ ret = kstrtol(p_token, 10, &x);
+
+ p_token = strsep(&p_buf, "\t\n ");
+ if (p_token)
+ ret = kstrtol(p_token, 10, &y);
+
+ if (lro_dbg_func[x] && (ARRAY_SIZE(lro_dbg_func) > x))
+ (*lro_dbg_func[x]) (y);
+
+ return count;
+}
+
+void hw_lro_auto_tlb_dump_v1(struct seq_file *seq, u32 index)
+{
+ int i;
+ struct mtk_lro_alt_v1 alt;
+ __be32 addr;
+ u32 tlb_info[9];
+ u32 dw_len, cnt, priority;
+ u32 entry;
+
+ if (index > 4)
+ index = index - 1;
+ entry = (index * 9) + 1;
+
+ /* read valid entries of the auto-learn table */
+ mtk_w32(g_eth, entry, MTK_FE_ALT_CF8);
+
+ for (i = 0; i < 9; i++)
+ tlb_info[i] = mtk_r32(g_eth, MTK_FE_ALT_SEQ_CFC);
+
+ memcpy(&alt, tlb_info, sizeof(struct mtk_lro_alt_v1));
+
+ dw_len = alt.alt_info7.dw_len;
+ cnt = alt.alt_info6.cnt;
+
+ if (mtk_r32(g_eth, MTK_PDMA_LRO_CTRL_DW0) & MTK_LRO_ALT_PKT_CNT_MODE)
+ priority = cnt; /* packet count */
+ else
+ priority = dw_len; /* byte count */
+
+ /* dump valid entries of the auto-learn table */
+ if (index >= 4)
+ seq_printf(seq, "\n===== TABLE Entry: %d (Act) =====\n", index);
+ else
+ seq_printf(seq, "\n===== TABLE Entry: %d (LRU) =====\n", index);
+
+ if (alt.alt_info8.ipv4) {
+ addr = htonl(alt.alt_info1.sip0);
+ seq_printf(seq, "SIP = %pI4 (IPv4)\n", &addr);
+ } else {
+ seq_printf(seq, "SIP = %08X:%08X:%08X:%08X (IPv6)\n",
+ alt.alt_info4.sip3, alt.alt_info3.sip2,
+ alt.alt_info2.sip1, alt.alt_info1.sip0);
+ }
+
+ seq_printf(seq, "DIP_ID = %d\n", alt.alt_info8.dip_id);
+ seq_printf(seq, "TCP SPORT = %d | TCP DPORT = %d\n",
+ alt.alt_info0.stp, alt.alt_info0.dtp);
+ seq_printf(seq, "VLAN_VID_VLD = %d\n", alt.alt_info6.vlan_vid_vld);
+ seq_printf(seq, "VLAN1 = %d | VLAN2 = %d | VLAN3 = %d | VLAN4 =%d\n",
+ (alt.alt_info5.vlan_vid0 & 0xfff),
+ ((alt.alt_info5.vlan_vid0 >> 12) & 0xfff),
+ ((alt.alt_info6.vlan_vid1 << 8) |
+ ((alt.alt_info5.vlan_vid0 >> 24) & 0xfff)),
+ ((alt.alt_info6.vlan_vid1 >> 4) & 0xfff));
+ seq_printf(seq, "TPUT = %d | FREQ = %d\n", dw_len, cnt);
+ seq_printf(seq, "PRIORITY = %d\n", priority);
+}
+
+void hw_lro_auto_tlb_dump_v2(struct seq_file *seq, u32 index)
+{
+ int i;
+ struct mtk_lro_alt_v2 alt;
+ u32 score = 0, ipv4 = 0;
+ u32 ipv6[4] = { 0 };
+ u32 tlb_info[12];
+
+ /* read valid entries of the auto-learn table */
+ mtk_w32(g_eth, index << MTK_LRO_ALT_INDEX_OFFSET, MTK_LRO_ALT_DBG);
+
+ for (i = 0; i < 11; i++)
+ tlb_info[i] = mtk_r32(g_eth, MTK_LRO_ALT_DBG_DATA);
+
+ memcpy(&alt, tlb_info, sizeof(struct mtk_lro_alt_v2));
+
-#define PROCREG_ESW_CNT "esw_cnt"
-#define PROCREG_TXRING "tx_ring"
-#define PROCREG_RXRING "rx_ring"
-#define PROCREG_DIR "mtketh"
-#define PROCREG_DBG_REGS "dbg_regs"
+ if (mtk_r32(g_eth, MTK_PDMA_LRO_CTRL_DW0) & MTK_LRO_ALT_PKT_CNT_MODE)
+ score = 1; /* packet count */
+ else
+ score = 0; /* byte count */
+
+ /* dump valid entries of the auto-learn table */
+ if (alt.alt_info0.valid) {
+ if (index < 5)
+ seq_printf(seq,
+ "\n===== TABLE Entry: %d (onging) =====\n",
+ index);
+ else
+ seq_printf(seq,
+ "\n===== TABLE Entry: %d (candidate) =====\n",
+ index);
+
+ if (alt.alt_info1.v4_valid) {
+ ipv4 = (alt.alt_info4.sip0_h << 23) |
+ alt.alt_info5.sip0_l;
+ seq_printf(seq, "SIP = 0x%x: (IPv4)\n", ipv4);
+
+ ipv4 = (alt.alt_info8.dip0_h << 23) |
+ alt.alt_info9.dip0_l;
+ seq_printf(seq, "DIP = 0x%x: (IPv4)\n", ipv4);
+ } else if (alt.alt_info1.v6_valid) {
+ ipv6[3] = (alt.alt_info1.sip3_h << 23) |
+ (alt.alt_info2.sip3_l << 9);
+ ipv6[2] = (alt.alt_info2.sip2_h << 23) |
+ (alt.alt_info3.sip2_l << 9);
+ ipv6[1] = (alt.alt_info3.sip1_h << 23) |
+ (alt.alt_info4.sip1_l << 9);
+ ipv6[0] = (alt.alt_info4.sip0_h << 23) |
+ (alt.alt_info5.sip0_l << 9);
+ seq_printf(seq, "SIP = 0x%x:0x%x:0x%x:0x%x (IPv6)\n",
+ ipv6[3], ipv6[2], ipv6[1], ipv6[0]);
+
+ ipv6[3] = (alt.alt_info5.dip3_h << 23) |
+ (alt.alt_info6.dip3_l << 9);
+ ipv6[2] = (alt.alt_info6.dip2_h << 23) |
+ (alt.alt_info7.dip2_l << 9);
+ ipv6[1] = (alt.alt_info7.dip1_h << 23) |
+ (alt.alt_info8.dip1_l << 9);
+ ipv6[0] = (alt.alt_info8.dip0_h << 23) |
+ (alt.alt_info9.dip0_l << 9);
+ seq_printf(seq, "DIP = 0x%x:0x%x:0x%x:0x%x (IPv6)\n",
+ ipv6[3], ipv6[2], ipv6[1], ipv6[0]);
+ }
+
+ seq_printf(seq, "TCP SPORT = %d | TCP DPORT = %d\n",
+ (alt.alt_info9.sp_h << 7) | (alt.alt_info10.sp_l),
+ alt.alt_info10.dp);
+ }
+}
+
+int hw_lro_auto_tlb_read(struct seq_file *seq, void *v)
+{
+ int i;
+ u32 reg_val;
+ u32 reg_op1, reg_op2, reg_op3, reg_op4;
+ u32 agg_cnt, agg_time, age_time;
+
+ seq_puts(seq, "Usage of /proc/mtketh/hw_lro_auto_tlb:\n");
+ seq_puts(seq, "echo [function] [setting] > /proc/mtketh/hw_lro_auto_tlb\n");
+ seq_puts(seq, "Functions:\n");
+ seq_puts(seq, "[0] = hwlro_agg_cnt_ctrl\n");
+ seq_puts(seq, "[1] = hwlro_agg_time_ctrl\n");
+ seq_puts(seq, "[2] = hwlro_age_time_ctrl\n");
+ seq_puts(seq, "[3] = hwlro_threshold_ctrl\n");
+ seq_puts(seq, "[4] = hwlro_ring_enable_ctrl\n");
+ seq_puts(seq, "[5] = hwlro_stats_enable_ctrl\n\n");
+
+ if (MTK_HAS_CAPS(g_eth->soc->caps, MTK_NETSYS_V2)) {
+ for (i = 1; i <= 8; i++)
+ hw_lro_auto_tlb_dump_v2(seq, i);
+ } else {
+ /* Read valid entries of the auto-learn table */
+ mtk_w32(g_eth, 0, MTK_FE_ALT_CF8);
+ reg_val = mtk_r32(g_eth, MTK_FE_ALT_SEQ_CFC);
+
+ seq_printf(seq,
+ "HW LRO Auto-learn Table: (MTK_FE_ALT_SEQ_CFC=0x%x)\n",
+ reg_val);
+
+ for (i = 7; i >= 0; i--) {
+ if (reg_val & (1 << i))
+ hw_lro_auto_tlb_dump_v1(seq, i);
+ }
+ }
+
+ /* Read the agg_time/age_time/agg_cnt of LRO rings */
+ seq_puts(seq, "\nHW LRO Ring Settings\n");
+
+ for (i = 1; i <= MTK_HW_LRO_RING_NUM; i++) {
+ reg_op1 = mtk_r32(g_eth, MTK_LRO_CTRL_DW1_CFG(i));
+ reg_op2 = mtk_r32(g_eth, MTK_LRO_CTRL_DW2_CFG(i));
+ reg_op3 = mtk_r32(g_eth, MTK_LRO_CTRL_DW3_CFG(i));
+ reg_op4 = mtk_r32(g_eth, MTK_PDMA_LRO_CTRL_DW2);
+
+ agg_cnt =
+ ((reg_op3 & 0x3) << 6) |
+ ((reg_op2 >> MTK_LRO_RING_AGG_CNT_L_OFFSET) & 0x3f);
+ agg_time = (reg_op2 >> MTK_LRO_RING_AGG_TIME_OFFSET) & 0xffff;
+ age_time =
+ ((reg_op2 & 0x3f) << 10) |
+ ((reg_op1 >> MTK_LRO_RING_AGE_TIME_L_OFFSET) & 0x3ff);
+ seq_printf(seq,
+ "Ring[%d]: MAX_AGG_CNT=%d, AGG_TIME=%d, AGE_TIME=%d, Threshold=%d\n",
+ (MTK_HAS_CAPS(g_eth->soc->caps, MTK_NETSYS_V2))? i+3 : i,
+ agg_cnt, agg_time, age_time, reg_op4);
+ }
+
+ seq_puts(seq, "\n");
+
+ return 0;
+}
+
+static int hw_lro_auto_tlb_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, hw_lro_auto_tlb_read, NULL);
+}
+
+static const struct file_operations hw_lro_auto_tlb_fops = {
+ .owner = THIS_MODULE,
+ .open = hw_lro_auto_tlb_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .write = hw_lro_auto_tlb_write,
+ .release = single_release
+};
struct proc_dir_entry *proc_reg_dir;
static struct proc_dir_entry *proc_esw_cnt, *proc_dbg_regs;
@@ -826,6 +1434,21 @@
if (!proc_dbg_regs)
pr_notice("!! FAIL to create %s PROC !!\n", PROCREG_DBG_REGS);
+ if (g_eth->hwlro) {
+ proc_hw_lro_stats =
+ proc_create(PROCREG_HW_LRO_STATS, 0, proc_reg_dir,
+ &hw_lro_stats_fops);
+ if (!proc_hw_lro_stats)
+ pr_info("!! FAIL to create %s PROC !!\n", PROCREG_HW_LRO_STATS);
+
+ proc_hw_lro_auto_tlb =
+ proc_create(PROCREG_HW_LRO_AUTO_TLB, 0, proc_reg_dir,
+ &hw_lro_auto_tlb_fops);
+ if (!proc_hw_lro_auto_tlb)
+ pr_info("!! FAIL to create %s PROC !!\n",
+ PROCREG_HW_LRO_AUTO_TLB);
+ }
+
return 0;
}
@@ -844,5 +1467,13 @@
if (proc_dbg_regs)
remove_proc_entry(PROCREG_DBG_REGS, proc_reg_dir);
+
+ if (g_eth->hwlro) {
+ if (proc_hw_lro_stats)
+ remove_proc_entry(PROCREG_HW_LRO_STATS, proc_reg_dir);
+
+ if (proc_hw_lro_auto_tlb)
+ remove_proc_entry(PROCREG_HW_LRO_AUTO_TLB, proc_reg_dir);
+ }
}
diff --git a/target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_eth_dbg.h b/target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_eth_dbg.h
index 0e96a60..b44f93e 100755
--- a/target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_eth_dbg.h
+++ b/target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_eth_dbg.h
@@ -45,6 +45,196 @@
#define MTKETH_MII_WRITE_CL45 0x89FD
#define REG_ESW_MAX 0xFC
+#define PROCREG_ESW_CNT "esw_cnt"
+#define PROCREG_TXRING "tx_ring"
+#define PROCREG_RXRING "rx_ring"
+#define PROCREG_DIR "mtketh"
+#define PROCREG_DBG_REGS "dbg_regs"
+#define PROCREG_HW_LRO_STATS "hw_lro_stats"
+#define PROCREG_HW_LRO_AUTO_TLB "hw_lro_auto_tlb"
+
+/* HW LRO flush reason */
+#define MTK_HW_LRO_AGG_FLUSH (1)
+#define MTK_HW_LRO_AGE_FLUSH (2)
+#define MTK_HW_LRO_NOT_IN_SEQ_FLUSH (3)
+#define MTK_HW_LRO_TIMESTAMP_FLUSH (4)
+#define MTK_HW_LRO_NON_RULE_FLUSH (5)
+
+#define SET_PDMA_RXRING_MAX_AGG_CNT(eth, x, y) \
+{ \
+ u32 reg_val1 = mtk_r32(eth, MTK_LRO_CTRL_DW2_CFG(x)); \
+ u32 reg_val2 = mtk_r32(eth, MTK_LRO_CTRL_DW3_CFG(x)); \
+ reg_val1 &= ~MTK_LRO_RING_AGG_CNT_L_MASK; \
+ reg_val2 &= ~MTK_LRO_RING_AGG_CNT_H_MASK; \
+ reg_val1 |= ((y) & 0x3f) << MTK_LRO_RING_AGG_CNT_L_OFFSET; \
+ reg_val2 |= (((y) >> 6) & 0x03) << \
+ MTK_LRO_RING_AGG_CNT_H_OFFSET; \
+ mtk_w32(eth, reg_val1, MTK_LRO_CTRL_DW2_CFG(x)); \
+ mtk_w32(eth, reg_val2, MTK_LRO_CTRL_DW3_CFG(x)); \
+}
+
+#define SET_PDMA_RXRING_AGG_TIME(eth, x, y) \
+{ \
+ u32 reg_val = mtk_r32(eth, MTK_LRO_CTRL_DW2_CFG(x)); \
+ reg_val &= ~MTK_LRO_RING_AGG_TIME_MASK; \
+ reg_val |= ((y) & 0xffff) << MTK_LRO_RING_AGG_TIME_OFFSET; \
+ mtk_w32(eth, reg_val, MTK_LRO_CTRL_DW2_CFG(x)); \
+}
+
+#define SET_PDMA_RXRING_AGE_TIME(eth, x, y) \
+{ \
+ u32 reg_val1 = mtk_r32(eth, MTK_LRO_CTRL_DW1_CFG(x)); \
+ u32 reg_val2 = mtk_r32(eth, MTK_LRO_CTRL_DW2_CFG(x)); \
+ reg_val1 &= ~MTK_LRO_RING_AGE_TIME_L_MASK; \
+ reg_val2 &= ~MTK_LRO_RING_AGE_TIME_H_MASK; \
+ reg_val1 |= ((y) & 0x3ff) << MTK_LRO_RING_AGE_TIME_L_OFFSET; \
+ reg_val2 |= (((y) >> 10) & 0x03f) << \
+ MTK_LRO_RING_AGE_TIME_H_OFFSET; \
+ mtk_w32(eth, reg_val1, MTK_LRO_CTRL_DW1_CFG(x)); \
+ mtk_w32(eth, reg_val2, MTK_LRO_CTRL_DW2_CFG(x)); \
+}
+
+#define SET_PDMA_LRO_BW_THRESHOLD(eth, x) \
+{ \
+ u32 reg_val = mtk_r32(eth, MTK_PDMA_LRO_CTRL_DW2); \
+ reg_val = (x); \
+ mtk_w32(eth, reg_val, MTK_PDMA_LRO_CTRL_DW2); \
+}
+
+#define SET_PDMA_RXRING_VALID(eth, x, y) \
+{ \
+ u32 reg_val = mtk_r32(eth, MTK_LRO_CTRL_DW2_CFG(x)); \
+ reg_val &= ~(0x1 << MTK_RX_PORT_VALID_OFFSET); \
+ reg_val |= ((y) & 0x1) << MTK_RX_PORT_VALID_OFFSET; \
+ mtk_w32(eth, reg_val, MTK_LRO_CTRL_DW2_CFG(x)); \
+}
+
+struct mtk_lro_alt_v1_info0 {
+ u32 dtp : 16;
+ u32 stp : 16;
+};
+
+struct mtk_lro_alt_v1_info1 {
+ u32 sip0 : 32;
+};
+
+struct mtk_lro_alt_v1_info2 {
+ u32 sip1 : 32;
+};
+
+struct mtk_lro_alt_v1_info3 {
+ u32 sip2 : 32;
+};
+
+struct mtk_lro_alt_v1_info4 {
+ u32 sip3 : 32;
+};
+
+struct mtk_lro_alt_v1_info5 {
+ u32 vlan_vid0 : 32;
+};
+
+struct mtk_lro_alt_v1_info6 {
+ u32 vlan_vid1 : 16;
+ u32 vlan_vid_vld : 4;
+ u32 cnt : 12;
+};
+
+struct mtk_lro_alt_v1_info7 {
+ u32 dw_len : 32;
+};
+
+struct mtk_lro_alt_v1_info8 {
+ u32 dip_id : 2;
+ u32 ipv6 : 1;
+ u32 ipv4 : 1;
+ u32 resv : 27;
+ u32 valid : 1;
+};
+
+struct mtk_lro_alt_v1 {
+ struct mtk_lro_alt_v1_info0 alt_info0;
+ struct mtk_lro_alt_v1_info1 alt_info1;
+ struct mtk_lro_alt_v1_info2 alt_info2;
+ struct mtk_lro_alt_v1_info3 alt_info3;
+ struct mtk_lro_alt_v1_info4 alt_info4;
+ struct mtk_lro_alt_v1_info5 alt_info5;
+ struct mtk_lro_alt_v1_info6 alt_info6;
+ struct mtk_lro_alt_v1_info7 alt_info7;
+ struct mtk_lro_alt_v1_info8 alt_info8;
+};
+
+struct mtk_lro_alt_v2_info0 {
+ u32 v2_id_h:3;
+ u32 v1_id:12;
+ u32 v0_id:12;
+ u32 v3_valid:1;
+ u32 v2_valid:1;
+ u32 v1_valid:1;
+ u32 v0_valid:1;
+ u32 valid:1;
+};
+
+struct mtk_lro_alt_v2_info1 {
+ u32 sip3_h:9;
+ u32 v6_valid:1;
+ u32 v4_valid:1;
+ u32 v3_id:12;
+ u32 v2_id_l:9;
+};
+
+struct mtk_lro_alt_v2_info2 {
+ u32 sip2_h:9;
+ u32 sip3_l:23;
+};
+struct mtk_lro_alt_v2_info3 {
+ u32 sip1_h:9;
+ u32 sip2_l:23;
+};
+struct mtk_lro_alt_v2_info4 {
+ u32 sip0_h:9;
+ u32 sip1_l:23;
+};
+struct mtk_lro_alt_v2_info5 {
+ u32 dip3_h:9;
+ u32 sip0_l:23;
+};
+struct mtk_lro_alt_v2_info6 {
+ u32 dip2_h:9;
+ u32 dip3_l:23;
+};
+struct mtk_lro_alt_v2_info7 {
+ u32 dip1_h:9;
+ u32 dip2_l:23;
+};
+struct mtk_lro_alt_v2_info8 {
+ u32 dip0_h:9;
+ u32 dip1_l:23;
+};
+struct mtk_lro_alt_v2_info9 {
+ u32 sp_h:9;
+ u32 dip0_l:23;
+};
+struct mtk_lro_alt_v2_info10 {
+ u32 resv:9;
+ u32 dp:16;
+ u32 sp_l:7;
+};
+
+struct mtk_lro_alt_v2 {
+ struct mtk_lro_alt_v2_info0 alt_info0;
+ struct mtk_lro_alt_v2_info1 alt_info1;
+ struct mtk_lro_alt_v2_info2 alt_info2;
+ struct mtk_lro_alt_v2_info3 alt_info3;
+ struct mtk_lro_alt_v2_info4 alt_info4;
+ struct mtk_lro_alt_v2_info5 alt_info5;
+ struct mtk_lro_alt_v2_info6 alt_info6;
+ struct mtk_lro_alt_v2_info7 alt_info7;
+ struct mtk_lro_alt_v2_info8 alt_info8;
+ struct mtk_lro_alt_v2_info9 alt_info9;
+ struct mtk_lro_alt_v2_info10 alt_info10;
+};
+
struct mtk_esw_reg {
unsigned int off;
unsigned int val;
@@ -82,5 +272,7 @@
int mtketh_debugfs_init(struct mtk_eth *eth);
void mtketh_debugfs_exit(struct mtk_eth *eth);
int mtk_do_priv_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd);
+void hw_lro_stats_update(u32 ring_no, struct mtk_rx_dma *rxd);
+void hw_lro_flush_stats_update(u32 ring_no, struct mtk_rx_dma *rxd);
#endif /* MTK_ETH_DBG_H */
diff --git a/target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_eth_soc.c b/target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_eth_soc.c
index 236f18d..2dbf968 100755
--- a/target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_eth_soc.c
+++ b/target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_eth_soc.c
@@ -1316,6 +1316,9 @@
return ð->rx_ring[0];
for (i = 0; i < MTK_MAX_RX_RING_NUM; i++) {
+ if (!IS_NORMAL_RING(i) && !IS_HW_LRO_RING(i))
+ continue;
+
ring = ð->rx_ring[i];
idx = NEXT_DESP_IDX(ring->calc_idx, ring->dma_size);
if (ring->dma[idx].rxd2 & RX_DMA_DONE) {
@@ -1482,6 +1485,11 @@
__func__, skb_hnat_entry(skb), skb_hnat_sport(skb),
skb_hnat_reason(skb), skb_hnat_alg(skb));
#endif
+ if (mtk_hwlro_stats_ebl &&
+ IS_HW_LRO_RING(ring->ring_no) && eth->hwlro) {
+ hw_lro_stats_update(ring->ring_no, &trxd);
+ hw_lro_flush_stats_update(ring->ring_no, &trxd);
+ }
skb_record_rx_queue(skb, 0);
napi_gro_receive(napi, skb);
@@ -1906,6 +1914,7 @@
ring->crx_idx_reg = (rx_flag == MTK_RX_FLAGS_QDMA) ?
MTK_QRX_CRX_IDX_CFG(ring_no) :
MTK_PRX_CRX_IDX_CFG(ring_no);
+ ring->ring_no = ring_no;
/* make sure that all changes to the dma ring are flushed before we
* continue
*/
@@ -1961,6 +1970,7 @@
static int mtk_hwlro_rx_init(struct mtk_eth *eth)
{
int i;
+ u32 val;
u32 ring_ctrl_dw1 = 0, ring_ctrl_dw2 = 0, ring_ctrl_dw3 = 0;
u32 lro_ctrl_dw0 = 0, lro_ctrl_dw3 = 0;
@@ -1981,7 +1991,7 @@
ring_ctrl_dw2 |= MTK_RING_MAX_AGG_CNT_L;
ring_ctrl_dw3 |= MTK_RING_MAX_AGG_CNT_H;
- for (i = 1; i < MTK_MAX_RX_RING_NUM; i++) {
+ for (i = 1; i <= MTK_HW_LRO_RING_NUM; i++) {
mtk_w32(eth, ring_ctrl_dw1, MTK_LRO_CTRL_DW1_CFG(i));
mtk_w32(eth, ring_ctrl_dw2, MTK_LRO_CTRL_DW2_CFG(i));
mtk_w32(eth, ring_ctrl_dw3, MTK_LRO_CTRL_DW3_CFG(i));
@@ -1997,24 +2007,38 @@
mtk_w32(eth, MTK_HW_LRO_BW_THRE, MTK_PDMA_LRO_CTRL_DW2);
/* auto-learn score delta setting */
- mtk_w32(eth, MTK_HW_LRO_REPLACE_DELTA, MTK_PDMA_LRO_ALT_SCORE_DELTA);
+ mtk_w32(eth, MTK_HW_LRO_REPLACE_DELTA, MTK_LRO_ALT_SCORE_DELTA);
/* set refresh timer for altering flows to 1 sec. (unit: 20us) */
mtk_w32(eth, (MTK_HW_LRO_TIMER_UNIT << 16) | MTK_HW_LRO_REFRESH_TIME,
MTK_PDMA_LRO_ALT_REFRESH_TIMER);
- /* set HW LRO mode & the max aggregation count for rx packets */
- lro_ctrl_dw3 |= MTK_ADMA_MODE | (MTK_HW_LRO_MAX_AGG_CNT & 0xff);
-
/* the minimal remaining room of SDL0 in RXD for lro aggregation */
lro_ctrl_dw3 |= MTK_LRO_MIN_RXD_SDL;
+ if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2)) {
+ val = mtk_r32(eth, MTK_PDMA_RX_CFG);
+ mtk_w32(eth, val | (MTK_PDMA_LRO_SDL << MTK_RX_CFG_SDL_OFFSET),
+ MTK_PDMA_RX_CFG);
+
+ lro_ctrl_dw0 |= MTK_PDMA_LRO_SDL << MTK_CTRL_DW0_SDL_OFFSET;
+ } else {
+ /* set HW LRO mode & the max aggregation count for rx packets */
+ lro_ctrl_dw3 |= MTK_ADMA_MODE | (MTK_HW_LRO_MAX_AGG_CNT & 0xff);
+ }
+
/* enable HW LRO */
lro_ctrl_dw0 |= MTK_LRO_EN;
+ /* enable cpu reason black list */
+ lro_ctrl_dw0 |= MTK_LRO_CRSN_BNW;
+
mtk_w32(eth, lro_ctrl_dw3, MTK_PDMA_LRO_CTRL_DW3);
mtk_w32(eth, lro_ctrl_dw0, MTK_PDMA_LRO_CTRL_DW0);
+ /* no use PPE cpu reason */
+ mtk_w32(eth, 0xffffffff, MTK_PDMA_LRO_CTRL_DW1);
+
return 0;
}
@@ -2024,12 +2048,12 @@
u32 val;
/* relinquish lro rings, flush aggregated packets */
- mtk_w32(eth, MTK_LRO_RING_RELINQUISH_REQ, MTK_PDMA_LRO_CTRL_DW0);
+ mtk_w32(eth, MTK_LRO_RING_RELINGUISH_REQ, MTK_PDMA_LRO_CTRL_DW0);
/* wait for relinquishments done */
for (i = 0; i < 10; i++) {
val = mtk_r32(eth, MTK_PDMA_LRO_CTRL_DW0);
- if (val & MTK_LRO_RING_RELINQUISH_DONE) {
+ if (val & MTK_LRO_RING_RELINGUISH_DONE) {
msleep(20);
continue;
}
@@ -2037,7 +2061,7 @@
}
/* invalidate lro rings */
- for (i = 1; i < MTK_MAX_RX_RING_NUM; i++)
+ for (i = 1; i <= MTK_HW_LRO_RING_NUM; i++)
mtk_w32(eth, 0, MTK_LRO_CTRL_DW2_CFG(i));
/* disable HW LRO */
@@ -2048,6 +2072,9 @@
{
u32 reg_val;
+ if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2))
+ idx += 1;
+
reg_val = mtk_r32(eth, MTK_LRO_CTRL_DW2_CFG(idx));
/* invalidate the IP setting */
@@ -2063,6 +2090,9 @@
{
u32 reg_val;
+ if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2))
+ idx += 1;
+
reg_val = mtk_r32(eth, MTK_LRO_CTRL_DW2_CFG(idx));
/* invalidate the IP setting */
@@ -2289,7 +2319,8 @@
return err;
if (eth->hwlro) {
- for (i = 1; i < MTK_MAX_RX_RING_NUM; i++) {
+ i = (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2)) ? 4 : 1;
+ for (; i < MTK_MAX_RX_RING_NUM; i++) {
err = mtk_rx_alloc(eth, i, MTK_RX_FLAGS_HWLRO);
if (err)
return err;
@@ -2332,8 +2363,10 @@
if (eth->hwlro) {
mtk_hwlro_rx_uninit(eth);
- for (i = 1; i < MTK_MAX_RX_RING_NUM; i++)
- mtk_rx_clean(eth, ð->rx_ring[i],0);
+
+ i = (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2)) ? 4 : 1;
+ for (; i < MTK_MAX_RX_RING_NUM; i++)
+ mtk_rx_clean(eth, ð->rx_ring[i], 0);
}
kfree(eth->scratch_head);
@@ -2407,7 +2440,7 @@
static int mtk_start_dma(struct mtk_eth *eth)
{
u32 rx_2b_offset = (NET_IP_ALIGN == 2) ? MTK_RX_2B_OFFSET : 0;
- int err;
+ int val, err;
err = mtk_dma_init(eth);
if (err) {
@@ -2442,6 +2475,11 @@
MTK_PDMA_GLO_CFG);
}
+ if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2) && eth->hwlro) {
+ val = mtk_r32(eth, MTK_PDMA_GLO_CFG);
+ mtk_w32(eth, val | MTK_RX_DMA_LRO_EN, MTK_PDMA_GLO_CFG);
+ }
+
return 0;
}
@@ -2614,7 +2652,7 @@
static int mtk_hw_init(struct mtk_eth *eth)
{
- int i, val, ret;
+ int i, ret;
if (test_and_set_bit(MTK_HW_INIT, ð->state))
return 0;
diff --git a/target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_eth_soc.h b/target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_eth_soc.h
index 2bac3e6..d02b248 100755
--- a/target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_eth_soc.h
+++ b/target/linux/mediatek/files-5.4/drivers/net/ethernet/mediatek/mtk_eth_soc.h
@@ -43,7 +43,6 @@
#define MTK_HW_FEATURES_MT7628 (NETIF_F_SG | NETIF_F_RXCSUM)
#define NEXT_DESP_IDX(X, Y) (((X) + 1) & ((Y) - 1))
-#define MTK_MAX_RX_RING_NUM 4
#define MTK_HW_LRO_DMA_SIZE 8
#define MTK_MAX_LRO_RX_LENGTH (4096 * 3)
@@ -72,6 +71,11 @@
/* Frame Engine Interrupt Grouping Register */
#define MTK_FE_INT_GRP 0x20
+/* Frame Engine LRO auto-learn table info */
+#define MTK_FE_ALT_CF8 0x300
+#define MTK_FE_ALT_SGL_CFC 0x304
+#define MTK_FE_ALT_SEQ_CFC 0x308
+
/* CDMP Ingress Control Register */
#define MTK_CDMQ_IG_CTRL 0x1400
#define MTK_CDMQ_STAG_EN BIT(0)
@@ -132,12 +136,41 @@
#define MTK_PRX_CRX_IDX_CFG(x) (MTK_PRX_CRX_IDX0 + (x * 0x10))
/* PDMA HW LRO Control Registers */
-#define MTK_PDMA_LRO_CTRL_DW0 (PDMA_BASE + 0x180)
-#define MTK_LRO_EN BIT(0)
+#define BITS(m, n) (~(BIT(m) - 1) & ((BIT(n) - 1) | BIT(n)))
+#if defined(CONFIG_MEDIATEK_NETSYS_V2)
+#define MTK_MAX_RX_RING_NUM (8)
+#define MTK_HW_LRO_RING_NUM (4)
+#define IS_HW_LRO_RING(ring_no) (((ring_no) > 3) && ((ring_no) < 8))
+#define MTK_PDMA_LRO_CTRL_DW0 (PDMA_BASE + 0x408)
+#define MTK_LRO_ALT_SCORE_DELTA (PDMA_BASE + 0x41c)
+#define MTK_LRO_RX_RING0_CTRL_DW1 (PDMA_BASE + 0x438)
+#define MTK_LRO_RX_RING0_CTRL_DW2 (PDMA_BASE + 0x43c)
+#define MTK_LRO_RX_RING0_CTRL_DW3 (PDMA_BASE + 0x440)
+#define MTK_L3_CKS_UPD_EN BIT(19)
+#define MTK_LRO_CRSN_BNW BIT(22)
+#define MTK_LRO_RING_RELINGUISH_REQ (0xf << 24)
+#define MTK_LRO_RING_RELINGUISH_DONE (0xf << 28)
+#else
+#define MTK_MAX_RX_RING_NUM (4)
+#define MTK_HW_LRO_RING_NUM (3)
+#define IS_HW_LRO_RING(ring_no) (((ring_no) > 0) && ((ring_no) < 4))
+#define MTK_PDMA_LRO_CTRL_DW0 (PDMA_BASE + 0x180)
+#define MTK_LRO_ALT_SCORE_DELTA (PDMA_BASE + 0x24c)
+#define MTK_LRO_RX_RING0_CTRL_DW1 (PDMA_BASE + 0x328)
+#define MTK_LRO_RX_RING0_CTRL_DW2 (PDMA_BASE + 0x32c)
+#define MTK_LRO_RX_RING0_CTRL_DW3 (PDMA_BASE + 0x330)
+#define MTK_LRO_CRSN_BNW BIT(6)
#define MTK_L3_CKS_UPD_EN BIT(7)
+#define MTK_LRO_RING_RELINGUISH_REQ (0x7 << 26)
+#define MTK_LRO_RING_RELINGUISH_DONE (0x7 << 29)
+#endif
+
+#define IS_NORMAL_RING(ring_no) ((ring_no) == 0)
+#define MTK_LRO_EN BIT(0)
#define MTK_LRO_ALT_PKT_CNT_MODE BIT(21)
-#define MTK_LRO_RING_RELINQUISH_REQ (0x7 << 26)
-#define MTK_LRO_RING_RELINQUISH_DONE (0x7 << 29)
+#define MTK_LRO_L4_CTRL_PSH_EN BIT(23)
+#define MTK_CTRL_DW0_SDL_OFFSET (3)
+#define MTK_CTRL_DW0_SDL_MASK BITS(3, 18)
#define MTK_PDMA_LRO_CTRL_DW1 (MTK_PDMA_LRO_CTRL_DW0 + 0x04)
#define MTK_PDMA_LRO_CTRL_DW2 (MTK_PDMA_LRO_CTRL_DW0 + 0x08)
@@ -147,9 +180,15 @@
/* PDMA Global Configuration Register */
#define MTK_PDMA_GLO_CFG (PDMA_BASE + 0x204)
+#define MTK_RX_DMA_LRO_EN BIT(8)
#define MTK_MULTI_EN BIT(10)
#define MTK_PDMA_SIZE_8DWORDS (1 << 4)
+/* PDMA Global Configuration Register */
+#define MTK_PDMA_RX_CFG (PDMA_BASE + 0x210)
+#define MTK_PDMA_LRO_SDL (0x3000)
+#define MTK_RX_CFG_SDL_OFFSET (16)
+
/* PDMA Reset Index Register */
#define MTK_PDMA_RST_IDX (PDMA_BASE + 0x208)
#define MTK_PST_DRX_IDX0 BIT(16)
@@ -171,22 +210,27 @@
/* PDMA Interrupt Mask Register */
#define MTK_PDMA_INT_MASK (PDMA_BASE + 0x228)
-/* PDMA HW LRO Alter Flow Delta Register */
-#define MTK_PDMA_LRO_ALT_SCORE_DELTA (PDMA_BASE + 0x24c)
-
/* PDMA Interrupt grouping registers */
#define MTK_PDMA_INT_GRP1 (PDMA_BASE + 0x250)
#define MTK_PDMA_INT_GRP2 (PDMA_BASE + 0x254)
/* PDMA HW LRO IP Setting Registers */
+#if defined(CONFIG_MEDIATEK_NETSYS_V2)
+#define MTK_LRO_RX_RING0_DIP_DW0 (PDMA_BASE + 0x414)
+#else
#define MTK_LRO_RX_RING0_DIP_DW0 (PDMA_BASE + 0x304)
+#endif
#define MTK_LRO_DIP_DW0_CFG(x) (MTK_LRO_RX_RING0_DIP_DW0 + (x * 0x40))
#define MTK_RING_MYIP_VLD BIT(9)
+/* PDMA HW LRO ALT Debug Registers */
+#define MTK_LRO_ALT_DBG (PDMA_BASE + 0x440)
+#define MTK_LRO_ALT_INDEX_OFFSET (8)
+
+/* PDMA HW LRO ALT Data Registers */
+#define MTK_LRO_ALT_DBG_DATA (PDMA_BASE + 0x444)
+
/* PDMA HW LRO Ring Control Registers */
-#define MTK_LRO_RX_RING0_CTRL_DW1 (PDMA_BASE + 0x328)
-#define MTK_LRO_RX_RING0_CTRL_DW2 (PDMA_BASE + 0x32c)
-#define MTK_LRO_RX_RING0_CTRL_DW3 (PDMA_BASE + 0x330)
#define MTK_LRO_CTRL_DW1_CFG(x) (MTK_LRO_RX_RING0_CTRL_DW1 + (x * 0x40))
#define MTK_LRO_CTRL_DW2_CFG(x) (MTK_LRO_RX_RING0_CTRL_DW2 + (x * 0x40))
#define MTK_LRO_CTRL_DW3_CFG(x) (MTK_LRO_RX_RING0_CTRL_DW3 + (x * 0x40))
@@ -198,6 +242,35 @@
#define MTK_RING_MAX_AGG_CNT_L ((MTK_HW_LRO_MAX_AGG_CNT & 0x3f) << 26)
#define MTK_RING_MAX_AGG_CNT_H ((MTK_HW_LRO_MAX_AGG_CNT >> 6) & 0x3)
+/* LRO_RX_RING_CTRL_DW masks */
+#define MTK_LRO_RING_AGG_TIME_MASK BITS(10, 25)
+#define MTK_LRO_RING_AGG_CNT_L_MASK BITS(26, 31)
+#define MTK_LRO_RING_AGG_CNT_H_MASK BITS(0, 1)
+#define MTK_LRO_RING_AGE_TIME_L_MASK BITS(22, 31)
+#define MTK_LRO_RING_AGE_TIME_H_MASK BITS(0, 5)
+
+/* LRO_RX_RING_CTRL_DW0 offsets */
+#define MTK_RX_IPV6_FORCE_OFFSET (0)
+#define MTK_RX_IPV4_FORCE_OFFSET (1)
+
+/* LRO_RX_RING_CTRL_DW1 offsets */
+#define MTK_LRO_RING_AGE_TIME_L_OFFSET (22)
+
+/* LRO_RX_RING_CTRL_DW2 offsets */
+#define MTK_LRO_RING_AGE_TIME_H_OFFSET (0)
+#define MTK_RX_MODE_OFFSET (6)
+#define MTK_RX_PORT_VALID_OFFSET (8)
+#define MTK_RX_MYIP_VALID_OFFSET (9)
+#define MTK_LRO_RING_AGG_TIME_OFFSET (10)
+#define MTK_LRO_RING_AGG_CNT_L_OFFSET (26)
+
+/* LRO_RX_RING_CTRL_DW3 offsets */
+#define MTK_LRO_RING_AGG_CNT_H_OFFSET (0)
+
+/* LRO_RX_RING_STP_DTP_DW offsets */
+#define MTK_RX_TCP_DEST_PORT_OFFSET (0)
+#define MTK_RX_TCP_SRC_PORT_OFFSET (16)
+
/* QDMA TX Queue Configuration Registers */
#define MTK_QTX_CFG(x) (QDMA_BASE + (x * 0x10))
#define QDMA_RES_THRES 4
@@ -379,6 +452,8 @@
#define RX_DMA_LSO BIT(30)
#define RX_DMA_PLEN0(_x) (((_x) & MTK_RX_DMA_BUF_LEN) << MTK_RX_DMA_BUF_SHIFT)
#define RX_DMA_GET_PLEN0(_x) (((_x) >> MTK_RX_DMA_BUF_SHIFT) & MTK_RX_DMA_BUF_LEN)
+#define RX_DMA_GET_AGG_CNT(_x) (((_x) >> 2) & 0xff)
+#define RX_DMA_GET_REV(_x) (((_x) >> 10) & 0x1f)
#define RX_DMA_VTAG BIT(15)
/* QDMA descriptor rxd3 */
@@ -402,6 +477,10 @@
#define RX_DMA_TCI_V2(_x) (((_x) >> 1) & (VLAN_PRIO_MASK | VLAN_VID_MASK))
#define RX_DMA_VPID_V2(x3, x4) ((((x3) & 1) << 15) | (((x4) >> 17) & 0x7fff))
+/* PDMA V2 descriptor rxd6 */
+#define RX_DMA_GET_FLUSH_RSN_V2(_x) ((_x) & 0x7)
+#define RX_DMA_GET_AGG_CNT_V2(_x) (((_x) >> 16) & 0xff)
+
/* PHY Indirect Access Control registers */
#define MTK_PHY_IAC 0x10004
#define PHY_IAC_ACCESS BIT(31)
@@ -778,6 +857,7 @@
* @frag_size: How big can each fragment be
* @buf_size: The size of each packet buffer
* @calc_idx: The current head of ring
+ * @ring_no: The index of ring
*/
struct mtk_rx_ring {
struct mtk_rx_dma *dma;
@@ -789,6 +869,7 @@
bool calc_idx_update;
u16 calc_idx;
u32 crx_idx_reg;
+ u32 ring_no;
};
enum mkt_eth_capabilities {
@@ -1057,6 +1138,7 @@
/* the struct describing the SoC. these are declared in the soc_xyz.c files */
extern const struct of_device_id of_mtk_match[];
+extern u32 mtk_hwlro_stats_ebl;
/* read the hardware status register */
void mtk_stats_update_mac(struct mtk_mac *mac);