aboutsummaryrefslogtreecommitdiffstats
path: root/docs
Commit message (Expand)AuthorAgeFilesLines
* build: Prevent more gzip timestampsJohn Crispin2015-07-141-1/+1
* docs: Fix typo buysbox -> busybox.John Crispin2015-04-091-3/+3
* docs: update remaining references to functions.shFelix Fietkau2012-12-202-3/+3
* update switch documentationLuka Perkov2012-10-121-1/+22
* update VLAN tag exampleLuka Perkov2012-10-121-2/+4
* fix styleLuka Perkov2012-10-121-13/+11
* kamikaze has been released long agoImre Kaloz2011-02-112-5/+4
* docs: fix a counting failFelix Fietkau2010-05-291-1/+1
* Add documentation for passphrase option.Felix Fietkau2009-09-241-0/+8
* implement doth (802.11h) and wmm (802.11e) options for broadcom as well; upda...Jo-Philipp Wich2009-09-221-0/+10
* configure the IPv6 from /etc/config/network and remove 6scripts's prefix opti...Florian Fainelli2009-07-051-3/+0
* add a short note on how to use OpenWrt for daily work, covering the use of qu...Florian Fainelli2009-06-283-0/+115
* comment and fix some parts of the documentation, thanks to Harsh TrivediFlorian Fainelli2009-06-083-5/+37
* a couple of small fixes in the Wireless documentationFlorian Fainelli2009-05-221-12/+25
* build: update svn url from https:// to svn://Andy Boyett2009-05-201-1/+1
* get rid of $Id$ - it has never helped us and it has broken too many patches ;)Felix Fietkau2009-04-171-3/+0
* document a little bit more the rationale of the 6bridge scriptFlorian Fainelli2009-04-021-0/+2
* allow mac80211 devices to be configured to do 802.11s, requires iwFlorian Fainelli2009-02-261-1/+9
* wireless.tex: update docs to reflect txpower moveAndy Boyett2009-01-311-2/+6
* document the BUILDONLY optionFelix Fietkau2009-01-131-0/+3
* Make the doc slightly more complete and add notes on how to add a new target ...Florian Fainelli2009-01-064-3/+173
* hostapd,madwifi: rename agmode option to hwmode. 11a and 11g aren't the only ...Andy Boyett2008-12-311-4/+4
* build.tex: fix formatting of example in build environments sectionAndy Boyett2008-12-301-1/+1
* update wireless documentation to reflect broadcom config changesJo-Philipp Wich2008-11-161-10/+16
* Document how to build binary ipkgsFlorian Fainelli2008-09-301-0/+53
* Document madwifi specific diversity and antenna settings (#3889Florian Fainelli2008-09-271-0/+8
* Add 802.1x client configuration support and corresponding documentation (#2069)Florian Fainelli2008-08-111-0/+33
* Support encrypted WDS connections (#2463)Florian Fainelli2008-08-091-1/+62
* add script for managing 'build environments' (.config+files/), including docu...Felix Fietkau2008-08-061-0/+57
* cosmetic fix for docs/ prereq checkFelix Fietkau2008-08-061-1/+1
* Update wireless documentationFlorian Fainelli2008-07-261-6/+29
* Document a bit more about IPv6 configurationFlorian Fainelli2008-07-211-2/+19
* fix a small dep bug in the docs/ dirFelix Fietkau2008-06-091-1/+2
* Fix docs build from a clean checkout, closes #2975Andy Boyett2008-01-071-1/+2
* Trivial patch to correct typos and spelling errors in OpenWRT documentation.\...Florian Fainelli2008-01-062-11/+11
* document the feeds systemFelix Fietkau2007-12-281-6/+8
* update build directory pathsFelix Fietkau2007-12-281-10/+10
* update target namesFelix Fietkau2007-12-281-5/+5
* remove some obsolete stuffFelix Fietkau2007-12-281-6/+0
* remove old 'Submitting patches' textFelix Fietkau2007-12-281-15/+0
* add SubmittingPatches document to docs/Felix Fietkau2007-12-283-1/+56
* START is no longer optional for /etc/rc.common based init scriptsFelix Fietkau2007-12-281-2/+1
* document Build/UninstallDevFelix Fietkau2007-12-281-0/+6
* document Build/InstallDevFelix Fietkau2007-12-281-0/+13
* add .gitignore for docs/Felix Fietkau2007-12-281-0/+15
* Allow specifying static IPv6 gateways (#2710)Florian Fainelli2007-11-161-0/+1
* Document the IPv6 connectivity with OpenWrt.Florian Fainelli2007-10-262-1/+47
* apply latex style patch posted on openwrt-devel@listsFelix Fietkau2007-09-292-7/+11
* Fix the option dns usages (#2174)Florian Fainelli2007-08-061-0/+2
* document config_foreach, close #2162Felix Fietkau2007-08-041-0/+19
href='#n550'>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 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263
// SPDX-License-Identifier: GPL-2.0+
#include <linux/bitfield.h>
#include <linux/module.h>
#include <linux/nvmem-consumer.h>
#include <linux/of_address.h>
#include <linux/of_platform.h>
#include <linux/pinctrl/consumer.h>
#include <linux/phy.h>

#define MTK_GPHY_ID_MT7981			0x03a29461
#define MTK_GPHY_ID_MT7988			0x03a29481

#define MTK_EXT_PAGE_ACCESS			0x1f
#define MTK_PHY_PAGE_STANDARD			0x0000
#define MTK_PHY_PAGE_EXTENDED_3			0x0003

#define MTK_PHY_LPI_REG_14			0x14
#define MTK_PHY_LPI_WAKE_TIMER_1000_MASK	GENMASK(8, 0)

#define MTK_PHY_LPI_REG_1c			0x1c
#define MTK_PHY_SMI_DET_ON_THRESH_MASK		GENMASK(13, 8)

#define MTK_PHY_PAGE_EXTENDED_2A30		0x2a30
#define MTK_PHY_PAGE_EXTENDED_52B5		0x52b5

#define ANALOG_INTERNAL_OPERATION_MAX_US	20
#define TXRESERVE_MIN				0
#define TXRESERVE_MAX				7

#define MTK_PHY_ANARG_RG			0x10
#define   MTK_PHY_TCLKOFFSET_MASK		GENMASK(12, 8)

/* Registers on MDIO_MMD_VEND1 */
#define MTK_PHY_TXVLD_DA_RG			0x12
#define   MTK_PHY_DA_TX_I2MPB_A_GBE_MASK	GENMASK(15, 10)
#define   MTK_PHY_DA_TX_I2MPB_A_TBT_MASK	GENMASK(5, 0)

#define MTK_PHY_TX_I2MPB_TEST_MODE_A2		0x16
#define   MTK_PHY_DA_TX_I2MPB_A_HBT_MASK	GENMASK(15, 10)
#define   MTK_PHY_DA_TX_I2MPB_A_TST_MASK	GENMASK(5, 0)

#define MTK_PHY_TX_I2MPB_TEST_MODE_B1		0x17
#define   MTK_PHY_DA_TX_I2MPB_B_GBE_MASK	GENMASK(13, 8)
#define   MTK_PHY_DA_TX_I2MPB_B_TBT_MASK	GENMASK(5, 0)

#define MTK_PHY_TX_I2MPB_TEST_MODE_B2		0x18
#define   MTK_PHY_DA_TX_I2MPB_B_HBT_MASK	GENMASK(13, 8)
#define   MTK_PHY_DA_TX_I2MPB_B_TST_MASK	GENMASK(5, 0)

#define MTK_PHY_TX_I2MPB_TEST_MODE_C1		0x19
#define   MTK_PHY_DA_TX_I2MPB_C_GBE_MASK	GENMASK(13, 8)
#define   MTK_PHY_DA_TX_I2MPB_C_TBT_MASK	GENMASK(5, 0)

#define MTK_PHY_TX_I2MPB_TEST_MODE_C2		0x20
#define   MTK_PHY_DA_TX_I2MPB_C_HBT_MASK	GENMASK(13, 8)
#define   MTK_PHY_DA_TX_I2MPB_C_TST_MASK	GENMASK(5, 0)

#define MTK_PHY_TX_I2MPB_TEST_MODE_D1		0x21
#define   MTK_PHY_DA_TX_I2MPB_D_GBE_MASK	GENMASK(13, 8)
#define   MTK_PHY_DA_TX_I2MPB_D_TBT_MASK	GENMASK(5, 0)

#define MTK_PHY_TX_I2MPB_TEST_MODE_D2		0x22
#define   MTK_PHY_DA_TX_I2MPB_D_HBT_MASK	GENMASK(13, 8)
#define   MTK_PHY_DA_TX_I2MPB_D_TST_MASK	GENMASK(5, 0)

#define MTK_PHY_RXADC_CTRL_RG7			0xc6
#define   MTK_PHY_DA_AD_BUF_BIAS_LP_MASK	GENMASK(9, 8)

#define MTK_PHY_RXADC_CTRL_RG9			0xc8
#define   MTK_PHY_DA_RX_PSBN_TBT_MASK		GENMASK(14, 12)
#define   MTK_PHY_DA_RX_PSBN_HBT_MASK		GENMASK(10, 8)
#define   MTK_PHY_DA_RX_PSBN_GBE_MASK		GENMASK(6, 4)
#define   MTK_PHY_DA_RX_PSBN_LP_MASK		GENMASK(2, 0)

#define MTK_PHY_LDO_OUTPUT_V			0xd7

#define MTK_PHY_RG_ANA_CAL_RG0			0xdb
#define   MTK_PHY_RG_CAL_CKINV			BIT(12)
#define   MTK_PHY_RG_ANA_CALEN			BIT(8)
#define   MTK_PHY_RG_ZCALEN_A			BIT(0)

#define MTK_PHY_RG_ANA_CAL_RG1			0xdc
#define   MTK_PHY_RG_ZCALEN_B			BIT(12)
#define   MTK_PHY_RG_ZCALEN_C			BIT(8)
#define   MTK_PHY_RG_ZCALEN_D			BIT(4)
#define   MTK_PHY_RG_TXVOS_CALEN		BIT(0)

#define MTK_PHY_RG_ANA_CAL_RG5			0xe0
#define   MTK_PHY_RG_REXT_TRIM_MASK		GENMASK(13, 8)

#define MTK_PHY_RG_TX_FILTER			0xfe

#define MTK_PHY_RG_LPI_PCS_DSP_CTRL_REG120	0x120
#define   MTK_PHY_LPI_SIG_EN_LO_THRESH1000_MASK	GENMASK(12, 8)
#define   MTK_PHY_LPI_SIG_EN_HI_THRESH1000_MASK	GENMASK(4, 0)

#define MTK_PHY_RG_LPI_PCS_DSP_CTRL_REG122	0x122
#define   MTK_PHY_LPI_NORM_MSE_HI_THRESH1000_MASK	GENMASK(7, 0)

#define MTK_PHY_RG_TESTMUX_ADC_CTRL		0x144
#define   MTK_PHY_RG_TXEN_DIG_MASK		GENMASK(5, 5)

#define MTK_PHY_RG_CR_TX_AMP_OFFSET_A_B		0x172
#define   MTK_PHY_CR_TX_AMP_OFFSET_A_MASK	GENMASK(13, 8)
#define   MTK_PHY_CR_TX_AMP_OFFSET_B_MASK	GENMASK(6, 0)

#define MTK_PHY_RG_CR_TX_AMP_OFFSET_C_D		0x173
#define   MTK_PHY_CR_TX_AMP_OFFSET_C_MASK	GENMASK(13, 8)
#define   MTK_PHY_CR_TX_AMP_OFFSET_D_MASK	GENMASK(6, 0)

#define MTK_PHY_RG_AD_CAL_COMP			0x17a
#define   MTK_PHY_AD_CAL_COMP_OUT_SHIFT		(8)

#define MTK_PHY_RG_AD_CAL_CLK			0x17b
#define   MTK_PHY_DA_CAL_CLK			BIT(0)

#define MTK_PHY_RG_AD_CALIN			0x17c
#define   MTK_PHY_DA_CALIN_FLAG			BIT(0)

#define MTK_PHY_RG_DASN_DAC_IN0_A		0x17d
#define   MTK_PHY_DASN_DAC_IN0_A_MASK		GENMASK(9, 0)

#define MTK_PHY_RG_DASN_DAC_IN0_B		0x17e
#define   MTK_PHY_DASN_DAC_IN0_B_MASK		GENMASK(9, 0)

#define MTK_PHY_RG_DASN_DAC_IN0_C		0x17f
#define   MTK_PHY_DASN_DAC_IN0_C_MASK		GENMASK(9, 0)

#define MTK_PHY_RG_DASN_DAC_IN0_D		0x180
#define   MTK_PHY_DASN_DAC_IN0_D_MASK		GENMASK(9, 0)

#define MTK_PHY_RG_DASN_DAC_IN1_A		0x181
#define   MTK_PHY_DASN_DAC_IN1_A_MASK		GENMASK(9, 0)

#define MTK_PHY_RG_DASN_DAC_IN1_B		0x182
#define   MTK_PHY_DASN_DAC_IN1_B_MASK		GENMASK(9, 0)

#define MTK_PHY_RG_DASN_DAC_IN1_C		0x183
#define   MTK_PHY_DASN_DAC_IN1_C_MASK		GENMASK(9, 0)

#define MTK_PHY_RG_DASN_DAC_IN1_D		0x184
#define   MTK_PHY_DASN_DAC_IN1_D_MASK		GENMASK(9, 0)

#define MTK_PHY_RG_DEV1E_REG19b			0x19b
#define   MTK_PHY_BYPASS_DSP_LPI_READY		BIT(8)

#define MTK_PHY_RG_LP_IIR2_K1_L			0x22a
#define MTK_PHY_RG_LP_IIR2_K1_U			0x22b
#define MTK_PHY_RG_LP_IIR2_K2_L			0x22c
#define MTK_PHY_RG_LP_IIR2_K2_U			0x22d
#define MTK_PHY_RG_LP_IIR2_K3_L			0x22e
#define MTK_PHY_RG_LP_IIR2_K3_U			0x22f
#define MTK_PHY_RG_LP_IIR2_K4_L			0x230
#define MTK_PHY_RG_LP_IIR2_K4_U			0x231
#define MTK_PHY_RG_LP_IIR2_K5_L			0x232
#define MTK_PHY_RG_LP_IIR2_K5_U			0x233

#define MTK_PHY_RG_DEV1E_REG234			0x234
#define   MTK_PHY_TR_OPEN_LOOP_EN_MASK		GENMASK(0, 0)
#define   MTK_PHY_LPF_X_AVERAGE_MASK		GENMASK(7, 4)
#define   MTK_PHY_TR_LP_IIR_EEE_EN		BIT(12)

#define MTK_PHY_RG_LPF_CNT_VAL			0x235

#define MTK_PHY_RG_DEV1E_REG238			0x238
#define   MTK_PHY_LPI_SLV_SEND_TX_TIMER_MASK	GENMASK(8, 0)
#define   MTK_PHY_LPI_SLV_SEND_TX_EN		BIT(12)

#define MTK_PHY_RG_DEV1E_REG239			0x239
#define   MTK_PHY_LPI_SEND_LOC_TIMER_MASK	GENMASK(8, 0)
#define   MTK_PHY_LPI_TXPCS_LOC_RCV		BIT(12)

#define MTK_PHY_RG_DEV1E_REG27C			0x27c
#define   MTK_PHY_VGASTATE_FFE_THR_ST1_MASK	GENMASK(12, 8)
#define MTK_PHY_RG_DEV1E_REG27D			0x27d
#define   MTK_PHY_VGASTATE_FFE_THR_ST2_MASK	GENMASK(4, 0)

#define MTK_PHY_RG_DEV1E_REG2C7			0x2c7
#define   MTK_PHY_MAX_GAIN_MASK			GENMASK(4, 0)
#define   MTK_PHY_MIN_GAIN_MASK			GENMASK(12, 8)

#define MTK_PHY_RG_DEV1E_REG2D1			0x2d1
#define   MTK_PHY_VCO_SLICER_THRESH_BITS_HIGH_EEE_MASK	GENMASK(7, 0)
#define   MTK_PHY_LPI_SKIP_SD_SLV_TR		BIT(8)
#define   MTK_PHY_LPI_TR_READY			BIT(9)
#define   MTK_PHY_LPI_VCO_EEE_STG0_EN		BIT(10)

#define MTK_PHY_RG_DEV1E_REG323			0x323
#define   MTK_PHY_EEE_WAKE_MAS_INT_DC		BIT(0)
#define   MTK_PHY_EEE_WAKE_SLV_INT_DC		BIT(4)

#define MTK_PHY_RG_DEV1E_REG324			0x324
#define   MTK_PHY_SMI_DETCNT_MAX_MASK		GENMASK(5, 0)
#define   MTK_PHY_SMI_DET_MAX_EN		BIT(8)

#define MTK_PHY_RG_DEV1E_REG326			0x326
#define   MTK_PHY_LPI_MODE_SD_ON		BIT(0)
#define   MTK_PHY_RESET_RANDUPD_CNT		BIT(1)
#define   MTK_PHY_TREC_UPDATE_ENAB_CLR		BIT(2)
#define   MTK_PHY_LPI_QUIT_WAIT_DFE_SIG_DET_OFF	BIT(4)
#define   MTK_PHY_TR_READY_SKIP_AFE_WAKEUP	BIT(5)

#define MTK_PHY_LDO_PUMP_EN_PAIRAB		0x502
#define MTK_PHY_LDO_PUMP_EN_PAIRCD		0x503

#define MTK_PHY_DA_TX_R50_PAIR_A		0x53d
#define MTK_PHY_DA_TX_R50_PAIR_B		0x53e
#define MTK_PHY_DA_TX_R50_PAIR_C		0x53f
#define MTK_PHY_DA_TX_R50_PAIR_D		0x540

/* Registers on MDIO_MMD_VEND2 */
#define MTK_PHY_LED0_ON_CTRL			0x24
#define   MTK_PHY_LED0_ON_MASK			GENMASK(6, 0)
#define   MTK_PHY_LED0_ON_LINK1000		BIT(0)
#define   MTK_PHY_LED0_ON_LINK100		BIT(1)
#define   MTK_PHY_LED0_ON_LINK10		BIT(2)
#define   MTK_PHY_LED0_ON_LINKDOWN		BIT(3)
#define   MTK_PHY_LED0_ON_FDX			BIT(4) /* Full duplex */
#define   MTK_PHY_LED0_ON_HDX			BIT(5) /* Half duplex */
#define   MTK_PHY_LED0_FORCE_ON			BIT(6)
#define   MTK_PHY_LED0_POLARITY			BIT(14)
#define   MTK_PHY_LED0_ENABLE			BIT(15)

#define MTK_PHY_LED0_BLINK_CTRL			0x25
#define   MTK_PHY_LED0_1000TX			BIT(0)
#define   MTK_PHY_LED0_1000RX			BIT(1)
#define   MTK_PHY_LED0_100TX			BIT(2)
#define   MTK_PHY_LED0_100RX			BIT(3)
#define   MTK_PHY_LED0_10TX			BIT(4)
#define   MTK_PHY_LED0_10RX			BIT(5)
#define   MTK_PHY_LED0_COLLISION		BIT(6)
#define   MTK_PHY_LED0_RX_CRC_ERR		BIT(7)
#define   MTK_PHY_LED0_RX_IDLE_ERR		BIT(8)
#define   MTK_PHY_LED0_FORCE_BLINK		BIT(9)

#define MTK_PHY_LED1_ON_CTRL			0x26
#define   MTK_PHY_LED1_ON_MASK			GENMASK(6, 0)
#define   MTK_PHY_LED1_ON_LINK1000		BIT(0)
#define   MTK_PHY_LED1_ON_LINK100		BIT(1)
#define   MTK_PHY_LED1_ON_LINK10		BIT(2)
#define   MTK_PHY_LED1_ON_LINKDOWN		BIT(3)
#define   MTK_PHY_LED1_ON_FDX			BIT(4) /* Full duplex */
#define   MTK_PHY_LED1_ON_HDX			BIT(5) /* Half duplex */
#define   MTK_PHY_LED1_FORCE_ON			BIT(6)
#define   MTK_PHY_LED1_POLARITY			BIT(14)
#define   MTK_PHY_LED1_ENABLE			BIT(15)

#define MTK_PHY_LED1_BLINK_CTRL			0x27
#define   MTK_PHY_LED1_1000TX			BIT(0)
#define   MTK_PHY_LED1_1000RX			BIT(1)
#define   MTK_PHY_LED1_100TX			BIT(2)
#define   MTK_PHY_LED1_100RX			BIT(3)
#define   MTK_PHY_LED1_10TX			BIT(4)
#define   MTK_PHY_LED1_10RX			BIT(5)
#define   MTK_PHY_LED1_COLLISION		BIT(6)
#define   MTK_PHY_LED1_RX_CRC_ERR		BIT(7)
#define   MTK_PHY_LED1_RX_IDLE_ERR		BIT(8)
#define   MTK_PHY_LED1_FORCE_BLINK		BIT(9)

#define MTK_PHY_RG_BG_RASEL			0x115
#define   MTK_PHY_RG_BG_RASEL_MASK		GENMASK(2, 0)

/* These macro privides efuse parsing for internal phy. */
#define EFS_DA_TX_I2MPB_A(x)			(((x) >> 0) & GENMASK(5, 0))
#define EFS_DA_TX_I2MPB_B(x)			(((x) >> 6) & GENMASK(5, 0))
#define EFS_DA_TX_I2MPB_C(x)			(((x) >> 12) & GENMASK(5, 0))
#define EFS_DA_TX_I2MPB_D(x)			(((x) >> 18) & GENMASK(5, 0))
#define EFS_DA_TX_AMP_OFFSET_A(x)		(((x) >> 24) & GENMASK(5, 0))

#define EFS_DA_TX_AMP_OFFSET_B(x)		(((x) >> 0) & GENMASK(5, 0))
#define EFS_DA_TX_AMP_OFFSET_C(x)		(((x) >> 6) & GENMASK(5, 0))
#define EFS_DA_TX_AMP_OFFSET_D(x)		(((x) >> 12) & GENMASK(5, 0))
#define EFS_DA_TX_R50_A(x)			(((x) >> 18) & GENMASK(5, 0))
#define EFS_DA_TX_R50_B(x)			(((x) >> 24) & GENMASK(5, 0))

#define EFS_DA_TX_R50_C(x)			(((x) >> 0) & GENMASK(5, 0))
#define EFS_DA_TX_R50_D(x)			(((x) >> 6) & GENMASK(5, 0))

#define EFS_RG_BG_RASEL(x)			(((x) >> 4) & GENMASK(2, 0))
#define EFS_RG_REXT_TRIM(x)			(((x) >> 7) & GENMASK(5, 0))

enum {
	NO_PAIR,
	PAIR_A,
	PAIR_B,
	PAIR_C,
	PAIR_D,
};

enum {
	GPHY_PORT0,
	GPHY_PORT1,
	GPHY_PORT2,
	GPHY_PORT3,
};

enum calibration_mode {
	EFUSE_K,
	SW_K
};

enum CAL_ITEM {
	REXT,
	TX_OFFSET,
	TX_AMP,
	TX_R50,
	TX_VCM
};

enum CAL_MODE {
	EFUSE_M,
	SW_M
};

struct mtk_socphy_shared_priv {
	u32			boottrap;
};

static int mtk_socphy_read_page(struct phy_device *phydev)
{
	return __phy_read(phydev, MTK_EXT_PAGE_ACCESS);
}

static int mtk_socphy_write_page(struct phy_device *phydev, int page)
{
	return __phy_write(phydev, MTK_EXT_PAGE_ACCESS, page);
}

/* One calibration cycle consists of:
 * 1.Set DA_CALIN_FLAG high to start calibration. Keep it high
 *   until AD_CAL_COMP is ready to output calibration result.
 * 2.Wait until DA_CAL_CLK is available.
 * 3.Fetch AD_CAL_COMP_OUT.
 */
static int cal_cycle(struct phy_device *phydev, int devad,
		     u32 regnum, u16 mask, u16 cal_val)
{
	int reg_val;
	int ret;

	phy_modify_mmd(phydev, devad, regnum,
		       mask, cal_val);
	phy_set_bits_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_AD_CALIN,
			 MTK_PHY_DA_CALIN_FLAG);

	ret = phy_read_mmd_poll_timeout(phydev, MDIO_MMD_VEND1,
					MTK_PHY_RG_AD_CAL_CLK, reg_val,
					reg_val & MTK_PHY_DA_CAL_CLK, 500,
					ANALOG_INTERNAL_OPERATION_MAX_US, false);
	if (ret) {
		phydev_err(phydev, "Calibration cycle timeout\n");
		return ret;
	}

	phy_clear_bits_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_AD_CALIN,
			   MTK_PHY_DA_CALIN_FLAG);
	ret = phy_read_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_AD_CAL_COMP) >>
			   MTK_PHY_AD_CAL_COMP_OUT_SHIFT;
	phydev_dbg(phydev, "cal_val: 0x%x, ret: %d\n", cal_val, ret);

	return ret;
}

static int rext_fill_result(struct phy_device *phydev, u16 *buf)
{
	phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_ANA_CAL_RG5,
		       MTK_PHY_RG_REXT_TRIM_MASK, buf[0] << 8);
	phy_modify_mmd(phydev, MDIO_MMD_VEND2, MTK_PHY_RG_BG_RASEL,
		       MTK_PHY_RG_BG_RASEL_MASK, buf[1]);

	return 0;
}

static int rext_cal_efuse(struct phy_device *phydev, u32 *buf)
{
	u16 rext_cal_val[2];

	rext_cal_val[0] = EFS_RG_REXT_TRIM(buf[3]);
	rext_cal_val[1] = EFS_RG_BG_RASEL(buf[3]);
	rext_fill_result(phydev, rext_cal_val);

	return 0;
}

static int tx_offset_fill_result(struct phy_device *phydev, u16 *buf)
{
	phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_CR_TX_AMP_OFFSET_A_B,
		       MTK_PHY_CR_TX_AMP_OFFSET_A_MASK, buf[0] << 8);
	phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_CR_TX_AMP_OFFSET_A_B,
		       MTK_PHY_CR_TX_AMP_OFFSET_B_MASK, buf[1]);
	phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_CR_TX_AMP_OFFSET_C_D,
		       MTK_PHY_CR_TX_AMP_OFFSET_C_MASK, buf[2] << 8);
	phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_CR_TX_AMP_OFFSET_C_D,
		       MTK_PHY_CR_TX_AMP_OFFSET_D_MASK, buf[3]);

	return 0;
}

