mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-12-27 04:54:41 +08:00
Merge branch 'net-mtk_eth_soc-improve-pcs-implementation'
Russell King says: ==================== net: mtk_eth_soc: improve PCS implementation As a result of invesigations from Frank Wunderlich, we know a lot more about the Mediatek "SGMII" PCS block, and can implement the PCS support correctly. This series achieves that, and Frank has tested the final result and reports that it works for him. The series could do with further testing by others, but I suspect that is unlikely to happen until it is merged based on past performances with this driver. Briefly, the patches in order: 1. Add a new helper to get the link timer duration in nanoseconds 2. Add definitions for the newly discovered registers and updates to bit definitions, including bitmasks for the BMCR, BMSR and two advertisement registers. 3. Remove unnecessary/unused error handling (functions always returning zero.) 4. Adding the missing pcs_get_state() implementation. 5. Converting the code to use regmap_update_bits() rather than open-coding read-modify-write sequences. 6. Adding out-of-band speed and duplex forcing for all non-inband modes not just the 802.3z link modes the code currently does. 7. Moving the release of the PHY power down to the main pcs_config() function. 8. Moving the interface speed selection to the main pcs_config() function. 9. Adding advertisement programming. 10. Adding correct link timer programming using the new helper in the first patch. 11. Adding support for 802.3z negotiation. There is one remaining issue - when configuring the PCS for in-band, for some reason the AN restart bit is always set. This should not be necessary, but requires further investigation with the hardware to find out whether it is really necessary. I suspect this was a work around for a previous poor implementation. ==================== Link: https://lore.kernel.org/r/Y1qDMw+DJLAJHT40@shell.armlinux.org.uk Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
commit
e385592066
@ -466,8 +466,10 @@
|
||||
#define ETHSYS_DMA_AG_MAP_PPE BIT(2)
|
||||
|
||||
/* SGMII subsystem config registers */
|
||||
/* Register to auto-negotiation restart */
|
||||
/* BMCR (low 16) BMSR (high 16) */
|
||||
#define SGMSYS_PCS_CONTROL_1 0x0
|
||||
#define SGMII_BMCR GENMASK(15, 0)
|
||||
#define SGMII_BMSR GENMASK(31, 16)
|
||||
#define SGMII_AN_RESTART BIT(9)
|
||||
#define SGMII_ISOLATE BIT(10)
|
||||
#define SGMII_AN_ENABLE BIT(12)
|
||||
@ -477,13 +479,18 @@
|
||||
#define SGMII_PCS_FAULT BIT(23)
|
||||
#define SGMII_AN_EXPANSION_CLR BIT(30)
|
||||
|
||||
#define SGMSYS_PCS_ADVERTISE 0x8
|
||||
#define SGMII_ADVERTISE GENMASK(15, 0)
|
||||
#define SGMII_LPA GENMASK(31, 16)
|
||||
|
||||
/* Register to programmable link timer, the unit in 2 * 8ns */
|
||||
#define SGMSYS_PCS_LINK_TIMER 0x18
|
||||
#define SGMII_LINK_TIMER_DEFAULT (0x186a0 & GENMASK(19, 0))
|
||||
#define SGMII_LINK_TIMER_MASK GENMASK(19, 0)
|
||||
#define SGMII_LINK_TIMER_DEFAULT (0x186a0 & SGMII_LINK_TIMER_MASK)
|
||||
|
||||
/* Register to control remote fault */
|
||||
#define SGMSYS_SGMII_MODE 0x20
|
||||
#define SGMII_IF_MODE_BIT0 BIT(0)
|
||||
#define SGMII_IF_MODE_SGMII BIT(0)
|
||||
#define SGMII_SPEED_DUPLEX_AN BIT(1)
|
||||
#define SGMII_SPEED_MASK GENMASK(3, 2)
|
||||
#define SGMII_SPEED_10 FIELD_PREP(SGMII_SPEED_MASK, 0)
|
||||
|
@ -19,62 +19,18 @@ static struct mtk_pcs *pcs_to_mtk_pcs(struct phylink_pcs *pcs)
|
||||
return container_of(pcs, struct mtk_pcs, pcs);
|
||||
}
|
||||
|
||||
/* For SGMII interface mode */
|
||||
static int mtk_pcs_setup_mode_an(struct mtk_pcs *mpcs)
|
||||
static void mtk_pcs_get_state(struct phylink_pcs *pcs,
|
||||
struct phylink_link_state *state)
|
||||
{
|
||||
unsigned int val;
|
||||
struct mtk_pcs *mpcs = pcs_to_mtk_pcs(pcs);
|
||||
unsigned int bm, adv;
|
||||
|
||||
/* Setup the link timer and QPHY power up inside SGMIISYS */
|
||||
regmap_write(mpcs->regmap, SGMSYS_PCS_LINK_TIMER,
|
||||
SGMII_LINK_TIMER_DEFAULT);
|
||||
/* Read the BMSR and LPA */
|
||||
regmap_read(mpcs->regmap, SGMSYS_PCS_CONTROL_1, &bm);
|
||||
regmap_read(mpcs->regmap, SGMSYS_PCS_ADVERTISE, &adv);
|
||||
|
||||
regmap_read(mpcs->regmap, SGMSYS_SGMII_MODE, &val);
|
||||
val |= SGMII_REMOTE_FAULT_DIS;
|
||||
regmap_write(mpcs->regmap, SGMSYS_SGMII_MODE, val);
|
||||
|
||||
regmap_read(mpcs->regmap, SGMSYS_PCS_CONTROL_1, &val);
|
||||
val |= SGMII_AN_RESTART;
|
||||
regmap_write(mpcs->regmap, SGMSYS_PCS_CONTROL_1, val);
|
||||
|
||||
regmap_read(mpcs->regmap, SGMSYS_QPHY_PWR_STATE_CTRL, &val);
|
||||
val &= ~SGMII_PHYA_PWD;
|
||||
regmap_write(mpcs->regmap, SGMSYS_QPHY_PWR_STATE_CTRL, val);
|
||||
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
/* For 1000BASE-X and 2500BASE-X interface modes, which operate at a
|
||||
* fixed speed.
|
||||
*/
|
||||
static int mtk_pcs_setup_mode_force(struct mtk_pcs *mpcs,
|
||||
phy_interface_t interface)
|
||||
{
|
||||
unsigned int val;
|
||||
|
||||
regmap_read(mpcs->regmap, mpcs->ana_rgc3, &val);
|
||||
val &= ~RG_PHY_SPEED_MASK;
|
||||
if (interface == PHY_INTERFACE_MODE_2500BASEX)
|
||||
val |= RG_PHY_SPEED_3_125G;
|
||||
regmap_write(mpcs->regmap, mpcs->ana_rgc3, val);
|
||||
|
||||
/* Disable SGMII AN */
|
||||
regmap_read(mpcs->regmap, SGMSYS_PCS_CONTROL_1, &val);
|
||||
val &= ~SGMII_AN_ENABLE;
|
||||
regmap_write(mpcs->regmap, SGMSYS_PCS_CONTROL_1, val);
|
||||
|
||||
/* Set the speed etc but leave the duplex unchanged */
|
||||
regmap_read(mpcs->regmap, SGMSYS_SGMII_MODE, &val);
|
||||
val &= SGMII_DUPLEX_FULL | ~SGMII_IF_MODE_MASK;
|
||||
val |= SGMII_SPEED_1000;
|
||||
regmap_write(mpcs->regmap, SGMSYS_SGMII_MODE, val);
|
||||
|
||||
/* Release PHYA power down state */
|
||||
regmap_read(mpcs->regmap, SGMSYS_QPHY_PWR_STATE_CTRL, &val);
|
||||
val &= ~SGMII_PHYA_PWD;
|
||||
regmap_write(mpcs->regmap, SGMSYS_QPHY_PWR_STATE_CTRL, val);
|
||||
|
||||
return 0;
|
||||
phylink_mii_c22_pcs_decode_state(state, FIELD_GET(SGMII_BMSR, bm),
|
||||
FIELD_GET(SGMII_LPA, adv));
|
||||
}
|
||||
|
||||
static int mtk_pcs_config(struct phylink_pcs *pcs, unsigned int mode,
|
||||
@ -83,46 +39,116 @@ static int mtk_pcs_config(struct phylink_pcs *pcs, unsigned int mode,
|
||||
bool permit_pause_to_mac)
|
||||
{
|
||||
struct mtk_pcs *mpcs = pcs_to_mtk_pcs(pcs);
|
||||
int err = 0;
|
||||
unsigned int rgc3, sgm_mode, bmcr;
|
||||
int advertise, link_timer;
|
||||
bool changed, use_an;
|
||||
|
||||
/* Setup SGMIISYS with the determined property */
|
||||
if (interface != PHY_INTERFACE_MODE_SGMII)
|
||||
err = mtk_pcs_setup_mode_force(mpcs, interface);
|
||||
else if (phylink_autoneg_inband(mode))
|
||||
err = mtk_pcs_setup_mode_an(mpcs);
|
||||
if (interface == PHY_INTERFACE_MODE_2500BASEX)
|
||||
rgc3 = RG_PHY_SPEED_3_125G;
|
||||
else
|
||||
rgc3 = 0;
|
||||
|
||||
return err;
|
||||
advertise = phylink_mii_c22_pcs_encode_advertisement(interface,
|
||||
advertising);
|
||||
if (advertise < 0)
|
||||
return advertise;
|
||||
|
||||
link_timer = phylink_get_link_timer_ns(interface);
|
||||
if (link_timer < 0)
|
||||
return link_timer;
|
||||
|
||||
/* Clearing IF_MODE_BIT0 switches the PCS to BASE-X mode, and
|
||||
* we assume that fixes it's speed at bitrate = line rate (in
|
||||
* other words, 1000Mbps or 2500Mbps).
|
||||
*/
|
||||
if (interface == PHY_INTERFACE_MODE_SGMII) {
|
||||
sgm_mode = SGMII_IF_MODE_SGMII;
|
||||
if (phylink_autoneg_inband(mode)) {
|
||||
sgm_mode |= SGMII_REMOTE_FAULT_DIS |
|
||||
SGMII_SPEED_DUPLEX_AN;
|
||||
use_an = true;
|
||||
} else {
|
||||
use_an = false;
|
||||
}
|
||||
} else if (phylink_autoneg_inband(mode)) {
|
||||
/* 1000base-X or 2500base-X autoneg */
|
||||
sgm_mode = SGMII_REMOTE_FAULT_DIS;
|
||||
use_an = linkmode_test_bit(ETHTOOL_LINK_MODE_Autoneg_BIT,
|
||||
advertising);
|
||||
} else {
|
||||
/* 1000base-X or 2500base-X without autoneg */
|
||||
sgm_mode = 0;
|
||||
use_an = false;
|
||||
}
|
||||
|
||||
if (use_an) {
|
||||
/* FIXME: Do we need to set AN_RESTART here? */
|
||||
bmcr = SGMII_AN_RESTART | SGMII_AN_ENABLE;
|
||||
} else {
|
||||
bmcr = 0;
|
||||
}
|
||||
|
||||
/* Configure the underlying interface speed */
|
||||
regmap_update_bits(mpcs->regmap, mpcs->ana_rgc3,
|
||||
RG_PHY_SPEED_3_125G, rgc3);
|
||||
|
||||
/* Update the advertisement, noting whether it has changed */
|
||||
regmap_update_bits_check(mpcs->regmap, SGMSYS_PCS_ADVERTISE,
|
||||
SGMII_ADVERTISE, advertise, &changed);
|
||||
|
||||
/* Setup the link timer and QPHY power up inside SGMIISYS */
|
||||
regmap_write(mpcs->regmap, SGMSYS_PCS_LINK_TIMER, link_timer / 2 / 8);
|
||||
|
||||
/* Update the sgmsys mode register */
|
||||
regmap_update_bits(mpcs->regmap, SGMSYS_SGMII_MODE,
|
||||
SGMII_REMOTE_FAULT_DIS | SGMII_SPEED_DUPLEX_AN |
|
||||
SGMII_IF_MODE_SGMII, sgm_mode);
|
||||
|
||||
/* Update the BMCR */
|
||||
regmap_update_bits(mpcs->regmap, SGMSYS_PCS_CONTROL_1,
|
||||
SGMII_AN_RESTART | SGMII_AN_ENABLE, bmcr);
|
||||
|
||||
/* Release PHYA power down state */
|
||||
regmap_update_bits(mpcs->regmap, SGMSYS_QPHY_PWR_STATE_CTRL,
|
||||
SGMII_PHYA_PWD, 0);
|
||||
|
||||
return changed;
|
||||
}
|
||||
|
||||
static void mtk_pcs_restart_an(struct phylink_pcs *pcs)
|
||||
{
|
||||
struct mtk_pcs *mpcs = pcs_to_mtk_pcs(pcs);
|
||||
unsigned int val;
|
||||
|
||||
regmap_read(mpcs->regmap, SGMSYS_PCS_CONTROL_1, &val);
|
||||
val |= SGMII_AN_RESTART;
|
||||
regmap_write(mpcs->regmap, SGMSYS_PCS_CONTROL_1, val);
|
||||
regmap_update_bits(mpcs->regmap, SGMSYS_PCS_CONTROL_1,
|
||||
SGMII_AN_RESTART, SGMII_AN_RESTART);
|
||||
}
|
||||
|
||||
static void mtk_pcs_link_up(struct phylink_pcs *pcs, unsigned int mode,
|
||||
phy_interface_t interface, int speed, int duplex)
|
||||
{
|
||||
struct mtk_pcs *mpcs = pcs_to_mtk_pcs(pcs);
|
||||
unsigned int val;
|
||||
unsigned int sgm_mode;
|
||||
|
||||
if (!phy_interface_mode_is_8023z(interface))
|
||||
return;
|
||||
if (!phylink_autoneg_inband(mode)) {
|
||||
/* Force the speed and duplex setting */
|
||||
if (speed == SPEED_10)
|
||||
sgm_mode = SGMII_SPEED_10;
|
||||
else if (speed == SPEED_100)
|
||||
sgm_mode = SGMII_SPEED_100;
|
||||
else
|
||||
sgm_mode = SGMII_SPEED_1000;
|
||||
|
||||
/* SGMII force duplex setting */
|
||||
regmap_read(mpcs->regmap, SGMSYS_SGMII_MODE, &val);
|
||||
val &= ~SGMII_DUPLEX_FULL;
|
||||
if (duplex == DUPLEX_FULL)
|
||||
val |= SGMII_DUPLEX_FULL;
|
||||
if (duplex == DUPLEX_FULL)
|
||||
sgm_mode |= SGMII_DUPLEX_FULL;
|
||||
|
||||
regmap_write(mpcs->regmap, SGMSYS_SGMII_MODE, val);
|
||||
regmap_update_bits(mpcs->regmap, SGMSYS_SGMII_MODE,
|
||||
SGMII_DUPLEX_FULL | SGMII_SPEED_MASK,
|
||||
sgm_mode);
|
||||
}
|
||||
}
|
||||
|
||||
static const struct phylink_pcs_ops mtk_pcs_ops = {
|
||||
.pcs_get_state = mtk_pcs_get_state,
|
||||
.pcs_config = mtk_pcs_config,
|
||||
.pcs_an_restart = mtk_pcs_restart_an,
|
||||
.pcs_link_up = mtk_pcs_link_up,
|
||||
|
@ -616,6 +616,30 @@ int phylink_speed_up(struct phylink *pl);
|
||||
|
||||
void phylink_set_port_modes(unsigned long *bits);
|
||||
|
||||
/**
|
||||
* phylink_get_link_timer_ns - return the PCS link timer value
|
||||
* @interface: link &typedef phy_interface_t mode
|
||||
*
|
||||
* Return the PCS link timer setting in nanoseconds for the PHY @interface
|
||||
* mode, or -EINVAL if not appropriate.
|
||||
*/
|
||||
static inline int phylink_get_link_timer_ns(phy_interface_t interface)
|
||||
{
|
||||
switch (interface) {
|
||||
case PHY_INTERFACE_MODE_SGMII:
|
||||
case PHY_INTERFACE_MODE_QSGMII:
|
||||
case PHY_INTERFACE_MODE_USXGMII:
|
||||
return 1600000;
|
||||
|
||||
case PHY_INTERFACE_MODE_1000BASEX:
|
||||
case PHY_INTERFACE_MODE_2500BASEX:
|
||||
return 10000000;
|
||||
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
void phylink_mii_c22_pcs_decode_state(struct phylink_link_state *state,
|
||||
u16 bmsr, u16 lpa);
|
||||
void phylink_mii_c22_pcs_get_state(struct mdio_device *pcs,
|
||||
|
Loading…
Reference in New Issue
Block a user