aboutsummaryrefslogtreecommitdiffstats
path: root/target/linux/mediatek/patches-5.4/0999-hnat.patch
blob: 50850a906e5301153289e18a64bfc75992f48c58 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
--- 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 "mtk_eth_soc.h"
 
@@ -1320,8 +1322,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;
@@ -2255,6 +2265,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(&eth->dma_refcnt, 1);
+#ifdef CONFIG_NET_MEDIATEK_OFFLOAD
+		mtk_ppe_probe(eth);
+#endif
 	}
 	else
 		refcount_inc(&eth->dma_refcnt);
@@ -2316,6 +2329,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;
 }
 
@@ -2829,6 +2845,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,
@@ -2860,6 +2897,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)
@@ -3202,6 +3242,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
@@ -787,6 +787,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
@@ -804,6 +811,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 */
@@ -829,6 +837,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
@@ -914,6 +939,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
@@ -946,6 +981,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);
@@ -958,4 +994,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,
+					     &eth->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,
+						       &eth->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);
+
+
+