1f629acc6SJiawen Wu // SPDX-License-Identifier: GPL-2.0
2f629acc6SJiawen Wu /* Copyright (c) 2015 - 2023 Beijing WangXun Technology Co., Ltd. */
3f629acc6SJiawen Wu
4f629acc6SJiawen Wu #include <linux/pcs/pcs-xpcs.h>
5f629acc6SJiawen Wu #include <linux/mdio.h>
6f629acc6SJiawen Wu #include "pcs-xpcs.h"
7f629acc6SJiawen Wu
8f629acc6SJiawen Wu /* VR_XS_PMA_MMD */
9f629acc6SJiawen Wu #define TXGBE_PMA_MMD 0x8020
10f629acc6SJiawen Wu #define TXGBE_TX_GENCTL1 0x11
11f629acc6SJiawen Wu #define TXGBE_TX_GENCTL1_VBOOST_LVL GENMASK(10, 8)
12f629acc6SJiawen Wu #define TXGBE_TX_GENCTL1_VBOOST_EN0 BIT(4)
13f629acc6SJiawen Wu #define TXGBE_TX_GEN_CTL2 0x12
14f629acc6SJiawen Wu #define TXGBE_TX_GEN_CTL2_TX0_WIDTH(v) FIELD_PREP(GENMASK(9, 8), v)
15f629acc6SJiawen Wu #define TXGBE_TX_RATE_CTL 0x14
16f629acc6SJiawen Wu #define TXGBE_TX_RATE_CTL_TX0_RATE(v) FIELD_PREP(GENMASK(2, 0), v)
17f629acc6SJiawen Wu #define TXGBE_RX_GEN_CTL2 0x32
18f629acc6SJiawen Wu #define TXGBE_RX_GEN_CTL2_RX0_WIDTH(v) FIELD_PREP(GENMASK(9, 8), v)
19f629acc6SJiawen Wu #define TXGBE_RX_GEN_CTL3 0x33
20f629acc6SJiawen Wu #define TXGBE_RX_GEN_CTL3_LOS_TRSHLD0 GENMASK(2, 0)
21f629acc6SJiawen Wu #define TXGBE_RX_RATE_CTL 0x34
22f629acc6SJiawen Wu #define TXGBE_RX_RATE_CTL_RX0_RATE(v) FIELD_PREP(GENMASK(1, 0), v)
23f629acc6SJiawen Wu #define TXGBE_RX_EQ_ATTN_CTL 0x37
24f629acc6SJiawen Wu #define TXGBE_RX_EQ_ATTN_LVL0 GENMASK(2, 0)
25f629acc6SJiawen Wu #define TXGBE_RX_EQ_CTL0 0x38
26f629acc6SJiawen Wu #define TXGBE_RX_EQ_CTL0_VGA1_GAIN(v) FIELD_PREP(GENMASK(15, 12), v)
27f629acc6SJiawen Wu #define TXGBE_RX_EQ_CTL0_VGA2_GAIN(v) FIELD_PREP(GENMASK(11, 8), v)
28f629acc6SJiawen Wu #define TXGBE_RX_EQ_CTL0_CTLE_POLE(v) FIELD_PREP(GENMASK(7, 5), v)
29f629acc6SJiawen Wu #define TXGBE_RX_EQ_CTL0_CTLE_BOOST(v) FIELD_PREP(GENMASK(4, 0), v)
30f629acc6SJiawen Wu #define TXGBE_RX_EQ_CTL4 0x3C
31f629acc6SJiawen Wu #define TXGBE_RX_EQ_CTL4_CONT_OFF_CAN0 BIT(4)
32f629acc6SJiawen Wu #define TXGBE_RX_EQ_CTL4_CONT_ADAPT0 BIT(0)
33f629acc6SJiawen Wu #define TXGBE_AFE_DFE_ENABLE 0x3D
34f629acc6SJiawen Wu #define TXGBE_DFE_EN_0 BIT(4)
35f629acc6SJiawen Wu #define TXGBE_AFE_EN_0 BIT(0)
36f629acc6SJiawen Wu #define TXGBE_DFE_TAP_CTL0 0x3E
37f629acc6SJiawen Wu #define TXGBE_MPLLA_CTL0 0x51
38f629acc6SJiawen Wu #define TXGBE_MPLLA_CTL2 0x53
39f629acc6SJiawen Wu #define TXGBE_MPLLA_CTL2_DIV16P5_CLK_EN BIT(10)
40f629acc6SJiawen Wu #define TXGBE_MPLLA_CTL2_DIV10_CLK_EN BIT(9)
41f629acc6SJiawen Wu #define TXGBE_MPLLA_CTL3 0x57
42f629acc6SJiawen Wu #define TXGBE_MISC_CTL0 0x70
43f629acc6SJiawen Wu #define TXGBE_MISC_CTL0_PLL BIT(15)
44f629acc6SJiawen Wu #define TXGBE_MISC_CTL0_CR_PARA_SEL BIT(14)
45f629acc6SJiawen Wu #define TXGBE_MISC_CTL0_RX_VREF(v) FIELD_PREP(GENMASK(12, 8), v)
46f629acc6SJiawen Wu #define TXGBE_VCO_CAL_LD0 0x72
47f629acc6SJiawen Wu #define TXGBE_VCO_CAL_REF0 0x76
48f629acc6SJiawen Wu
txgbe_read_pma(struct dw_xpcs * xpcs,int reg)49f629acc6SJiawen Wu static int txgbe_read_pma(struct dw_xpcs *xpcs, int reg)
50f629acc6SJiawen Wu {
51f629acc6SJiawen Wu return xpcs_read(xpcs, MDIO_MMD_PMAPMD, TXGBE_PMA_MMD + reg);
52f629acc6SJiawen Wu }
53f629acc6SJiawen Wu
txgbe_write_pma(struct dw_xpcs * xpcs,int reg,u16 val)54f629acc6SJiawen Wu static int txgbe_write_pma(struct dw_xpcs *xpcs, int reg, u16 val)
55f629acc6SJiawen Wu {
56f629acc6SJiawen Wu return xpcs_write(xpcs, MDIO_MMD_PMAPMD, TXGBE_PMA_MMD + reg, val);
57f629acc6SJiawen Wu }
58f629acc6SJiawen Wu
txgbe_pma_config_10gbaser(struct dw_xpcs * xpcs)59f629acc6SJiawen Wu static void txgbe_pma_config_10gbaser(struct dw_xpcs *xpcs)
60f629acc6SJiawen Wu {
61f629acc6SJiawen Wu int val;
62f629acc6SJiawen Wu
63f629acc6SJiawen Wu txgbe_write_pma(xpcs, TXGBE_MPLLA_CTL0, 0x21);
64f629acc6SJiawen Wu txgbe_write_pma(xpcs, TXGBE_MPLLA_CTL3, 0);
65f629acc6SJiawen Wu val = txgbe_read_pma(xpcs, TXGBE_TX_GENCTL1);
66f629acc6SJiawen Wu val = u16_replace_bits(val, 0x5, TXGBE_TX_GENCTL1_VBOOST_LVL);
67f629acc6SJiawen Wu txgbe_write_pma(xpcs, TXGBE_TX_GENCTL1, val);
68f629acc6SJiawen Wu txgbe_write_pma(xpcs, TXGBE_MISC_CTL0, TXGBE_MISC_CTL0_PLL |
69f629acc6SJiawen Wu TXGBE_MISC_CTL0_CR_PARA_SEL | TXGBE_MISC_CTL0_RX_VREF(0xF));
70f629acc6SJiawen Wu txgbe_write_pma(xpcs, TXGBE_VCO_CAL_LD0, 0x549);
71f629acc6SJiawen Wu txgbe_write_pma(xpcs, TXGBE_VCO_CAL_REF0, 0x29);
72f629acc6SJiawen Wu txgbe_write_pma(xpcs, TXGBE_TX_RATE_CTL, 0);
73f629acc6SJiawen Wu txgbe_write_pma(xpcs, TXGBE_RX_RATE_CTL, 0);
74f629acc6SJiawen Wu txgbe_write_pma(xpcs, TXGBE_TX_GEN_CTL2, TXGBE_TX_GEN_CTL2_TX0_WIDTH(3));
75f629acc6SJiawen Wu txgbe_write_pma(xpcs, TXGBE_RX_GEN_CTL2, TXGBE_RX_GEN_CTL2_RX0_WIDTH(3));
76f629acc6SJiawen Wu txgbe_write_pma(xpcs, TXGBE_MPLLA_CTL2, TXGBE_MPLLA_CTL2_DIV16P5_CLK_EN |
77f629acc6SJiawen Wu TXGBE_MPLLA_CTL2_DIV10_CLK_EN);
78f629acc6SJiawen Wu
79f629acc6SJiawen Wu txgbe_write_pma(xpcs, TXGBE_RX_EQ_CTL0, TXGBE_RX_EQ_CTL0_CTLE_POLE(2) |
80f629acc6SJiawen Wu TXGBE_RX_EQ_CTL0_CTLE_BOOST(5));
81f629acc6SJiawen Wu val = txgbe_read_pma(xpcs, TXGBE_RX_EQ_ATTN_CTL);
82f629acc6SJiawen Wu val &= ~TXGBE_RX_EQ_ATTN_LVL0;
83f629acc6SJiawen Wu txgbe_write_pma(xpcs, TXGBE_RX_EQ_ATTN_CTL, val);
84f629acc6SJiawen Wu txgbe_write_pma(xpcs, TXGBE_DFE_TAP_CTL0, 0xBE);
85f629acc6SJiawen Wu val = txgbe_read_pma(xpcs, TXGBE_AFE_DFE_ENABLE);
86f629acc6SJiawen Wu val &= ~(TXGBE_DFE_EN_0 | TXGBE_AFE_EN_0);
87f629acc6SJiawen Wu txgbe_write_pma(xpcs, TXGBE_AFE_DFE_ENABLE, val);
88f629acc6SJiawen Wu val = txgbe_read_pma(xpcs, TXGBE_RX_EQ_CTL4);
89f629acc6SJiawen Wu val &= ~TXGBE_RX_EQ_CTL4_CONT_ADAPT0;
90f629acc6SJiawen Wu txgbe_write_pma(xpcs, TXGBE_RX_EQ_CTL4, val);
91f629acc6SJiawen Wu }
92f629acc6SJiawen Wu
txgbe_pma_config_1g(struct dw_xpcs * xpcs)93f629acc6SJiawen Wu static void txgbe_pma_config_1g(struct dw_xpcs *xpcs)
94f629acc6SJiawen Wu {
95f629acc6SJiawen Wu int val;
96f629acc6SJiawen Wu
97f629acc6SJiawen Wu val = txgbe_read_pma(xpcs, TXGBE_TX_GENCTL1);
98f629acc6SJiawen Wu val = u16_replace_bits(val, 0x5, TXGBE_TX_GENCTL1_VBOOST_LVL);
99f629acc6SJiawen Wu val &= ~TXGBE_TX_GENCTL1_VBOOST_EN0;
100f629acc6SJiawen Wu txgbe_write_pma(xpcs, TXGBE_TX_GENCTL1, val);
101f629acc6SJiawen Wu txgbe_write_pma(xpcs, TXGBE_MISC_CTL0, TXGBE_MISC_CTL0_PLL |
102f629acc6SJiawen Wu TXGBE_MISC_CTL0_CR_PARA_SEL | TXGBE_MISC_CTL0_RX_VREF(0xF));
103f629acc6SJiawen Wu
104f629acc6SJiawen Wu txgbe_write_pma(xpcs, TXGBE_RX_EQ_CTL0, TXGBE_RX_EQ_CTL0_VGA1_GAIN(7) |
105f629acc6SJiawen Wu TXGBE_RX_EQ_CTL0_VGA2_GAIN(7) | TXGBE_RX_EQ_CTL0_CTLE_BOOST(6));
106f629acc6SJiawen Wu val = txgbe_read_pma(xpcs, TXGBE_RX_EQ_ATTN_CTL);
107f629acc6SJiawen Wu val &= ~TXGBE_RX_EQ_ATTN_LVL0;
108f629acc6SJiawen Wu txgbe_write_pma(xpcs, TXGBE_RX_EQ_ATTN_CTL, val);
109f629acc6SJiawen Wu txgbe_write_pma(xpcs, TXGBE_DFE_TAP_CTL0, 0);
110f629acc6SJiawen Wu val = txgbe_read_pma(xpcs, TXGBE_RX_GEN_CTL3);
111f629acc6SJiawen Wu val = u16_replace_bits(val, 0x4, TXGBE_RX_GEN_CTL3_LOS_TRSHLD0);
112*2f4e3926SJiawen Wu txgbe_write_pma(xpcs, TXGBE_RX_GEN_CTL3, val);
113f629acc6SJiawen Wu
114f629acc6SJiawen Wu txgbe_write_pma(xpcs, TXGBE_MPLLA_CTL0, 0x20);
115f629acc6SJiawen Wu txgbe_write_pma(xpcs, TXGBE_MPLLA_CTL3, 0x46);
116f629acc6SJiawen Wu txgbe_write_pma(xpcs, TXGBE_VCO_CAL_LD0, 0x540);
117f629acc6SJiawen Wu txgbe_write_pma(xpcs, TXGBE_VCO_CAL_REF0, 0x2A);
118f629acc6SJiawen Wu txgbe_write_pma(xpcs, TXGBE_AFE_DFE_ENABLE, 0);
119f629acc6SJiawen Wu txgbe_write_pma(xpcs, TXGBE_RX_EQ_CTL4, TXGBE_RX_EQ_CTL4_CONT_OFF_CAN0);
120f629acc6SJiawen Wu txgbe_write_pma(xpcs, TXGBE_TX_RATE_CTL, TXGBE_TX_RATE_CTL_TX0_RATE(3));
121f629acc6SJiawen Wu txgbe_write_pma(xpcs, TXGBE_RX_RATE_CTL, TXGBE_RX_RATE_CTL_RX0_RATE(3));
122f629acc6SJiawen Wu txgbe_write_pma(xpcs, TXGBE_TX_GEN_CTL2, TXGBE_TX_GEN_CTL2_TX0_WIDTH(1));
123f629acc6SJiawen Wu txgbe_write_pma(xpcs, TXGBE_RX_GEN_CTL2, TXGBE_RX_GEN_CTL2_RX0_WIDTH(1));
124f629acc6SJiawen Wu txgbe_write_pma(xpcs, TXGBE_MPLLA_CTL2, TXGBE_MPLLA_CTL2_DIV10_CLK_EN);
125f629acc6SJiawen Wu }
126f629acc6SJiawen Wu
txgbe_pcs_poll_power_up(struct dw_xpcs * xpcs)127f629acc6SJiawen Wu static int txgbe_pcs_poll_power_up(struct dw_xpcs *xpcs)
128f629acc6SJiawen Wu {
129f629acc6SJiawen Wu int val, ret;
130f629acc6SJiawen Wu
131f629acc6SJiawen Wu /* Wait xpcs power-up good */
132f629acc6SJiawen Wu ret = read_poll_timeout(xpcs_read_vpcs, val,
133f629acc6SJiawen Wu (val & DW_PSEQ_ST) == DW_PSEQ_ST_GOOD,
134f629acc6SJiawen Wu 10000, 1000000, false,
135f629acc6SJiawen Wu xpcs, DW_VR_XS_PCS_DIG_STS);
136f629acc6SJiawen Wu if (ret < 0)
137f629acc6SJiawen Wu dev_err(&xpcs->mdiodev->dev, "xpcs power-up timeout\n");
138f629acc6SJiawen Wu
139f629acc6SJiawen Wu return ret;
140f629acc6SJiawen Wu }
141f629acc6SJiawen Wu
txgbe_pma_init_done(struct dw_xpcs * xpcs)142f629acc6SJiawen Wu static int txgbe_pma_init_done(struct dw_xpcs *xpcs)
143f629acc6SJiawen Wu {
144f629acc6SJiawen Wu int val, ret;
145f629acc6SJiawen Wu
146f629acc6SJiawen Wu xpcs_write_vpcs(xpcs, DW_VR_XS_PCS_DIG_CTRL1, DW_VR_RST | DW_EN_VSMMD1);
147f629acc6SJiawen Wu
148f629acc6SJiawen Wu /* wait pma initialization done */
149f629acc6SJiawen Wu ret = read_poll_timeout(xpcs_read_vpcs, val, !(val & DW_VR_RST),
150f629acc6SJiawen Wu 100000, 10000000, false,
151f629acc6SJiawen Wu xpcs, DW_VR_XS_PCS_DIG_CTRL1);
152f629acc6SJiawen Wu if (ret < 0)
153f629acc6SJiawen Wu dev_err(&xpcs->mdiodev->dev, "xpcs pma initialization timeout\n");
154f629acc6SJiawen Wu
155f629acc6SJiawen Wu return ret;
156f629acc6SJiawen Wu }
157f629acc6SJiawen Wu
txgbe_xpcs_mode_quirk(struct dw_xpcs * xpcs)158f629acc6SJiawen Wu static bool txgbe_xpcs_mode_quirk(struct dw_xpcs *xpcs)
159f629acc6SJiawen Wu {
160f629acc6SJiawen Wu int ret;
161f629acc6SJiawen Wu
162f629acc6SJiawen Wu /* When txgbe do LAN reset, PCS will change to default 10GBASE-R mode */
163f629acc6SJiawen Wu ret = xpcs_read(xpcs, MDIO_MMD_PCS, MDIO_CTRL2);
164f629acc6SJiawen Wu ret &= MDIO_PCS_CTRL2_TYPE;
1652a22b7aeSJiawen Wu if ((ret == MDIO_PCS_CTRL2_10GBR &&
1662a22b7aeSJiawen Wu xpcs->interface != PHY_INTERFACE_MODE_10GBASER) ||
1672a22b7aeSJiawen Wu xpcs->interface == PHY_INTERFACE_MODE_SGMII)
168f629acc6SJiawen Wu return true;
169f629acc6SJiawen Wu
170f629acc6SJiawen Wu return false;
171f629acc6SJiawen Wu }
172f629acc6SJiawen Wu
txgbe_xpcs_switch_mode(struct dw_xpcs * xpcs,phy_interface_t interface)173f629acc6SJiawen Wu int txgbe_xpcs_switch_mode(struct dw_xpcs *xpcs, phy_interface_t interface)
174f629acc6SJiawen Wu {
175f629acc6SJiawen Wu int val, ret;
176f629acc6SJiawen Wu
177f629acc6SJiawen Wu switch (interface) {
178f629acc6SJiawen Wu case PHY_INTERFACE_MODE_10GBASER:
179f629acc6SJiawen Wu case PHY_INTERFACE_MODE_SGMII:
180f629acc6SJiawen Wu case PHY_INTERFACE_MODE_1000BASEX:
181f629acc6SJiawen Wu break;
182f629acc6SJiawen Wu default:
183f629acc6SJiawen Wu return 0;
184f629acc6SJiawen Wu }
185f629acc6SJiawen Wu
186f629acc6SJiawen Wu if (xpcs->interface == interface && !txgbe_xpcs_mode_quirk(xpcs))
187f629acc6SJiawen Wu return 0;
188f629acc6SJiawen Wu
189f629acc6SJiawen Wu xpcs->interface = interface;
190f629acc6SJiawen Wu
191f629acc6SJiawen Wu ret = txgbe_pcs_poll_power_up(xpcs);
192f629acc6SJiawen Wu if (ret < 0)
193f629acc6SJiawen Wu return ret;
194f629acc6SJiawen Wu
195f629acc6SJiawen Wu if (interface == PHY_INTERFACE_MODE_10GBASER) {
196f629acc6SJiawen Wu xpcs_write(xpcs, MDIO_MMD_PCS, MDIO_CTRL2, MDIO_PCS_CTRL2_10GBR);
197f629acc6SJiawen Wu val = xpcs_read(xpcs, MDIO_MMD_PMAPMD, MDIO_CTRL1);
198f629acc6SJiawen Wu val |= MDIO_CTRL1_SPEED10G;
199f629acc6SJiawen Wu xpcs_write(xpcs, MDIO_MMD_PMAPMD, MDIO_CTRL1, val);
200f629acc6SJiawen Wu txgbe_pma_config_10gbaser(xpcs);
201f629acc6SJiawen Wu } else {
202f629acc6SJiawen Wu xpcs_write(xpcs, MDIO_MMD_PCS, MDIO_CTRL2, MDIO_PCS_CTRL2_10GBX);
203f629acc6SJiawen Wu xpcs_write(xpcs, MDIO_MMD_PMAPMD, MDIO_CTRL1, 0);
204f629acc6SJiawen Wu xpcs_write(xpcs, MDIO_MMD_PCS, MDIO_CTRL1, 0);
205f629acc6SJiawen Wu txgbe_pma_config_1g(xpcs);
206f629acc6SJiawen Wu }
207f629acc6SJiawen Wu
208f629acc6SJiawen Wu return txgbe_pma_init_done(xpcs);
209f629acc6SJiawen Wu }
210