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 9767bd8e14..70e8f37eb3 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 @@ -1119,6 +1119,135 @@ static int rtpcs_93xx_init_serdes_common(struct rtpcs_ctrl *ctrl) return 0; } +static int rtpcs_93xx_sds_get_pll_config(struct rtpcs_serdes *sds, enum rtpcs_sds_pll_type pll, + enum rtpcs_sds_pll_speed *speed) +{ + struct rtpcs_serdes *even_sds = rtpcs_sds_get_even(sds); + int sbit, speed_val; + + /* + * PLL config 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. + */ + + sbit = pll == RTPCS_SDS_PLL_TYPE_LC ? 8 : 12; + speed_val = rtpcs_sds_read_bits(even_sds, 0x20, 0x12, sbit + 3, sbit); + if (speed_val < 0) + return speed_val; + + /* bit 0 is force-bit, bits [3:1] are speed selector */ + *speed = (enum rtpcs_sds_pll_speed)(speed_val >> 1); + return 0; +} + +static int rtpcs_93xx_sds_set_pll_config(struct rtpcs_serdes *sds, enum rtpcs_sds_pll_type pll, + enum rtpcs_sds_pll_speed speed) +{ + struct rtpcs_serdes *even_sds = rtpcs_sds_get_even(sds); + int sbit = pll == RTPCS_SDS_PLL_TYPE_LC ? 8 : 12; + int ret; + + if (speed >= RTPCS_SDS_PLL_SPD_END) + return -EINVAL; + + if (pll >= RTPCS_SDS_PLL_TYPE_END) + return -EINVAL; + + if ((pll == RTPCS_SDS_PLL_TYPE_RING) && (speed == RTPCS_SDS_PLL_SPD_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. + */ + + ret = rtpcs_sds_write_bits(even_sds, 0x20, 0x12, 3, 0, 0xf); + if (ret < 0) + return ret; + + /* bit 0 is force-bit, bits [3:1] are speed selector */ + ret = rtpcs_sds_write_bits(even_sds, 0x20, 0x12, sbit + 3, sbit, (speed << 1) | BIT(0)); + if (ret < 0) + return ret; + + if (sds->ops->reset_cmu) + ret = sds->ops->reset_cmu(sds, pll); + + return ret; +} + +static int rtpcs_93xx_sds_config_cmu(struct rtpcs_serdes *sds, enum rtpcs_sds_mode hw_mode) +{ + struct rtpcs_serdes *nb_sds = rtpcs_sds_get_neighbor(sds); + enum rtpcs_sds_pll_speed speed, neighbor_speed; + enum rtpcs_sds_pll_type pll, neighbor_pll; + bool speed_changed = true; + int ret; + + /* + * 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 + * + * However, when we want to configure 10G speed while the other SerDes is already using + * the LC PLL for a slower speed, there is no way to avoid reconfiguration. Note that + * this can even happen when the other SerDes is not actually in use, because changing + * the state of a SerDes back to RTL930X_SDS_OFF is not (yet) implemented. + */ + + ret = nb_sds->ops->get_pll_select(nb_sds, &neighbor_pll); + if (ret < 0) + return ret; + + ret = rtpcs_93xx_sds_get_pll_config(nb_sds, neighbor_pll, &neighbor_speed); + if (ret < 0) + return ret; + + ret = rtpcs_sds_select_pll_speed(hw_mode, &speed); + if (ret < 0) + return ret; + + if (nb_sds->hw_mode == RTPCS_SDS_MODE_OFF) + pll = speed == RTPCS_SDS_PLL_SPD_10000 ? RTPCS_SDS_PLL_TYPE_LC + : RTPCS_SDS_PLL_TYPE_RING; + else if (speed == neighbor_speed) { + speed_changed = false; + pll = neighbor_pll; + } else if (neighbor_pll == RTPCS_SDS_PLL_TYPE_RING) + pll = RTPCS_SDS_PLL_TYPE_LC; + else if (speed == RTPCS_SDS_PLL_SPD_10000) { + pr_info("%s: SDS %d needs LC PLL, reconfigure SDS %d to use ring PLL\n", + __func__, sds->id, nb_sds->id); + ret = nb_sds->ops->reconfigure_to_pll(nb_sds, RTPCS_SDS_PLL_TYPE_RING); + if (ret < 0) + return ret; + + pll = RTPCS_SDS_PLL_TYPE_LC; + } else + pll = RTPCS_SDS_PLL_TYPE_RING; + + if (speed_changed) + ret = rtpcs_93xx_sds_set_pll_config(sds, pll, speed); + + ret = sds->ops->set_pll_select(sds, hw_mode, pll); + if (ret < 0) + return ret; + + pr_info("%s: SDS %d using %s PLL for mode %d\n", __func__, sds->id, + pll == RTPCS_SDS_PLL_TYPE_LC ? "LC" : "ring", hw_mode); + + return ret; +} + /* RTL930X */ /* @@ -1290,27 +1419,6 @@ static int rtpcs_930x_sds_get_pll_select(struct rtpcs_serdes *sds, enum rtpcs_sd return 0; } -static int rtpcs_930x_sds_get_pll_config(struct rtpcs_serdes *sds, enum rtpcs_sds_pll_type pll, - enum rtpcs_sds_pll_speed *speed) -{ - struct rtpcs_serdes *even_sds = rtpcs_sds_get_even(sds); - int sbit, speed_val; - - /* - * 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. - */ - - sbit = pll == RTPCS_SDS_PLL_TYPE_LC ? 8 : 12; - speed_val = rtpcs_sds_read_bits(even_sds, 0x20, 0x12, sbit + 3, sbit); - if (speed_val < 0) - return speed_val; - - /* bit 0 is force-bit, bits [3:1] are speed selector */ - *speed = (enum rtpcs_sds_pll_speed)(speed_val >> 1); - return 0; -} - static int rtpcs_930x_sds_set_pll_select(struct rtpcs_serdes *sds, enum rtpcs_sds_mode hw_mode, enum rtpcs_sds_pll_type pll) { @@ -1323,11 +1431,10 @@ static int rtpcs_930x_sds_set_pll_select(struct rtpcs_serdes *sds, enum rtpcs_sd return rtpcs_sds_write_bits(even_sds, 0x20, 0x12, pbit + 1, pbit, (pll << 1) | BIT(0)); } -static int rtpcs_930x_sds_reset_cmu(struct rtpcs_serdes *sds) +static int rtpcs_930x_sds_reset_cmu(struct rtpcs_serdes *sds, enum rtpcs_sds_pll_type pll) { struct rtpcs_serdes *even_sds = rtpcs_sds_get_even(sds); int reset_sequence[4] = { 3, 2, 3, 1 }; - enum rtpcs_sds_pll_type pll; int bit, i, ret; /* @@ -1335,9 +1442,6 @@ static int rtpcs_930x_sds_reset_cmu(struct rtpcs_serdes *sds) * of the Otto platform have different reset sequences. Luckily it always boils down * to flipping two bits in a special sequence. */ - ret = rtpcs_930x_sds_get_pll_select(sds, &pll); - if (ret < 0) - return ret; bit = pll == RTPCS_SDS_PLL_TYPE_LC ? 2 : 0; @@ -1350,40 +1454,6 @@ static int rtpcs_930x_sds_reset_cmu(struct rtpcs_serdes *sds) return 0; } -static int rtpcs_930x_sds_set_pll_config(struct rtpcs_serdes *sds, enum rtpcs_sds_pll_type pll, - enum rtpcs_sds_pll_speed speed) -{ - struct rtpcs_serdes *even_sds = rtpcs_sds_get_even(sds); - int sbit = pll == RTPCS_SDS_PLL_TYPE_LC ? 8 : 12; - int ret; - - if (speed >= RTPCS_SDS_PLL_SPD_END) - return -EINVAL; - - if (pll >= RTPCS_SDS_PLL_TYPE_END) - return -EINVAL; - - if ((pll == RTPCS_SDS_PLL_TYPE_RING) && (speed == RTPCS_SDS_PLL_SPD_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. - */ - - ret = rtpcs_sds_write_bits(even_sds, 0x20, 0x12, 3, 0, 0xf); - if (ret < 0) - return ret; - - /* bit 0 is force-bit, bits [3:1] are speed selector */ - ret = rtpcs_sds_write_bits(even_sds, 0x20, 0x12, sbit + 3, sbit, (speed << 1) | BIT(0)); - if (ret < 0) - return ret; - - return rtpcs_930x_sds_reset_cmu(sds); -} - static int rtpcs_930x_sds_wait_clock_ready(struct rtpcs_serdes *sds) { struct rtpcs_serdes *even_sds = rtpcs_sds_get_even(sds); @@ -1441,14 +1511,14 @@ static int rtpcs_930x_sds_reconfigure_to_pll(struct rtpcs_serdes *sds, enum rtpc if (ret < 0) return ret; - ret = rtpcs_930x_sds_get_pll_config(sds, old_pll, &speed); + ret = rtpcs_93xx_sds_get_pll_config(sds, old_pll, &speed); if (ret < 0) return ret; rtpcs_930x_sds_set_power(sds, false); __rtpcs_930x_sds_set_ip_mode(sds, RTPCS_930X_SDS_OFF); - ret = rtpcs_930x_sds_set_pll_config(sds, pll, speed); + ret = rtpcs_93xx_sds_set_pll_config(sds, pll, speed); if (ret < 0) return ret; @@ -1464,80 +1534,6 @@ static int rtpcs_930x_sds_reconfigure_to_pll(struct rtpcs_serdes *sds, enum rtpc return 0; } -static int rtpcs_930x_sds_config_cmu(struct rtpcs_serdes *sds, - enum rtpcs_sds_mode hw_mode) -{ - struct rtpcs_serdes *nb_sds = rtpcs_sds_get_neighbor(sds); - enum rtpcs_sds_pll_speed speed, neighbor_speed; - enum rtpcs_sds_pll_type pll, neighbor_pll; - bool speed_changed = true; - int neighbor_mode, ret; - - /* - * 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 - * - * However, when we want to configure 10G speed while the other SerDes is already using - * the LC PLL for a slower speed, there is no way to avoid reconfiguration. Note that - * this can even happen when the other SerDes is not actually in use, because changing - * the state of a SerDes back to RTL930X_SDS_OFF is not (yet) implemented. - */ - - neighbor_mode = __rtpcs_930x_sds_get_ip_mode(nb_sds); - - ret = rtpcs_930x_sds_get_pll_select(sds, &neighbor_pll); - if (ret < 0) - return ret; - - ret = rtpcs_930x_sds_get_pll_config(nb_sds, neighbor_pll, &neighbor_speed); - if (ret < 0) - return ret; - - ret = rtpcs_sds_select_pll_speed(hw_mode, &speed); - if (ret < 0) - return ret; - - if (!neighbor_mode) - pll = speed == RTPCS_SDS_PLL_SPD_10000 ? RTPCS_SDS_PLL_TYPE_LC - : RTPCS_SDS_PLL_TYPE_RING; - else if (speed == neighbor_speed) { - speed_changed = false; - pll = neighbor_pll; - } else if (neighbor_pll == RTPCS_SDS_PLL_TYPE_RING) - pll = RTPCS_SDS_PLL_TYPE_LC; - else if (speed == RTPCS_SDS_PLL_SPD_10000) { - pr_info("%s: SDS %d needs LC PLL, reconfigure SDS %d to use ring PLL\n", - __func__, sds->id, nb_sds->id); - ret = rtpcs_930x_sds_reconfigure_to_pll(nb_sds, RTPCS_SDS_PLL_TYPE_RING); - if (ret < 0) - return ret; - - pll = RTPCS_SDS_PLL_TYPE_LC; - } else - pll = RTPCS_SDS_PLL_TYPE_RING; - - if (speed_changed) - ret = rtpcs_930x_sds_set_pll_config(sds, pll, speed); - - ret = rtpcs_930x_sds_set_pll_select(sds, hw_mode, pll); - if (ret < 0) - return ret; - - pr_info("%s: SDS %d using %s PLL for mode %d\n", __func__, sds->id, - pll == RTPCS_SDS_PLL_TYPE_LC ? "LC" : "ring", hw_mode); - - return ret; -} - static void rtpcs_930x_sds_reset_state_machine(struct rtpcs_serdes *sds) { rtpcs_sds_write_bits(sds, 0x06, 0x02, 12, 12, 0x01); /* SM_RESET bit */ @@ -1627,7 +1623,7 @@ static int rtpcs_930x_sds_set_ip_mode(struct rtpcs_serdes *sds, if (hw_mode == RTPCS_SDS_MODE_OFF) return 0; - ret = rtpcs_930x_sds_config_cmu(sds, hw_mode); + ret = rtpcs_93xx_sds_config_cmu(sds, hw_mode); if (ret < 0) pr_err("%s: SDS %d could not configure PLL for mode %d: %d\n", __func__, sds->id, hw_mode, ret); @@ -4300,6 +4296,10 @@ static const struct rtpcs_serdes_ops rtpcs_930x_sds_ops = { .xsg_write = rtpcs_930x_sds_op_xsg_write, .set_autoneg = rtpcs_93xx_sds_set_autoneg, .restart_autoneg = rtpcs_generic_sds_restart_autoneg, + .get_pll_select = rtpcs_930x_sds_get_pll_select, + .set_pll_select = rtpcs_930x_sds_set_pll_select, + .reset_cmu = rtpcs_930x_sds_reset_cmu, + .reconfigure_to_pll = rtpcs_930x_sds_reconfigure_to_pll, }; static const struct rtpcs_sds_regs rtpcs_930x_sds_regs = {