static int tx_offset_cal_efuse(struct phy_device *phydev, u32 *buf)
{
	u16 tx_offset_cal_val[4];

	tx_offset_cal_val[0] = EFS_DA_TX_AMP_OFFSET_A(buf[0]);
	tx_offset_cal_val[1] = EFS_DA_TX_AMP_OFFSET_B(buf[1]);
	tx_offset_cal_val[2] = EFS_DA_TX_AMP_OFFSET_C(buf[1]);
	tx_offset_cal_val[3] = EFS_DA_TX_AMP_OFFSET_D(buf[1]);

	tx_offset_fill_result(phydev, tx_offset_cal_val);

	return 0;
}

static int tx_amp_fill_result(struct phy_device *phydev, u16 *buf)
{
	int i;
	int bias[16] = {};
	const int vals_9461[16] = { 7, 1, 4, 7,
				    7, 1, 4, 7,
				    7, 1, 4, 7,
				    7, 1, 4, 7 };
	const int vals_9481[16] = { 10, 6, 6, 10,
				    10, 6, 6, 10,
				    10, 6, 6, 10,
				    10, 6, 6, 10 };
	switch (phydev->drv->phy_id) {
	case MTK_GPHY_ID_MT7981:
		/* We add some calibration to efuse values
		 * due to board level influence.
		 * GBE: +7, TBT: +1, HBT: +4, TST: +7
		 */
		memcpy(bias, (const void *)vals_9461, sizeof(bias));
		break;
	case MTK_GPHY_ID_MT7988:
		memcpy(bias, (const void *)vals_9481, sizeof(bias));
		break;
	}

	/* Prevent overflow */
	for (i = 0; i < 12; i++) {
		if (buf[i >> 2] + bias[i] > 63) {
			buf[i >> 2] = 63;
			bias[i] = 0;
		}
	}

	phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_TXVLD_DA_RG,
		       MTK_PHY_DA_TX_I2MPB_A_GBE_MASK, (buf[0] + bias[0]) << 10);
	phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_TXVLD_DA_RG,
		       MTK_PHY_DA_TX_I2MPB_A_TBT_MASK, buf[0] + bias[1]);
	phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_TX_I2MPB_TEST_MODE_A2,
		       MTK_PHY_DA_TX_I2MPB_A_HBT_MASK, (buf[0] + bias[2]) << 10);
	phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_TX_I2MPB_TEST_MODE_A2,
		       MTK_PHY_DA_TX_I2MPB_A_TST_MASK, buf[0] + bias[3]);

	phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_TX_I2MPB_TEST_MODE_B1,
		       MTK_PHY_DA_TX_I2MPB_B_GBE_MASK, (buf[1] + bias[4]) << 8);
	phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_TX_I2MPB_TEST_MODE_B1,
		       MTK_PHY_DA_TX_I2MPB_B_TBT_MASK, buf[1] + bias[5]);
	phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_TX_I2MPB_TEST_MODE_B2,
		       MTK_PHY_DA_TX_I2MPB_B_HBT_MASK, (buf[1] + bias[6]) << 8);
	phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_TX_I2MPB_TEST_MODE_B2,
		       MTK_PHY_DA_TX_I2MPB_B_TST_MASK, buf[1] + bias[7]);

	phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_TX_I2MPB_TEST_MODE_C1,
		       MTK_PHY_DA_TX_I2MPB_C_GBE_MASK, (buf[2] + bias[8]) << 8);
	phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_TX_I2MPB_TEST_MODE_C1,
		       MTK_PHY_DA_TX_I2MPB_C_TBT_MASK, buf[2] + bias[9]);
	phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_TX_I2MPB_TEST_MODE_C2,
		       MTK_PHY_DA_TX_I2MPB_C_HBT_MASK, (buf[2] + bias[10]) << 8);
	phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_TX_I2MPB_TEST_MODE_C2,
		       MTK_PHY_DA_TX_I2MPB_C_TST_MASK, buf[2] + bias[11]);

	phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_TX_I2MPB_TEST_MODE_D1,
		       MTK_PHY_DA_TX_I2MPB_D_GBE_MASK, (buf[3] + bias[12]) << 8);
	phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_TX_I2MPB_TEST_MODE_D1,
		       MTK_PHY_DA_TX_I2MPB_D_TBT_MASK, buf[3] + bias[13]);
	phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_TX_I2MPB_TEST_MODE_D2,
		       MTK_PHY_DA_TX_I2MPB_D_HBT_MASK, (buf[3] + bias[14]) << 8);
	phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_TX_I2MPB_TEST_MODE_D2,
		       MTK_PHY_DA_TX_I2MPB_D_TST_MASK, buf[3] + bias[15]);

	return 0;
}

