From 9e0cba597ac78a4e4a190ea840f680362193ee87 Mon Sep 17 00:00:00 2001 From: Jonas Jelonek Date: Mon, 20 Oct 2025 22:06:33 +0000 Subject: [PATCH] realtek: pcs: rtl930x: import SerDes setup code from PHY driver Import SerDes configuration code from PHY driver into the PCS driver. Only do mandatory adjustments, rename the function to adhere to the naming scheme, adjust all SerDes access calls. Signed-off-by: Jonas Jelonek Link: https://github.com/openwrt/openwrt/pull/20539 Signed-off-by: Robert Marko --- .../files-6.12/drivers/net/pcs/pcs-rtl-otto.c | 1784 +++++++++++++++++ 1 file changed, 1784 insertions(+) diff --git a/target/linux/realtek/files-6.12/drivers/net/pcs/pcs-rtl-otto.c b/target/linux/realtek/files-6.12/drivers/net/pcs/pcs-rtl-otto.c index a0fee490fa..fde8483444 100644 --- a/target/linux/realtek/files-6.12/drivers/net/pcs/pcs-rtl-otto.c +++ b/target/linux/realtek/files-6.12/drivers/net/pcs/pcs-rtl-otto.c @@ -10,6 +10,8 @@ #include #include +#include + #define RTPCS_PORT_CNT 57 #define RTPCS_SPEED_10 0 @@ -55,6 +57,40 @@ #define RTL93XX_MODEL_NAME_INFO (0x0004) #define RTL93XX_CHIP_INFO (0x0008) +#define PHY_PAGE_2 2 +#define PHY_PAGE_4 4 + +#define RTL9300_PHY_ID_MASK 0xf0ffffff + +/* RTL930X SerDes supports the following modes: + * 0x02: SGMII 0x04: 1000BX_FIBER 0x05: FIBER100 + * 0x06: QSGMII 0x09: RSGMII 0x0d: USXGMII + * 0x10: XSGMII 0x12: HISGMII 0x16: 2500Base_X + * 0x17: RXAUI_LITE 0x19: RXAUI_PLUS 0x1a: 10G Base-R + * 0x1b: 10GR1000BX_AUTO 0x1f: OFF + */ +#define RTL930X_SDS_MODE_SGMII 0x02 +#define RTL930X_SDS_MODE_1000BASEX 0x04 +#define RTL930X_SDS_MODE_USXGMII 0x0d +#define RTL930X_SDS_MODE_XGMII 0x10 +#define RTL930X_SDS_MODE_2500BASEX 0x16 +#define RTL930X_SDS_MODE_10GBASER 0x1a +#define RTL930X_SDS_OFF 0x1f +#define RTL930X_SDS_MASK 0x1f + +/* RTL930X SerDes supports two submodes when mode is USXGMII: + * 0x00: USXGMII (aka USXGMII_SX) + * 0x02: 10G_QXGMII (aka USXGMII_QX) + */ +#define RTL930X_SDS_SUBMODE_USXGMII_SX 0x0 +#define RTL930X_SDS_SUBMODE_USXGMII_QX 0x2 + +#define RTSDS_930X_PLL_1000 0x1 +#define RTSDS_930X_PLL_10000 0x5 +#define RTSDS_930X_PLL_2500 0x3 +#define RTSDS_930X_PLL_LC 0x3 +#define RTSDS_930X_PLL_RING 0x1 + /* Registers of the internal SerDes of the 9310 */ #define RTL931X_SERDES_MODE_CTRL (0x13cc) #define RTL931X_PS_SERDES_OFF_MODE_CTRL_ADDR (0x13F4) @@ -176,6 +212,1754 @@ static struct rtpcs_link *rtpcs_phylink_pcs_to_link(struct phylink_pcs *pcs) /* Variant-specific functions */ +/* RTL930X */ + +/* The access registers for SDS_MODE_SEL and the LSB for each SDS within */ +u16 rtpcs_930x_sds_regs[] = { 0x0194, 0x0194, 0x0194, 0x0194, 0x02a0, 0x02a0, 0x02a0, 0x02a0, + 0x02A4, 0x02A4, 0x0198, 0x0198 }; +u8 rtpcs_930x_sds_lsb[] = { 0, 6, 12, 18, 0, 6, 12, 18, 0, 6, 0, 6}; + +u16 rtpcs_930x_sds_submode_regs[] = { 0x1cc, 0x1cc, 0x2d8, 0x2d8, 0x2d8, 0x2d8, + 0x2d8, 0x2d8}; +u8 rtpcs_930x_sds_submode_lsb[] = { 0, 5, 0, 5, 10, 15, 20, 25 }; + +static void rtpcs_930x_sds_set(int sds_num, u32 mode) +{ + pr_info("%s %d\n", __func__, mode); + if (sds_num < 0 || sds_num > 11) { + pr_err("Wrong SerDes number: %d\n", sds_num); + return; + } + + sw_w32_mask(RTL930X_SDS_MASK << rtpcs_930x_sds_lsb[sds_num], + mode << rtpcs_930x_sds_lsb[sds_num], + rtpcs_930x_sds_regs[sds_num]); + mdelay(10); + + pr_debug("%s: 194:%08x 198:%08x 2a0:%08x 2a4:%08x\n", __func__, + sw_r32(0x194), sw_r32(0x198), sw_r32(0x2a0), sw_r32(0x2a4)); +} + +__attribute__((unused)) +static u32 rtpcs_930x_sds_mode_get(int sds_num) +{ + u32 v; + + if (sds_num < 0 || sds_num > 11) { + pr_err("Wrong SerDes number: %d\n", sds_num); + return 0; + } + + v = sw_r32(rtpcs_930x_sds_regs[sds_num]); + v >>= rtpcs_930x_sds_lsb[sds_num]; + + return v & RTL930X_SDS_MASK; +} + +__attribute__((unused)) +static u32 rtpcs_930x_sds_submode_get(int sds_num) +{ + u32 v; + + if (sds_num < 2 || sds_num > 9) { + pr_err("%s: unsupported SerDes %d\n", __func__, sds_num); + return 0; + } + + v = sw_r32(rtpcs_930x_sds_submode_regs[sds_num]); + v >>= rtpcs_930x_sds_submode_lsb[sds_num]; + + return v & RTL930X_SDS_MASK; +} + +static void rtpcs_930x_sds_submode_set(int sds, u32 submode) +{ + if (sds < 2 || sds > 9) { + pr_err("%s: submode unsupported on serdes %d\n", __func__, sds); + return; + } + + if (submode != RTL930X_SDS_SUBMODE_USXGMII_SX && + submode != RTL930X_SDS_SUBMODE_USXGMII_QX) { + pr_err("%s: unsupported submode 0x%x\n", __func__, submode); + } + + sw_w32_mask(RTL930X_SDS_MASK << rtpcs_930x_sds_submode_lsb[sds-2], + submode << rtpcs_930x_sds_submode_lsb[sds-2], + rtpcs_930x_sds_submode_regs[sds-2]); +} + +static void rtpcs_930x_sds_rx_reset(struct rtpcs_ctrl *ctrl, int sds_num, + phy_interface_t phy_if) +{ + int page = 0x2e; /* 10GR and USXGMII */ + + if (phy_if == PHY_INTERFACE_MODE_1000BASEX) + page = 0x24; + + rtpcs_sds_write_bits(ctrl, sds_num, page, 0x15, 4, 4, 0x1); + mdelay(5); + rtpcs_sds_write_bits(ctrl, sds_num, page, 0x15, 4, 4, 0x0); +} + +static void rtpcs_930x_sds_get_pll_data(struct rtpcs_ctrl *ctrl, int sds, + int *pll, int *speed) +{ + int sbit, pbit = sds & 1 ? 6 : 4; + int base_sds = sds & ~1; + + /* + * PLL data is shared between adjacent SerDes in the even lane. Each SerDes defines + * what PLL it needs (ring or LC) while the PLL itself stores the current speed. + */ + + *pll = rtpcs_sds_read_bits(ctrl, base_sds, 0x20, 0x12, pbit + 1, pbit); + sbit = *pll == RTSDS_930X_PLL_LC ? 8 : 12; + *speed = rtpcs_sds_read_bits(ctrl, base_sds, 0x20, 0x12, sbit + 3, sbit); +} + +static int rtpcs_930x_sds_set_pll_data(struct rtpcs_ctrl *ctrl, int sds, + int pll, int speed) +{ + int sbit = pll == RTSDS_930X_PLL_LC ? 8 : 12; + int pbit = sds & 1 ? 6 : 4; + int base_sds = sds & ~1; + + if ((speed != RTSDS_930X_PLL_1000) && + (speed != RTSDS_930X_PLL_2500) && + (speed != RTSDS_930X_PLL_10000)) + return -EINVAL; + + if ((pll != RTSDS_930X_PLL_RING) && (pll != RTSDS_930X_PLL_LC)) + return -EINVAL; + + if ((pll == RTSDS_930X_PLL_RING) && (speed == RTSDS_930X_PLL_10000)) + return -EINVAL; + + /* + * A SerDes clock can either be taken from the low speed ring PLL or the high speed + * LC PLL. As it is unclear if disabling PLLs has any positive or negative effect, + * always activate both. + */ + + rtpcs_sds_write_bits(ctrl, base_sds, 0x20, 0x12, 3, 0, 0xf); + rtpcs_sds_write_bits(ctrl, base_sds, 0x20, 0x12, pbit + 1, pbit, pll); + rtpcs_sds_write_bits(ctrl, base_sds, 0x20, 0x12, sbit + 3, sbit, speed); + + return 0; +} + +static void rtpcs_930x_sds_reset_cmu(struct rtpcs_ctrl *ctrl, int sds) +{ + int reset_sequence[4] = { 3, 2, 3, 1 }; + int base_sds = sds & ~1; + int pll, speed, i, bit; + + /* + * After the PLL speed has changed, the CMU must take over the new values. The models + * of the Otto platform have different reset sequences. Luckily it always boils down + * to flipping two bits in a special sequence. + */ + + rtpcs_930x_sds_get_pll_data(ctrl, sds, &pll, &speed); + bit = pll == RTSDS_930X_PLL_LC ? 2 : 0; + + for (i = 0; i < ARRAY_SIZE(reset_sequence); i++) + rtpcs_sds_write_bits(ctrl, base_sds, 0x21, 0x0b, bit + 1, bit, + reset_sequence[i]); +} + +static int rtpcs_930x_sds_wait_clock_ready(struct rtpcs_ctrl *ctrl, int sds) +{ + int i, base_sds = sds & ~1, ready, ready_cnt = 0, bit = (sds & 1) + 4; + + /* + * While reconfiguring a SerDes it might take some time until its clock is in sync with + * the PLL. During that timespan the ready signal might toggle randomly. According to + * GPL sources it is enough to verify that 3 consecutive clock ready checks say "ok". + */ + + for (i = 0; i < 20; i++) { + usleep_range(10000, 15000); + + rtpcs_sds_write(ctrl, base_sds, 0x1f, 0x02, 53); + ready = rtpcs_sds_read_bits(ctrl, base_sds, 0x1f, 0x14, bit, bit); + + ready_cnt = ready ? ready_cnt + 1 : 0; + if (ready_cnt >= 3) + return 0; + } + + return -EBUSY; +} + +static int rtpcs_930x_sds_get_internal_mode(struct rtpcs_ctrl *ctrl, int sds) +{ + return rtpcs_sds_read_bits(ctrl, sds, 0x1f, 0x09, 11, 7); +} + +static void rtpcs_930x_sds_set_internal_mode(struct rtpcs_ctrl *ctrl, int sds, + int mode) +{ + rtpcs_sds_write_bits(ctrl, sds, 0x1f, 0x09, 6, 6, 0x1); /* Force mode enable */ + rtpcs_sds_write_bits(ctrl, sds, 0x1f, 0x09, 11, 7, mode); +} + +static void rtpcs_930x_sds_set_power(struct rtpcs_ctrl *ctrl, int sds, bool on) +{ + int power_down = on ? 0x0 : 0x3; + int rx_enable = on ? 0x3 : 0x1; + + rtpcs_sds_write_bits(ctrl, sds, 0x20, 0x00, 7, 6, power_down); + rtpcs_sds_write_bits(ctrl, sds, 0x20, 0x00, 5, 4, rx_enable); +} + +static int rtpcs_930x_sds_config_pll(struct rtpcs_ctrl *ctrl, int sds, + phy_interface_t interface) +{ + int neighbor_speed, neighbor_mode, neighbor_pll, neighbor = sds ^ 1; + bool speed_changed = true; + int pll, speed; + + /* + * A SerDes pair on the RTL930x is driven by two PLLs. A low speed ring PLL can generate + * signals of 1.25G and 3.125G for link speeds of 1G/2.5G. A high speed LC PLL can + * additionally generate a 10.3125G signal for 10G speeds. To drive the pair at different + * speeds each SerDes must use its own PLL. But what if the SerDess attached to the ring + * PLL suddenly needs 10G but the LC PLL is running at 1G? To avoid reconfiguring the + * "partner" SerDes we must choose wisely what assignment serves the current needs. The + * logic boils down to the following rules: + * + * - Use ring PLL for slow 1G speeds + * - Use LC PLL for fast 10G speeds + * - For 2.5G prefer ring over LC PLL + */ + + neighbor_mode = rtpcs_930x_sds_get_internal_mode(ctrl, neighbor); + rtpcs_930x_sds_get_pll_data(ctrl, neighbor, &neighbor_pll, &neighbor_speed); + + if ((interface == PHY_INTERFACE_MODE_1000BASEX) || + (interface == PHY_INTERFACE_MODE_SGMII)) + speed = RTSDS_930X_PLL_1000; + else if (interface == PHY_INTERFACE_MODE_2500BASEX) + speed = RTSDS_930X_PLL_2500; + else if (interface == PHY_INTERFACE_MODE_10GBASER) + speed = RTSDS_930X_PLL_10000; + else + return -ENOTSUPP; + + if (!neighbor_mode) + pll = speed == RTSDS_930X_PLL_10000 ? RTSDS_930X_PLL_LC : RTSDS_930X_PLL_RING; + else if (speed == neighbor_speed) { + speed_changed = false; + pll = neighbor_pll; + } else if (neighbor_pll == RTSDS_930X_PLL_RING) + pll = RTSDS_930X_PLL_LC; + else if (speed == RTSDS_930X_PLL_10000) + return -ENOTSUPP; /* caller wants 10G but only ring PLL available */ + else + pll = RTSDS_930X_PLL_RING; + + rtpcs_930x_sds_set_pll_data(ctrl, sds, pll, speed); + + if (speed_changed) + rtpcs_930x_sds_reset_cmu(ctrl, sds); + + pr_info("%s: SDS %d using %s PLL for %s\n", __func__, sds, + pll == RTSDS_930X_PLL_LC ? "LC" : "ring", phy_modes(interface)); + + return 0; +} + +static void rtpcs_930x_sds_reset_state_machine(struct rtpcs_ctrl *ctrl, int sds) +{ + rtpcs_sds_write_bits(ctrl, sds, 0x06, 0x02, 12, 12, 0x01); /* SM_RESET bit */ + usleep_range(10000, 20000); + rtpcs_sds_write_bits(ctrl, sds, 0x06, 0x02, 12, 12, 0x00); + usleep_range(10000, 20000); +} + +static int rtpcs_930x_sds_init_state_machine(struct rtpcs_ctrl *ctrl, int sds, + phy_interface_t interface) +{ + int loopback, link, cnt = 20, ret = -EBUSY; + + if (interface != PHY_INTERFACE_MODE_10GBASER) + return 0; + /* + * After a SerDes mode change it takes some time until the frontend state machine + * works properly for 10G. To verify operation readyness run a connection check via + * loopback. + */ + loopback = rtpcs_sds_read_bits(ctrl, sds, 0x06, 0x01, 2, 2); /* CFG_AFE_LPK bit */ + rtpcs_sds_write_bits(ctrl, sds, 0x06, 0x01, 2, 2, 0x01); + + while (cnt-- && ret) { + rtpcs_930x_sds_reset_state_machine(ctrl, sds); + link = rtpcs_sds_read_bits(ctrl, sds, 0x05, 0x00, 12, 12); /* 10G link state (latched) */ + link = rtpcs_sds_read_bits(ctrl, sds, 0x05, 0x00, 12, 12); + if (link) + ret = 0; + } + + rtpcs_sds_write_bits(ctrl, sds, 0x06, 0x01, 2, 2, loopback); + rtpcs_930x_sds_reset_state_machine(ctrl, sds); + + return ret; +} + +static void rtpcs_930x_sds_force_mode(struct rtpcs_ctrl *ctrl, int sds, + phy_interface_t interface) +{ + int mode; + + /* + * TODO: Usually one would expect that it is enough to modify the SDS_MODE_SEL_* + * registers (lets call it MAC setup). It seems as if this complex sequence is only + * needed for modes that cannot be set by the SoC itself. Additionally it is unclear + * if this sequence should quit early in case of errors. + */ + + switch (interface) { + case PHY_INTERFACE_MODE_SGMII: + mode = RTL930X_SDS_MODE_SGMII; + break; + case PHY_INTERFACE_MODE_1000BASEX: + mode = RTL930X_SDS_MODE_1000BASEX; + break; + case PHY_INTERFACE_MODE_2500BASEX: + mode = RTL930X_SDS_MODE_2500BASEX; + break; + case PHY_INTERFACE_MODE_10GBASER: + mode = RTL930X_SDS_MODE_10GBASER; + break; + case PHY_INTERFACE_MODE_NA: + mode = RTL930X_SDS_OFF; + break; + default: + pr_err("%s: SDS %d does not support %s\n", __func__, sds, phy_modes(interface)); + return; + } + + rtpcs_930x_sds_set_power(ctrl, sds, false); + rtpcs_930x_sds_set_internal_mode(ctrl, sds, RTL930X_SDS_OFF); + if (interface == PHY_INTERFACE_MODE_NA) + return; + + if (rtpcs_930x_sds_config_pll(ctrl, sds, interface)) + pr_err("%s: SDS %d could not configure PLL for %s\n", __func__, + sds, phy_modes(interface)); + + rtpcs_930x_sds_set_internal_mode(ctrl, sds, mode); + if (rtpcs_930x_sds_wait_clock_ready(ctrl, sds)) + pr_err("%s: SDS %d could not sync clock\n", __func__, sds); + + if (rtpcs_930x_sds_init_state_machine(ctrl, sds, interface)) + pr_err("%s: SDS %d could not reset state machine\n", __func__, sds); + + rtpcs_930x_sds_set_power(ctrl, sds, true); + rtpcs_930x_sds_rx_reset(ctrl, sds, interface); +} + +static void rtpcs_930x_sds_mode_set(struct rtpcs_ctrl *ctrl, int sds, + phy_interface_t phy_mode) +{ + u32 mode; + u32 submode; + + if (sds < 0 || sds > 11) { + pr_err("%s: invalid SerDes number: %d\n", __func__, sds); + return; + } + + switch(phy_mode) { + case PHY_INTERFACE_MODE_SGMII: + case PHY_INTERFACE_MODE_1000BASEX: + case PHY_INTERFACE_MODE_2500BASEX: + case PHY_INTERFACE_MODE_10GBASER: + rtpcs_930x_sds_force_mode(ctrl, sds, phy_mode); + return; + case PHY_INTERFACE_MODE_10G_QXGMII: + mode = RTL930X_SDS_MODE_USXGMII; + submode = RTL930X_SDS_SUBMODE_USXGMII_QX; + break; + default: + pr_warn("%s: unsupported mode %s\n", __func__, phy_modes(phy_mode)); + return; + } + + /* SerDes off first. */ + rtpcs_930x_sds_set(sds, RTL930X_SDS_OFF); + + /* Set the mode. */ + rtpcs_930x_sds_set(sds, mode); + + /* Set the submode if needed. */ + if (phy_mode == PHY_INTERFACE_MODE_10G_QXGMII) { + rtpcs_930x_sds_submode_set(sds, submode); + } +} + + +static void rtpcs_930x_sds_tx_config(struct rtpcs_ctrl *ctrl, int sds, + phy_interface_t phy_if) +{ + /* parameters: rtl9303_80G_txParam_s2 */ + int impedance = 0x8; + int pre_amp = 0x2; + int main_amp = 0x9; + int post_amp = 0x2; + int pre_en = 0x1; + int post_en = 0x1; + int page; + + switch(phy_if) { + case PHY_INTERFACE_MODE_1000BASEX: + case PHY_INTERFACE_MODE_SGMII: + pre_amp = 0x1; + main_amp = 0x9; + post_amp = 0x1; + page = 0x25; + break; + case PHY_INTERFACE_MODE_2500BASEX: + pre_amp = 0; + post_amp = 0x8; + pre_en = 0; + page = 0x29; + break; + case PHY_INTERFACE_MODE_10GBASER: + case PHY_INTERFACE_MODE_USXGMII: + case PHY_INTERFACE_MODE_10G_QXGMII: + case PHY_INTERFACE_MODE_XGMII: + pre_en = 0; + pre_amp = 0; + main_amp = 0x10; + post_amp = 0; + post_en = 0; + page = 0x2f; + break; + default: + pr_err("%s: unsupported PHY mode\n", __func__); + return; + } + + rtpcs_sds_write_bits(ctrl, sds, page, 0x01, 15, 11, pre_amp); + rtpcs_sds_write_bits(ctrl, sds, page, 0x06, 4, 0, post_amp); + rtpcs_sds_write_bits(ctrl, sds, page, 0x07, 0, 0, pre_en); + rtpcs_sds_write_bits(ctrl, sds, page, 0x07, 3, 3, post_en); + rtpcs_sds_write_bits(ctrl, sds, page, 0x07, 8, 4, main_amp); + rtpcs_sds_write_bits(ctrl, sds, page, 0x18, 15, 12, impedance); +} + +/* Wait for clock ready, this assumes the SerDes is in XGMII mode + * timeout is in ms + */ +__attribute__((unused)) +static int rtpcs_930x_sds_clock_wait(struct rtpcs_ctrl *ctrl, int timeout) +{ + u32 v; + unsigned long start = jiffies; + + do { + rtpcs_sds_write_bits(ctrl, 2, 0x1f, 0x2, 15, 0, 53); + v = rtpcs_sds_read_bits(ctrl, 2, 0x1f, 20, 5, 4); + if (v == 3) + return 0; + } while (jiffies < start + (HZ / 1000) * timeout); + + return 1; +} + +static void rtpcs_930x_sds_mac_link_config(struct rtpcs_ctrl *ctrl, int sds, + bool tx_normal, bool rx_normal) +{ + u32 v10, v1; + + v10 = rtpcs_sds_read(ctrl, sds, 6, 2); /* 10GBit, page 6, reg 2 */ + v1 = rtpcs_sds_read(ctrl, sds, 0, 0); /* 1GBit, page 0, reg 0 */ + pr_info("%s: registers before %08x %08x\n", __func__, v10, v1); + + v10 &= ~(BIT(13) | BIT(14)); + v1 &= ~(BIT(8) | BIT(9)); + + v10 |= rx_normal ? 0 : BIT(13); + v1 |= rx_normal ? 0 : BIT(9); + + v10 |= tx_normal ? 0 : BIT(14); + v1 |= tx_normal ? 0 : BIT(8); + + rtpcs_sds_write(ctrl, sds, 6, 2, v10); + rtpcs_sds_write(ctrl, sds, 0, 0, v1); + + v10 = rtpcs_sds_read(ctrl, sds, 6, 2); + v1 = rtpcs_sds_read(ctrl, sds, 0, 0); + pr_info("%s: registers after %08x %08x\n", __func__, v10, v1); +} + +__attribute__((unused)) +static void rtpcs_930x_sds_rxcal_dcvs_manual(struct rtpcs_ctrl *ctrl, u32 sds_num, + u32 dcvs_id, bool manual, u32 dvcs_list[]) +{ + if (manual) { + switch(dcvs_id) { + case 0: + rtpcs_sds_write_bits(ctrl, sds_num, 0x2e, 0x1e, 14, 14, 0x1); + rtpcs_sds_write_bits(ctrl, sds_num, 0x2f, 0x03, 5, 5, dvcs_list[0]); + rtpcs_sds_write_bits(ctrl, sds_num, 0x2f, 0x03, 4, 0, dvcs_list[1]); + break; + case 1: + rtpcs_sds_write_bits(ctrl, sds_num, 0x2e, 0x1e, 13, 13, 0x1); + rtpcs_sds_write_bits(ctrl, sds_num, 0x2e, 0x1d, 15, 15, dvcs_list[0]); + rtpcs_sds_write_bits(ctrl, sds_num, 0x2e, 0x1d, 14, 11, dvcs_list[1]); + break; + case 2: + rtpcs_sds_write_bits(ctrl, sds_num, 0x2e, 0x1e, 12, 12, 0x1); + rtpcs_sds_write_bits(ctrl, sds_num, 0x2e, 0x1d, 10, 10, dvcs_list[0]); + rtpcs_sds_write_bits(ctrl, sds_num, 0x2e, 0x1d, 9, 6, dvcs_list[1]); + break; + case 3: + rtpcs_sds_write_bits(ctrl, sds_num, 0x2e, 0x1e, 11, 11, 0x1); + rtpcs_sds_write_bits(ctrl, sds_num, 0x2e, 0x1d, 5, 5, dvcs_list[0]); + rtpcs_sds_write_bits(ctrl, sds_num, 0x2e, 0x1d, 4, 1, dvcs_list[1]); + break; + case 4: + rtpcs_sds_write_bits(ctrl, sds_num, 0x2e, 0x01, 15, 15, 0x1); + rtpcs_sds_write_bits(ctrl, sds_num, 0x2e, 0x11, 10, 10, dvcs_list[0]); + rtpcs_sds_write_bits(ctrl, sds_num, 0x2e, 0x11, 9, 6, dvcs_list[1]); + break; + case 5: + rtpcs_sds_write_bits(ctrl, sds_num, 0x2e, 0x02, 11, 11, 0x1); + rtpcs_sds_write_bits(ctrl, sds_num, 0x2e, 0x11, 4, 4, dvcs_list[0]); + rtpcs_sds_write_bits(ctrl, sds_num, 0x2e, 0x11, 3, 0, dvcs_list[1]); + break; + default: + break; + } + } else { + switch(dcvs_id) { + case 0: + rtpcs_sds_write_bits(ctrl, sds_num, 0x2e, 0x1e, 14, 14, 0x0); + break; + case 1: + rtpcs_sds_write_bits(ctrl, sds_num, 0x2e, 0x1e, 13, 13, 0x0); + break; + case 2: + rtpcs_sds_write_bits(ctrl, sds_num, 0x2e, 0x1e, 12, 12, 0x0); + break; + case 3: + rtpcs_sds_write_bits(ctrl, sds_num, 0x2e, 0x1e, 11, 11, 0x0); + break; + case 4: + rtpcs_sds_write_bits(ctrl, sds_num, 0x2e, 0x01, 15, 15, 0x0); + break; + case 5: + rtpcs_sds_write_bits(ctrl, sds_num, 0x2e, 0x02, 11, 11, 0x0); + break; + default: + break; + } + mdelay(1); + } +} + +__attribute__((unused)) +static void rtpcs_930x_sds_rxcal_dcvs_get(struct rtpcs_ctrl *ctrl, u32 sds_num, + u32 dcvs_id, u32 dcvs_list[]) +{ + u32 dcvs_sign_out = 0, dcvs_coef_bin = 0; + bool dcvs_manual; + + if (!(sds_num % 2)) + rtpcs_sds_write(ctrl, sds_num, 0x1f, 0x2, 0x2f); + else + rtpcs_sds_write(ctrl, sds_num - 1, 0x1f, 0x2, 0x31); + + /* ##Page0x2E, Reg0x15[9], REG0_RX_EN_TEST=[1] */ + rtpcs_sds_write_bits(ctrl, sds_num, 0x2e, 0x15, 9, 9, 0x1); + + /* ##Page0x21, Reg0x06[11 6], REG0_RX_DEBUG_SEL=[1 0 x x x x] */ + rtpcs_sds_write_bits(ctrl, sds_num, 0x21, 0x06, 11, 6, 0x20); + + switch(dcvs_id) { + case 0: + rtpcs_sds_write_bits(ctrl, sds_num, 0x2f, 0x0c, 5, 0, 0x22); + mdelay(1); + + /* ##DCVS0 Read Out */ + dcvs_sign_out = rtpcs_sds_read_bits(ctrl, sds_num, 0x1f, 0x14, 4, 4); + dcvs_coef_bin = rtpcs_sds_read_bits(ctrl, sds_num, 0x1f, 0x14, 3, 0); + dcvs_manual = !!rtpcs_sds_read_bits(ctrl, sds_num, 0x2e, 0x1e, 14, 14); + break; + + case 1: + rtpcs_sds_write_bits(ctrl, sds_num, 0x2f, 0x0c, 5, 0, 0x23); + mdelay(1); + + /* ##DCVS0 Read Out */ + dcvs_coef_bin = rtpcs_sds_read_bits(ctrl, sds_num, 0x1f, 0x14, 4, 4); + dcvs_coef_bin = rtpcs_sds_read_bits(ctrl, sds_num, 0x1f, 0x14, 3, 0); + dcvs_manual = !!rtpcs_sds_read_bits(ctrl, sds_num, 0x2e, 0x1e, 13, 13); + break; + + case 2: + rtpcs_sds_write_bits(ctrl, sds_num, 0x2f, 0x0c, 5, 0, 0x24); + mdelay(1); + + /* ##DCVS0 Read Out */ + dcvs_sign_out = rtpcs_sds_read_bits(ctrl, sds_num, 0x1f, 0x14, 4, 4); + dcvs_coef_bin = rtpcs_sds_read_bits(ctrl, sds_num, 0x1f, 0x14, 3, 0); + dcvs_manual = !!rtpcs_sds_read_bits(ctrl, sds_num, 0x2e, 0x1e, 12, 12); + break; + case 3: + rtpcs_sds_write_bits(ctrl, sds_num, 0x2f, 0x0c, 5, 0, 0x25); + mdelay(1); + + /* ##DCVS0 Read Out */ + dcvs_sign_out = rtpcs_sds_read_bits(ctrl, sds_num, 0x1f, 0x14, 4, 4); + dcvs_coef_bin = rtpcs_sds_read_bits(ctrl, sds_num, 0x1f, 0x14, 3, 0); + dcvs_manual = rtpcs_sds_read_bits(ctrl, sds_num, 0x2e, 0x1e, 11, 11); + break; + + case 4: + rtpcs_sds_write_bits(ctrl, sds_num, 0x2f, 0x0c, 5, 0, 0x2c); + mdelay(1); + + /* ##DCVS0 Read Out */ + dcvs_sign_out = rtpcs_sds_read_bits(ctrl, sds_num, 0x1f, 0x14, 4, 4); + dcvs_coef_bin = rtpcs_sds_read_bits(ctrl, sds_num, 0x1f, 0x14, 3, 0); + dcvs_manual = !!rtpcs_sds_read_bits(ctrl, sds_num, 0x2e, 0x01, 15, 15); + break; + + case 5: + rtpcs_sds_write_bits(ctrl, sds_num, 0x2f, 0x0c, 5, 0, 0x2d); + mdelay(1); + + /* ##DCVS0 Read Out */ + dcvs_sign_out = rtpcs_sds_read_bits(ctrl, sds_num, 0x1f, 0x14, 4, 4); + dcvs_coef_bin = rtpcs_sds_read_bits(ctrl, sds_num, 0x1f, 0x14, 3, 0); + dcvs_manual = rtpcs_sds_read_bits(ctrl, sds_num, 0x2e, 0x02, 11, 11); + break; + + default: + break; + } + + if (dcvs_sign_out) + pr_info("%s DCVS %u Sign: -", __func__, dcvs_id); + else + pr_info("%s DCVS %u Sign: +", __func__, dcvs_id); + + pr_info("DCVS %u even coefficient = %u", dcvs_id, dcvs_coef_bin); + pr_info("DCVS %u manual = %u", dcvs_id, dcvs_manual); + + dcvs_list[0] = dcvs_sign_out; + dcvs_list[1] = dcvs_coef_bin; +} + +static void rtpcs_930x_sds_rxcal_leq_manual(struct rtpcs_ctrl *ctrl, u32 sds_num, + bool manual, u32 leq_gray) +{ + if (manual) { + rtpcs_sds_write_bits(ctrl, sds_num, 0x2e, 0x18, 15, 15, 0x1); + rtpcs_sds_write_bits(ctrl, sds_num, 0x2e, 0x16, 14, 10, leq_gray); + } else { + rtpcs_sds_write_bits(ctrl, sds_num, 0x2e, 0x18, 15, 15, 0x0); + mdelay(100); + } +} + +static void rtpcs_930x_sds_rxcal_leq_offset_manual(struct rtpcs_ctrl *ctrl, + u32 sds_num, bool manual, + u32 offset) +{ + if (manual) { + rtpcs_sds_write_bits(ctrl, sds_num, 0x2e, 0x17, 6, 2, offset); + } else { + rtpcs_sds_write_bits(ctrl, sds_num, 0x2e, 0x17, 6, 2, offset); + mdelay(1); + } +} + +#define GRAY_BITS 5 +static u32 rtpcs_930x_sds_rxcal_gray_to_binary(u32 gray_code) +{ + int i, j, m; + u32 g[GRAY_BITS]; + u32 c[GRAY_BITS]; + u32 leq_binary = 0; + + for(i = 0; i < GRAY_BITS; i++) + g[i] = (gray_code & BIT(i)) >> i; + + m = GRAY_BITS - 1; + + c[m] = g[m]; + + for(i = 0; i < m; i++) { + c[i] = g[i]; + for(j = i + 1; j < GRAY_BITS; j++) + c[i] = c[i] ^ g[j]; + } + + for(i = 0; i < GRAY_BITS; i++) + leq_binary += c[i] << i; + + return leq_binary; +} + +static u32 rtpcs_930x_sds_rxcal_leq_read(struct rtpcs_ctrl *ctrl, int sds_num) +{ + u32 leq_gray, leq_bin; + bool leq_manual; + + if (!(sds_num % 2)) + rtpcs_sds_write(ctrl, sds_num, 0x1f, 0x2, 0x2f); + else + rtpcs_sds_write(ctrl, sds_num - 1, 0x1f, 0x2, 0x31); + + /* ##Page0x2E, Reg0x15[9], REG0_RX_EN_TEST=[1] */ + rtpcs_sds_write_bits(ctrl, sds_num, 0x2e, 0x15, 9, 9, 0x1); + + /* ##Page0x21, Reg0x06[11 6], REG0_RX_DEBUG_SEL=[0 1 x x x x] */ + rtpcs_sds_write_bits(ctrl, sds_num, 0x21, 0x06, 11, 6, 0x10); + mdelay(1); + + /* ##LEQ Read Out */ + leq_gray = rtpcs_sds_read_bits(ctrl, sds_num, 0x1f, 0x14, 7, 3); + leq_manual = !!rtpcs_sds_read_bits(ctrl, sds_num, 0x2e, 0x18, 15, 15); + leq_bin = rtpcs_930x_sds_rxcal_gray_to_binary(leq_gray); + + pr_info("LEQ_gray: %u, LEQ_bin: %u", leq_gray, leq_bin); + pr_info("LEQ manual: %u", leq_manual); + + return leq_bin; +} + +static void rtpcs_930x_sds_rxcal_vth_manual(struct rtpcs_ctrl *ctrl, u32 sds_num, + bool manual, u32 vth_list[]) +{ + if (manual) { + rtpcs_sds_write_bits(ctrl, sds_num, 0x2e, 0x0f, 13, 13, 0x1); + rtpcs_sds_write_bits(ctrl, sds_num, 0x2e, 0x13, 5, 3, vth_list[0]); + rtpcs_sds_write_bits(ctrl, sds_num, 0x2e, 0x13, 2, 0, vth_list[1]); + } else { + rtpcs_sds_write_bits(ctrl, sds_num, 0x2e, 0x0f, 13, 13, 0x0); + mdelay(10); + } +} + +static void rtpcs_930x_sds_rxcal_vth_get(struct rtpcs_ctrl *ctrl, u32 sds_num, + u32 vth_list[]) +{ + u32 vth_manual; + + /* ##Page0x1F, Reg0x02[15 0], REG_DBGO_SEL=[0x002F]; */ /* Lane0 */ + /* ##Page0x1F, Reg0x02[15 0], REG_DBGO_SEL=[0x0031]; */ /* Lane1 */ + if (!(sds_num % 2)) + rtpcs_sds_write(ctrl, sds_num, 0x1f, 0x2, 0x2f); + else + rtpcs_sds_write(ctrl, sds_num - 1, 0x1f, 0x2, 0x31); + + /* ##Page0x2E, Reg0x15[9], REG0_RX_EN_TEST=[1] */ + rtpcs_sds_write_bits(ctrl, sds_num, 0x2e, 0x15, 9, 9, 0x1); + /* ##Page0x21, Reg0x06[11 6], REG0_RX_DEBUG_SEL=[1 0 x x x x] */ + rtpcs_sds_write_bits(ctrl, sds_num, 0x21, 0x06, 11, 6, 0x20); + /* ##Page0x2F, Reg0x0C[5 0], REG0_COEF_SEL=[0 0 1 1 0 0] */ + rtpcs_sds_write_bits(ctrl, sds_num, 0x2f, 0x0c, 5, 0, 0xc); + + mdelay(1); + + /* ##VthP & VthN Read Out */ + vth_list[0] = rtpcs_sds_read_bits(ctrl, sds_num, 0x1f, 0x14, 2, 0); /* v_thp set bin */ + vth_list[1] = rtpcs_sds_read_bits(ctrl, sds_num, 0x1f, 0x14, 5, 3); /* v_thn set bin */ + + pr_info("vth_set_bin = %d", vth_list[0]); + pr_info("vth_set_bin = %d", vth_list[1]); + + vth_manual = !!rtpcs_sds_read_bits(ctrl, sds_num, 0x2e, 0x0f, 13, 13); + pr_info("Vth Maunal = %d", vth_manual); +} + +static void rtpcs_930x_sds_rxcal_tap_manual(struct rtpcs_ctrl *ctrl, u32 sds_num, + int tap_id, bool manual, u32 tap_list[]) +{ + if (manual) { + switch(tap_id) { + case 0: + /* ##REG0_LOAD_IN_INIT[0]=1; REG0_TAP0_INIT[5:0]=Tap0_Value */ + rtpcs_sds_write_bits(ctrl, sds_num, 0x2e, 0x0f, tap_id + 7, tap_id + 7, 0x1); + rtpcs_sds_write_bits(ctrl, sds_num, 0x2f, 0x03, 5, 5, tap_list[0]); + rtpcs_sds_write_bits(ctrl, sds_num, 0x2f, 0x03, 4, 0, tap_list[1]); + break; + case 1: + rtpcs_sds_write_bits(ctrl, sds_num, 0x2e, 0x0f, tap_id + 7, tap_id + 7, 0x1); + rtpcs_sds_write_bits(ctrl, sds_num, 0x21, 0x07, 6, 6, tap_list[0]); + rtpcs_sds_write_bits(ctrl, sds_num, 0x2e, 0x09, 11, 6, tap_list[1]); + rtpcs_sds_write_bits(ctrl, sds_num, 0x21, 0x07, 5, 5, tap_list[2]); + rtpcs_sds_write_bits(ctrl, sds_num, 0x2f, 0x12, 5, 0, tap_list[3]); + break; + case 2: + rtpcs_sds_write_bits(ctrl, sds_num, 0x2e, 0x0f, tap_id + 7, tap_id + 7, 0x1); + rtpcs_sds_write_bits(ctrl, sds_num, 0x2e, 0x09, 5, 5, tap_list[0]); + rtpcs_sds_write_bits(ctrl, sds_num, 0x2e, 0x09, 4, 0, tap_list[1]); + rtpcs_sds_write_bits(ctrl, sds_num, 0x2e, 0x0a, 11, 11, tap_list[2]); + rtpcs_sds_write_bits(ctrl, sds_num, 0x2e, 0x0a, 10, 6, tap_list[3]); + break; + case 3: + rtpcs_sds_write_bits(ctrl, sds_num, 0x2e, 0x0f, tap_id + 7, tap_id + 7, 0x1); + rtpcs_sds_write_bits(ctrl, sds_num, 0x2e, 0x0a, 5, 5, tap_list[0]); + rtpcs_sds_write_bits(ctrl, sds_num, 0x2e, 0x0a, 4, 0, tap_list[1]); + rtpcs_sds_write_bits(ctrl, sds_num, 0x2e, 0x06, 5, 5, tap_list[2]); + rtpcs_sds_write_bits(ctrl, sds_num, 0x2e, 0x06, 4, 0, tap_list[3]); + break; + case 4: + rtpcs_sds_write_bits(ctrl, sds_num, 0x2e, 0x0f, tap_id + 7, tap_id + 7, 0x1); + rtpcs_sds_write_bits(ctrl, sds_num, 0x2f, 0x01, 5, 5, tap_list[0]); + rtpcs_sds_write_bits(ctrl, sds_num, 0x2f, 0x01, 4, 0, tap_list[1]); + rtpcs_sds_write_bits(ctrl, sds_num, 0x2e, 0x06, 11, 11, tap_list[2]); + rtpcs_sds_write_bits(ctrl, sds_num, 0x2e, 0x06, 10, 6, tap_list[3]); + break; + default: + break; + } + } else { + rtpcs_sds_write_bits(ctrl, sds_num, 0x2e, 0x0f, tap_id + 7, tap_id + 7, 0x0); + mdelay(10); + } +} + +static void rtpcs_930x_sds_rxcal_tap_get(struct rtpcs_ctrl *ctrl, u32 sds_num, + u32 tap_id, u32 tap_list[]) +{ + u32 tap0_sign_out; + u32 tap0_coef_bin; + u32 tap_sign_out_even; + u32 tap_coef_bin_even; + u32 tap_sign_out_odd; + u32 tap_coef_bin_odd; + bool tap_manual; + + if (!(sds_num % 2)) + rtpcs_sds_write(ctrl, sds_num, 0x1f, 0x2, 0x2f); + else + rtpcs_sds_write(ctrl, sds_num - 1, 0x1f, 0x2, 0x31); + + /* ##Page0x2E, Reg0x15[9], REG0_RX_EN_TEST=[1] */ + rtpcs_sds_write_bits(ctrl, sds_num, 0x2e, 0x15, 9, 9, 0x1); + /* ##Page0x21, Reg0x06[11 6], REG0_RX_DEBUG_SEL=[1 0 x x x x] */ + rtpcs_sds_write_bits(ctrl, sds_num, 0x21, 0x06, 11, 6, 0x20); + + if (!tap_id) { + /* ##Page0x2F, Reg0x0C[5 0], REG0_COEF_SEL=[0 0 0 0 0 1] */ + rtpcs_sds_write_bits(ctrl, sds_num, 0x2f, 0x0c, 5, 0, 0); + /* ##Tap1 Even Read Out */ + mdelay(1); + tap0_sign_out = rtpcs_sds_read_bits(ctrl, sds_num, 0x1f, 0x14, 5, 5); + tap0_coef_bin = rtpcs_sds_read_bits(ctrl, sds_num, 0x1f, 0x14, 4, 0); + + if (tap0_sign_out == 1) + pr_info("Tap0 Sign : -"); + else + pr_info("Tap0 Sign : +"); + + pr_info("tap0_coef_bin = %d", tap0_coef_bin); + + tap_list[0] = tap0_sign_out; + tap_list[1] = tap0_coef_bin; + + tap_manual = !!rtpcs_sds_read_bits(ctrl, sds_num, 0x2e, 0x0f, 7, 7); + pr_info("tap0 manual = %u",tap_manual); + } else { + /* ##Page0x2F, Reg0x0C[5 0], REG0_COEF_SEL=[0 0 0 0 0 1] */ + rtpcs_sds_write_bits(ctrl, sds_num, 0x2f, 0x0c, 5, 0, tap_id); + mdelay(1); + /* ##Tap1 Even Read Out */ + tap_sign_out_even = rtpcs_sds_read_bits(ctrl, sds_num, 0x1f, 0x14, 5, 5); + tap_coef_bin_even = rtpcs_sds_read_bits(ctrl, sds_num, 0x1f, 0x14, 4, 0); + + /* ##Page0x2F, Reg0x0C[5 0], REG0_COEF_SEL=[0 0 0 1 1 0] */ + rtpcs_sds_write_bits(ctrl, sds_num, 0x2f, 0x0c, 5, 0, (tap_id + 5)); + /* ##Tap1 Odd Read Out */ + tap_sign_out_odd = rtpcs_sds_read_bits(ctrl, sds_num, 0x1f, 0x14, 5, 5); + tap_coef_bin_odd = rtpcs_sds_read_bits(ctrl, sds_num, 0x1f, 0x14, 4, 0); + + if (tap_sign_out_even == 1) + pr_info("Tap %u even sign: -", tap_id); + else + pr_info("Tap %u even sign: +", tap_id); + + pr_info("Tap %u even coefficient = %u", tap_id, tap_coef_bin_even); + + if (tap_sign_out_odd == 1) + pr_info("Tap %u odd sign: -", tap_id); + else + pr_info("Tap %u odd sign: +", tap_id); + + pr_info("Tap %u odd coefficient = %u", tap_id,tap_coef_bin_odd); + + tap_list[0] = tap_sign_out_even; + tap_list[1] = tap_coef_bin_even; + tap_list[2] = tap_sign_out_odd; + tap_list[3] = tap_coef_bin_odd; + + tap_manual = rtpcs_sds_read_bits(ctrl, sds_num, 0x2e, 0x0f, tap_id + 7, tap_id + 7); + pr_info("tap %u manual = %d",tap_id, tap_manual); + } +} + +__attribute__((unused)) +static void rtpcs_930x_sds_do_rx_calibration_1(struct rtpcs_ctrl *ctrl, int sds, + phy_interface_t phy_mode) +{ + /* From both rtl9300_rxCaliConf_serdes_myParam and rtl9300_rxCaliConf_phy_myParam */ + int tap0_init_val = 0x1f; /* Initial Decision Fed Equalizer 0 tap */ + int vth_min = 0x0; + + pr_info("start_1.1.1 initial value for sds %d\n", sds); + rtpcs_sds_write(ctrl, sds, 6, 0, 0); + + /* FGCAL */ + rtpcs_sds_write_bits(ctrl, sds, 0x2e, 0x01, 14, 14, 0x00); + rtpcs_sds_write_bits(ctrl, sds, 0x2e, 0x1c, 10, 5, 0x20); + rtpcs_sds_write_bits(ctrl, sds, 0x2f, 0x02, 0, 0, 0x01); + + /* DCVS */ + rtpcs_sds_write_bits(ctrl, sds, 0x2e, 0x1e, 14, 11, 0x00); + rtpcs_sds_write_bits(ctrl, sds, 0x2e, 0x01, 15, 15, 0x00); + rtpcs_sds_write_bits(ctrl, sds, 0x2e, 0x02, 11, 11, 0x00); + rtpcs_sds_write_bits(ctrl, sds, 0x2e, 0x1c, 4, 0, 0x00); + rtpcs_sds_write_bits(ctrl, sds, 0x2e, 0x1d, 15, 11, 0x00); + rtpcs_sds_write_bits(ctrl, sds, 0x2e, 0x1d, 10, 6, 0x00); + rtpcs_sds_write_bits(ctrl, sds, 0x2e, 0x1d, 5, 1, 0x00); + rtpcs_sds_write_bits(ctrl, sds, 0x2e, 0x02, 10, 6, 0x00); + rtpcs_sds_write_bits(ctrl, sds, 0x2e, 0x11, 4, 0, 0x00); + rtpcs_sds_write_bits(ctrl, sds, 0x2f, 0x00, 3, 0, 0x0f); + rtpcs_sds_write_bits(ctrl, sds, 0x2e, 0x04, 6, 6, 0x01); + rtpcs_sds_write_bits(ctrl, sds, 0x2e, 0x04, 7, 7, 0x01); + + /* LEQ (Long Term Equivalent signal level) */ + rtpcs_sds_write_bits(ctrl, sds, 0x2e, 0x16, 14, 8, 0x00); + + /* DFE (Decision Fed Equalizer) */ + rtpcs_sds_write_bits(ctrl, sds, 0x2f, 0x03, 5, 0, tap0_init_val); + rtpcs_sds_write_bits(ctrl, sds, 0x2e, 0x09, 11, 6, 0x00); + rtpcs_sds_write_bits(ctrl, sds, 0x2e, 0x09, 5, 0, 0x00); + rtpcs_sds_write_bits(ctrl, sds, 0x2e, 0x0a, 5, 0, 0x00); + rtpcs_sds_write_bits(ctrl, sds, 0x2f, 0x01, 5, 0, 0x00); + rtpcs_sds_write_bits(ctrl, sds, 0x2f, 0x12, 5, 0, 0x00); + rtpcs_sds_write_bits(ctrl, sds, 0x2e, 0x0a, 11, 6, 0x00); + rtpcs_sds_write_bits(ctrl, sds, 0x2e, 0x06, 5, 0, 0x00); + rtpcs_sds_write_bits(ctrl, sds, 0x2f, 0x01, 5, 0, 0x00); + + /* Vth */ + rtpcs_sds_write_bits(ctrl, sds, 0x2e, 0x13, 5, 3, 0x07); + rtpcs_sds_write_bits(ctrl, sds, 0x2e, 0x13, 2, 0, 0x07); + rtpcs_sds_write_bits(ctrl, sds, 0x2f, 0x0b, 5, 3, vth_min); + + pr_info("end_1.1.1 --\n"); + + pr_info("start_1.1.2 Load DFE init. value\n"); + + rtpcs_sds_write_bits(ctrl, sds, 0x2e, 0x0f, 13, 7, 0x7f); + + pr_info("end_1.1.2\n"); + + pr_info("start_1.1.3 disable LEQ training,enable DFE clock\n"); + + rtpcs_sds_write_bits(ctrl, sds, 0x2e, 0x17, 7, 7, 0x00); + rtpcs_sds_write_bits(ctrl, sds, 0x2e, 0x17, 6, 2, 0x00); + rtpcs_sds_write_bits(ctrl, sds, 0x2e, 0x0c, 8, 8, 0x00); + rtpcs_sds_write_bits(ctrl, sds, 0x2e, 0x0b, 4, 4, 0x01); + rtpcs_sds_write_bits(ctrl, sds, 0x2e, 0x12, 14, 14, 0x00); + rtpcs_sds_write_bits(ctrl, sds, 0x2f, 0x02, 15, 15, 0x00); + + pr_info("end_1.1.3 --\n"); + + pr_info("start_1.1.4 offset cali setting\n"); + + rtpcs_sds_write_bits(ctrl, sds, 0x2e, 0x0f, 15, 14, 0x03); + + pr_info("end_1.1.4\n"); + + pr_info("start_1.1.5 LEQ and DFE setting\n"); + + /* TODO: make this work for DAC cables of different lengths */ + /* For a 10GBit serdes wit Fibre, SDS 8 or 9 */ + if (phy_mode == PHY_INTERFACE_MODE_10GBASER || + phy_mode == PHY_INTERFACE_MODE_1000BASEX || + phy_mode == PHY_INTERFACE_MODE_SGMII) + rtpcs_sds_write_bits(ctrl, sds, 0x2e, 0x16, 3, 2, 0x02); + else + pr_err("%s not PHY-based or SerDes, implement DAC!\n", __func__); + + /* No serdes, check for Aquantia PHYs */ + rtpcs_sds_write_bits(ctrl, sds, 0x2e, 0x16, 3, 2, 0x02); + + rtpcs_sds_write_bits(ctrl, sds, 0x2e, 0x0f, 6, 0, 0x5f); + rtpcs_sds_write_bits(ctrl, sds, 0x2f, 0x05, 7, 2, 0x1f); + rtpcs_sds_write_bits(ctrl, sds, 0x2e, 0x19, 9, 5, 0x1f); + rtpcs_sds_write_bits(ctrl, sds, 0x2f, 0x0b, 15, 9, 0x3c); + rtpcs_sds_write_bits(ctrl, sds, 0x2e, 0x0b, 1, 0, 0x03); + + pr_info("end_1.1.5\n"); +} + +static void rtpcs_930x_sds_do_rx_calibration_2_1(struct rtpcs_ctrl *ctrl, + u32 sds_num) +{ + pr_info("start_1.2.1 ForegroundOffsetCal_Manual\n"); + + /* Gray config endis to 1 */ + rtpcs_sds_write_bits(ctrl, sds_num, 0x2f, 0x02, 2, 2, 0x01); + + /* ForegroundOffsetCal_Manual(auto mode) */ + rtpcs_sds_write_bits(ctrl, sds_num, 0x2e, 0x01, 14, 14, 0x00); + + pr_info("end_1.2.1"); +} + +static void rtpcs_930x_sds_do_rx_calibration_2_2(struct rtpcs_ctrl *ctrl, + int sds_num) +{ + /* Force Rx-Run = 0 */ + rtpcs_sds_write_bits(ctrl, sds_num, 0x2e, 0x15, 8, 8, 0x0); + + rtpcs_930x_sds_rx_reset(ctrl, sds_num, PHY_INTERFACE_MODE_10GBASER); +} + +static void rtpcs_930x_sds_do_rx_calibration_2_3(struct rtpcs_ctrl *ctrl, + int sds_num) +{ + u32 fgcal_binary, fgcal_gray; + u32 offset_range; + + pr_info("start_1.2.3 Foreground Calibration\n"); + + while(1) { + if (!(sds_num % 2)) + rtpcs_sds_write(ctrl, sds_num, 0x1f, 0x2, 0x2f); + else + rtpcs_sds_write(ctrl, sds_num - 1, 0x1f, 0x2, 0x31); + + /* ##Page0x2E, Reg0x15[9], REG0_RX_EN_TEST=[1] */ + rtpcs_sds_write_bits(ctrl, sds_num, 0x2e, 0x15, 9, 9, 0x1); + /* ##Page0x21, Reg0x06[11 6], REG0_RX_DEBUG_SEL=[1 0 x x x x] */ + rtpcs_sds_write_bits(ctrl, sds_num, 0x21, 0x06, 11, 6, 0x20); + /* ##Page0x2F, Reg0x0C[5 0], REG0_COEF_SEL=[0 0 1 1 1 1] */ + rtpcs_sds_write_bits(ctrl, sds_num, 0x2f, 0x0c, 5, 0, 0xf); + /* ##FGCAL read gray */ + fgcal_gray = rtpcs_sds_read_bits(ctrl, sds_num, 0x1f, 0x14, 5, 0); + /* ##Page0x2F, Reg0x0C[5 0], REG0_COEF_SEL=[0 0 1 1 1 0] */ + rtpcs_sds_write_bits(ctrl, sds_num, 0x2f, 0x0c, 5, 0, 0xe); + /* ##FGCAL read binary */ + fgcal_binary = rtpcs_sds_read_bits(ctrl, sds_num, 0x1f, 0x14, 5, 0); + + pr_info("%s: fgcal_gray: %d, fgcal_binary %d\n", + __func__, fgcal_gray, fgcal_binary); + + offset_range = rtpcs_sds_read_bits(ctrl, sds_num, 0x2e, 0x15, 15, 14); + + if (fgcal_binary > 60 || fgcal_binary < 3) { + if (offset_range == 3) { + pr_info("%s: Foreground Calibration result marginal!", __func__); + break; + } else { + offset_range++; + rtpcs_sds_write_bits(ctrl, sds_num, 0x2e, 0x15, 15, 14, offset_range); + rtpcs_930x_sds_do_rx_calibration_2_2(ctrl, sds_num); + } + } else { + break; + } + } + pr_info("%s: end_1.2.3\n", __func__); +} + +__attribute__((unused)) +static void rtpcs_930x_sds_do_rx_calibration_2(struct rtpcs_ctrl *ctrl, int sds) +{ + rtpcs_930x_sds_rx_reset(ctrl, sds, PHY_INTERFACE_MODE_10GBASER); + rtpcs_930x_sds_do_rx_calibration_2_1(ctrl, sds); + rtpcs_930x_sds_do_rx_calibration_2_2(ctrl, sds); + rtpcs_930x_sds_do_rx_calibration_2_3(ctrl, sds); +} + +static void rtpcs_930x_sds_rxcal_3_1(struct rtpcs_ctrl *ctrl, int sds_num, + phy_interface_t phy_mode) +{ + pr_info("start_1.3.1"); + + /* ##1.3.1 */ + if (phy_mode != PHY_INTERFACE_MODE_10GBASER && + phy_mode != PHY_INTERFACE_MODE_1000BASEX && + phy_mode != PHY_INTERFACE_MODE_SGMII) + rtpcs_sds_write_bits(ctrl, sds_num, 0x2e, 0xc, 8, 8, 0); + + rtpcs_sds_write_bits(ctrl, sds_num, 0x2e, 0x17, 7, 7, 0x0); + rtpcs_930x_sds_rxcal_leq_manual(ctrl, sds_num, false, 0); + + pr_info("end_1.3.1"); +} + +static void rtpcs_930x_sds_rxcal_3_2(struct rtpcs_ctrl *ctrl, int sds_num, + phy_interface_t phy_mode) +{ + u32 sum10 = 0, avg10, int10; + int dac_long_cable_offset; + bool eq_hold_enabled; + int i; + + if (phy_mode == PHY_INTERFACE_MODE_10GBASER || + phy_mode == PHY_INTERFACE_MODE_1000BASEX || + phy_mode == PHY_INTERFACE_MODE_SGMII) { + /* rtl9300_rxCaliConf_serdes_myParam */ + dac_long_cable_offset = 3; + eq_hold_enabled = true; + } else { + /* rtl9300_rxCaliConf_phy_myParam */ + dac_long_cable_offset = 0; + eq_hold_enabled = false; + } + + if (phy_mode != PHY_INTERFACE_MODE_10GBASER) + pr_warn("%s: LEQ only valid for 10GR!\n", __func__); + + pr_info("start_1.3.2"); + + for(i = 0; i < 10; i++) { + sum10 += rtpcs_930x_sds_rxcal_leq_read(ctrl, sds_num); + mdelay(10); + } + + avg10 = (sum10 / 10) + (((sum10 % 10) >= 5) ? 1 : 0); + int10 = sum10 / 10; + + pr_info("sum10:%u, avg10:%u, int10:%u", sum10, avg10, int10); + + if (phy_mode == PHY_INTERFACE_MODE_10GBASER || + phy_mode == PHY_INTERFACE_MODE_1000BASEX || + phy_mode == PHY_INTERFACE_MODE_SGMII) { + if (dac_long_cable_offset) { + rtpcs_930x_sds_rxcal_leq_offset_manual(ctrl, sds_num, 1, + dac_long_cable_offset); + rtpcs_sds_write_bits(ctrl, sds_num, 0x2e, 0x17, 7, 7, + eq_hold_enabled); + if (phy_mode == PHY_INTERFACE_MODE_10GBASER) + rtpcs_930x_sds_rxcal_leq_manual(ctrl, sds_num, + true, avg10); + } else { + if (sum10 >= 5) { + rtpcs_930x_sds_rxcal_leq_offset_manual(ctrl, sds_num, 1, 3); + rtpcs_sds_write_bits(ctrl, sds_num, 0x2e, 0x17, 7, 7, 0x1); + if (phy_mode == PHY_INTERFACE_MODE_10GBASER) + rtpcs_930x_sds_rxcal_leq_manual(ctrl, sds_num, true, avg10); + } else { + rtpcs_930x_sds_rxcal_leq_offset_manual(ctrl, sds_num, 1, 0); + rtpcs_sds_write_bits(ctrl, sds_num, 0x2e, 0x17, 7, 7, 0x1); + if (phy_mode == PHY_INTERFACE_MODE_10GBASER) + rtpcs_930x_sds_rxcal_leq_manual(ctrl, sds_num, true, avg10); + } + } + } + + pr_info("Sds:%u LEQ = %u",sds_num, rtpcs_930x_sds_rxcal_leq_read(ctrl, sds_num)); + + pr_info("end_1.3.2"); +} + +__attribute__((unused)) +static void rtpcs_930x_sds_do_rx_calibration_3(struct rtpcs_ctrl *ctrl, int sds_num, + phy_interface_t phy_mode) +{ + rtpcs_930x_sds_rxcal_3_1(ctrl, sds_num, phy_mode); + + if (phy_mode == PHY_INTERFACE_MODE_10GBASER || + phy_mode == PHY_INTERFACE_MODE_1000BASEX || + phy_mode == PHY_INTERFACE_MODE_SGMII) + rtpcs_930x_sds_rxcal_3_2(ctrl, sds_num, phy_mode); +} + +static void rtpcs_930x_sds_do_rx_calibration_4_1(struct rtpcs_ctrl *ctrl, int sds_num) +{ + u32 vth_list[2] = {0, 0}; + u32 tap0_list[4] = {0, 0, 0, 0}; + + pr_info("start_1.4.1"); + + /* ##1.4.1 */ + rtpcs_930x_sds_rxcal_vth_manual(ctrl, sds_num, false, vth_list); + rtpcs_930x_sds_rxcal_tap_manual(ctrl, sds_num, 0, false, tap0_list); + mdelay(200); + + pr_info("end_1.4.1"); +} + +static void rtpcs_930x_sds_do_rx_calibration_4_2(struct rtpcs_ctrl *ctrl, u32 sds_num) +{ + u32 vth_list[2]; + u32 tap_list[4]; + + pr_info("start_1.4.2"); + + rtpcs_930x_sds_rxcal_vth_get(ctrl, sds_num, vth_list); + rtpcs_930x_sds_rxcal_vth_manual(ctrl, sds_num, true, vth_list); + + mdelay(100); + + rtpcs_930x_sds_rxcal_tap_get(ctrl, sds_num, 0, tap_list); + rtpcs_930x_sds_rxcal_tap_manual(ctrl, sds_num, 0, true, tap_list); + + pr_info("end_1.4.2"); +} + +static void rtpcs_930x_sds_do_rx_calibration_4(struct rtpcs_ctrl *ctrl, u32 sds_num) +{ + rtpcs_930x_sds_do_rx_calibration_4_1(ctrl, sds_num); + rtpcs_930x_sds_do_rx_calibration_4_2(ctrl, sds_num); +} + +static void rtpcs_930x_sds_do_rx_calibration_5_2(struct rtpcs_ctrl *ctrl, u32 sds_num) +{ + u32 tap1_list[4] = {0}; + u32 tap2_list[4] = {0}; + u32 tap3_list[4] = {0}; + u32 tap4_list[4] = {0}; + + pr_info("start_1.5.2"); + + rtpcs_930x_sds_rxcal_tap_manual(ctrl, sds_num, 1, false, tap1_list); + rtpcs_930x_sds_rxcal_tap_manual(ctrl, sds_num, 2, false, tap2_list); + rtpcs_930x_sds_rxcal_tap_manual(ctrl, sds_num, 3, false, tap3_list); + rtpcs_930x_sds_rxcal_tap_manual(ctrl, sds_num, 4, false, tap4_list); + + mdelay(30); + + pr_info("end_1.5.2"); +} + +static void rtpcs_930x_sds_do_rx_calibration_5(struct rtpcs_ctrl *ctrl, u32 sds_num, + phy_interface_t phy_mode) +{ + if (phy_mode == PHY_INTERFACE_MODE_10GBASER) /* dfeTap1_4Enable true */ + rtpcs_930x_sds_do_rx_calibration_5_2(ctrl, sds_num); +} + + +static void rtpcs_930x_sds_do_rx_calibration_dfe_disable(struct rtpcs_ctrl *ctrl, u32 sds_num) +{ + u32 tap1_list[4] = {0}; + u32 tap2_list[4] = {0}; + u32 tap3_list[4] = {0}; + u32 tap4_list[4] = {0}; + + rtpcs_930x_sds_rxcal_tap_manual(ctrl, sds_num, 1, true, tap1_list); + rtpcs_930x_sds_rxcal_tap_manual(ctrl, sds_num, 2, true, tap2_list); + rtpcs_930x_sds_rxcal_tap_manual(ctrl, sds_num, 3, true, tap3_list); + rtpcs_930x_sds_rxcal_tap_manual(ctrl, sds_num, 4, true, tap4_list); + + mdelay(10); +} + +static void rtpcs_930x_sds_do_rx_calibration(struct rtpcs_ctrl *ctrl, int sds, + phy_interface_t phy_mode) +{ + u32 latch_sts; + + rtpcs_930x_sds_do_rx_calibration_1(ctrl, sds, phy_mode); + rtpcs_930x_sds_do_rx_calibration_2(ctrl, sds); + rtpcs_930x_sds_do_rx_calibration_4(ctrl, sds); + rtpcs_930x_sds_do_rx_calibration_5(ctrl, sds, phy_mode); + mdelay(20); + + /* Do this only for 10GR mode, SDS active in mode 0x1a */ + if (rtpcs_sds_read_bits(ctrl, sds, 0x1f, 9, 11, 7) == RTL930X_SDS_MODE_10GBASER) { + pr_info("%s: SDS enabled\n", __func__); + latch_sts = rtpcs_sds_read_bits(ctrl, sds, 0x4, 1, 2, 2); + mdelay(1); + latch_sts = rtpcs_sds_read_bits(ctrl, sds, 0x4, 1, 2, 2); + if (latch_sts) { + rtpcs_930x_sds_do_rx_calibration_dfe_disable(ctrl, sds); + rtpcs_930x_sds_do_rx_calibration_4(ctrl, sds); + rtpcs_930x_sds_do_rx_calibration_5(ctrl, sds, phy_mode); + } + } +} + +static int rtpcs_930x_sds_sym_err_reset(struct rtpcs_ctrl *ctrl, int sds_num, + phy_interface_t phy_mode) +{ + switch (phy_mode) { + case PHY_INTERFACE_MODE_XGMII: + break; + + case PHY_INTERFACE_MODE_10GBASER: + /* Read twice to clear */ + rtpcs_sds_read(ctrl, sds_num, 5, 1); + rtpcs_sds_read(ctrl, sds_num, 5, 1); + break; + + case PHY_INTERFACE_MODE_1000BASEX: + case PHY_INTERFACE_MODE_SGMII: + case PHY_INTERFACE_MODE_10G_QXGMII: + rtpcs_sds_write_bits(ctrl, sds_num, 0x1, 24, 2, 0, 0); + rtpcs_sds_write_bits(ctrl, sds_num, 0x1, 3, 15, 8, 0); + rtpcs_sds_write_bits(ctrl, sds_num, 0x1, 2, 15, 0, 0); + break; + + default: + pr_info("%s unsupported phy mode\n", __func__); + return -1; + } + + return 0; +} + +static u32 rtpcs_930x_sds_sym_err_get(struct rtpcs_ctrl *ctrl, int sds_num, + phy_interface_t phy_mode) +{ + u32 v = 0; + + switch (phy_mode) { + case PHY_INTERFACE_MODE_XGMII: + case PHY_INTERFACE_MODE_10G_QXGMII: + break; + + case PHY_INTERFACE_MODE_1000BASEX: + case PHY_INTERFACE_MODE_SGMII: + case PHY_INTERFACE_MODE_10GBASER: + v = rtpcs_sds_read(ctrl, sds_num, 5, 1); + return v & 0xff; + + default: + pr_info("%s unsupported PHY-mode\n", __func__); + } + + return v; +} + +static int rtpcs_930x_sds_check_calibration(struct rtpcs_ctrl *ctrl, int sds_num, + phy_interface_t phy_mode) +{ + u32 errors1, errors2; + + rtpcs_930x_sds_sym_err_reset(ctrl, sds_num, phy_mode); + rtpcs_930x_sds_sym_err_reset(ctrl, sds_num, phy_mode); + + /* Count errors during 1ms */ + errors1 = rtpcs_930x_sds_sym_err_get(ctrl, sds_num, phy_mode); + mdelay(1); + errors2 = rtpcs_930x_sds_sym_err_get(ctrl, sds_num, phy_mode); + + switch (phy_mode) { + case PHY_INTERFACE_MODE_1000BASEX: + case PHY_INTERFACE_MODE_SGMII: + case PHY_INTERFACE_MODE_XGMII: + if ((errors2 - errors1 > 100) || + (errors1 >= 0xffff00) || (errors2 >= 0xffff00)) { + pr_info("%s XSGMII error rate too high\n", __func__); + return 1; + } + break; + case PHY_INTERFACE_MODE_10GBASER: + case PHY_INTERFACE_MODE_10G_QXGMII: + if (errors2 > 0) { + pr_info("%s: %s error rate too high\n", __func__, phy_modes(phy_mode)); + return 1; + } + break; + default: + return 1; + } + + return 0; +} + +static void rtpcs_930x_phy_enable_10g_1g(struct rtpcs_ctrl *ctrl, int sds_num) +{ + u32 v; + + /* Enable 1GBit PHY */ + v = rtpcs_sds_read(ctrl, sds_num, PHY_PAGE_2, MII_BMCR); + pr_info("%s 1gbit phy: %08x\n", __func__, v); + v &= ~BMCR_PDOWN; + rtpcs_sds_write(ctrl, sds_num, PHY_PAGE_2, MII_BMCR, v); + pr_info("%s 1gbit phy enabled: %08x\n", __func__, v); + + /* Enable 10GBit PHY */ + v = rtpcs_sds_read(ctrl, sds_num, PHY_PAGE_4, MII_BMCR); + pr_info("%s 10gbit phy: %08x\n", __func__, v); + v &= ~BMCR_PDOWN; + rtpcs_sds_write(ctrl, sds_num, PHY_PAGE_4, MII_BMCR, v); + pr_info("%s 10gbit phy after: %08x\n", __func__, v); + + /* dal_longan_construct_mac_default_10gmedia_fiber */ + v = rtpcs_sds_read(ctrl, sds_num, 0x1f, 11); + pr_info("%s set medium: %08x\n", __func__, v); + v |= BIT(1); + rtpcs_sds_write(ctrl, sds_num, 0x1f, 11, v); + pr_info("%s set medium after: %08x\n", __func__, v); +} + +static int rtpcs_930x_sds_10g_idle(struct rtpcs_ctrl *ctrl, int sds_num) +{ + bool busy; + int i = 0; + + do { + if (sds_num % 2) { + rtpcs_sds_write_bits(ctrl, sds_num - 1, 0x1f, 0x2, 15, 0, 53); + busy = !!rtpcs_sds_read_bits(ctrl, sds_num - 1, 0x1f, 0x14, 1, 1); + } else { + rtpcs_sds_write_bits(ctrl, sds_num, 0x1f, 0x2, 15, 0, 53); + busy = !!rtpcs_sds_read_bits(ctrl, sds_num, 0x1f, 0x14, 0, 0); + } + i++; + } while (busy && i < 100); + + if (i < 100) + return 0; + + pr_warn("%s WARNING: Waiting for RX idle timed out, SDS %d\n", __func__, sds_num); + return -EIO; +} + +static const sds_config rtpcs_930x_sds_cfg_10gr_even[] = +{ + /* 1G */ + {0x00, 0x0E, 0x3053}, {0x01, 0x14, 0x0100}, {0x21, 0x03, 0x8206}, + {0x21, 0x05, 0x40B0}, {0x21, 0x06, 0x0010}, {0x21, 0x07, 0xF09F}, + {0x21, 0x0C, 0x0007}, {0x21, 0x0D, 0x6009}, {0x21, 0x0E, 0x0000}, + {0x21, 0x0F, 0x0008}, {0x24, 0x00, 0x0668}, {0x24, 0x02, 0xD020}, + {0x24, 0x06, 0xC000}, {0x24, 0x0B, 0x1892}, {0x24, 0x0F, 0xFFDF}, + {0x24, 0x12, 0x03C4}, {0x24, 0x13, 0x027F}, {0x24, 0x14, 0x1311}, + {0x24, 0x16, 0x00C9}, {0x24, 0x17, 0xA100}, {0x24, 0x1A, 0x0001}, + {0x24, 0x1C, 0x0400}, {0x25, 0x01, 0x0300}, {0x25, 0x02, 0x1017}, + {0x25, 0x03, 0xFFDF}, {0x25, 0x05, 0x7F7C}, {0x25, 0x07, 0x8100}, + {0x25, 0x08, 0x0001}, {0x25, 0x09, 0xFFD4}, {0x25, 0x0A, 0x7C2F}, + {0x25, 0x0E, 0x003F}, {0x25, 0x0F, 0x0121}, {0x25, 0x10, 0x0020}, + {0x25, 0x11, 0x8840}, {0x2B, 0x13, 0x0050}, {0x2B, 0x18, 0x8E88}, + {0x2B, 0x19, 0x4902}, {0x2B, 0x1D, 0x2501}, {0x2D, 0x13, 0x0050}, + {0x2D, 0x18, 0x8E88}, {0x2D, 0x19, 0x4902}, {0x2D, 0x1D, 0x2641}, + {0x2F, 0x13, 0x0050}, {0x2F, 0x18, 0x8E88}, {0x2F, 0x19, 0x4902}, + {0x2F, 0x1D, 0x66E1}, + /* 3.125G */ + {0x28, 0x00, 0x0668}, {0x28, 0x02, 0xD020}, {0x28, 0x06, 0xC000}, + {0x28, 0x0B, 0x1892}, {0x28, 0x0F, 0xFFDF}, {0x28, 0x12, 0x01C4}, + {0x28, 0x13, 0x027F}, {0x28, 0x14, 0x1311}, {0x28, 0x16, 0x00C9}, + {0x28, 0x17, 0xA100}, {0x28, 0x1A, 0x0001}, {0x28, 0x1C, 0x0400}, + {0x29, 0x01, 0x0300}, {0x29, 0x02, 0x1017}, {0x29, 0x03, 0xFFDF}, + {0x29, 0x05, 0x7F7C}, {0x29, 0x07, 0x8100}, {0x29, 0x08, 0x0001}, + {0x29, 0x09, 0xFFD4}, {0x29, 0x0A, 0x7C2F}, {0x29, 0x0E, 0x003F}, + {0x29, 0x0F, 0x0121}, {0x29, 0x10, 0x0020}, {0x29, 0x11, 0x8840}, + /* 10G */ + {0x06, 0x0D, 0x0F00}, {0x06, 0x00, 0x0000}, {0x06, 0x01, 0xC800}, + {0x21, 0x03, 0x8206}, {0x21, 0x05, 0x40B0}, {0x21, 0x06, 0x0010}, + {0x21, 0x07, 0xF09F}, {0x21, 0x0C, 0x0007}, {0x21, 0x0D, 0x6009}, + {0x21, 0x0E, 0x0000}, {0x21, 0x0F, 0x0008}, {0x2E, 0x00, 0xA668}, + {0x2E, 0x02, 0xD020}, {0x2E, 0x06, 0xC000}, {0x2E, 0x0B, 0x1892}, + {0x2E, 0x0F, 0xFFDF}, {0x2E, 0x11, 0x8280}, {0x2E, 0x12, 0x0044}, + {0x2E, 0x13, 0x027F}, {0x2E, 0x14, 0x1311}, {0x2E, 0x17, 0xA100}, + {0x2E, 0x1A, 0x0001}, {0x2E, 0x1C, 0x0400}, {0x2F, 0x01, 0x0300}, + {0x2F, 0x02, 0x1217}, {0x2F, 0x03, 0xFFDF}, {0x2F, 0x05, 0x7F7C}, + {0x2F, 0x07, 0x80C4}, {0x2F, 0x08, 0x0001}, {0x2F, 0x09, 0xFFD4}, + {0x2F, 0x0A, 0x7C2F}, {0x2F, 0x0E, 0x003F}, {0x2F, 0x0F, 0x0121}, + {0x2F, 0x10, 0x0020}, {0x2F, 0x11, 0x8840}, {0x2F, 0x14, 0xE008}, + {0x2B, 0x13, 0x0050}, {0x2B, 0x18, 0x8E88}, {0x2B, 0x19, 0x4902}, + {0x2B, 0x1D, 0x2501}, {0x2D, 0x13, 0x0050}, {0x2D, 0x17, 0x4109}, + {0x2D, 0x18, 0x8E88}, {0x2D, 0x19, 0x4902}, {0x2D, 0x1C, 0x1109}, + {0x2D, 0x1D, 0x2641}, {0x2F, 0x13, 0x0050}, {0x2F, 0x18, 0x8E88}, + {0x2F, 0x19, 0x4902}, {0x2F, 0x1D, 0x76E1}, +}; + +static const sds_config rtpcs_930x_sds_cfg_10gr_odd[] = +{ + /* 1G */ + {0x00, 0x0E, 0x3053}, {0x01, 0x14, 0x0100}, {0x21, 0x03, 0x8206}, + {0x21, 0x06, 0x0010}, {0x21, 0x07, 0xF09F}, {0x21, 0x0A, 0x0003}, + {0x21, 0x0B, 0x0005}, {0x21, 0x0C, 0x0007}, {0x21, 0x0D, 0x6009}, + {0x21, 0x0E, 0x0000}, {0x21, 0x0F, 0x0008}, {0x24, 0x00, 0x0668}, + {0x24, 0x02, 0xD020}, {0x24, 0x06, 0xC000}, {0x24, 0x0B, 0x1892}, + {0x24, 0x0F, 0xFFDF}, {0x24, 0x12, 0x03C4}, {0x24, 0x13, 0x027F}, + {0x24, 0x14, 0x1311}, {0x24, 0x16, 0x00C9}, {0x24, 0x17, 0xA100}, + {0x24, 0x1A, 0x0001}, {0x24, 0x1C, 0x0400}, {0x25, 0x00, 0x820F}, + {0x25, 0x01, 0x0300}, {0x25, 0x02, 0x1017}, {0x25, 0x03, 0xFFDF}, + {0x25, 0x05, 0x7F7C}, {0x25, 0x07, 0x8100}, {0x25, 0x08, 0x0001}, + {0x25, 0x09, 0xFFD4}, {0x25, 0x0A, 0x7C2F}, {0x25, 0x0E, 0x003F}, + {0x25, 0x0F, 0x0121}, {0x25, 0x10, 0x0020}, {0x25, 0x11, 0x8840}, + {0x2B, 0x13, 0x3D87}, {0x2B, 0x14, 0x3108}, {0x2D, 0x13, 0x3C87}, + {0x2D, 0x14, 0x1808}, + /* 3.125G */ + {0x28, 0x00, 0x0668}, {0x28, 0x02, 0xD020}, {0x28, 0x06, 0xC000}, + {0x28, 0x0B, 0x1892}, {0x28, 0x0F, 0xFFDF}, {0x28, 0x12, 0x01C4}, + {0x28, 0x13, 0x027F}, {0x28, 0x14, 0x1311}, {0x28, 0x16, 0x00C9}, + {0x28, 0x17, 0xA100}, {0x28, 0x1A, 0x0001}, {0x28, 0x1C, 0x0400}, + {0x29, 0x00, 0x820F}, {0x29, 0x01, 0x0300}, {0x29, 0x02, 0x1017}, + {0x29, 0x03, 0xFFDF}, {0x29, 0x05, 0x7F7C}, {0x29, 0x07, 0x8100}, + {0x29, 0x08, 0x0001}, {0x29, 0x0A, 0x7C2F}, {0x29, 0x0E, 0x003F}, + {0x29, 0x0F, 0x0121}, {0x29, 0x10, 0x0020}, {0x29, 0x11, 0x8840}, + /* 10G */ + {0x06, 0x0D, 0x0F00}, {0x06, 0x00, 0x0000}, {0x06, 0x01, 0xC800}, + {0x21, 0x03, 0x8206}, {0x21, 0x05, 0x40B0}, {0x21, 0x06, 0x0010}, + {0x21, 0x07, 0xF09F}, {0x21, 0x0A, 0x0003}, {0x21, 0x0B, 0x0005}, + {0x21, 0x0C, 0x0007}, {0x21, 0x0D, 0x6009}, {0x21, 0x0E, 0x0000}, + {0x21, 0x0F, 0x0008}, {0x2E, 0x00, 0xA668}, {0x2E, 0x02, 0xD020}, + {0x2E, 0x06, 0xC000}, {0x2E, 0x0B, 0x1892}, {0x2E, 0x0F, 0xFFDF}, + {0x2E, 0x11, 0x8280}, {0x2E, 0x12, 0x0044}, {0x2E, 0x13, 0x027F}, + {0x2E, 0x14, 0x1311}, {0x2E, 0x17, 0xA100}, {0x2E, 0x1A, 0x0001}, + {0x2E, 0x1C, 0x0400}, {0x2F, 0x00, 0x820F}, {0x2F, 0x01, 0x0300}, + {0x2F, 0x02, 0x1217}, {0x2F, 0x03, 0xFFDF}, {0x2F, 0x05, 0x7F7C}, + {0x2F, 0x07, 0x80C4}, {0x2F, 0x08, 0x0001}, {0x2F, 0x09, 0xFFD4}, + {0x2F, 0x0A, 0x7C2F}, {0x2F, 0x0E, 0x003F}, {0x2F, 0x0F, 0x0121}, + {0x2F, 0x10, 0x0020}, {0x2F, 0x11, 0x8840}, {0x2B, 0x13, 0x3D87}, + {0x2B, 0x14, 0x3108}, {0x2D, 0x13, 0x3C87}, {0x2D, 0x14, 0x1808}, +}; + +static const sds_config rtpcs_930x_sds_cfg_10g_2500bx_even[] = +{ + {0x00, 0x0E, 0x3053}, {0x01, 0x14, 0x0100}, + {0x21, 0x03, 0x8206}, {0x21, 0x05, 0x40B0}, {0x21, 0x06, 0x0010}, {0x21, 0x07, 0xF09F}, + {0x21, 0x0C, 0x0007}, {0x21, 0x0D, 0x6009}, {0x21, 0x0E, 0x0000}, {0x21, 0x0F, 0x0008}, + {0x24, 0x00, 0x0668}, {0x24, 0x02, 0xD020}, {0x24, 0x06, 0xC000}, {0x24, 0x0B, 0x1892}, + {0x24, 0x0F, 0xFFDF}, {0x24, 0x12, 0x03C4}, {0x24, 0x13, 0x027F}, {0x24, 0x14, 0x1311}, + {0x24, 0x16, 0x00C9}, {0x24, 0x17, 0xA100}, {0x24, 0x1A, 0x0001}, {0x24, 0x1C, 0x0400}, + {0x25, 0x01, 0x0300}, {0x25, 0x02, 0x1017}, {0x25, 0x03, 0xFFDF}, {0x25, 0x05, 0x7F7C}, + {0x25, 0x07, 0x8100}, {0x25, 0x08, 0x0001}, {0x25, 0x09, 0xFFD4}, {0x25, 0x0A, 0x7C2F}, + {0x25, 0x0E, 0x003F}, {0x25, 0x0F, 0x0121}, {0x25, 0x10, 0x0020}, {0x25, 0x11, 0x8840}, + {0x28, 0x00, 0x0668}, {0x28, 0x02, 0xD020}, {0x28, 0x06, 0xC000}, {0x28, 0x0B, 0x1892}, + {0x28, 0x0F, 0xFFDF}, {0x28, 0x12, 0x01C4}, {0x28, 0x13, 0x027F}, {0x28, 0x14, 0x1311}, + {0x28, 0x16, 0x00C9}, {0x28, 0x17, 0xA100}, {0x28, 0x1A, 0x0001}, {0x28, 0x1C, 0x0400}, + {0x29, 0x01, 0x0300}, {0x29, 0x02, 0x1017}, {0x29, 0x03, 0xFFDF}, {0x29, 0x05, 0x7F7C}, + {0x29, 0x07, 0x8100}, {0x29, 0x08, 0x0001}, {0x29, 0x09, 0xFFD4}, {0x29, 0x0A, 0x7C2F}, + {0x29, 0x0E, 0x003F}, {0x29, 0x0F, 0x0121}, {0x29, 0x10, 0x0020}, {0x29, 0x11, 0x8840}, + {0x2B, 0x13, 0x0050}, {0x2B, 0x18, 0x8E88}, {0x2B, 0x19, 0x4902}, {0x2B, 0x1D, 0x2501}, + {0x2D, 0x13, 0x0050}, {0x2D, 0x18, 0x8E88}, {0x2D, 0x17, 0x4109}, {0x2D, 0x19, 0x4902}, + {0x2D, 0x1C, 0x1109}, {0x2D, 0x1D, 0x2641}, + {0x2F, 0x13, 0x0050}, {0x2F, 0x18, 0x8E88}, {0x2F, 0x19, 0x4902}, {0x2F, 0x1D, 0x66E1}, +}; + +static const sds_config rtpcs_930x_sds_cfg_10g_2500bx_odd[] = +{ + {0x00, 0x0E, 0x3053}, {0x01, 0x14, 0x0100}, + {0x21, 0x03, 0x8206}, {0x21, 0x06, 0x0010}, {0x21, 0x07, 0xF09F}, {0x21, 0x0A, 0x0003}, + {0x21, 0x0B, 0x0005}, {0x21, 0x0C, 0x0007}, {0x21, 0x0D, 0x6009}, {0x21, 0x0E, 0x0000}, + {0x21, 0x0F, 0x0008}, + {0x24, 0x00, 0x0668}, {0x24, 0x02, 0xD020}, {0x24, 0x06, 0xC000}, {0x24, 0x0B, 0x1892}, + {0x24, 0x0F, 0xFFDF}, {0x24, 0x12, 0x03C4}, {0x24, 0x13, 0x027F}, {0x24, 0x14, 0x1311}, + {0x24, 0x16, 0x00C9}, {0x24, 0x17, 0xA100}, {0x24, 0x1A, 0x0001}, {0x24, 0x1C, 0x0400}, + {0x25, 0x00, 0x820F}, {0x25, 0x01, 0x0300}, {0x25, 0x02, 0x1017}, {0x25, 0x03, 0xFFDF}, + {0x25, 0x05, 0x7F7C}, {0x25, 0x07, 0x8100}, {0x25, 0x08, 0x0001}, {0x25, 0x09, 0xFFD4}, + {0x25, 0x0A, 0x7C2F}, {0x25, 0x0E, 0x003F}, {0x25, 0x0F, 0x0121}, {0x25, 0x10, 0x0020}, + {0x25, 0x11, 0x8840}, + {0x28, 0x00, 0x0668}, {0x28, 0x02, 0xD020}, {0x28, 0x06, 0xC000}, {0x28, 0x0B, 0x1892}, + {0x28, 0x0F, 0xFFDF}, {0x28, 0x12, 0x01C4}, {0x28, 0x13, 0x027F}, {0x28, 0x14, 0x1311}, + {0x28, 0x16, 0x00C9}, {0x28, 0x17, 0xA100}, {0x28, 0x1A, 0x0001}, {0x28, 0x1C, 0x0400}, + {0x29, 0x00, 0x820F}, {0x29, 0x01, 0x0300}, {0x29, 0x02, 0x1017}, {0x29, 0x03, 0xFFDF}, + {0x29, 0x05, 0x7F7C}, {0x29, 0x07, 0x8100}, {0x29, 0x08, 0x0001}, {0x29, 0x0A, 0x7C2F}, + {0x29, 0x0E, 0x003F}, {0x29, 0x0F, 0x0121}, {0x29, 0x10, 0x0020}, {0x29, 0x11, 0x8840}, + {0x2B, 0x13, 0x3D87}, {0x2B, 0x14, 0x3108}, + {0x2D, 0x13, 0x3C87}, {0x2D, 0x14, 0x1808}, +}; + +static sds_config rtpcs_930x_sds_cfg_usxgmii_qx_even[] = +{ + {0x06, 0x00, 0x0000}, {0x06, 0x0D, 0x0F00}, {0x06, 0x0E, 0x055A}, {0x06, 0x1D, 0x0600}, + {0x07, 0x10, 0x6003}, {0x06, 0x13, 0x68C1}, {0x06, 0x14, 0xF021}, {0x07, 0x06, 0x1401}, + {0x21, 0x03, 0x8206}, {0x21, 0x05, 0x40B0}, {0x21, 0x06, 0x0010}, {0x21, 0x07, 0xF09F}, + {0x21, 0x0C, 0x0007}, {0x21, 0x0D, 0x6009}, {0x21, 0x0E, 0x0000}, {0x21, 0x0F, 0x0008}, + {0x2E, 0x00, 0xA668}, {0x2E, 0x01, 0x2088}, {0x2E, 0x02, 0xD020}, {0x2E, 0x06, 0xC000}, + {0x2E, 0x0B, 0x1892}, {0x2E, 0x0F, 0xFFDF}, {0x2E, 0x11, 0x8280}, {0x2E, 0x12, 0x0484}, + {0x2E, 0x13, 0x027F}, {0x2E, 0x14, 0x1311}, {0x2E, 0x17, 0xA100}, {0x2E, 0x1A, 0x0001}, + {0x2E, 0x1C, 0x0400}, {0x2F, 0x01, 0x0300}, {0x2F, 0x02, 0x1017}, {0x2F, 0x03, 0xFFDF}, + {0x2F, 0x05, 0x7F7C}, {0x2F, 0x07, 0x8104}, {0x2F, 0x08, 0x0001}, {0x2F, 0x09, 0xFFD4}, + {0x2F, 0x0A, 0x7C2F}, {0x2F, 0x0E, 0x003F}, {0x2F, 0x0F, 0x0121}, {0x2F, 0x10, 0x0020}, + {0x2F, 0x11, 0x8840}, + {0x2B, 0x13, 0x0050}, {0x2B, 0x18, 0x8E88}, {0x2B, 0x19, 0x4902}, {0x2B, 0x1D, 0x2501}, + {0x2D, 0x13, 0x0050}, {0x2D, 0x18, 0x8E88}, {0x2D, 0x19, 0x4902}, {0x2D, 0x1D, 0x2641}, + {0x2F, 0x13, 0x0050}, {0x2F, 0x18, 0x8E88}, {0x2F, 0x19, 0x4902}, {0x2F, 0x1D, 0x66E1}, + /* enable IEEE 802.3az EEE */ + {0x06, 0x03, 0xc45c}, +}; + +static sds_config rtpcs_930x_sds_cfg_usxgmii_qx_odd[] = +{ + {0x06, 0x00, 0x0000}, {0x06, 0x0D, 0x0F00}, {0x06, 0x0E, 0x055A}, {0x06, 0x1D, 0x0600}, + {0x07, 0x10, 0x6003}, {0x06, 0x13, 0x68C1}, {0x06, 0x14, 0xF021}, {0x07, 0x06, 0x1401}, + {0x21, 0x03, 0x8206}, {0x21, 0x05, 0x40B0}, {0x21, 0x06, 0x0010}, {0x21, 0x07, 0xF09F}, + {0x21, 0x0A, 0x0003}, {0x21, 0x0B, 0x0005}, {0x21, 0x0C, 0x0007}, {0x21, 0x0D, 0x6009}, + {0x21, 0x0E, 0x0000}, {0x21, 0x0F, 0x0008}, + {0x2E, 0x00, 0xA668}, {0x2E, 0x02, 0xD020}, {0x2E, 0x06, 0xC000}, {0x2E, 0x0B, 0x1892}, + {0x2E, 0x0F, 0xFFDF}, {0x2E, 0x11, 0x8280}, {0x2E, 0x12, 0x0484}, {0x2E, 0x13, 0x027F}, + {0x2E, 0x14, 0x1311}, {0x2E, 0x17, 0xA100}, {0x2E, 0x1A, 0x0001}, {0x2E, 0x1C, 0x0400}, + {0x2F, 0x00, 0x820F}, {0x2F, 0x01, 0x0300}, {0x2F, 0x02, 0x1017}, {0x2F, 0x03, 0xFFDF}, + {0x2F, 0x05, 0x7F7C}, {0x2F, 0x07, 0x8104}, {0x2F, 0x08, 0x0001}, {0x2F, 0x09, 0xFFD4}, + {0x2F, 0x0A, 0x7C2F}, {0x2F, 0x0E, 0x003F}, {0x2F, 0x0F, 0x0121}, {0x2F, 0x10, 0x0020}, + {0x2F, 0x11, 0x8840}, + {0x2B, 0x13, 0x3D87}, {0x2B, 0x14, 0x3108}, + {0x2D, 0x13, 0x3C87}, {0x2D, 0x14, 0x1808}, + /* enable IEEE 802.3az EEE */ + {0x06, 0x03, 0xc45c}, +}; + +static void rtpcs_930x_sds_usxgmii_config(struct rtpcs_ctrl *ctrl, int sds, + int nway_en, u32 opcode, u32 am_period, + u32 all_am_markers, u32 an_table, + u32 sync_bit) +{ + rtpcs_sds_write_bits(ctrl, sds, 0x7, 0x11, 0, 0, nway_en); + rtpcs_sds_write_bits(ctrl, sds, 0x7, 0x11, 1, 1, nway_en); + rtpcs_sds_write_bits(ctrl, sds, 0x7, 0x11, 2, 2, nway_en); + rtpcs_sds_write_bits(ctrl, sds, 0x7, 0x11, 3, 3, nway_en); + rtpcs_sds_write_bits(ctrl, sds, 0x6, 0x12, 15, 0, am_period); + rtpcs_sds_write_bits(ctrl, sds, 0x6, 0x13, 7, 0, all_am_markers); + rtpcs_sds_write_bits(ctrl, sds, 0x6, 0x13, 15, 8, all_am_markers); + rtpcs_sds_write_bits(ctrl, sds, 0x6, 0x14, 7, 0, all_am_markers); + rtpcs_sds_write_bits(ctrl, sds, 0x6, 0x14, 15, 8, all_am_markers); + rtpcs_sds_write_bits(ctrl, sds, 0x6, 0x15, 7, 0, all_am_markers); + rtpcs_sds_write_bits(ctrl, sds, 0x6, 0x15, 15, 8, all_am_markers); + rtpcs_sds_write_bits(ctrl, sds, 0x6, 0x16, 7, 0, all_am_markers); + rtpcs_sds_write_bits(ctrl, sds, 0x6, 0x16, 15, 8, all_am_markers); + rtpcs_sds_write_bits(ctrl, sds, 0x6, 0x17, 7, 0, all_am_markers); + rtpcs_sds_write_bits(ctrl, sds, 0x6, 0x17, 15, 8, all_am_markers); + rtpcs_sds_write_bits(ctrl, sds, 0x6, 0x18, 7, 0, all_am_markers); + rtpcs_sds_write_bits(ctrl, sds, 0x6, 0x18, 15, 8, all_am_markers); + rtpcs_sds_write_bits(ctrl, sds, 0x7, 0x10, 7, 0, opcode); + rtpcs_sds_write_bits(ctrl, sds, 0x6, 0xe, 10, 10, an_table); + rtpcs_sds_write_bits(ctrl, sds, 0x6, 0x1d, 11, 10, sync_bit); +} + +static void rtpcs_930x_sds_patch(struct rtpcs_ctrl *ctrl, int sds, phy_interface_t mode) +{ + const bool even_sds = ((sds & 1) == 0); + const sds_config *config; + size_t count; + + switch (mode) { + case PHY_INTERFACE_MODE_1000BASEX: + case PHY_INTERFACE_MODE_SGMII: + case PHY_INTERFACE_MODE_10GBASER: + if (even_sds) { + config = rtpcs_930x_sds_cfg_10gr_even; + count = ARRAY_SIZE(rtpcs_930x_sds_cfg_10gr_even); + } else { + config = rtpcs_930x_sds_cfg_10gr_odd; + count = ARRAY_SIZE(rtpcs_930x_sds_cfg_10gr_odd); + } + break; + + case PHY_INTERFACE_MODE_2500BASEX: + if (even_sds) { + config = rtpcs_930x_sds_cfg_10g_2500bx_even; + count = ARRAY_SIZE(rtpcs_930x_sds_cfg_10g_2500bx_even); + } else { + config = rtpcs_930x_sds_cfg_10g_2500bx_odd; + count = ARRAY_SIZE(rtpcs_930x_sds_cfg_10g_2500bx_odd); + } + break; + + case PHY_INTERFACE_MODE_10G_QXGMII: + if (even_sds) { + config = rtpcs_930x_sds_cfg_usxgmii_qx_even; + count = ARRAY_SIZE(rtpcs_930x_sds_cfg_usxgmii_qx_even); + } else { + config = rtpcs_930x_sds_cfg_usxgmii_qx_odd; + count = ARRAY_SIZE(rtpcs_930x_sds_cfg_usxgmii_qx_odd); + } + break; + + default: + pr_warn("%s: unsupported mode %s on serdes %d\n", __func__, phy_modes(mode), sds); + return; + } + + for (size_t i = 0; i < count; ++i) { + rtpcs_sds_write(ctrl, sds, config[i].page, config[i].reg, config[i].data); + } + + if (mode == PHY_INTERFACE_MODE_10G_QXGMII) { + /* Default configuration */ + rtpcs_930x_sds_usxgmii_config(ctrl, sds, 1, 0xaa, 0x5078, 0, 1, 0x1); + } +} + +__attribute__((unused)) +static int rtpcs_930x_sds_cmu_band_get(struct rtpcs_ctrl *ctrl, int sds) +{ + u32 page; + u32 en; + u32 cmu_band; + +/* page = rtl9300_sds_cmu_page_get(sds); */ + page = 0x25; /* 10GR and 1000BX */ + sds = (sds % 2) ? (sds - 1) : (sds); + + rtpcs_sds_write_bits(ctrl, sds, page, 0x1c, 15, 15, 1); + rtpcs_sds_write_bits(ctrl, sds + 1, page, 0x1c, 15, 15, 1); + + en = rtpcs_sds_read_bits(ctrl, sds, page, 27, 1, 1); + if(!en) { /* Auto mode */ + rtpcs_sds_write(ctrl, sds, 0x1f, 0x02, 31); + + cmu_band = rtpcs_sds_read_bits(ctrl, sds, 0x1f, 0x15, 5, 1); + } else { + cmu_band = rtpcs_sds_read_bits(ctrl, sds, page, 30, 4, 0); + } + + return cmu_band; +} + +#define RTL930X_MAC_FORCE_MODE_CTRL (0xCA1C) +__attribute__((unused)) +static int rtpcs_930x_setup_serdes(struct rtpcs_ctrl *ctrl, int port, int sds_num, + phy_interface_t phy_mode) +{ + int calib_tries = 0; + + /* Turn Off Serdes */ + rtpcs_930x_sds_set(sds_num, RTL930X_SDS_OFF); + + /* Apply serdes patches */ + rtpcs_930x_sds_patch(ctrl, sds_num, phy_mode); + + /* Maybe use dal_longan_sds_init */ + + /* dal_longan_construct_serdesConfig_init */ /* Serdes Construct */ + rtpcs_930x_phy_enable_10g_1g(ctrl, sds_num); + + /* Disable MAC */ + sw_w32_mask(0, 1, RTL930X_MAC_FORCE_MODE_CTRL + 4 * port); + mdelay(20); + + /* ----> dal_longan_sds_mode_set */ + pr_info("%s: Configuring RTL9300 SERDES %d\n", __func__, sds_num); + + /* Configure link to MAC */ + rtpcs_930x_sds_mac_link_config(ctrl, sds_num, true, true); /* MAC Construct */ + + /* Re-Enable MAC */ + sw_w32_mask(1, 0, RTL930X_MAC_FORCE_MODE_CTRL + 4 * port); + + /* Enable SDS in desired mode */ + rtpcs_930x_sds_mode_set(ctrl, sds_num, phy_mode); + + /* Enable Fiber RX */ + rtpcs_sds_write_bits(ctrl, sds_num, 0x20, 2, 12, 12, 0); + + /* Calibrate SerDes receiver in loopback mode */ + rtpcs_930x_sds_10g_idle(ctrl, sds_num); + do { + rtpcs_930x_sds_do_rx_calibration(ctrl, sds_num, phy_mode); + calib_tries++; + mdelay(50); + } while (rtpcs_930x_sds_check_calibration(ctrl, sds_num, phy_mode) && calib_tries < 3); + if (calib_tries >= 3) + pr_warn("%s: SerDes RX calibration failed\n", __func__); + + /* Leave loopback mode */ + rtpcs_930x_sds_tx_config(ctrl, sds_num, phy_mode); + + return 0; +} + /* RTL931X */ static void rtpcs_931x_sds_reset(struct rtpcs_ctrl *ctrl, u32 sds)