[][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/ra_dbg_hwlro.c b/target/linux/mediatek/files-5.4/drivers/net/ethernet/raeth/ra_dbg_hwlro.c
new file mode 100644
index 0000000..1ecad66
--- /dev/null
+++ b/target/linux/mediatek/files-5.4/drivers/net/ethernet/raeth/ra_dbg_hwlro.c
@@ -0,0 +1,629 @@
+/* Copyright 2016 MediaTek Inc.
+ * Author: Nelson Chang <nelson.chang@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 "raether_hwlro.h"
+#include "ra_dbg_proc.h"
+
+/* HW LRO proc */
+#define HW_LRO_RING_NUM 3
+#define MAX_HW_LRO_AGGR 64
+
+typedef int (*HWLRO_DBG_FUNC) (int par1, int par2);
+unsigned int hw_lro_agg_num_cnt[HW_LRO_RING_NUM][MAX_HW_LRO_AGGR + 1];
+unsigned int hw_lro_agg_size_cnt[HW_LRO_RING_NUM][16];
+unsigned int hw_lro_tot_agg_cnt[HW_LRO_RING_NUM];
+unsigned int hw_lro_tot_flush_cnt[HW_LRO_RING_NUM];
+
+/* HW LRO flush reason proc */
+#define HW_LRO_AGG_FLUSH (1)
+#define HW_LRO_AGE_FLUSH (2)
+#define HW_LRO_NOT_IN_SEQ_FLUSH (3)
+#define HW_LRO_TIMESTAMP_FLUSH (4)
+#define HW_LRO_NON_RULE_FLUSH (5)
+
+unsigned int hw_lro_agg_flush_cnt[HW_LRO_RING_NUM];
+unsigned int hw_lro_age_flush_cnt[HW_LRO_RING_NUM];
+unsigned int hw_lro_seq_flush_cnt[HW_LRO_RING_NUM];
+unsigned int hw_lro_timestamp_flush_cnt[HW_LRO_RING_NUM];
+unsigned int hw_lro_norule_flush_cnt[HW_LRO_RING_NUM];
+
+static struct proc_dir_entry *proc_rx_ring1, *proc_rx_ring2, *proc_rx_ring3;
+static struct proc_dir_entry *proc_hw_lro_stats, *proc_hw_lro_auto_tlb;
+
+int rx_lro_ring_read(struct seq_file *seq, void *v,
+ struct PDMA_rxdesc *rx_ring_p)
+{
+ struct PDMA_rxdesc *rx_ring;
+ int i = 0;
+
+ rx_ring =
+ kmalloc(sizeof(struct PDMA_rxdesc) * NUM_LRO_RX_DESC, GFP_KERNEL);
+ if (!rx_ring) {
+ seq_puts(seq, " allocate temp rx_ring fail.\n");
+ return 0;
+ }
+
+ for (i = 0; i < NUM_LRO_RX_DESC; i++)
+ memcpy(&rx_ring[i], &rx_ring_p[i], sizeof(struct PDMA_rxdesc));
+
+ for (i = 0; i < NUM_LRO_RX_DESC; i++) {
+ seq_printf(seq, "%d: %08x %08x %08x %08x\n", i,
+ *(int *)&rx_ring[i].rxd_info1,
+ *(int *)&rx_ring[i].rxd_info2,
+ *(int *)&rx_ring[i].rxd_info3,
+ *(int *)&rx_ring[i].rxd_info4);
+ }
+
+ kfree(rx_ring);
+ return 0;
+}
+
+int rx_ring1_read(struct seq_file *seq, void *v)
+{
+ struct END_DEVICE *ei_local = netdev_priv(dev_raether);
+
+ rx_lro_ring_read(seq, v, ei_local->rx_ring[1]);
+
+ return 0;
+}
+
+int rx_ring2_read(struct seq_file *seq, void *v)
+{
+ struct END_DEVICE *ei_local = netdev_priv(dev_raether);
+
+ rx_lro_ring_read(seq, v, ei_local->rx_ring[2]);
+
+ return 0;
+}
+
+int rx_ring3_read(struct seq_file *seq, void *v)
+{
+ struct END_DEVICE *ei_local = netdev_priv(dev_raether);
+
+ rx_lro_ring_read(seq, v, ei_local->rx_ring[3]);
+
+ return 0;
+}
+
+static int rx_ring1_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, rx_ring1_read, NULL);
+}
+
+static int rx_ring2_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, rx_ring2_read, NULL);
+}
+
+static int rx_ring3_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, rx_ring3_read, NULL);
+}
+
+static const struct file_operations rx_ring1_fops = {
+ .owner = THIS_MODULE,
+ .open = rx_ring1_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release
+};
+
+static const struct file_operations rx_ring2_fops = {
+ .owner = THIS_MODULE,
+ .open = rx_ring2_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release
+};
+
+static const struct file_operations rx_ring3_fops = {
+ .owner = THIS_MODULE,
+ .open = rx_ring3_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release
+};
+
+static int hw_lro_len_update(unsigned int agg_size)
+{
+ int len_idx;
+
+ if (agg_size > 65000)
+ len_idx = 13;
+ else if (agg_size > 60000)
+ len_idx = 12;
+ else if (agg_size > 55000)
+ len_idx = 11;
+ else if (agg_size > 50000)
+ len_idx = 10;
+ else if (agg_size > 45000)
+ len_idx = 9;
+ else if (agg_size > 40000)
+ len_idx = 8;
+ else if (agg_size > 35000)
+ len_idx = 7;
+ else if (agg_size > 30000)
+ len_idx = 6;
+ else if (agg_size > 25000)
+ len_idx = 5;
+ else if (agg_size > 20000)
+ len_idx = 4;
+ else if (agg_size > 15000)
+ len_idx = 3;
+ else if (agg_size > 10000)
+ len_idx = 2;
+ else if (agg_size > 5000)
+ len_idx = 1;
+ else
+ len_idx = 0;
+
+ return len_idx;
+}
+
+void hw_lro_stats_update(unsigned int ring_num, struct PDMA_rxdesc *rx_ring)
+{
+ unsigned int agg_cnt = rx_ring->rxd_info2.LRO_AGG_CNT;
+ unsigned int agg_size = (rx_ring->rxd_info2.PLEN1 << 14) |
+ rx_ring->rxd_info2.PLEN0;
+
+ if ((ring_num > 0) && (ring_num < 4)) {
+ hw_lro_agg_size_cnt[ring_num - 1]
+ [hw_lro_len_update(agg_size)]++;
+ hw_lro_agg_num_cnt[ring_num - 1][agg_cnt]++;
+ hw_lro_tot_flush_cnt[ring_num - 1]++;
+ hw_lro_tot_agg_cnt[ring_num - 1] += agg_cnt;
+ }
+}
+
+void hw_lro_flush_stats_update(unsigned int ring_num,
+ struct PDMA_rxdesc *rx_ring)
+{
+ unsigned int flush_reason = rx_ring->rxd_info2.REV;
+
+ if ((ring_num > 0) && (ring_num < 4)) {
+ if ((flush_reason & 0x7) == HW_LRO_AGG_FLUSH)
+ hw_lro_agg_flush_cnt[ring_num - 1]++;
+ else if ((flush_reason & 0x7) == HW_LRO_AGE_FLUSH)
+ hw_lro_age_flush_cnt[ring_num - 1]++;
+ else if ((flush_reason & 0x7) == HW_LRO_NOT_IN_SEQ_FLUSH)
+ hw_lro_seq_flush_cnt[ring_num - 1]++;
+ else if ((flush_reason & 0x7) == HW_LRO_TIMESTAMP_FLUSH)
+ hw_lro_timestamp_flush_cnt[ring_num - 1]++;
+ else if ((flush_reason & 0x7) == HW_LRO_NON_RULE_FLUSH)
+ hw_lro_norule_flush_cnt[ring_num - 1]++;
+ }
+}
+EXPORT_SYMBOL(hw_lro_flush_stats_update);
+
+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(struct seq_file *seq, void *v)
+{
+ int i;
+ struct END_DEVICE *ei_local = netdev_priv(dev_raether);
+
+ seq_puts(seq, "HW LRO statistic dump:\n");
+
+ /* Agg number count */
+ seq_puts(seq, "Cnt: RING1 | RING2 | RING3 | Total\n");
+ for (i = 0; i <= MAX_HW_LRO_AGGR; 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]);
+ }
+
+ /* CONFIG_RAETH_HW_LRO_REASON_DBG */
+ if (ei_local->features & FE_HW_LRO_DBG) {
+ 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;
+}
+
+static int hw_lro_stats_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, hw_lro_stats_read, 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 par1, int par2)
+{
+ SET_PDMA_RXRING_MAX_AGG_CNT(ADMA_RX_RING1, par2);
+ SET_PDMA_RXRING_MAX_AGG_CNT(ADMA_RX_RING2, par2);
+ SET_PDMA_RXRING_MAX_AGG_CNT(ADMA_RX_RING3, par2);
+ return 0;
+}
+
+int hwlro_agg_time_ctrl(int par1, int par2)
+{
+ SET_PDMA_RXRING_AGG_TIME(ADMA_RX_RING1, par2);
+ SET_PDMA_RXRING_AGG_TIME(ADMA_RX_RING2, par2);
+ SET_PDMA_RXRING_AGG_TIME(ADMA_RX_RING3, par2);
+ return 0;
+}
+
+int hwlro_age_time_ctrl(int par1, int par2)
+{
+ SET_PDMA_RXRING_AGE_TIME(ADMA_RX_RING1, par2);
+ SET_PDMA_RXRING_AGE_TIME(ADMA_RX_RING2, par2);
+ SET_PDMA_RXRING_AGE_TIME(ADMA_RX_RING3, par2);
+ return 0;
+}
+
+int hwlro_threshold_ctrl(int par1, int par2)
+{
+ /* bandwidth threshold setting */
+ SET_PDMA_LRO_BW_THRESHOLD(par2);
+ return 0;
+}
+
+int hwlro_ring_enable_ctrl(int par1, int par2)
+{
+ if (!par2) {
+ pr_info("[hwlro_ring_enable_ctrl]Disable HW LRO rings\n");
+ SET_PDMA_RXRING_VALID(ADMA_RX_RING0, 0);
+ SET_PDMA_RXRING_VALID(ADMA_RX_RING1, 0);
+ SET_PDMA_RXRING_VALID(ADMA_RX_RING2, 0);
+ SET_PDMA_RXRING_VALID(ADMA_RX_RING3, 0);
+ } else {
+ pr_info("[hwlro_ring_enable_ctrl]Enable HW LRO rings\n");
+ SET_PDMA_RXRING_VALID(ADMA_RX_RING0, 1);
+ SET_PDMA_RXRING_VALID(ADMA_RX_RING1, 1);
+ SET_PDMA_RXRING_VALID(ADMA_RX_RING2, 1);
+ SET_PDMA_RXRING_VALID(ADMA_RX_RING3, 1);
+ }
+
+ return 0;
+}
+
+static const HWLRO_DBG_FUNC hw_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,
+};
+
+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;
+ int len = count;
+ long x = 0, y = 0;
+ char *p_token = NULL;
+ char *p_delimiter = " \t";
+ int ret;
+
+ pr_info("[hw_lro_auto_tlb_write]write parameter len = %d\n\r",
+ (int)len);
+ 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';
+ pr_info("[hw_lro_auto_tlb_write]write parameter data = %s\n\r", buf);
+
+ 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);
+ pr_info("y = %ld\n\r", y);
+ }
+
+ if (hw_lro_dbg_func[x] &&
+ (ARRAY_SIZE(hw_lro_dbg_func) > x)) {
+ (*hw_lro_dbg_func[x]) (x, y);
+ }
+
+ return count;
+}
+
+void hw_lro_auto_tlb_dump(struct seq_file *seq, unsigned int index)
+{
+ int i;
+ struct PDMA_LRO_AUTO_TLB_INFO pdma_lro_auto_tlb;
+ unsigned int tlb_info[9];
+ unsigned int dw_len, cnt, priority;
+ unsigned int entry;
+
+ if (index > 4)
+ index = index - 1;
+ entry = (index * 9) + 1;
+
+ /* read valid entries of the auto-learn table */
+ sys_reg_write(PDMA_FE_ALT_CF8, entry);
+
+ /* seq_printf(seq, "\nEntry = %d\n", entry); */
+ for (i = 0; i < 9; i++) {
+ tlb_info[i] = sys_reg_read(PDMA_FE_ALT_SEQ_CFC);
+ /* seq_printf(seq, "tlb_info[%d] = 0x%x\n", i, tlb_info[i]); */
+ }
+ memcpy(&pdma_lro_auto_tlb, tlb_info,
+ sizeof(struct PDMA_LRO_AUTO_TLB_INFO));
+
+ dw_len = pdma_lro_auto_tlb.auto_tlb_info7.DW_LEN;
+ cnt = pdma_lro_auto_tlb.auto_tlb_info6.CNT;
+
+ if (sys_reg_read(ADMA_LRO_CTRL_DW0) & PDMA_LRO_ALT_SCORE_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 (pdma_lro_auto_tlb.auto_tlb_info8.IPV4) {
+ seq_printf(seq, "SIP = 0x%x:0x%x:0x%x:0x%x (IPv4)\n",
+ pdma_lro_auto_tlb.auto_tlb_info4.SIP3,
+ pdma_lro_auto_tlb.auto_tlb_info3.SIP2,
+ pdma_lro_auto_tlb.auto_tlb_info2.SIP1,
+ pdma_lro_auto_tlb.auto_tlb_info1.SIP0);
+ } else {
+ seq_printf(seq, "SIP = 0x%x:0x%x:0x%x:0x%x (IPv6)\n",
+ pdma_lro_auto_tlb.auto_tlb_info4.SIP3,
+ pdma_lro_auto_tlb.auto_tlb_info3.SIP2,
+ pdma_lro_auto_tlb.auto_tlb_info2.SIP1,
+ pdma_lro_auto_tlb.auto_tlb_info1.SIP0);
+ }
+ seq_printf(seq, "DIP_ID = %d\n",
+ pdma_lro_auto_tlb.auto_tlb_info8.DIP_ID);
+ seq_printf(seq, "TCP SPORT = %d | TCP DPORT = %d\n",
+ pdma_lro_auto_tlb.auto_tlb_info0.STP,
+ pdma_lro_auto_tlb.auto_tlb_info0.DTP);
+ seq_printf(seq, "VLAN_VID_VLD = %d\n",
+ pdma_lro_auto_tlb.auto_tlb_info6.VLAN_VID_VLD);
+ seq_printf(seq, "VLAN1 = %d | VLAN2 = %d | VLAN3 = %d | VLAN4 =%d\n",
+ (pdma_lro_auto_tlb.auto_tlb_info5.VLAN_VID0 & 0xfff),
+ ((pdma_lro_auto_tlb.auto_tlb_info5.VLAN_VID0 >> 12) & 0xfff),
+ ((pdma_lro_auto_tlb.auto_tlb_info6.VLAN_VID1 << 8) |
+ ((pdma_lro_auto_tlb.auto_tlb_info5.VLAN_VID0 >> 24)
+ & 0xfff)),
+ ((pdma_lro_auto_tlb.auto_tlb_info6.VLAN_VID1 >> 4) & 0xfff));
+ seq_printf(seq, "TPUT = %d | FREQ = %d\n", dw_len, cnt);
+ seq_printf(seq, "PRIORITY = %d\n", priority);
+}
+
+int hw_lro_auto_tlb_read(struct seq_file *seq, void *v)
+{
+ int i;
+ unsigned int reg_val;
+ unsigned int reg_op1, reg_op2, reg_op3, reg_op4;
+ unsigned int agg_cnt, agg_time, age_time;
+
+ seq_puts(seq, "Usage of /proc/mt76xx/hw_lro_auto_tlb:\n");
+ seq_puts(seq, "echo [function] [setting] > /proc/mt76xx/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\n");
+
+ /* Read valid entries of the auto-learn table */
+ sys_reg_write(PDMA_FE_ALT_CF8, 0);
+ reg_val = sys_reg_read(PDMA_FE_ALT_SEQ_CFC);
+
+ seq_printf(seq,
+ "HW LRO Auto-learn Table: (PDMA_LRO_ALT_CFC_RSEQ_DBG=0x%x)\n",
+ reg_val);
+
+ for (i = 7; i >= 0; i--) {
+ if (reg_val & (1 << i))
+ hw_lro_auto_tlb_dump(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 <= 3; i++) {
+ reg_op1 = sys_reg_read(LRO_RX_RING0_CTRL_DW1 + (i * 0x40));
+ reg_op2 = sys_reg_read(LRO_RX_RING0_CTRL_DW2 + (i * 0x40));
+ reg_op3 = sys_reg_read(LRO_RX_RING0_CTRL_DW3 + (i * 0x40));
+ reg_op4 = sys_reg_read(ADMA_LRO_CTRL_DW2);
+ agg_cnt =
+ ((reg_op3 & 0x03) << PDMA_LRO_AGG_CNT_H_OFFSET) |
+ ((reg_op2 >> PDMA_LRO_RING_AGG_CNT1_OFFSET) & 0x3f);
+ agg_time = (reg_op2 >> PDMA_LRO_RING_AGG_OFFSET) & 0xffff;
+ age_time =
+ ((reg_op2 & 0x03f) << PDMA_LRO_AGE_H_OFFSET) |
+ ((reg_op1 >> PDMA_LRO_RING_AGE1_OFFSET) & 0x3ff);
+ seq_printf(seq,
+ "Ring[%d]: MAX_AGG_CNT=%d, AGG_TIME=%d, AGE_TIME=%d, Threshold=%d\n",
+ 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
+};
+
+int hwlro_debug_proc_init(struct proc_dir_entry *proc_reg_dir)
+{
+ proc_rx_ring1 =
+ proc_create(PROCREG_RXRING1, 0, proc_reg_dir, &rx_ring1_fops);
+ if (!proc_rx_ring1)
+ pr_info("!! FAIL to create %s PROC !!\n", PROCREG_RXRING1);
+
+ proc_rx_ring2 =
+ proc_create(PROCREG_RXRING2, 0, proc_reg_dir, &rx_ring2_fops);
+ if (!proc_rx_ring2)
+ pr_info("!! FAIL to create %s PROC !!\n", PROCREG_RXRING2);
+
+ proc_rx_ring3 =
+ proc_create(PROCREG_RXRING3, 0, proc_reg_dir, &rx_ring3_fops);
+ if (!proc_rx_ring3)
+ pr_info("!! FAIL to create %s PROC !!\n", PROCREG_RXRING3);
+
+ 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;
+}
+EXPORT_SYMBOL(hwlro_debug_proc_init);
+
+void hwlro_debug_proc_exit(struct proc_dir_entry *proc_reg_dir)
+{
+ if (proc_rx_ring1)
+ remove_proc_entry(PROCREG_RXRING1, proc_reg_dir);
+ if (proc_rx_ring2)
+ remove_proc_entry(PROCREG_RXRING2, proc_reg_dir);
+ if (proc_rx_ring3)
+ remove_proc_entry(PROCREG_RXRING3, proc_reg_dir);
+ 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);
+}
+EXPORT_SYMBOL(hwlro_debug_proc_exit);