static int tx_amp_cal_efuse(struct phy_device *phydev, u32 *buf)
{
	u16 tx_amp_cal_val[4];

	tx_amp_cal_val[0] = EFS_DA_TX_I2MPB_A(buf[0]);
	tx_amp_cal_val[1] = EFS_DA_TX_I2MPB_B(buf[0]);
	tx_amp_cal_val[2] = EFS_DA_TX_I2MPB_C(buf[0]);
	tx_amp_cal_val[3] = EFS_DA_TX_I2MPB_D(buf[0]);
	tx_amp_fill_result(phydev, tx_amp_cal_val);

	return 0;
}

static int tx_r50_fill_result(struct phy_device *phydev, u16 tx_r50_cal_val,
			      u8 txg_calen_x)
{
	int bias = 0;
	u16 reg, val;

	if (phydev->drv->phy_id == MTK_GPHY_ID_MT7988)
		bias = -2;

	val = clamp_val(bias + tx_r50_cal_val, 0, 63);

	switch (txg_calen_x) {
	case PAIR_A:
		reg = MTK_PHY_DA_TX_R50_PAIR_A;
		break;
	case PAIR_B:
		reg = MTK_PHY_DA_TX_R50_PAIR_B;
		break;
	case PAIR_C:
		reg = MTK_PHY_DA_TX_R50_PAIR_C;
		break;
	case PAIR_D:
		reg = MTK_PHY_DA_TX_R50_PAIR_D;
		break;
	default:
		return -EINVAL;
	}

	phy_write_mmd(phydev, MDIO_MMD_VEND1, reg, val | val << 8);

	return 0;
}

