mirror of
https://git.openwrt.org/openwrt/openwrt.git
synced 2026-03-14 23:09:45 +01:00
realtek: pcs: make rtl930x CMU config generic
Generalize the RTL930x CMU configuration to support RTL931x as well. Both implementations differ only in minor details, allowing them to share common code and avoid duplication. Affected functions are moved up in the code to the 93xx common area and slightly renamed. Existing variant-specific functions are adjusted too and assigned to the previously added SerDes operation hooks. Signed-off-by: Jonas Jelonek <jelonek.jonas@gmail.com> Link: https://github.com/openwrt/openwrt/pull/22198 Signed-off-by: Hauke Mehrtens <hauke@hauke-m.de>
This commit is contained in:
parent
8e29b78726
commit
eccb5c6fa1
1 changed files with 137 additions and 137 deletions
|
|
@ -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 = {
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue