mirror of
https://github.com/openwrt/openwrt.git
synced 2024-12-23 15:32:33 +00:00
f0cc5f6c0a
Backport upstream changes to initialize GDM settings and reset PPE Allow GMAC to recognize the special tag to fix PPE packet parsing Improve GRO performance by passing PPE L4 hash as skb hash Signed-off-by: Felix Fietkau <nbd@nbd.name>
1219 lines
35 KiB
Diff
1219 lines
35 KiB
Diff
--- a/drivers/net/ethernet/mediatek/Kconfig
|
|
+++ b/drivers/net/ethernet/mediatek/Kconfig
|
|
@@ -15,4 +15,8 @@ config NET_MEDIATEK_SOC
|
|
This driver supports the gigabit ethernet MACs in the
|
|
MediaTek SoC family.
|
|
|
|
+config NET_MEDIATEK_OFFLOAD
|
|
+ def_bool NET_MEDIATEK_SOC
|
|
+ depends on NET_MEDIATEK_SOC
|
|
+
|
|
endif #NET_VENDOR_MEDIATEK
|
|
--- a/drivers/net/ethernet/mediatek/Makefile
|
|
+++ b/drivers/net/ethernet/mediatek/Makefile
|
|
@@ -5,3 +5,4 @@
|
|
|
|
obj-$(CONFIG_NET_MEDIATEK_SOC) += mtk_eth.o
|
|
mtk_eth-y := mtk_eth_soc.o mtk_sgmii.o mtk_eth_path.o
|
|
+mtk_eth-$(CONFIG_NET_MEDIATEK_OFFLOAD) += mtk_offload.o mtk_debugfs.o
|
|
--- /dev/null
|
|
+++ b/drivers/net/ethernet/mediatek/mtk_debugfs.c
|
|
@@ -0,0 +1,117 @@
|
|
+/* This program is free software; you can redistribute it and/or modify
|
|
+ * it under the terms of the GNU General Public License as published by
|
|
+ * the Free Software Foundation; version 2 of the License
|
|
+ *
|
|
+ * 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.
|
|
+ *
|
|
+ * Copyright (C) 2014-2016 Sean Wang <sean.wang@mediatek.com>
|
|
+ * Copyright (C) 2016-2017 John Crispin <blogic@openwrt.org>
|
|
+ */
|
|
+
|
|
+#include "mtk_offload.h"
|
|
+
|
|
+static const char *mtk_foe_entry_state_str[] = {
|
|
+ "INVALID",
|
|
+ "UNBIND",
|
|
+ "BIND",
|
|
+ "FIN"
|
|
+};
|
|
+
|
|
+static const char *mtk_foe_packet_type_str[] = {
|
|
+ "IPV4_HNAPT",
|
|
+ "IPV4_HNAT",
|
|
+ "IPV6_1T_ROUTE",
|
|
+ "IPV4_DSLITE",
|
|
+ "IPV6_3T_ROUTE",
|
|
+ "IPV6_5T_ROUTE",
|
|
+ "IPV6_6RD",
|
|
+};
|
|
+
|
|
+#define IPV4_HNAPT 0
|
|
+#define IPV4_HNAT 1
|
|
+#define IS_IPV4_HNAPT(x) (((x)->bfib1.pkt_type == IPV4_HNAPT) ? 1: 0)
|
|
+struct mtk_eth *_eth;
|
|
+#define es(entry) (mtk_foe_entry_state_str[entry->bfib1.state])
|
|
+//#define ei(entry, end) (MTK_PPE_TBL_SZ - (int)(end - entry))
|
|
+#define ei(entry, end) (MTK_PPE_ENTRY_CNT - (int)(end - entry))
|
|
+#define pt(entry) (mtk_foe_packet_type_str[entry->ipv4_hnapt.bfib1.pkt_type])
|
|
+
|
|
+static int mtk_ppe_debugfs_foe_show(struct seq_file *m, void *private)
|
|
+{
|
|
+ struct mtk_eth *eth = _eth;
|
|
+ struct mtk_foe_entry *entry, *end;
|
|
+ int i = 0;
|
|
+
|
|
+ entry = eth->foe_table;
|
|
+ end = eth->foe_table + MTK_PPE_ENTRY_CNT;
|
|
+
|
|
+ while (entry < end) {
|
|
+ if (!entry->bfib1.state) {
|
|
+
|
|
+ } else if (IS_IPV4_HNAPT(entry)) {
|
|
+ __be32 saddr = htonl(entry->ipv4_hnapt.sip);
|
|
+ __be32 daddr = htonl(entry->ipv4_hnapt.dip);
|
|
+ __be32 nsaddr = htonl(entry->ipv4_hnapt.new_sip);
|
|
+ __be32 ndaddr = htonl(entry->ipv4_hnapt.new_dip);
|
|
+ unsigned char h_dest[ETH_ALEN];
|
|
+ unsigned char h_source[ETH_ALEN];
|
|
+
|
|
+ *((u32*) h_source) = swab32(entry->ipv4_hnapt.smac_hi);
|
|
+ *((u16*) &h_source[4]) = swab16(entry->ipv4_hnapt.smac_lo);
|
|
+ *((u32*) h_dest) = swab32(entry->ipv4_hnapt.dmac_hi);
|
|
+ *((u16*) &h_dest[4]) = swab16(entry->ipv4_hnapt.dmac_lo);
|
|
+ seq_printf(m,
|
|
+ "(%x)0x%05x|state=%s|type=%s|"
|
|
+ "%pI4:%d->%pI4:%d=>%pI4:%d->%pI4:%d|%pM=>%pM|"
|
|
+ "etype=0x%04x|info1=0x%x|info2=0x%x|"
|
|
+ "vlan1=%d|vlan2=%d\n",
|
|
+ i,
|
|
+ ei(entry, end), es(entry), pt(entry),
|
|
+ &saddr, entry->ipv4_hnapt.sport,
|
|
+ &daddr, entry->ipv4_hnapt.dport,
|
|
+ &nsaddr, entry->ipv4_hnapt.new_sport,
|
|
+ &ndaddr, entry->ipv4_hnapt.new_dport, h_source,
|
|
+ h_dest, ntohs(entry->ipv4_hnapt.etype),
|
|
+ entry->ipv4_hnapt.info_blk1,
|
|
+ entry->ipv4_hnapt.info_blk2,
|
|
+ entry->ipv4_hnapt.vlan1,
|
|
+ entry->ipv4_hnapt.vlan2);
|
|
+ } else
|
|
+ seq_printf(m, "0x%05x state=%s\n",
|
|
+ ei(entry, end), es(entry));
|
|
+ entry++;
|
|
+ i++;
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int mtk_ppe_debugfs_foe_open(struct inode *inode, struct file *file)
|
|
+{
|
|
+ return single_open(file, mtk_ppe_debugfs_foe_show, file->private_data);
|
|
+}
|
|
+
|
|
+static const struct file_operations mtk_ppe_debugfs_foe_fops = {
|
|
+ .open = mtk_ppe_debugfs_foe_open,
|
|
+ .read = seq_read,
|
|
+ .llseek = seq_lseek,
|
|
+ .release = single_release,
|
|
+};
|
|
+
|
|
+int mtk_ppe_debugfs_init(struct mtk_eth *eth)
|
|
+{
|
|
+ struct dentry *root;
|
|
+
|
|
+ _eth = eth;
|
|
+
|
|
+ root = debugfs_create_dir("mtk_ppe", NULL);
|
|
+ if (!root)
|
|
+ return -ENOMEM;
|
|
+
|
|
+ debugfs_create_file("all_entry", S_IRUGO, root, eth, &mtk_ppe_debugfs_foe_fops);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c
|
|
+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c
|
|
@@ -19,6 +19,8 @@
|
|
#include <linux/interrupt.h>
|
|
#include <linux/pinctrl/devinfo.h>
|
|
#include <linux/phylink.h>
|
|
+#include <linux/netfilter.h>
|
|
+#include <net/netfilter/nf_flow_table.h>
|
|
#include <net/dsa.h>
|
|
|
|
#include "mtk_eth_soc.h"
|
|
@@ -1327,8 +1329,16 @@ static int mtk_poll_rx(struct napi_struc
|
|
(trxd.rxd2 & RX_DMA_VTAG))
|
|
__vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q),
|
|
RX_DMA_VID(trxd.rxd3));
|
|
- skb_record_rx_queue(skb, 0);
|
|
- napi_gro_receive(napi, skb);
|
|
+#ifdef CONFIG_NET_MEDIATEK_OFFLOAD
|
|
+ if (mtk_offload_check_rx(eth, skb, trxd.rxd4) == 0) {
|
|
+#endif
|
|
+ skb_record_rx_queue(skb, 0);
|
|
+ napi_gro_receive(napi, skb);
|
|
+#ifdef CONFIG_NET_MEDIATEK_OFFLOAD
|
|
+ } else {
|
|
+ dev_kfree_skb(skb);
|
|
+ }
|
|
+#endif
|
|
|
|
skip_rx:
|
|
ring->data[idx] = new_data;
|
|
@@ -2292,6 +2302,9 @@ static int mtk_open(struct net_device *d
|
|
mtk_tx_irq_enable(eth, MTK_TX_DONE_INT);
|
|
mtk_rx_irq_enable(eth, MTK_RX_DONE_INT);
|
|
refcount_set(ð->dma_refcnt, 1);
|
|
+#ifdef CONFIG_NET_MEDIATEK_OFFLOAD
|
|
+ mtk_ppe_probe(eth);
|
|
+#endif
|
|
}
|
|
else
|
|
refcount_inc(ð->dma_refcnt);
|
|
@@ -2355,6 +2368,9 @@ static int mtk_stop(struct net_device *d
|
|
|
|
mtk_dma_free(eth);
|
|
|
|
+#ifdef CONFIG_NET_MEDIATEK_OFFLOAD
|
|
+ mtk_ppe_remove(eth);
|
|
+#endif
|
|
return 0;
|
|
}
|
|
|
|
@@ -2853,6 +2869,27 @@ static int mtk_set_rxnfc(struct net_devi
|
|
return ret;
|
|
}
|
|
|
|
+#ifdef CONFIG_NET_MEDIATEK_OFFLOAD
|
|
+static int
|
|
+mtk_flow_offload(enum flow_offload_type type, struct flow_offload *flow,
|
|
+ struct flow_offload_hw_path *src,
|
|
+ struct flow_offload_hw_path *dest)
|
|
+{
|
|
+ struct mtk_mac *mac = netdev_priv(src->dev);
|
|
+ struct mtk_eth *eth = mac->hw;
|
|
+
|
|
+ if (!eth->soc->offload_version)
|
|
+ return -EINVAL;
|
|
+
|
|
+ if (src->dev->base_addr != dest->dev->base_addr)
|
|
+ return -EINVAL;
|
|
+
|
|
+ mac = netdev_priv(src->dev);
|
|
+
|
|
+ return mtk_flow_offload_add(eth, type, flow, src, dest);
|
|
+}
|
|
+#endif
|
|
+
|
|
static const struct ethtool_ops mtk_ethtool_ops = {
|
|
.get_link_ksettings = mtk_get_link_ksettings,
|
|
.set_link_ksettings = mtk_set_link_ksettings,
|
|
@@ -2884,6 +2921,9 @@ static const struct net_device_ops mtk_n
|
|
#ifdef CONFIG_NET_POLL_CONTROLLER
|
|
.ndo_poll_controller = mtk_poll_controller,
|
|
#endif
|
|
+#ifdef CONFIG_NET_MEDIATEK_OFFLOAD
|
|
+ .ndo_flow_offload = mtk_flow_offload,
|
|
+#endif
|
|
};
|
|
|
|
static int mtk_add_mac(struct mtk_eth *eth, struct device_node *np)
|
|
@@ -3226,6 +3266,7 @@ static const struct mtk_soc_data mt7622_
|
|
.hw_features = MTK_HW_FEATURES,
|
|
.required_clks = MT7622_CLKS_BITMAP,
|
|
.required_pctl = false,
|
|
+ .offload_version = MTK_OFFLOAD_V2,
|
|
};
|
|
|
|
static const struct mtk_soc_data mt7623_data = {
|
|
--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h
|
|
+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h
|
|
@@ -790,6 +790,13 @@ enum mkt_eth_capabilities {
|
|
MTK_MUX_U3_GMAC2_TO_QPHY | \
|
|
MTK_MUX_GMAC12_TO_GEPHY_SGMII | MTK_QDMA)
|
|
|
|
+enum mtk_flow_offload_version {
|
|
+ MTK_OFFLOAD_NONE = 0,
|
|
+ MTK_OFFLOAD_V1,
|
|
+ MTK_OFFLOAD_V2,
|
|
+ MTK_OFFLOAD_V3,
|
|
+};
|
|
+
|
|
/* struct mtk_eth_data - This is the structure holding all differences
|
|
* among various plaforms
|
|
* @ana_rgc3: The offset for register ANA_RGC3 related to
|
|
@@ -807,6 +814,7 @@ struct mtk_soc_data {
|
|
u32 required_clks;
|
|
bool required_pctl;
|
|
netdev_features_t hw_features;
|
|
+ enum mtk_flow_offload_version offload_version;
|
|
};
|
|
|
|
/* currently no SoC has more than 2 macs */
|
|
@@ -832,6 +840,23 @@ struct mtk_sgmii {
|
|
u32 ana_rgc3;
|
|
};
|
|
|
|
+
|
|
+struct mib_entry {
|
|
+ u32 byt_cnt_l;
|
|
+ u16 byt_cnt_h;
|
|
+ u32 pkt_cnt_l;
|
|
+ u8 pkt_cnt_h;
|
|
+ u8 resv0;
|
|
+ u32 resv1;
|
|
+} __packed __aligned(4);
|
|
+
|
|
+struct hnat_accounting {
|
|
+ u64 bytes;
|
|
+ u64 packets;
|
|
+};
|
|
+
|
|
+
|
|
+
|
|
/* struct mtk_eth - This is the main datasructure for holding the state
|
|
* of the driver
|
|
* @dev: The device pointer
|
|
@@ -917,6 +942,16 @@ struct mtk_eth {
|
|
u32 tx_int_status_reg;
|
|
u32 rx_dma_l4_valid;
|
|
int ip_align;
|
|
+
|
|
+ struct reset_control *rst_ppe;
|
|
+ struct mtk_foe_entry *foe_table;
|
|
+ dma_addr_t foe_table_phys;
|
|
+ struct flow_offload __rcu **foe_flow_table;
|
|
+
|
|
+ struct mib_entry *foe_mib_cpu;
|
|
+ dma_addr_t foe_mib_dev;
|
|
+ struct hnat_accounting *acct;
|
|
+ bool per_flow_accounting;
|
|
};
|
|
|
|
/* struct mtk_mac - the structure that holds the info about the MACs of the
|
|
@@ -949,6 +984,7 @@ void mtk_stats_update_mac(struct mtk_mac
|
|
|
|
void mtk_w32(struct mtk_eth *eth, u32 val, unsigned reg);
|
|
u32 mtk_r32(struct mtk_eth *eth, unsigned reg);
|
|
+u32 mtk_m32(struct mtk_eth *eth, u32 mask, u32 set, unsigned reg);
|
|
|
|
int mtk_sgmii_init(struct mtk_sgmii *ss, struct device_node *np,
|
|
u32 ana_rgc3);
|
|
@@ -961,4 +997,13 @@ int mtk_gmac_sgmii_path_setup(struct mtk
|
|
int mtk_gmac_gephy_path_setup(struct mtk_eth *eth, int mac_id);
|
|
int mtk_gmac_rgmii_path_setup(struct mtk_eth *eth, int mac_id);
|
|
|
|
+int mtk_ppe_probe(struct mtk_eth *eth);
|
|
+void mtk_ppe_remove(struct mtk_eth *eth);
|
|
+int mtk_flow_offload_add(struct mtk_eth *eth,
|
|
+ enum flow_offload_type type,
|
|
+ struct flow_offload *flow,
|
|
+ struct flow_offload_hw_path *src,
|
|
+ struct flow_offload_hw_path *dest);
|
|
+int mtk_offload_check_rx(struct mtk_eth *eth, struct sk_buff *skb, u32 rxd4);
|
|
+
|
|
#endif /* MTK_ETH_H */
|
|
--- /dev/null
|
|
+++ b/drivers/net/ethernet/mediatek/mtk_offload.c
|
|
@@ -0,0 +1,593 @@
|
|
+/* This program is free software; you can redistribute it and/or modify
|
|
+ * it under the terms of the GNU General Public License as published by
|
|
+ * the Free Software Foundation; version 2 of the License
|
|
+ *
|
|
+ * 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.
|
|
+ *
|
|
+ * Copyright (C) 2018 John Crispin <john@phrozen.org>
|
|
+ */
|
|
+
|
|
+#include "mtk_offload.h"
|
|
+
|
|
+#define INVALID 0
|
|
+#define UNBIND 1
|
|
+#define BIND 2
|
|
+#define FIN 3
|
|
+
|
|
+#define IPV4_HNAPT 0
|
|
+#define IPV4_HNAT 1
|
|
+
|
|
+static u32
|
|
+mtk_flow_hash_v4(struct flow_offload_tuple *tuple)
|
|
+{
|
|
+ u32 ports = ntohs(tuple->src_port) << 16 | ntohs(tuple->dst_port);
|
|
+ u32 src = ntohl(tuple->dst_v4.s_addr);
|
|
+ u32 dst = ntohl(tuple->src_v4.s_addr);
|
|
+ u32 hash = (ports & src) | ((~ports) & dst);
|
|
+ u32 hash_23_0 = hash & 0xffffff;
|
|
+ u32 hash_31_24 = hash & 0xff000000;
|
|
+
|
|
+ hash = ports ^ src ^ dst ^ ((hash_23_0 << 8) | (hash_31_24 >> 24));
|
|
+ hash = ((hash & 0xffff0000) >> 16 ) ^ (hash & 0xfffff);
|
|
+ hash &= 0x7ff;
|
|
+ hash *= 2;;
|
|
+
|
|
+ return hash;
|
|
+}
|
|
+
|
|
+static int
|
|
+mtk_foe_prepare_v4(struct mtk_foe_entry *entry,
|
|
+ struct flow_offload_tuple *tuple,
|
|
+ struct flow_offload_tuple *dest_tuple,
|
|
+ struct flow_offload_hw_path *src,
|
|
+ struct flow_offload_hw_path *dest)
|
|
+{
|
|
+ int is_mcast = !!is_multicast_ether_addr(dest->eth_dest);
|
|
+
|
|
+ if (tuple->l4proto == IPPROTO_UDP)
|
|
+ entry->ipv4_hnapt.bfib1.udp = 1;
|
|
+
|
|
+ entry->ipv4_hnapt.etype = htons(ETH_P_IP);
|
|
+ entry->ipv4_hnapt.bfib1.pkt_type = IPV4_HNAPT;
|
|
+ entry->ipv4_hnapt.iblk2.fqos = 0;
|
|
+ entry->ipv4_hnapt.bfib1.ttl = 1;
|
|
+ entry->ipv4_hnapt.bfib1.cah = 1;
|
|
+ entry->ipv4_hnapt.bfib1.ka = 1;
|
|
+ entry->ipv4_hnapt.iblk2.mcast = is_mcast;
|
|
+ entry->ipv4_hnapt.iblk2.dscp = 0;
|
|
+ entry->ipv4_hnapt.iblk2.port_mg = 0x3f;
|
|
+ entry->ipv4_hnapt.iblk2.port_ag = 0x1f;
|
|
+#ifdef CONFIG_NET_MEDIATEK_HW_QOS
|
|
+ entry->ipv4_hnapt.iblk2.qid = 1;
|
|
+ entry->ipv4_hnapt.iblk2.fqos = 1;
|
|
+#endif
|
|
+#ifdef CONFIG_RALINK
|
|
+ entry->ipv4_hnapt.iblk2.dp = 1;
|
|
+ if ((dest->flags & FLOW_OFFLOAD_PATH_VLAN) && (dest->vlan_id > 1))
|
|
+ entry->ipv4_hnapt.iblk2.qid += 8;
|
|
+#else
|
|
+ entry->ipv4_hnapt.iblk2.dp = (dest->dev->name[3] - '0') + 1;
|
|
+#endif
|
|
+
|
|
+ entry->ipv4_hnapt.sip = ntohl(tuple->src_v4.s_addr);
|
|
+ entry->ipv4_hnapt.dip = ntohl(tuple->dst_v4.s_addr);
|
|
+ entry->ipv4_hnapt.sport = ntohs(tuple->src_port);
|
|
+ entry->ipv4_hnapt.dport = ntohs(tuple->dst_port);
|
|
+
|
|
+ entry->ipv4_hnapt.new_sip = ntohl(dest_tuple->dst_v4.s_addr);
|
|
+ entry->ipv4_hnapt.new_dip = ntohl(dest_tuple->src_v4.s_addr);
|
|
+ entry->ipv4_hnapt.new_sport = ntohs(dest_tuple->dst_port);
|
|
+ entry->ipv4_hnapt.new_dport = ntohs(dest_tuple->src_port);
|
|
+
|
|
+ entry->bfib1.state = BIND;
|
|
+
|
|
+ if (dest->flags & FLOW_OFFLOAD_PATH_PPPOE) {
|
|
+ entry->bfib1.psn = 1;
|
|
+ entry->ipv4_hnapt.etype = htons(ETH_P_PPP_SES);
|
|
+ entry->ipv4_hnapt.pppoe_id = dest->pppoe_sid;
|
|
+ }
|
|
+
|
|
+ if (dest->flags & FLOW_OFFLOAD_PATH_VLAN) {
|
|
+ entry->ipv4_hnapt.vlan1 = dest->vlan_id;
|
|
+ entry->bfib1.vlan_layer = 1;
|
|
+
|
|
+ switch (dest->vlan_proto) {
|
|
+ case htons(ETH_P_8021Q):
|
|
+ entry->ipv4_hnapt.bfib1.vpm = 1;
|
|
+ break;
|
|
+ case htons(ETH_P_8021AD):
|
|
+ entry->ipv4_hnapt.bfib1.vpm = 2;
|
|
+ break;
|
|
+ default:
|
|
+ return -EINVAL;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static void
|
|
+mtk_foe_set_mac(struct mtk_foe_entry *entry, u8 *smac, u8 *dmac)
|
|
+{
|
|
+ entry->ipv4_hnapt.dmac_hi = swab32(*((u32*) dmac));
|
|
+ entry->ipv4_hnapt.dmac_lo = swab16(*((u16*) &dmac[4]));
|
|
+ entry->ipv4_hnapt.smac_hi = swab32(*((u32*) smac));
|
|
+ entry->ipv4_hnapt.smac_lo = swab16(*((u16*) &smac[4]));
|
|
+}
|
|
+
|
|
+static int
|
|
+mtk_check_entry_available(struct mtk_eth *eth, u32 hash)
|
|
+{
|
|
+ struct mtk_foe_entry entry = ((struct mtk_foe_entry *)eth->foe_table)[hash];
|
|
+
|
|
+ return (entry.bfib1.state == BIND)? 0:1;
|
|
+}
|
|
+
|
|
+static void
|
|
+mtk_foe_write(struct mtk_eth *eth, u32 hash,
|
|
+ struct mtk_foe_entry *entry)
|
|
+{
|
|
+ struct mtk_foe_entry *table = (struct mtk_foe_entry *)eth->foe_table;
|
|
+
|
|
+ memcpy(&table[hash], entry, sizeof(*entry));
|
|
+}
|
|
+
|
|
+int mtk_flow_offload_add(struct mtk_eth *eth,
|
|
+ enum flow_offload_type type,
|
|
+ struct flow_offload *flow,
|
|
+ struct flow_offload_hw_path *src,
|
|
+ struct flow_offload_hw_path *dest)
|
|
+{
|
|
+ struct flow_offload_tuple *otuple = &flow->tuplehash[FLOW_OFFLOAD_DIR_ORIGINAL].tuple;
|
|
+ struct flow_offload_tuple *rtuple = &flow->tuplehash[FLOW_OFFLOAD_DIR_REPLY].tuple;
|
|
+ u32 time_stamp = mtk_r32(eth, 0x0010) & (0x7fff);
|
|
+ u32 ohash, rhash;
|
|
+ struct mtk_foe_entry orig = {
|
|
+ .bfib1.time_stamp = time_stamp,
|
|
+ .bfib1.psn = 0,
|
|
+ };
|
|
+ struct mtk_foe_entry reply = {
|
|
+ .bfib1.time_stamp = time_stamp,
|
|
+ .bfib1.psn = 0,
|
|
+ };
|
|
+
|
|
+ if (otuple->l4proto != IPPROTO_TCP && otuple->l4proto != IPPROTO_UDP)
|
|
+ return -EINVAL;
|
|
+
|
|
+ if (type == FLOW_OFFLOAD_DEL) {
|
|
+ flow = NULL;
|
|
+ synchronize_rcu();
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ switch (otuple->l3proto) {
|
|
+ case AF_INET:
|
|
+ if (mtk_foe_prepare_v4(&orig, otuple, rtuple, src, dest) ||
|
|
+ mtk_foe_prepare_v4(&reply, rtuple, otuple, dest, src))
|
|
+ return -EINVAL;
|
|
+
|
|
+ ohash = mtk_flow_hash_v4(otuple);
|
|
+ rhash = mtk_flow_hash_v4(rtuple);
|
|
+ break;
|
|
+
|
|
+ case AF_INET6:
|
|
+ return -EINVAL;
|
|
+
|
|
+ default:
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ /* Two-way hash: when hash collision occurs, the hash value will be shifted to the next position. */
|
|
+ if (!mtk_check_entry_available(eth, ohash)){
|
|
+ if (!mtk_check_entry_available(eth, ohash + 1))
|
|
+ return -EINVAL;
|
|
+ ohash += 1;
|
|
+ }
|
|
+ if (!mtk_check_entry_available(eth, rhash)){
|
|
+ if (!mtk_check_entry_available(eth, rhash + 1))
|
|
+ return -EINVAL;
|
|
+ rhash += 1;
|
|
+ }
|
|
+
|
|
+ mtk_foe_set_mac(&orig, dest->eth_src, dest->eth_dest);
|
|
+ mtk_foe_set_mac(&reply, src->eth_src, src->eth_dest);
|
|
+ mtk_foe_write(eth, ohash, &orig);
|
|
+ mtk_foe_write(eth, rhash, &reply);
|
|
+ rcu_assign_pointer(eth->foe_flow_table[ohash], flow);
|
|
+ rcu_assign_pointer(eth->foe_flow_table[rhash], flow);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+#ifdef CONFIG_NET_MEDIATEK_HW_QOS
|
|
+
|
|
+#define QDMA_TX_SCH_TX 0x1a14
|
|
+
|
|
+static void mtk_ppe_scheduler(struct mtk_eth *eth, int id, u32 rate)
|
|
+{
|
|
+ int exp = 0, shift = 0;
|
|
+ u32 reg = mtk_r32(eth, QDMA_TX_SCH_TX);
|
|
+ u32 val = 0;
|
|
+
|
|
+ if (rate)
|
|
+ val = BIT(11);
|
|
+
|
|
+ while (rate > 127) {
|
|
+ rate /= 10;
|
|
+ exp++;
|
|
+ }
|
|
+
|
|
+ val |= (rate & 0x7f) << 4;
|
|
+ val |= exp & 0xf;
|
|
+ if (id)
|
|
+ shift = 16;
|
|
+ reg &= ~(0xffff << shift);
|
|
+ reg |= val << shift;
|
|
+ mtk_w32(eth, val, QDMA_TX_SCH_TX);
|
|
+}
|
|
+
|
|
+#define QTX_CFG(x) (0x1800 + (x * 0x10))
|
|
+#define QTX_SCH(x) (0x1804 + (x * 0x10))
|
|
+
|
|
+static void mtk_ppe_queue(struct mtk_eth *eth, int id, int sched, int weight, int resv, u32 min_rate, u32 max_rate)
|
|
+{
|
|
+ int max_exp = 0, min_exp = 0;
|
|
+ u32 reg;
|
|
+
|
|
+ if (id >= 16)
|
|
+ return;
|
|
+
|
|
+ reg = mtk_r32(eth, QTX_SCH(id));
|
|
+ reg &= 0x70000000;
|
|
+
|
|
+ if (sched)
|
|
+ reg |= BIT(31);
|
|
+
|
|
+ if (min_rate)
|
|
+ reg |= BIT(27);
|
|
+
|
|
+ if (max_rate)
|
|
+ reg |= BIT(11);
|
|
+
|
|
+ while (max_rate > 127) {
|
|
+ max_rate /= 10;
|
|
+ max_exp++;
|
|
+ }
|
|
+
|
|
+ while (min_rate > 127) {
|
|
+ min_rate /= 10;
|
|
+ min_exp++;
|
|
+ }
|
|
+
|
|
+ reg |= (min_rate & 0x7f) << 20;
|
|
+ reg |= (min_exp & 0xf) << 16;
|
|
+ reg |= (weight & 0xf) << 12;
|
|
+ reg |= (max_rate & 0x7f) << 4;
|
|
+ reg |= max_exp & 0xf;
|
|
+ mtk_w32(eth, reg, QTX_SCH(id));
|
|
+
|
|
+ resv &= 0xff;
|
|
+ reg = mtk_r32(eth, QTX_CFG(id));
|
|
+ reg &= 0xffff0000;
|
|
+ reg |= (resv << 8) | resv;
|
|
+ mtk_w32(eth, reg, QTX_CFG(id));
|
|
+}
|
|
+#endif
|
|
+
|
|
+static int mtk_init_foe_table(struct mtk_eth *eth)
|
|
+{
|
|
+ if (eth->foe_table)
|
|
+ return 0;
|
|
+
|
|
+ eth->foe_flow_table = devm_kcalloc(eth->dev, MTK_PPE_ENTRY_CNT,
|
|
+ sizeof(*eth->foe_flow_table),
|
|
+ GFP_KERNEL);
|
|
+ if (!eth->foe_flow_table)
|
|
+ return -EINVAL;
|
|
+
|
|
+ /* map the FOE table */
|
|
+ eth->foe_table = dmam_alloc_coherent(eth->dev, MTK_PPE_TBL_SZ,
|
|
+ ð->foe_table_phys, GFP_KERNEL);
|
|
+ if (!eth->foe_table) {
|
|
+ dev_err(eth->dev, "failed to allocate foe table\n");
|
|
+ kfree(eth->foe_flow_table);
|
|
+ return -ENOMEM;
|
|
+ }
|
|
+
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int mtk_ppe_start(struct mtk_eth *eth)
|
|
+{
|
|
+ u32 foe_mib_tb_sz;
|
|
+ u32 foe_etry_num = MTK_PPE_ENTRY_CNT;
|
|
+
|
|
+ int ret;
|
|
+
|
|
+ ret = mtk_init_foe_table(eth);
|
|
+ if (ret)
|
|
+ return ret;
|
|
+
|
|
+ /* tell the PPE about the tables base address */
|
|
+ mtk_w32(eth, eth->foe_table_phys, MTK_REG_PPE_TB_BASE);
|
|
+
|
|
+ /* flush the table */
|
|
+ memset(eth->foe_table, 0, MTK_PPE_TBL_SZ);
|
|
+
|
|
+ eth->per_flow_accounting = false; //true;
|
|
+
|
|
+ if (eth->per_flow_accounting) {
|
|
+ foe_mib_tb_sz = foe_etry_num * sizeof(struct mib_entry);
|
|
+ eth->foe_mib_cpu = dma_alloc_coherent(eth->dev, foe_mib_tb_sz,
|
|
+ ð->foe_mib_dev, GFP_KERNEL);
|
|
+ if (!eth->foe_mib_cpu)
|
|
+ return -1;
|
|
+ mtk_w32(eth, eth->foe_mib_dev, MTK_REG_PPE_MIB_TB_BASE);
|
|
+ memset(eth->foe_mib_cpu, 0, foe_mib_tb_sz);
|
|
+
|
|
+ eth->acct =
|
|
+ kzalloc(foe_etry_num * sizeof(struct hnat_accounting),
|
|
+ GFP_KERNEL);
|
|
+ if (!eth->acct)
|
|
+ return -1;
|
|
+ }
|
|
+
|
|
+ /* setup hashing */
|
|
+ mtk_m32(eth,
|
|
+ MTK_PPE_TB_CFG_HASH_MODE_MASK | MTK_PPE_TB_CFG_TBL_SZ_MASK,
|
|
+ MTK_PPE_TB_CFG_HASH_MODE1 | MTK_PPE_TB_CFG_TBL_SZ_4K,
|
|
+ MTK_REG_PPE_TB_CFG);
|
|
+
|
|
+ /* set the default hashing seed */
|
|
+ mtk_w32(eth, MTK_PPE_HASH_SEED, MTK_REG_PPE_HASH_SEED);
|
|
+
|
|
+ /* each foe entry is 80bytes and is setup by cpu forwarding*/
|
|
+ mtk_m32(eth, MTK_PPE_CAH_CTRL_X_MODE | MTK_PPE_TB_CFG_ENTRY_SZ_MASK |
|
|
+ MTK_PPE_TB_CFG_SMA_MASK,
|
|
+ MTK_PPE_TB_CFG_ENTRY_SZ_64B | MTK_PPE_TB_CFG_SMA_FWD_CPU,
|
|
+ MTK_REG_PPE_TB_CFG);
|
|
+
|
|
+ /* set ip proto */
|
|
+ //writel(0xFFFFFFFF, host->ppe_base + PPE_IP_PROT_CHK);
|
|
+ mtk_w32(eth, 0xFFFFFFFF, MTK_REG_PPE_IP_PROT_CHK);
|
|
+
|
|
+ /* setup caching */
|
|
+ // cr_set_field(host->ppe_base + PPE_CAH_CTRL, CAH_X_MODE, 1);
|
|
+ mtk_m32(eth, 1, MTK_PPE_CAH_CTRL_X_MODE, MTK_REG_PPE_CAH_CTRL);
|
|
+ // cr_set_field(host->ppe_base + PPE_CAH_CTRL, CAH_X_MODE, 0);
|
|
+ mtk_m32(eth, 0, MTK_PPE_CAH_CTRL_X_MODE, MTK_REG_PPE_CAH_CTRL);
|
|
+ // cr_set_field(host->ppe_base + PPE_CAH_CTRL, CAH_EN, 1);
|
|
+ mtk_m32(eth, MTK_PPE_CAH_CTRL_X_MODE, MTK_PPE_CAH_CTRL_EN,
|
|
+ MTK_REG_PPE_CAH_CTRL);
|
|
+
|
|
+ /* enable FOE */
|
|
+ /* cr_set_bits(host->ppe_base + PPE_FLOW_CFG,
|
|
+ BIT_UDP_IP4F_NAT_EN | BIT_IPV4_NAT_EN | BIT_IPV4_NAPT_EN |
|
|
+ BIT_IPV4_NAT_FRAG_EN | BIT_IPV4_HASH_GREK |
|
|
+ BIT_IPV4_DSL_EN | BIT_IPV6_6RD_EN |
|
|
+ BIT_IPV6_3T_ROUTE_EN | BIT_IPV6_5T_ROUTE_EN); */
|
|
+ mtk_m32(eth, 0, MTK_PPE_FLOW_CFG_IPV4_NAT_FRAG_EN |
|
|
+ MTK_PPE_FLOW_CFG_IPV4_NAPT_EN | MTK_PPE_FLOW_CFG_IPV4_NAT_EN |
|
|
+ MTK_PPE_FLOW_CFG_IPV4_GREK_EN,
|
|
+ MTK_REG_PPE_FLOW_CFG);
|
|
+
|
|
+ mtk_w32(eth, 0x000a7780, MTK_REG_PPE_FLOW_CFG);
|
|
+
|
|
+ /* setup flow entry un/bind aging */
|
|
+ // cr_set_field(host->ppe_base + PPE_TB_CFG, NTU_AGE, 1);
|
|
+ // cr_set_field(host->ppe_base + PPE_TB_CFG, UNBD_AGE, 1);
|
|
+ // cr_set_field(host->ppe_base + PPE_TB_CFG, TCP_AGE, 1);
|
|
+ // cr_set_field(host->ppe_base + PPE_TB_CFG, UDP_AGE, 1);
|
|
+ // cr_set_field(host->ppe_base + PPE_TB_CFG, FIN_AGE, 1);
|
|
+ mtk_m32(eth, 0,
|
|
+ MTK_PPE_TB_CFG_UNBD_AGE | MTK_PPE_TB_CFG_NTU_AGE |
|
|
+ MTK_PPE_TB_CFG_FIN_AGE | MTK_PPE_TB_CFG_UDP_AGE |
|
|
+ MTK_PPE_TB_CFG_TCP_AGE,
|
|
+ MTK_REG_PPE_TB_CFG);
|
|
+
|
|
+ // cr_set_field(host->ppe_base + PPE_UNB_AGE, UNB_MNP, 1000);
|
|
+ // cr_set_field(host->ppe_base + PPE_UNB_AGE, UNB_DLTA, 3);
|
|
+ mtk_m32(eth, MTK_PPE_UNB_AGE_MNP_MASK | MTK_PPE_UNB_AGE_DLTA_MASK,
|
|
+ MTK_PPE_UNB_AGE_MNP | MTK_PPE_UNB_AGE_DLTA,
|
|
+ MTK_REG_PPE_UNB_AGE);
|
|
+
|
|
+ // cr_set_field(host->ppe_base + PPE_BND_AGE_0, UDP_DLTA, 12);
|
|
+ // cr_set_field(host->ppe_base + PPE_BND_AGE_0, NTU_DLTA, 1);
|
|
+ mtk_m32(eth, MTK_PPE_BND_AGE0_NTU_DLTA_MASK |
|
|
+ MTK_PPE_BND_AGE0_UDP_DLTA_MASK,
|
|
+ MTK_PPE_BND_AGE0_NTU_DLTA | MTK_PPE_BND_AGE0_UDP_DLTA,
|
|
+ MTK_REG_PPE_BND_AGE0);
|
|
+ mtk_w32(eth, 0x0001000c, MTK_REG_PPE_BND_AGE0);
|
|
+
|
|
+ // cr_set_field(host->ppe_base + PPE_BND_AGE_1, FIN_DLTA, 1);
|
|
+ // cr_set_field(host->ppe_base + PPE_BND_AGE_1, TCP_DLTA, 7);
|
|
+ mtk_m32(eth, MTK_PPE_BND_AGE1_FIN_DLTA_MASK |
|
|
+ MTK_PPE_BND_AGE1_TCP_DLTA_MASK,
|
|
+ MTK_PPE_BND_AGE1_FIN_DLTA | MTK_PPE_BND_AGE1_TCP_DLTA,
|
|
+ MTK_REG_PPE_BND_AGE1);
|
|
+ mtk_w32(eth, 0x00010007, MTK_REG_PPE_BND_AGE1);
|
|
+
|
|
+ /* setup flow entry keep alive */
|
|
+ // cr_set_field(host->ppe_base + PPE_TB_CFG, SCAN_MODE, 2);
|
|
+ // cr_set_field(host->ppe_base + PPE_TB_CFG, KA_CFG, 3);
|
|
+ mtk_m32(eth, MTK_PPE_TB_CFG_KA_MASK | MTK_PPE_TB_CFG_SCAN_MODE_MASK,
|
|
+ MTK_PPE_TB_CFG_KA | MTK_PPE_TB_CFG_SCAN_MODE,
|
|
+ MTK_REG_PPE_TB_CFG);
|
|
+ // cr_set_field(host->ppe_base + PPE_KA, KA_T, 1);
|
|
+ // cr_set_field(host->ppe_base + PPE_KA, TCP_KA, 1);
|
|
+ // cr_set_field(host->ppe_base + PPE_KA, UDP_KA, 1);
|
|
+ mtk_w32(eth, MTK_PPE_KA_UDP | MTK_PPE_KA_TCP | MTK_PPE_KA_T, MTK_REG_PPE_KA);
|
|
+
|
|
+ /* setup flow entry rate limit */
|
|
+ mtk_w32(eth, (0x3fff << 16) | 0x3fff, MTK_REG_PPE_BIND_LMT_0);
|
|
+ mtk_w32(eth, 0x2000000 | MTK_PPE_NTU_KA | 0x3fff, MTK_REG_PPE_BIND_LMT_1);
|
|
+ /* 30 packets per second */
|
|
+ mtk_m32(eth, MTK_PPE_BNDR_RATE_MASK, 0x1e, MTK_REG_PPE_BNDR);
|
|
+
|
|
+ /* enable the PPE */
|
|
+ mtk_m32(eth, 0, MTK_PPE_GLO_CFG_EN, MTK_REG_PPE_GLO_CFG);
|
|
+
|
|
+ /* set the default forwarding port to PDMA */
|
|
+ mtk_w32(eth, 0x0, MTK_REG_PPE_DFT_CPORT);
|
|
+
|
|
+ /* disallow packets with TTL=0 */
|
|
+ mtk_m32(eth, 0, MTK_PPE_GLO_CFG_TTL0_DROP, MTK_REG_PPE_GLO_CFG);
|
|
+
|
|
+ /*enable ppe mib counter*/
|
|
+ if (eth->per_flow_accounting) {
|
|
+ mtk_w32(eth, 0x3, MTK_REG_PPE_MIB_CFG);
|
|
+ mtk_w32(eth, 0x3, MTK_REG_PPE_MIB_CAH_CTRL);
|
|
+ }
|
|
+
|
|
+ /* send all traffic from gmac to the ppe */
|
|
+ mtk_m32(eth, 0xffff, 0x4444, MTK_GDMA_FWD_CFG(0));
|
|
+ mtk_m32(eth, 0xffff, 0x4444, MTK_GDMA_FWD_CFG(1));
|
|
+
|
|
+ mtk_w32(eth, 0x00027fb4, MTK_REG_PPE_TB_CFG);
|
|
+
|
|
+ dev_info(eth->dev, "PPE started\n");
|
|
+
|
|
+#ifdef CONFIG_NET_MEDIATEK_HW_QOS
|
|
+ mtk_ppe_scheduler(eth, 0, 500000);
|
|
+ mtk_ppe_scheduler(eth, 1, 500000);
|
|
+ mtk_ppe_queue(eth, 0, 0, 7, 32, 250000, 0);
|
|
+ mtk_ppe_queue(eth, 1, 0, 7, 32, 250000, 0);
|
|
+ mtk_ppe_queue(eth, 8, 1, 7, 32, 250000, 0);
|
|
+ mtk_ppe_queue(eth, 9, 1, 7, 32, 250000, 0);
|
|
+#endif
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int mtk_ppe_busy_wait(struct mtk_eth *eth)
|
|
+{
|
|
+ unsigned long t_start = jiffies;
|
|
+ u32 r = 0;
|
|
+
|
|
+ while (1) {
|
|
+ r = mtk_r32(eth, MTK_REG_PPE_GLO_CFG);
|
|
+ if (!(r & MTK_PPE_GLO_CFG_BUSY))
|
|
+ return 0;
|
|
+ if (time_after(jiffies, t_start + HZ))
|
|
+ break;
|
|
+ usleep_range(10, 20);
|
|
+ }
|
|
+
|
|
+ dev_err(eth->dev, "ppe: table busy timeout - resetting\n");
|
|
+ reset_control_reset(eth->rst_ppe);
|
|
+
|
|
+ return -ETIMEDOUT;
|
|
+}
|
|
+
|
|
+static int mtk_ppe_stop(struct mtk_eth *eth)
|
|
+{
|
|
+ u32 r1 = 0, r2 = 0;
|
|
+ int i;
|
|
+
|
|
+ /* discard all traffic while we disable the PPE */
|
|
+ mtk_m32(eth, 0xffff, 0x7777, MTK_GDMA_FWD_CFG(0));
|
|
+ mtk_m32(eth, 0xffff, 0x7777, MTK_GDMA_FWD_CFG(1));
|
|
+
|
|
+ if (mtk_ppe_busy_wait(eth))
|
|
+ return -ETIMEDOUT;
|
|
+
|
|
+ /* invalidate all flow table entries */
|
|
+ for (i = 0; i < MTK_PPE_ENTRY_CNT; i++)
|
|
+ eth->foe_table[i].bfib1.state = FOE_STATE_INVALID;
|
|
+
|
|
+ /* disable caching */
|
|
+ mtk_m32(eth, 0, MTK_PPE_CAH_CTRL_X_MODE, MTK_REG_PPE_CAH_CTRL);
|
|
+ mtk_m32(eth, MTK_PPE_CAH_CTRL_X_MODE | MTK_PPE_CAH_CTRL_EN, 0,
|
|
+ MTK_REG_PPE_CAH_CTRL);
|
|
+
|
|
+ /* flush cache has to be ahead of hnat diable --*/
|
|
+ mtk_m32(eth, MTK_PPE_GLO_CFG_EN, 0, MTK_REG_PPE_GLO_CFG);
|
|
+
|
|
+ /* disable FOE */
|
|
+ mtk_m32(eth,
|
|
+ MTK_PPE_FLOW_CFG_IPV4_NAT_FRAG_EN |
|
|
+ MTK_PPE_FLOW_CFG_IPV4_NAPT_EN | MTK_PPE_FLOW_CFG_IPV4_NAT_EN |
|
|
+ MTK_PPE_FLOW_CFG_FUC_FOE | MTK_PPE_FLOW_CFG_FMC_FOE,
|
|
+ 0, MTK_REG_PPE_FLOW_CFG);
|
|
+
|
|
+ /* disable FOE aging */
|
|
+ mtk_m32(eth, 0,
|
|
+ MTK_PPE_TB_CFG_FIN_AGE | MTK_PPE_TB_CFG_UDP_AGE |
|
|
+ MTK_PPE_TB_CFG_TCP_AGE | MTK_PPE_TB_CFG_UNBD_AGE |
|
|
+ MTK_PPE_TB_CFG_NTU_AGE, MTK_REG_PPE_TB_CFG);
|
|
+
|
|
+ r1 = mtk_r32(eth, 0x100);
|
|
+ r2 = mtk_r32(eth, 0x10c);
|
|
+
|
|
+ dev_info(eth->dev, "0x100 = 0x%x, 0x10c = 0x%x\n", r1, r2);
|
|
+
|
|
+ if (((r1 & 0xff00) >> 0x8) >= (r1 & 0xff) ||
|
|
+ ((r1 & 0xff00) >> 0x8) >= (r2 & 0xff)) {
|
|
+ dev_info(eth->dev, "reset pse\n");
|
|
+ mtk_w32(eth, 0x1, 0x4);
|
|
+ }
|
|
+
|
|
+ /* set the foe entry base address to 0 */
|
|
+ mtk_w32(eth, 0, MTK_REG_PPE_TB_BASE);
|
|
+
|
|
+ if (mtk_ppe_busy_wait(eth))
|
|
+ return -ETIMEDOUT;
|
|
+
|
|
+ /* send all traffic back to the DMA engine */
|
|
+ mtk_m32(eth, 0xffff, 0x0, MTK_GDMA_FWD_CFG(0));
|
|
+ mtk_m32(eth, 0xffff, 0x0, MTK_GDMA_FWD_CFG(1));
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static void mtk_offload_keepalive(struct mtk_eth *eth, unsigned int hash)
|
|
+{
|
|
+ struct flow_offload *flow;
|
|
+
|
|
+ rcu_read_lock();
|
|
+ flow = rcu_dereference(eth->foe_flow_table[hash]);
|
|
+ if (flow)
|
|
+ flow->timeout = jiffies + 30 * HZ;
|
|
+ rcu_read_unlock();
|
|
+}
|
|
+
|
|
+int mtk_offload_check_rx(struct mtk_eth *eth, struct sk_buff *skb, u32 rxd4)
|
|
+{
|
|
+ unsigned int hash;
|
|
+
|
|
+ switch (FIELD_GET(MTK_RXD4_CPU_REASON, rxd4)) {
|
|
+ case MTK_CPU_REASON_KEEPALIVE_UC_OLD_HDR:
|
|
+ case MTK_CPU_REASON_KEEPALIVE_MC_NEW_HDR:
|
|
+ case MTK_CPU_REASON_KEEPALIVE_DUP_OLD_HDR:
|
|
+ hash = FIELD_GET(MTK_RXD4_FOE_ENTRY, rxd4);
|
|
+ mtk_offload_keepalive(eth, hash);
|
|
+ return -1;
|
|
+ case MTK_CPU_REASON_PACKET_SAMPLING:
|
|
+ return -1;
|
|
+ default:
|
|
+ return 0;
|
|
+ }
|
|
+}
|
|
+
|
|
+int mtk_ppe_probe(struct mtk_eth *eth)
|
|
+{
|
|
+ int err;
|
|
+
|
|
+ err = mtk_ppe_start(eth);
|
|
+ if (err)
|
|
+ return err;
|
|
+
|
|
+ err = mtk_ppe_debugfs_init(eth);
|
|
+ if (err)
|
|
+ return err;
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+void mtk_ppe_remove(struct mtk_eth *eth)
|
|
+{
|
|
+ mtk_ppe_stop(eth);
|
|
+}
|
|
--- /dev/null
|
|
+++ b/drivers/net/ethernet/mediatek/mtk_offload.h
|
|
@@ -0,0 +1,298 @@
|
|
+/* This program is free software; you can redistribute it and/or modify
|
|
+ * it under the terms of the GNU General Public License as published by
|
|
+ * the Free Software Foundation; version 2 of the License
|
|
+ *
|
|
+ * 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.
|
|
+ *
|
|
+ * Copyright (C) 2014-2016 Sean Wang <sean.wang@mediatek.com>
|
|
+ * Copyright (C) 2016-2017 John Crispin <blogic@openwrt.org>
|
|
+ */
|
|
+
|
|
+#include <linux/dma-mapping.h>
|
|
+#include <linux/delay.h>
|
|
+#include <linux/if.h>
|
|
+#include <linux/io.h>
|
|
+#include <linux/module.h>
|
|
+#include <linux/of_device.h>
|
|
+#include <linux/platform_device.h>
|
|
+#include <linux/reset.h>
|
|
+#include <linux/netfilter.h>
|
|
+#include <linux/netdevice.h>
|
|
+#include <net/netfilter/nf_flow_table.h>
|
|
+#include <linux/debugfs.h>
|
|
+#include <linux/etherdevice.h>
|
|
+#include <linux/bitfield.h>
|
|
+
|
|
+#include "mtk_eth_soc.h"
|
|
+
|
|
+#ifdef CONFIG_RALINK
|
|
+/* ramips compat */
|
|
+#define mtk_eth fe_priv
|
|
+#define MTK_GDMA_FWD_CFG(x) (0x500 + (x * 0x1000))
|
|
+#define mtk_m32 fe_m32
|
|
+
|
|
+static inline u32
|
|
+mtk_r32(struct mtk_eth *eth, u32 reg)
|
|
+{
|
|
+ return fe_r32(reg);
|
|
+}
|
|
+
|
|
+static inline void
|
|
+mtk_w32(struct mtk_eth *eth, u32 val, u32 reg)
|
|
+{
|
|
+ fe_w32(val, reg);
|
|
+}
|
|
+#endif
|
|
+
|
|
+#define MTK_REG_PPE_GLO_CFG 0xe00
|
|
+#define MTK_PPE_GLO_CFG_BUSY BIT(31)
|
|
+#define MTK_PPE_GLO_CFG_TTL0_DROP BIT(4)
|
|
+#define MTK_PPE_GLO_CFG_EN BIT(0)
|
|
+
|
|
+#define MTK_REG_PPE_FLOW_CFG 0xe04
|
|
+#define MTK_PPE_FLOW_CFG_IPV4_GREK_EN BIT(19)
|
|
+#define MTK_PPE_FLOW_CFG_IPV4_NAT_FRAG_EN BIT(17)
|
|
+#define MTK_PPE_FLOW_CFG_IPV4_NAPT_EN BIT(13)
|
|
+#define MTK_PPE_FLOW_CFG_IPV4_NAT_EN BIT(12)
|
|
+#define MTK_PPE_FLOW_CFG_FUC_FOE BIT(2)
|
|
+#define MTK_PPE_FLOW_CFG_FMC_FOE BIT(1)
|
|
+
|
|
+#define MTK_REG_PPE_IP_PROT_CHK 0xe08
|
|
+
|
|
+#define MTK_REG_PPE_TB_BASE 0xe20
|
|
+
|
|
+#define MTK_REG_PPE_BNDR 0xe28
|
|
+#define MTK_PPE_BNDR_RATE_MASK 0xffff
|
|
+
|
|
+#define MTK_REG_PPE_BIND_LMT_0 0xe2C
|
|
+
|
|
+#define MTK_REG_PPE_BIND_LMT_1 0xe30
|
|
+#define MTK_PPE_NTU_KA BIT(16)
|
|
+
|
|
+#define MTK_REG_PPE_KA 0xe34
|
|
+#define MTK_PPE_KA_T BIT(0)
|
|
+#define MTK_PPE_KA_TCP BIT(16)
|
|
+#define MTK_PPE_KA_UDP BIT(24)
|
|
+
|
|
+#define MTK_REG_PPE_UNB_AGE 0xe38
|
|
+#define MTK_PPE_UNB_AGE_MNP_MASK (0xffff << 16)
|
|
+#define MTK_PPE_UNB_AGE_MNP (1000 << 16)
|
|
+#define MTK_PPE_UNB_AGE_DLTA_MASK 0xff
|
|
+#define MTK_PPE_UNB_AGE_DLTA 3
|
|
+
|
|
+#define MTK_REG_PPE_BND_AGE0 0xe3c
|
|
+#define MTK_PPE_BND_AGE0_NTU_DLTA_MASK (0xffff << 16)
|
|
+#define MTK_PPE_BND_AGE0_NTU_DLTA (5 << 16)
|
|
+#define MTK_PPE_BND_AGE0_UDP_DLTA_MASK 0xffff
|
|
+#define MTK_PPE_BND_AGE0_UDP_DLTA 5
|
|
+
|
|
+#define MTK_REG_PPE_BND_AGE1 0xe40
|
|
+#define MTK_PPE_BND_AGE1_FIN_DLTA_MASK (0xffff << 16)
|
|
+#define MTK_PPE_BND_AGE1_FIN_DLTA (5 << 16)
|
|
+#define MTK_PPE_BND_AGE1_TCP_DLTA_MASK 0xffff
|
|
+#define MTK_PPE_BND_AGE1_TCP_DLTA 5
|
|
+
|
|
+#define MTK_REG_PPE_DFT_CPORT 0xe48
|
|
+
|
|
+#define MTK_REG_PPE_TB_CFG 0xe1c
|
|
+#define MTK_PPE_TB_CFG_X_MODE_MASK (3 << 18)
|
|
+#define MTK_PPE_TB_CFG_HASH_MODE1 BIT(14)
|
|
+#define MTK_PPE_TB_CFG_HASH_MODE_MASK (0x3 << 14)
|
|
+#define MTK_PPE_TB_CFG_KA (3 << 12)
|
|
+#define MTK_PPE_TB_CFG_KA_MASK (0x3 << 12)
|
|
+#define MTK_PPE_TB_CFG_SCAN_MODE (2 << 16)
|
|
+#define MTK_PPE_TB_CFG_SCAN_MODE_MASK (0x3 << 16)
|
|
+#define MTK_PPE_TB_CFG_FIN_AGE BIT(11)
|
|
+#define MTK_PPE_TB_CFG_UDP_AGE BIT(10)
|
|
+#define MTK_PPE_TB_CFG_TCP_AGE BIT(9)
|
|
+#define MTK_PPE_TB_CFG_UNBD_AGE BIT(8)
|
|
+#define MTK_PPE_TB_CFG_NTU_AGE BIT(7)
|
|
+#define MTK_PPE_TB_CFG_SMA_FWD_CPU (0x3 << 4)
|
|
+#define MTK_PPE_TB_CFG_SMA_MASK (0x3 << 4)
|
|
+#define MTK_PPE_TB_CFG_ENTRY_SZ_64B 0
|
|
+#define MTK_PPE_TB_CFG_ENTRY_SZ_80B 1
|
|
+#define MTK_PPE_TB_CFG_ENTRY_SZ_MASK BIT(3)
|
|
+#define MTK_PPE_TB_CFG_TBL_SZ_4K 4
|
|
+#define MTK_PPE_TB_CFG_TBL_SZ_MASK 0x7
|
|
+
|
|
+#define MTK_REG_PPE_HASH_SEED 0xe44
|
|
+#define MTK_PPE_HASH_SEED 0x12345678
|
|
+
|
|
+
|
|
+#define MTK_REG_PPE_CAH_CTRL 0xf20
|
|
+#define MTK_PPE_CAH_CTRL_X_MODE BIT(9)
|
|
+#define MTK_PPE_CAH_CTRL_EN BIT(0)
|
|
+
|
|
+#define MTK_REG_PPE_MIB_CFG 0xf34
|
|
+#define MTK_REG_PPE_MIB_TB_BASE 0xf38
|
|
+#define MTK_REG_PPE_MIB_CAH_CTRL 0Xf50
|
|
+
|
|
+
|
|
+struct mtk_foe_unbind_info_blk {
|
|
+ u32 time_stamp:8;
|
|
+ u32 pcnt:16; /* packet count */
|
|
+ u32 preb:1;
|
|
+ u32 pkt_type:3;
|
|
+ u32 state:2;
|
|
+ u32 udp:1;
|
|
+ u32 sta:1; /* static entry */
|
|
+} __attribute__ ((packed));
|
|
+
|
|
+struct mtk_foe_bind_info_blk {
|
|
+ u32 time_stamp:15;
|
|
+ u32 ka:1; /* keep alive */
|
|
+ u32 vlan_layer:3;
|
|
+ u32 psn:1; /* egress packet has PPPoE session */
|
|
+#ifdef CONFIG_RALINK
|
|
+ u32 vpm:2; /* 0:ethertype remark, 1:0x8100(CR default) */
|
|
+#else
|
|
+ u32 vpm:1; /* 0:ethertype remark, 1:0x8100(CR default) */
|
|
+ u32 ps:1; /* packet sampling */
|
|
+#endif
|
|
+ u32 cah:1; /* cacheable flag */
|
|
+ u32 rmt:1; /* remove tunnel ip header (6rd/dslite only) */
|
|
+ u32 ttl:1;
|
|
+ u32 pkt_type:3;
|
|
+ u32 state:2;
|
|
+ u32 udp:1;
|
|
+ u32 sta:1; /* static entry */
|
|
+} __attribute__ ((packed));
|
|
+
|
|
+struct mtk_foe_info_blk2 {
|
|
+ u32 qid:4; /* QID in Qos Port */
|
|
+ u32 fqos:1; /* force to PSE QoS port */
|
|
+ u32 dp:3; /* force to PSE port x
|
|
+ 0:PSE,1:GSW, 2:GMAC,4:PPE,5:QDMA,7=DROP */
|
|
+ u32 mcast:1; /* multicast this packet to CPU */
|
|
+ u32 pcpl:1; /* OSBN */
|
|
+ u32 mlen:1; /* 0:post 1:pre packet length in meter */
|
|
+ u32 alen:1; /* 0:post 1:pre packet length in accounting */
|
|
+ u32 port_mg:6; /* port meter group */
|
|
+ u32 port_ag:6; /* port account group */
|
|
+ u32 dscp:8; /* DSCP value */
|
|
+} __attribute__ ((packed));
|
|
+
|
|
+/* info blk2 for WHNAT */
|
|
+struct hnat_info_blk2_whnat {
|
|
+ u32 qid : 4; /* QID[3:0] in Qos Port */
|
|
+ u32 fqos : 1; /* force to PSE QoS port */
|
|
+ u32 dp : 3; /* force to PSE port x
|
|
+ * 0:PSE,1:GSW, 2:GMAC,4:PPE,5:QDMA,7=DROP
|
|
+ */
|
|
+ u32 mcast : 1; /* multicast this packet to CPU */
|
|
+ u32 pcpl : 1; /* OSBN */
|
|
+ u32 mibf : 1; /* 0:off 1:on PPE MIB counter */
|
|
+ u32 alen : 1; /* 0:post 1:pre packet length in accounting */
|
|
+ u32 qid2 : 2; /* QID[5:4] in Qos Port */
|
|
+ u32 resv : 2;
|
|
+ u32 wdmaid : 1; /* 0:to pcie0 dev 1:to pcie1 dev */
|
|
+ u32 winfoi : 1; /* 0:off 1:on Wi-Fi hwnat support */
|
|
+ u32 port_ag : 6; /* port account group */
|
|
+ u32 dscp : 8; /* DSCP value */
|
|
+} __attribute__ ((packed));
|
|
+
|
|
+struct hnat_winfo {
|
|
+ u32 bssid : 6; /* WiFi Bssidx */
|
|
+ u32 wcid : 8; /* WiFi wtable Idx */
|
|
+ u32 rxid : 2; /* WiFi Ring idx */
|
|
+} __attribute__ ((packed));
|
|
+
|
|
+struct mtk_foe_ipv4_hnapt {
|
|
+ union {
|
|
+ struct mtk_foe_bind_info_blk bfib1;
|
|
+ struct mtk_foe_unbind_info_blk udib1;
|
|
+ u32 info_blk1;
|
|
+ };
|
|
+ u32 sip;
|
|
+ u32 dip;
|
|
+ u16 dport;
|
|
+ u16 sport;
|
|
+ union {
|
|
+ struct mtk_foe_info_blk2 iblk2;
|
|
+ struct hnat_info_blk2_whnat iblk2w;
|
|
+ u32 info_blk2;
|
|
+ };
|
|
+ u32 new_sip;
|
|
+ u32 new_dip;
|
|
+ u16 new_dport;
|
|
+ u16 new_sport;
|
|
+ u32 resv1;
|
|
+ u32 resv2;
|
|
+ u32 resv3:26;
|
|
+ u32 act_dp:6; /* UDF */
|
|
+ u16 vlan1;
|
|
+ u16 etype;
|
|
+ u32 dmac_hi;
|
|
+ union {
|
|
+ struct hnat_winfo winfo;
|
|
+ u16 vlan2;
|
|
+ };
|
|
+ u16 dmac_lo;
|
|
+ u32 smac_hi;
|
|
+ u16 pppoe_id;
|
|
+ u16 smac_lo;
|
|
+} __attribute__ ((packed));
|
|
+
|
|
+struct mtk_foe_entry {
|
|
+ union {
|
|
+ struct mtk_foe_unbind_info_blk udib1;
|
|
+ struct mtk_foe_bind_info_blk bfib1;
|
|
+ struct mtk_foe_ipv4_hnapt ipv4_hnapt;
|
|
+ };
|
|
+};
|
|
+
|
|
+enum mtk_foe_entry_state {
|
|
+ FOE_STATE_INVALID = 0,
|
|
+ FOE_STATE_UNBIND = 1,
|
|
+ FOE_STATE_BIND = 2,
|
|
+ FOE_STATE_FIN = 3
|
|
+};
|
|
+
|
|
+
|
|
+#define MTK_RXD4_FOE_ENTRY GENMASK(13, 0)
|
|
+#define MTK_RXD4_CPU_REASON GENMASK(18, 14)
|
|
+#define MTK_RXD4_SRC_PORT GENMASK(21, 19)
|
|
+#define MTK_RXD4_ALG GENMASK(31, 22)
|
|
+
|
|
+enum mtk_foe_cpu_reason {
|
|
+ MTK_CPU_REASON_TTL_EXCEEDED = 0x02,
|
|
+ MTK_CPU_REASON_OPTION_HEADER = 0x03,
|
|
+ MTK_CPU_REASON_NO_FLOW = 0x07,
|
|
+ MTK_CPU_REASON_IPV4_FRAG = 0x08,
|
|
+ MTK_CPU_REASON_IPV4_DSLITE_FRAG = 0x09,
|
|
+ MTK_CPU_REASON_IPV4_DSLITE_NO_TCP_UDP = 0x0a,
|
|
+ MTK_CPU_REASON_IPV6_6RD_NO_TCP_UDP = 0x0b,
|
|
+ MTK_CPU_REASON_TCP_FIN_SYN_RST = 0x0c,
|
|
+ MTK_CPU_REASON_UN_HIT = 0x0d,
|
|
+ MTK_CPU_REASON_HIT_UNBIND = 0x0e,
|
|
+ MTK_CPU_REASON_HIT_UNBIND_RATE_REACHED = 0x0f,
|
|
+ MTK_CPU_REASON_HIT_BIND_TCP_FIN = 0x10,
|
|
+ MTK_CPU_REASON_HIT_TTL_1 = 0x11,
|
|
+ MTK_CPU_REASON_HIT_BIND_VLAN_VIOLATION = 0x12,
|
|
+ MTK_CPU_REASON_KEEPALIVE_UC_OLD_HDR = 0x13,
|
|
+ MTK_CPU_REASON_KEEPALIVE_MC_NEW_HDR = 0x14,
|
|
+ MTK_CPU_REASON_KEEPALIVE_DUP_OLD_HDR = 0x15,
|
|
+ MTK_CPU_REASON_HIT_BIND_FORCE_CPU = 0x16,
|
|
+ MTK_CPU_REASON_TUNNEL_OPTION_HEADER = 0x17,
|
|
+ MTK_CPU_REASON_MULTICAST_TO_CPU = 0x18,
|
|
+ MTK_CPU_REASON_MULTICAST_TO_GMAC1_CPU = 0x19,
|
|
+ MTK_CPU_REASON_HIT_PRE_BIND = 0x1a,
|
|
+ MTK_CPU_REASON_PACKET_SAMPLING = 0x1b,
|
|
+ MTK_CPU_REASON_EXCEED_MTU = 0x1c,
|
|
+ MTK_CPU_REASON_PPE_BYPASS = 0x1e,
|
|
+ MTK_CPU_REASON_INVALID = 0x1f,
|
|
+};
|
|
+
|
|
+
|
|
+/* our table size is 4K */
|
|
+#define MTK_PPE_ENTRY_CNT 0x4000
|
|
+#define MTK_PPE_TBL_SZ \
|
|
+ (MTK_PPE_ENTRY_CNT * sizeof(struct mtk_foe_entry))
|
|
+
|
|
+int mtk_ppe_debugfs_init(struct mtk_eth *eth);
|
|
+
|
|
+
|
|
+
|