static int tx_r50_cal_efuse(struct phy_device *phydev, u32 *buf,
			    u8 txg_calen_x)
{
	u16 tx_r50_cal_val;

	switch (txg_calen_x) {
	case PAIR_A:
		tx_r50_cal_val = EFS_DA_TX_R50_A(buf[1]);
		break;
	case PAIR_B:
		tx_r50_cal_val = EFS_DA_TX_R50_B(buf[1]);
		break;
	case PAIR_C:
		tx_r50_cal_val = EFS_DA_TX_R50_C(buf[2]);
		break;
	case PAIR_D:
		tx_r50_cal_val = EFS_DA_TX_R50_D(buf[2]);
		break;
	default:
		return -EINVAL;
	}
	tx_r50_fill_result(phydev, tx_r50_cal_val, txg_calen_x);

	return 0;
}

static int tx_vcm_cal_sw(struct phy_device *phydev, u8 rg_txreserve_x)
{
	u8 lower_idx, upper_idx, txreserve_val;
	u8 lower_ret, upper_ret;
	int ret;

	phy_set_bits_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_ANA_CAL_RG0,
			 MTK_PHY_RG_ANA_CALEN);
	phy_clear_bits_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_ANA_CAL_RG0,
			   MTK_PHY_RG_CAL_CKINV);
	phy_set_bits_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_ANA_CAL_RG1,
			 MTK_PHY_RG_TXVOS_CALEN);

	switch (rg_txreserve_x) {
	case PAIR_A:
		phy_clear_bits_mmd(phydev, MDIO_MMD_VEND1,
				   MTK_PHY_RG_DASN_DAC_IN0_A,
				   MTK_PHY_DASN_DAC_IN0_A_MASK);
		phy_clear_bits_mmd(phydev, MDIO_MMD_VEND1,
				   MTK_PHY_RG_DASN_DAC_IN1_A,
				   MTK_PHY_DASN_DAC_IN1_A_MASK);
		phy_set_bits_mmd(phydev, MDIO_MMD_VEND1,
				 MTK_PHY_RG_ANA_CAL_RG0,
				 MTK_PHY_RG_ZCALEN_A);
		break;
	case PAIR_B:
		phy_clear_bits_mmd(phydev, MDIO_MMD_VEND1,
				   MTK_PHY_RG_DASN_DAC_IN0_B,
				   MTK_PHY_DASN_DAC_IN0_B_MASK);
		phy_clear_bits_mmd(phydev, MDIO_MMD_VEND1,
				   MTK_PHY_RG_DASN_DAC_IN1_B,
				   MTK_PHY_DASN_DAC_IN1_B_MASK);
		phy_set_bits_mmd(phydev, MDIO_MMD_VEND1,
				 MTK_PHY_RG_ANA_CAL_RG1,
				 MTK_PHY_RG_ZCALEN_B);
		break;
	case PAIR_C:
		phy_clear_bits_mmd(phydev, MDIO_MMD_VEND1,
				   MTK_PHY_RG_DASN_DAC_IN0_C,
				   MTK_PHY_DASN_DAC_IN0_C_MASK);
		phy_clear_bits_mmd(phydev, MDIO_MMD_VEND1,
				   MTK_PHY_RG_DASN_DAC_IN1_C,
				   MTK_PHY_DASN_DAC_IN1_C_MASK);
		phy_set_bits_mmd(phydev, MDIO_MMD_VEND1,
				 MTK_PHY_RG_ANA_CAL_RG1,
				 MTK_PHY_RG_ZCALEN_C);
		break;
	case PAIR_D:
		phy_clear_bits_mmd(phydev, MDIO_MMD_VEND1,
				   MTK_PHY_RG_DASN_DAC_IN0_D,
				   MTK_PHY_DASN_DAC_IN0_D_MASK);
		phy_clear_bits_mmd(phydev, MDIO_MMD_VEND1,
				   MTK_PHY_RG_DASN_DAC_IN1_D,
				   MTK_PHY_DASN_DAC_IN1_D_MASK);
		phy_set_bits_mmd(phydev, MDIO_MMD_VEND1,
				 MTK_PHY_RG_ANA_CAL_RG1,
				 MTK_PHY_RG_ZCALEN_D);
		break;
	default:
		ret = -EINVAL;
		goto restore;
	}

	lower_idx = TXRESERVE_MIN;
	upper_idx = TXRESERVE_MAX;

	phydev_dbg(phydev, "Start TX-VCM SW cal.\n");
	while ((upper_idx - lower_idx) > 1) {
		txreserve_val = DIV_ROUND_CLOSEST(lower_idx + upper_idx, 2);
		ret = cal_cycle(phydev, MDIO_MMD_VEND1, MTK_PHY_RXADC_CTRL_RG9,
				MTK_PHY_DA_RX_PSBN_TBT_MASK |
				MTK_PHY_DA_RX_PSBN_HBT_MASK |
				MTK_PHY_DA_RX_PSBN_GBE_MASK |
				MTK_PHY_DA_RX_PSBN_LP_MASK,
				txreserve_val << 12 | txreserve_val << 8 |
				txreserve_val << 4 | txreserve_val);
		if (ret == 1) {
			upper_idx = txreserve_val;
			upper_ret = ret;
		} else if (ret == 0) {
			lower_idx = txreserve_val;
			lower_ret = ret;
		} else {
			goto restore;
		}
	}

	if (lower_idx == TXRESERVE_MIN) {
		lower_ret = cal_cycle(phydev, MDIO_MMD_VEND1,
				      MTK_PHY_RXADC_CTRL_RG9,
				      MTK_PHY_DA_RX_PSBN_TBT_MASK |
				      MTK_PHY_DA_RX_PSBN_HBT_MASK |
				      MTK_PHY_DA_RX_PSBN_GBE_MASK |
				      MTK_PHY_DA_RX_PSBN_LP_MASK,
				      lower_idx << 12 | lower_idx << 8 |
				      lower_idx << 4 | lower_idx);
		ret = lower_ret;
	} else if (upper_idx == TXRESERVE_MAX) {
		upper_ret = cal_cycle(phydev, MDIO_MMD_VEND1,
				      MTK_PHY_RXADC_CTRL_RG9,
				      MTK_PHY_DA_RX_PSBN_TBT_MASK |
				      MTK_PHY_DA_RX_PSBN_HBT_MASK |
				      MTK_PHY_DA_RX_PSBN_GBE_MASK |
				      MTK_PHY_DA_RX_PSBN_LP_MASK,
				      upper_idx << 12 | upper_idx << 8 |
				      upper_idx << 4 | upper_idx);
		ret = upper_ret;
	}
	if (ret < 0)
		goto restore;

	/* We calibrate TX-VCM in different logic. Check upper index and then
	 * lower index. If this calibration is valid, apply lower index's result.
	 */
	ret = upper_ret - lower_ret;
	if (ret == 1) {
		ret = 0;
		/* Make sure we use upper_idx in our calibration system */
		cal_cycle(phydev, MDIO_MMD_VEND1, MTK_PHY_RXADC_CTRL_RG9,
			  MTK_PHY_DA_RX_PSBN_TBT_MASK |
			  MTK_PHY_DA_RX_PSBN_HBT_MASK |
			  MTK_PHY_DA_RX_PSBN_GBE_MASK |
			  MTK_PHY_DA_RX_PSBN_LP_MASK,
			  upper_idx << 12 | upper_idx << 8 |
			  upper_idx << 4 | upper_idx);
		phydev_dbg(phydev, "TX-VCM SW cal result: 0x%x\n", upper_idx);
	} else if (lower_idx == TXRESERVE_MIN && upper_ret == 1 &&
		   lower_ret == 1) {
		ret = 0;
		cal_cycle(phydev, MDIO_MMD_VEND1, MTK_PHY_RXADC_CTRL_RG9,
			  MTK_PHY_DA_RX_PSBN_TBT_MASK |
			  MTK_PHY_DA_RX_PSBN_HBT_MASK |
			  MTK_PHY_DA_RX_PSBN_GBE_MASK |
			  MTK_PHY_DA_RX_PSBN_LP_MASK,
			  lower_idx << 12 | lower_idx << 8 |
			  lower_idx << 4 | lower_idx);
		phydev_warn(phydev, "TX-VCM SW cal result at low margin 0x%x\n",
			    lower_idx);
	} else if (upper_idx == TXRESERVE_MAX && upper_ret == 0 &&
		   lower_ret == 0) {
		ret = 0;
		phydev_warn(phydev, "TX-VCM SW cal result at high margin 0x%x\n",
			    upper_idx);
	} else {
		ret = -EINVAL;
	}

restore:
	phy_clear_bits_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_ANA_CAL_RG0,
			   MTK_PHY_RG_ANA_CALEN);
	phy_clear_bits_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_ANA_CAL_RG1,
			   MTK_PHY_RG_TXVOS_CALEN);
	phy_clear_bits_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_ANA_CAL_RG0,
			   MTK_PHY_RG_ZCALEN_A);
	phy_clear_bits_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_ANA_CAL_RG1,
			   MTK_PHY_RG_ZCALEN_B | MTK_PHY_RG_ZCALEN_C |
			   MTK_PHY_RG_ZCALEN_D);

	return ret;
}

static void mt798x_phy_common_finetune(struct phy_device *phydev)
{
	phy_select_page(phydev, MTK_PHY_PAGE_EXTENDED_52B5);
	/* EnabRandUpdTrig = 1 */
	__phy_write(phydev, 0x11, 0x2f00);
	__phy_write(phydev, 0x12, 0xe);
	__phy_write(phydev, 0x10, 0x8fb0);

	/* NormMseLoThresh = 85 */
	__phy_write(phydev, 0x11, 0x55a0);
	__phy_write(phydev, 0x12, 0x0);
	__phy_write(phydev, 0x10, 0x83aa);

	/* TrFreeze = 0 */
	__phy_write(phydev, 0x11, 0x0);
	__phy_write(phydev, 0x12, 0x0);
	__phy_write(phydev, 0x10, 0x9686);

	/* SSTrKp1000Slv = 5 */
	__phy_write(phydev, 0x11, 0xbaef);
	__phy_write(phydev, 0x12, 0x2e);
	__phy_write(phydev, 0x10, 0x968c);

	/* MrvlTrFix100Kp = 3, MrvlTrFix100Kf = 2,
	 * MrvlTrFix1000Kp = 3, MrvlTrFix1000Kf = 2
	 */
	__phy_write(phydev, 0x11, 0xd10a);
	__phy_write(phydev, 0x12, 0x34);
	__phy_write(phydev, 0x10, 0x8f82);

	/* VcoSlicerThreshBitsHigh */
	__phy_write(phydev, 0x11, 0x5555);
	__phy_write(phydev, 0x12, 0x55);
	__phy_write(phydev, 0x10, 0x8ec0);
	phy_restore_page(phydev, MTK_PHY_PAGE_STANDARD, 0);

	/* TR_OPEN_LOOP_EN = 1, lpf_x_average = 9*/
	phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_DEV1E_REG234,
		       MTK_PHY_TR_OPEN_LOOP_EN_MASK | MTK_PHY_LPF_X_AVERAGE_MASK,
		       BIT(0) | FIELD_PREP(MTK_PHY_LPF_X_AVERAGE_MASK, 0x9));

	/* rg_tr_lpf_cnt_val = 512 */
	phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_LPF_CNT_VAL, 0x200);

	/* IIR2 related */
	phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_LP_IIR2_K1_L, 0x82);
	phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_LP_IIR2_K1_U, 0x0);
	phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_LP_IIR2_K2_L, 0x103);
	phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_LP_IIR2_K2_U, 0x0);
	phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_LP_IIR2_K3_L, 0x82);
	phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_LP_IIR2_K3_U, 0x0);
	phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_LP_IIR2_K4_L, 0xd177);
	phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_LP_IIR2_K4_U, 0x3);
	phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_LP_IIR2_K5_L, 0x2c82);
	phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_LP_IIR2_K5_U, 0xe);

	/* FFE peaking */
	phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_DEV1E_REG27C,
		       MTK_PHY_VGASTATE_FFE_THR_ST1_MASK, 0x1b << 8);
	phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_DEV1E_REG27D,
		       MTK_PHY_VGASTATE_FFE_THR_ST2_MASK, 0x1e);

	/* Disable LDO pump */
	phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_LDO_PUMP_EN_PAIRAB, 0x0);
	phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_LDO_PUMP_EN_PAIRCD, 0x0);
	/* Adjust LDO output voltage */
	phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_LDO_OUTPUT_V, 0x2222);
}

static void mt7981_phy_finetune(struct phy_device *phydev)
{
	u16 val[8] = { 0x01ce, 0x01c1,
		       0x020f, 0x0202,
		       0x03d0, 0x03c0,
		       0x0013, 0x0005 };
	int i, k;

	/* 100M eye finetune:
	 * Keep middle level of TX MLT3 shapper as default.
	 * Only change TX MLT3 overshoot level here.
	 */
	for (k = 0, i = 1; i < 12; i++) {
		if (i % 3 == 0)
			continue;
		phy_write_mmd(phydev, MDIO_MMD_VEND1, i, val[k++]);
	}

	phy_select_page(phydev, MTK_PHY_PAGE_EXTENDED_52B5);
	/* SlvDSPreadyTime = 24, MasDSPreadyTime = 24 */
	__phy_write(phydev, 0x11, 0xc71);
	__phy_write(phydev, 0x12, 0xc);
	__phy_write(phydev, 0x10, 0x8fae);

	/* ResetSyncOffset = 6 */
	__phy_write(phydev, 0x11, 0x600);
	__phy_write(phydev, 0x12, 0x0);
	__phy_write(phydev, 0x10, 0x8fc0);

	/* VgaDecRate = 1 */
	__phy_write(phydev, 0x11, 0x4c2a);
	__phy_write(phydev, 0x12, 0x3e);
	__phy_write(phydev, 0x10, 0x8fa4);

	/* FfeUpdGainForce = 4 */
	__phy_write(phydev, 0x11, 0x240);
	__phy_write(phydev, 0x12, 0x0);
	__phy_write(phydev, 0x10, 0x9680);

	phy_restore_page(phydev, MTK_PHY_PAGE_STANDARD, 0);
}

static void mt7988_phy_finetune(struct phy_device *phydev)
{
	u16 val[12] = { 0x0187, 0x01cd, 0x01c8, 0x0182,
			0x020d, 0x0206, 0x0384, 0x03d0,
			0x03c6, 0x030a, 0x0011, 0x0005 };
	int i;

	/* Set default MLT3 shaper first */
	for (i = 0; i < 12; i++)
		phy_write_mmd(phydev, MDIO_MMD_VEND1, i, val[i]);

	/* TCT finetune */
	phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_TX_FILTER, 0x5);

	/* Disable TX power saving */
	phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RXADC_CTRL_RG7,
		       MTK_PHY_DA_AD_BUF_BIAS_LP_MASK, 0x3 << 8);

	phy_select_page(phydev, MTK_PHY_PAGE_EXTENDED_52B5);

	/* SlvDSPreadyTime = 24, MasDSPreadyTime = 12 */
	__phy_write(phydev, 0x11, 0x671);
	__phy_write(phydev, 0x12, 0xc);
	__phy_write(phydev, 0x10, 0x8fae);

	/* ResetSyncOffset = 5 */
	__phy_write(phydev, 0x11, 0x500);
	__phy_write(phydev, 0x12, 0x0);
	__phy_write(phydev, 0x10, 0x8fc0);

	/* VgaDecRate is 1 at default on mt7988 */

	phy_restore_page(phydev, MTK_PHY_PAGE_STANDARD, 0);

	phy_select_page(phydev, MTK_PHY_PAGE_EXTENDED_2A30);
	/* TxClkOffset = 2 */
	__phy_modify(phydev, MTK_PHY_ANARG_RG, MTK_PHY_TCLKOFFSET_MASK,
		     FIELD_PREP(MTK_PHY_TCLKOFFSET_MASK, 0x2));
	phy_restore_page(phydev, MTK_PHY_PAGE_STANDARD, 0);
}

static void mt798x_phy_eee(struct phy_device *phydev)
{
	phy_modify_mmd(phydev, MDIO_MMD_VEND1,
		       MTK_PHY_RG_LPI_PCS_DSP_CTRL_REG120,
		       MTK_PHY_LPI_SIG_EN_LO_THRESH1000_MASK |
		       MTK_PHY_LPI_SIG_EN_HI_THRESH1000_MASK,
		       FIELD_PREP(MTK_PHY_LPI_SIG_EN_LO_THRESH1000_MASK, 0x0) |
		       FIELD_PREP(MTK_PHY_LPI_SIG_EN_HI_THRESH1000_MASK, 0x14));

	phy_modify_mmd(phydev, MDIO_MMD_VEND1,
		       MTK_PHY_RG_LPI_PCS_DSP_CTRL_REG122,
		       MTK_PHY_LPI_NORM_MSE_HI_THRESH1000_MASK,
		       FIELD_PREP(MTK_PHY_LPI_NORM_MSE_HI_THRESH1000_MASK,
				  0xff));

	phy_clear_bits_mmd(phydev, MDIO_MMD_VEND1,
			   MTK_PHY_RG_TESTMUX_ADC_CTRL,
			   MTK_PHY_RG_TXEN_DIG_MASK);

	phy_set_bits_mmd(phydev, MDIO_MMD_VEND1,
			 MTK_PHY_RG_DEV1E_REG19b, MTK_PHY_BYPASS_DSP_LPI_READY);

	phy_clear_bits_mmd(phydev, MDIO_MMD_VEND1,
			   MTK_PHY_RG_DEV1E_REG234, MTK_PHY_TR_LP_IIR_EEE_EN);

	phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_DEV1E_REG238,
		       MTK_PHY_LPI_SLV_SEND_TX_TIMER_MASK |
		       MTK_PHY_LPI_SLV_SEND_TX_EN,
		       FIELD_PREP(MTK_PHY_LPI_SLV_SEND_TX_TIMER_MASK, 0x120));

	phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_DEV1E_REG239,
		       MTK_PHY_LPI_SEND_LOC_TIMER_MASK |
		       MTK_PHY_LPI_TXPCS_LOC_RCV,
		       FIELD_PREP(MTK_PHY_LPI_SEND_LOC_TIMER_MASK, 0x117));

	phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_DEV1E_REG2C7,
		       MTK_PHY_MAX_GAIN_MASK | MTK_PHY_MIN_GAIN_MASK,
		       FIELD_PREP(MTK_PHY_MAX_GAIN_MASK, 0x8) |
		       FIELD_PREP(MTK_PHY_MIN_GAIN_MASK, 0x13));

	phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_DEV1E_REG2D1,
		       MTK_PHY_VCO_SLICER_THRESH_BITS_HIGH_EEE_MASK,
		       FIELD_PREP(MTK_PHY_VCO_SLICER_THRESH_BITS_HIGH_EEE_MASK,
				  0x33) |
		       MTK_PHY_LPI_SKIP_SD_SLV_TR | MTK_PHY_LPI_TR_READY |
		       MTK_PHY_LPI_VCO_EEE_STG0_EN);

	phy_set_bits_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_DEV1E_REG323,
			 MTK_PHY_EEE_WAKE_MAS_INT_DC |
			 MTK_PHY_EEE_WAKE_SLV_INT_DC);

	phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_DEV1E_REG324,
		       MTK_PHY_SMI_DETCNT_MAX_MASK,
		       FIELD_PREP(MTK_PHY_SMI_DETCNT_MAX_MASK, 0x3f) |
		       MTK_PHY_SMI_DET_MAX_EN);

	phy_set_bits_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_DEV1E_REG326,
			 MTK_PHY_LPI_MODE_SD_ON | MTK_PHY_RESET_RANDUPD_CNT |
			 MTK_PHY_TREC_UPDATE_ENAB_CLR |
			 MTK_PHY_LPI_QUIT_WAIT_DFE_SIG_DET_OFF |
			 MTK_PHY_TR_READY_SKIP_AFE_WAKEUP);

	phy_select_page(phydev, MTK_PHY_PAGE_EXTENDED_52B5);
	/* Regsigdet_sel_1000 = 0 */
	__phy_write(phydev, 0x11, 0xb);
	__phy_write(phydev, 0x12, 0x0);
	__phy_write(phydev, 0x10, 0x9690);

	/* REG_EEE_st2TrKf1000 = 3 */
	__phy_write(phydev, 0x11, 0x114f);
	__phy_write(phydev, 0x12, 0x2);
	__phy_write(phydev, 0x10, 0x969a);

	/* RegEEE_slv_wake_tr_timer_tar = 6, RegEEE_slv_remtx_timer_tar = 20 */
	__phy_write(phydev, 0x11, 0x3028);
	__phy_write(phydev, 0x12, 0x0);
	__phy_write(phydev, 0x10, 0x969e);

	/* RegEEE_slv_wake_int_timer_tar = 8 */
	__phy_write(phydev, 0x11, 0x5010);
	__phy_write(phydev, 0x12, 0x0);
	__phy_write(phydev, 0x10, 0x96a0);

	/* RegEEE_trfreeze_timer2 = 586 */
	__phy_write(phydev, 0x11, 0x24a);
	__phy_write(phydev, 0x12, 0x0);
	__phy_write(phydev, 0x10, 0x96a8);

	/* RegEEE100Stg1_tar = 16 */
	__phy_write(phydev, 0x11, 0x3210);
	__phy_write(phydev, 0x12, 0x0);
	__phy_write(phydev, 0x10, 0x96b8);

	/* REGEEE_wake_slv_tr_wait_dfesigdet_en = 1 */
	__phy_write(phydev, 0x11, 0x1463);
	__phy_write(phydev, 0x12, 0x0);
	__phy_write(phydev, 0x10, 0x96ca);

	/* DfeTailEnableVgaThresh1000 = 27 */
	__phy_write(phydev, 0x11, 0x36);
	__phy_write(phydev, 0x12, 0x0);
	__phy_write(phydev, 0x10, 0x8f80);
	phy_restore_page(phydev, MTK_PHY_PAGE_STANDARD, 0);

	phy_select_page(phydev, MTK_PHY_PAGE_EXTENDED_3);
	__phy_modify(phydev, MTK_PHY_LPI_REG_14, MTK_PHY_LPI_WAKE_TIMER_1000_MASK,
		     FIELD_PREP(MTK_PHY_LPI_WAKE_TIMER_1000_MASK, 0x19c));

	__phy_modify(phydev, MTK_PHY_LPI_REG_1c, MTK_PHY_SMI_DET_ON_THRESH_MASK,
		     FIELD_PREP(MTK_PHY_SMI_DET_ON_THRESH_MASK, 0xc));
	phy_restore_page(phydev, MTK_PHY_PAGE_STANDARD, 0);

	phy_modify_mmd(phydev, MDIO_MMD_VEND1,
		       MTK_PHY_RG_LPI_PCS_DSP_CTRL_REG122,
		       MTK_PHY_LPI_NORM_MSE_HI_THRESH1000_MASK,
		       FIELD_PREP(MTK_PHY_LPI_NORM_MSE_HI_THRESH1000_MASK, 0xff));
}

static int cal_sw(struct phy_device *phydev, enum CAL_ITEM cal_item,
		  u8 start_pair, u8 end_pair)
{
	u8 pair_n;
	int ret;

	for (pair_n = start_pair; pair_n <= end_pair; pair_n++) {
		/* TX_OFFSET & TX_AMP have no SW calibration. */
		switch (cal_item) {
		case TX_VCM:
			ret = tx_vcm_cal_sw(phydev, pair_n);
			break;
		default:
			return -EINVAL;
		}
		if (ret)
			return ret;
	}
	return 0;
}

static int cal_efuse(struct phy_device *phydev, enum CAL_ITEM cal_item,
		     u8 start_pair, u8 end_pair, u32 *buf)
{
	u8 pair_n;
	int ret;

	for (pair_n = start_pair; pair_n <= end_pair; pair_n++) {
		/* TX_VCM has no efuse calibration. */
		switch (cal_item) {
		case REXT:
			ret = rext_cal_efuse(phydev, buf);
			break;
		case TX_OFFSET:
			ret = tx_offset_cal_efuse(phydev, buf);
			break;
		case TX_AMP:
			ret = tx_amp_cal_efuse(phydev, buf);
			break;
		case TX_R50:
			ret = tx_r50_cal_efuse(phydev, buf, pair_n);
			break;
		default:
			return -EINVAL;
		}
		if (ret)
			return ret;
	}

	return 0;
}

static int start_cal(struct phy_device *phydev, enum CAL_ITEM cal_item,
		     enum CAL_MODE cal_mode, u8 start_pair,
		     u8 end_pair, u32 *buf)
{
	int ret;

	switch (cal_mode) {
	case EFUSE_M:
		ret = cal_efuse(phydev, cal_item, start_pair,
				end_pair, buf);
		break;
	case SW_M:
		ret = cal_sw(phydev, cal_item, start_pair, end_pair);
		break;
	default:
		return -EINVAL;
	}

	if (ret) {
		phydev_err(phydev, "cal %d failed\n", cal_item);
		return -EIO;
	}

	return 0;
}

static int mt798x_phy_calibration(struct phy_device *phydev)
{
	int ret = 0;
	u32 *buf;
	size_t len;
	struct nvmem_cell *cell;

	cell = nvmem_cell_get(&phydev->mdio.dev, "phy-cal-data");
	if (IS_ERR(cell)) {
		if (PTR_ERR(cell) == -EPROBE_DEFER)
			return PTR_ERR(cell);
		return 0;
	}

	buf = (u32 *)nvmem_cell_read(cell, &len);
	if (IS_ERR(buf))
		return PTR_ERR(buf);
	nvmem_cell_put(cell);

	if (!buf[0] || !buf[1] || !buf[2] || !buf[3] || len < 4 * sizeof(u32)) {
		phydev_err(phydev, "invalid efuse data\n");
		ret = -EINVAL;
		goto out;
	}

	ret = start_cal(phydev, REXT, EFUSE_M, NO_PAIR, NO_PAIR, buf);
	if (ret)
		goto out;
	ret = start_cal(phydev, TX_OFFSET, EFUSE_M, NO_PAIR, NO_PAIR, buf);
	if (ret)
		goto out;
	ret = start_cal(phydev, TX_AMP, EFUSE_M, NO_PAIR, NO_PAIR, buf);
	if (ret)
		goto out;
	ret = start_cal(phydev, TX_R50, EFUSE_M, PAIR_A, PAIR_D, buf);
	if (ret)
		goto out;
	ret = start_cal(phydev, TX_VCM, SW_M, PAIR_A, PAIR_A, buf);
	if (ret)
		goto out;

out:
	kfree(buf);
	return ret;
}

static int mt798x_phy_config_init(struct phy_device *phydev)
{
	switch (phydev->drv->phy_id) {
	case MTK_GPHY_ID_MT7981:
		mt7981_phy_finetune(phydev);
		break;
	case MTK_GPHY_ID_MT7988:
		mt7988_phy_finetune(phydev);
		break;
	}

	mt798x_phy_common_finetune(phydev);
	mt798x_phy_eee(phydev);

	return mt798x_phy_calibration(phydev);
}

static int mt7988_phy_setup_led(struct phy_device *phydev)
{
	struct mtk_socphy_shared_priv *priv = phydev->shared->priv;
	int port = phydev->mdio.addr;
	u32 reg = priv->boottrap;
	struct pinctrl *pinctrl;

	phy_write_mmd(phydev, MDIO_MMD_VEND2, MTK_PHY_LED0_ON_CTRL,
		      MTK_PHY_LED0_ENABLE | MTK_PHY_LED0_POLARITY |
		      MTK_PHY_LED0_ON_LINK10 |
		      MTK_PHY_LED0_ON_LINK100 |
		      MTK_PHY_LED0_ON_LINK1000);
	phy_write_mmd(phydev, MDIO_MMD_VEND2, MTK_PHY_LED1_ON_CTRL,
		      MTK_PHY_LED1_ENABLE | MTK_PHY_LED1_POLARITY |
		      MTK_PHY_LED1_ON_LINK10 |
		      MTK_PHY_LED1_ON_LINK100 |
		      MTK_PHY_LED1_ON_LINK1000);

	if ((port == GPHY_PORT0 && reg & BIT(8)) ||
	    (port == GPHY_PORT1 && reg & BIT(9)) ||
	    (port == GPHY_PORT2 && reg & BIT(10)) ||
	    (port == GPHY_PORT3 && reg & BIT(11))) {
		phy_clear_bits_mmd(phydev, MDIO_MMD_VEND2, MTK_PHY_LED0_ON_CTRL,
				   MTK_PHY_LED0_POLARITY);
		phy_clear_bits_mmd(phydev, MDIO_MMD_VEND2, MTK_PHY_LED1_ON_CTRL,
				   MTK_PHY_LED1_POLARITY);
	}

	phy_write_mmd(phydev, MDIO_MMD_VEND2, MTK_PHY_LED0_BLINK_CTRL,
		      MTK_PHY_LED0_1000TX | MTK_PHY_LED0_1000RX |
		      MTK_PHY_LED0_100TX  | MTK_PHY_LED0_100RX  |
		      MTK_PHY_LED0_10TX   | MTK_PHY_LED0_10RX);
	phy_write_mmd(phydev, MDIO_MMD_VEND2, MTK_PHY_LED1_BLINK_CTRL,
		      MTK_PHY_LED1_1000TX | MTK_PHY_LED1_1000RX |
		      MTK_PHY_LED1_100TX  | MTK_PHY_LED1_100RX  |
		      MTK_PHY_LED1_10TX   | MTK_PHY_LED1_10RX);

	pinctrl = devm_pinctrl_get_select(&phydev->mdio.dev, "gbe-led");
	if (IS_ERR(pinctrl)) {
		dev_err(&phydev->mdio.bus->dev, "Failed to setup LED pins\n");
		return PTR_ERR(pinctrl);
	}

	return 0;
}

static int mt7988_phy_probe_shared(struct phy_device *phydev)
{
	struct mtk_socphy_shared_priv *priv = phydev->shared->priv;
	void __iomem *boottrap;
	struct device_node *np;
	u32 reg;

	np = of_find_compatible_node(NULL, NULL, "mediatek,boottrap");
	if (!np)
		return -ENOENT;

	boottrap = of_iomap(np, 0);
	if (!boottrap)
		return -ENOMEM;

	reg = readl(boottrap);
	iounmap(boottrap);

	priv->boottrap = reg;

	return 0;
}

static int mt7981_phy_probe(struct phy_device *phydev)
{
	return mt798x_phy_calibration(phydev);
}

static int mt7988_phy_probe(struct phy_device *phydev)
{
	int err;

	err = devm_phy_package_join(&phydev->mdio.dev, phydev, 0,
				    sizeof(struct mtk_socphy_shared_priv));
	if (err)
		return err;

	if (phy_package_probe_once(phydev)) {
		err = mt7988_phy_probe_shared(phydev);
		if (err)
			return err;
	}

	mt7988_phy_setup_led(phydev);

	return mt798x_phy_calibration(phydev);
}

static struct phy_driver mtk_socphy_driver[] = {
	{
		PHY_ID_MATCH_EXACT(MTK_GPHY_ID_MT7981),
		.name		= "MediaTek MT7981 PHY",
		.config_init	= mt798x_phy_config_init,
		.config_intr	= genphy_no_config_intr,
		.handle_interrupt = genphy_handle_interrupt_no_ack,
		.probe		= mt7981_phy_probe,
		.suspend	= genphy_suspend,
		.resume		= genphy_resume,
		.read_page	= mtk_socphy_read_page,
		.write_page	= mtk_socphy_write_page,
	},
	{
		PHY_ID_MATCH_EXACT(MTK_GPHY_ID_MT7988),
		.name		= "MediaTek MT7988 PHY",
		.config_init	= mt798x_phy_config_init,
		.config_intr	= genphy_no_config_intr,
		.handle_interrupt = genphy_handle_interrupt_no_ack,
		.probe		= mt7988_phy_probe,
		.suspend	= genphy_suspend,
		.resume		= genphy_resume,
		.read_page	= mtk_socphy_read_page,
		.write_page	= mtk_socphy_write_page,
	},
};

module_phy_driver(mtk_socphy_driver);

static struct mdio_device_id __maybe_unused mtk_socphy_tbl[] = {
	{ PHY_ID_MATCH_EXACT(MTK_GPHY_ID_MT7981) },
	{ PHY_ID_MATCH_EXACT(MTK_GPHY_ID_MT7988) },
	{ }
};

MODULE_DESCRIPTION("MediaTek SoC Gigabit Ethernet PHY driver");
MODULE_AUTHOR("Daniel Golle <daniel@makrotopia.org>");
MODULE_AUTHOR("SkyLake Huang <SkyLake.Huang@mediatek.com>");
MODULE_LICENSE("GPL");

MODULE_DEVICE_TABLE(mdio, mtk_socphy_tbl);