Commit cb89580e authored by Jakub Kicinski's avatar Jakub Kicinski

Merge branch 'mtk_eth_soc-phylink-updates'

Russell King says:

====================
mtk_eth_soc phylink updates

This series ultimately updates mtk_eth_soc to use phylink_pcs, with some
fixes along the way.

Previous attempts to update this driver (which is now marked as legacy)
have failed due to lack of testing. I am hoping that this time will be
different; Marek can test RGMII modes, but not SGMII. So all that we
know is that this patch series probably doesn't break RGMII.

1) remove unused mac_mode and sgmii flags members from structures.
2) remove unnecessary interpretation of speed when configuring 1000
   and 2500 Base-X
3) move configuration of SGMII duplex setting from mac_config() to
   link_up()
4) only pass in interface mode to mtk_sgmii_setup_mode_force()
5) move decision about which mtk_sgmii_setup_mode_*() function to call
   into mtk_sgmii.c
6) add a fixme comment for RGMII explaning why the call to
   mtk_gmac0_rgmii_adjust() is completely wrong - this needs to be
   addressed by someone who has the hardware and can test an appropriate
   fix. This fixme means that the driver still can't become non-legacy.
7) move gmac setup from mac_config() to mac_finish() - this preserves
   the order that we write to the hardware when we eventually convert to
   phylink_pcs()
8) move configuration of syscfg0 in SGMII/802.3z mode to mac_finish()
   for the same reasons as (7).
9) convert mtk_sgmii.c code structure and the mtk_sgmii structure to
   suit conversion to phylink_pcs
10) finally convert to phylink_pcs

As there has been no feedback from mtk_eth_soc maintainers to my RFC
on April 6th, not my reminder on April 11th, so it's now time to merge
this anyway. Mediatek code seems to be submitted to the kernel and
then the maintainers scarper...
====================

Link: https://lore.kernel.org/r/YoUIX+BN/ZbyXzTT@shell.armlinux.org.ukTested-by: default avatarMarek Behún <kabel@kernel.org>
Signed-off-by: default avatarJakub Kicinski <kuba@kernel.org>
parents 1c213311 14a44ab0
......@@ -263,14 +263,33 @@ static void mtk_gmac0_rgmii_adjust(struct mtk_eth *eth,
mtk_w32(eth, val, TRGMII_TCK_CTRL);
}
static struct phylink_pcs *mtk_mac_select_pcs(struct phylink_config *config,
phy_interface_t interface)
{
struct mtk_mac *mac = container_of(config, struct mtk_mac,
phylink_config);
struct mtk_eth *eth = mac->hw;
unsigned int sid;
if (interface == PHY_INTERFACE_MODE_SGMII ||
phy_interface_mode_is_8023z(interface)) {
sid = (MTK_HAS_CAPS(eth->soc->caps, MTK_SHARED_SGMII)) ?
0 : mac->id;
return mtk_sgmii_select_pcs(eth->sgmii, sid);
}
return NULL;
}
static void mtk_mac_config(struct phylink_config *config, unsigned int mode,
const struct phylink_link_state *state)
{
struct mtk_mac *mac = container_of(config, struct mtk_mac,
phylink_config);
struct mtk_eth *eth = mac->hw;
u32 mcr_cur, mcr_new, sid, i;
int val, ge_mode, err = 0;
u32 i;
/* MT76x8 has no hardware settings between for the MAC */
if (!MTK_HAS_CAPS(eth->soc->caps, MTK_SOC_MT7628) &&
......@@ -327,6 +346,14 @@ static void mtk_mac_config(struct phylink_config *config, unsigned int mode,
state->interface))
goto err_phy;
} else {
/* FIXME: this is incorrect. Not only does it
* use state->speed (which is not guaranteed
* to be correct) but it also makes use of it
* in a code path that will only be reachable
* when the PHY interface mode changes, not
* when the speed changes. Consequently, RGMII
* is probably broken.
*/
mtk_gmac0_rgmii_adjust(mac->hw,
state->interface,
state->speed);
......@@ -383,38 +410,14 @@ static void mtk_mac_config(struct phylink_config *config, unsigned int mode,
SYSCFG0_SGMII_MASK,
~(u32)SYSCFG0_SGMII_MASK);
/* Decide how GMAC and SGMIISYS be mapped */
sid = (MTK_HAS_CAPS(eth->soc->caps, MTK_SHARED_SGMII)) ?
0 : mac->id;
/* Setup SGMIISYS with the determined property */
if (state->interface != PHY_INTERFACE_MODE_SGMII)
err = mtk_sgmii_setup_mode_force(eth->sgmii, sid,
state);
else if (phylink_autoneg_inband(mode))
err = mtk_sgmii_setup_mode_an(eth->sgmii, sid);
if (err)
goto init_err;
regmap_update_bits(eth->ethsys, ETHSYS_SYSCFG0,
SYSCFG0_SGMII_MASK, val);
/* Save the syscfg0 value for mac_finish */
mac->syscfg0 = val;
} else if (phylink_autoneg_inband(mode)) {
dev_err(eth->dev,
"In-band mode not supported in non SGMII mode!\n");
return;
}
/* Setup gmac */
mcr_cur = mtk_r32(mac->hw, MTK_MAC_MCR(mac->id));
mcr_new = mcr_cur;
mcr_new |= MAC_MCR_IPG_CFG | MAC_MCR_FORCE_MODE |
MAC_MCR_BACKOFF_EN | MAC_MCR_BACKPR_EN | MAC_MCR_FORCE_LINK;
/* Only update control register when needed! */
if (mcr_new != mcr_cur)
mtk_w32(mac->hw, mcr_new, MTK_MAC_MCR(mac->id));
return;
err_phy:
......@@ -427,6 +430,33 @@ static void mtk_mac_config(struct phylink_config *config, unsigned int mode,
mac->id, phy_modes(state->interface), err);
}
static int mtk_mac_finish(struct phylink_config *config, unsigned int mode,
phy_interface_t interface)
{
struct mtk_mac *mac = container_of(config, struct mtk_mac,
phylink_config);
struct mtk_eth *eth = mac->hw;
u32 mcr_cur, mcr_new;
/* Enable SGMII */
if (interface == PHY_INTERFACE_MODE_SGMII ||
phy_interface_mode_is_8023z(interface))
regmap_update_bits(eth->ethsys, ETHSYS_SYSCFG0,
SYSCFG0_SGMII_MASK, mac->syscfg0);
/* Setup gmac */
mcr_cur = mtk_r32(mac->hw, MTK_MAC_MCR(mac->id));
mcr_new = mcr_cur;
mcr_new |= MAC_MCR_IPG_CFG | MAC_MCR_FORCE_MODE |
MAC_MCR_BACKOFF_EN | MAC_MCR_BACKPR_EN | MAC_MCR_FORCE_LINK;
/* Only update control register when needed! */
if (mcr_new != mcr_cur)
mtk_w32(mac->hw, mcr_new, MTK_MAC_MCR(mac->id));
return 0;
}
static void mtk_mac_pcs_get_state(struct phylink_config *config,
struct phylink_link_state *state)
{
......@@ -459,14 +489,6 @@ static void mtk_mac_pcs_get_state(struct phylink_config *config,
state->pause |= MLO_PAUSE_TX;
}
static void mtk_mac_an_restart(struct phylink_config *config)
{
struct mtk_mac *mac = container_of(config, struct mtk_mac,
phylink_config);
mtk_sgmii_restart_an(mac->hw, mac->id);
}
static void mtk_mac_link_down(struct phylink_config *config, unsigned int mode,
phy_interface_t interface)
{
......@@ -485,8 +507,9 @@ static void mtk_mac_link_up(struct phylink_config *config,
{
struct mtk_mac *mac = container_of(config, struct mtk_mac,
phylink_config);
u32 mcr = mtk_r32(mac->hw, MTK_MAC_MCR(mac->id));
u32 mcr;
mcr = mtk_r32(mac->hw, MTK_MAC_MCR(mac->id));
mcr &= ~(MAC_MCR_SPEED_100 | MAC_MCR_SPEED_1000 |
MAC_MCR_FORCE_DPX | MAC_MCR_FORCE_TX_FC |
MAC_MCR_FORCE_RX_FC);
......@@ -518,9 +541,10 @@ static void mtk_mac_link_up(struct phylink_config *config,
static const struct phylink_mac_ops mtk_phylink_ops = {
.validate = phylink_generic_validate,
.mac_select_pcs = mtk_mac_select_pcs,
.mac_pcs_get_state = mtk_mac_pcs_get_state,
.mac_an_restart = mtk_mac_an_restart,
.mac_config = mtk_mac_config,
.mac_finish = mtk_mac_finish,
.mac_link_down = mtk_mac_link_down,
.mac_link_up = mtk_mac_link_up,
};
......@@ -2982,14 +3006,11 @@ static int mtk_add_mac(struct mtk_eth *eth, struct device_node *np)
/* mac config is not set */
mac->interface = PHY_INTERFACE_MODE_NA;
mac->mode = MLO_AN_PHY;
mac->speed = SPEED_UNKNOWN;
mac->phylink_config.dev = &eth->netdev[id]->dev;
mac->phylink_config.type = PHYLINK_NETDEV;
/* This driver makes use of state->speed/state->duplex in
* mac_config
*/
/* This driver makes use of state->speed in mac_config */
mac->phylink_config.legacy_pre_march2020 = true;
mac->phylink_config.mac_capabilities = MAC_ASYM_PAUSE | MAC_SYM_PAUSE |
MAC_10 | MAC_100 | MAC_1000 | MAC_2500FD;
......
......@@ -17,6 +17,7 @@
#include <linux/phylink.h>
#include <linux/rhashtable.h>
#include <linux/dim.h>
#include <linux/bitfield.h>
#include "mtk_ppe.h"
#define MTK_QDMA_PAGE_SIZE 2048
......@@ -493,9 +494,10 @@
#define SGMSYS_SGMII_MODE 0x20
#define SGMII_IF_MODE_BIT0 BIT(0)
#define SGMII_SPEED_DUPLEX_AN BIT(1)
#define SGMII_SPEED_10 0x0
#define SGMII_SPEED_100 BIT(2)
#define SGMII_SPEED_1000 BIT(3)
#define SGMII_SPEED_MASK GENMASK(3, 2)
#define SGMII_SPEED_10 FIELD_PREP(SGMII_SPEED_MASK, 0)
#define SGMII_SPEED_100 FIELD_PREP(SGMII_SPEED_MASK, 1)
#define SGMII_SPEED_1000 FIELD_PREP(SGMII_SPEED_MASK, 2)
#define SGMII_DUPLEX_FULL BIT(4)
#define SGMII_IF_MODE_BIT5 BIT(5)
#define SGMII_REMOTE_FAULT_DIS BIT(8)
......@@ -867,24 +869,25 @@ struct mtk_soc_data {
/* currently no SoC has more than 2 macs */
#define MTK_MAX_DEVS 2
#define MTK_SGMII_PHYSPEED_AN BIT(31)
#define MTK_SGMII_PHYSPEED_MASK GENMASK(2, 0)
#define MTK_SGMII_PHYSPEED_1000 BIT(0)
#define MTK_SGMII_PHYSPEED_2500 BIT(1)
#define MTK_HAS_FLAGS(flags, _x) (((flags) & (_x)) == (_x))
/* struct mtk_sgmii - This is the structure holding sgmii regmap and its
* characteristics
/* struct mtk_pcs - This structure holds each sgmii regmap and associated
* data
* @regmap: The register map pointing at the range used to setup
* SGMII modes
* @flags: The enum refers to which mode the sgmii wants to run on
* @ana_rgc3: The offset refers to register ANA_RGC3 related to regmap
* @pcs: Phylink PCS structure
*/
struct mtk_pcs {
struct regmap *regmap;
u32 ana_rgc3;
struct phylink_pcs pcs;
};
/* struct mtk_sgmii - This is the structure holding sgmii regmap and its
* characteristics
* @pcs Array of individual PCS structures
*/
struct mtk_sgmii {
struct regmap *regmap[MTK_MAX_DEVS];
u32 flags[MTK_MAX_DEVS];
u32 ana_rgc3;
struct mtk_pcs pcs[MTK_MAX_DEVS];
};
/* struct mtk_eth - This is the main datasructure for holding the state
......@@ -999,7 +1002,6 @@ struct mtk_eth {
struct mtk_mac {
int id;
phy_interface_t interface;
unsigned int mode;
int speed;
struct device_node *of_node;
struct phylink *phylink;
......@@ -1008,6 +1010,7 @@ struct mtk_mac {
struct mtk_hw_stats *hw_stats;
__be32 hwlro_ip[MTK_MAX_LRO_IP_CNT];
int hwlro_ip_cnt;
unsigned int syscfg0;
};
/* the struct describing the SoC. these are declared in the soc_xyz.c files */
......@@ -1019,12 +1022,9 @@ void mtk_stats_update_mac(struct mtk_mac *mac);
void mtk_w32(struct mtk_eth *eth, u32 val, unsigned reg);
u32 mtk_r32(struct mtk_eth *eth, unsigned reg);
struct phylink_pcs *mtk_sgmii_select_pcs(struct mtk_sgmii *ss, int id);
int mtk_sgmii_init(struct mtk_sgmii *ss, struct device_node *np,
u32 ana_rgc3);
int mtk_sgmii_setup_mode_an(struct mtk_sgmii *ss, int id);
int mtk_sgmii_setup_mode_force(struct mtk_sgmii *ss, int id,
const struct phylink_link_state *state);
void mtk_sgmii_restart_an(struct mtk_eth *eth, int mac_id);
int mtk_gmac_sgmii_path_setup(struct mtk_eth *eth, int mac_id);
int mtk_gmac_gephy_path_setup(struct mtk_eth *eth, int mac_id);
......
......@@ -9,119 +9,151 @@
#include <linux/mfd/syscon.h>
#include <linux/of.h>
#include <linux/phylink.h>
#include <linux/regmap.h>
#include "mtk_eth_soc.h"
int mtk_sgmii_init(struct mtk_sgmii *ss, struct device_node *r, u32 ana_rgc3)
static struct mtk_pcs *pcs_to_mtk_pcs(struct phylink_pcs *pcs)
{
struct device_node *np;
int i;
ss->ana_rgc3 = ana_rgc3;
for (i = 0; i < MTK_MAX_DEVS; i++) {
np = of_parse_phandle(r, "mediatek,sgmiisys", i);
if (!np)
break;
ss->regmap[i] = syscon_node_to_regmap(np);
of_node_put(np);
if (IS_ERR(ss->regmap[i]))
return PTR_ERR(ss->regmap[i]);
}
return 0;
return container_of(pcs, struct mtk_pcs, pcs);
}
int mtk_sgmii_setup_mode_an(struct mtk_sgmii *ss, int id)
/* For SGMII interface mode */
static int mtk_pcs_setup_mode_an(struct mtk_pcs *mpcs)
{
unsigned int val;
if (!ss->regmap[id])
return -EINVAL;
/* Setup the link timer and QPHY power up inside SGMIISYS */
regmap_write(ss->regmap[id], SGMSYS_PCS_LINK_TIMER,
regmap_write(mpcs->regmap, SGMSYS_PCS_LINK_TIMER,
SGMII_LINK_TIMER_DEFAULT);
regmap_read(ss->regmap[id], SGMSYS_SGMII_MODE, &val);
regmap_read(mpcs->regmap, SGMSYS_SGMII_MODE, &val);
val |= SGMII_REMOTE_FAULT_DIS;
regmap_write(ss->regmap[id], SGMSYS_SGMII_MODE, val);
regmap_write(mpcs->regmap, SGMSYS_SGMII_MODE, val);
regmap_read(ss->regmap[id], SGMSYS_PCS_CONTROL_1, &val);
regmap_read(mpcs->regmap, SGMSYS_PCS_CONTROL_1, &val);
val |= SGMII_AN_RESTART;
regmap_write(ss->regmap[id], SGMSYS_PCS_CONTROL_1, val);
regmap_write(mpcs->regmap, SGMSYS_PCS_CONTROL_1, val);
regmap_read(ss->regmap[id], SGMSYS_QPHY_PWR_STATE_CTRL, &val);
regmap_read(mpcs->regmap, SGMSYS_QPHY_PWR_STATE_CTRL, &val);
val &= ~SGMII_PHYA_PWD;
regmap_write(ss->regmap[id], SGMSYS_QPHY_PWR_STATE_CTRL, val);
regmap_write(mpcs->regmap, SGMSYS_QPHY_PWR_STATE_CTRL, val);
return 0;
}
int mtk_sgmii_setup_mode_force(struct mtk_sgmii *ss, int id,
const struct phylink_link_state *state)
/* 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;
if (!ss->regmap[id])
return -EINVAL;
regmap_read(ss->regmap[id], ss->ana_rgc3, &val);
regmap_read(mpcs->regmap, mpcs->ana_rgc3, &val);
val &= ~RG_PHY_SPEED_MASK;
if (state->interface == PHY_INTERFACE_MODE_2500BASEX)
if (interface == PHY_INTERFACE_MODE_2500BASEX)
val |= RG_PHY_SPEED_3_125G;
regmap_write(ss->regmap[id], ss->ana_rgc3, val);
regmap_write(mpcs->regmap, mpcs->ana_rgc3, val);
/* Disable SGMII AN */
regmap_read(ss->regmap[id], SGMSYS_PCS_CONTROL_1, &val);
regmap_read(mpcs->regmap, SGMSYS_PCS_CONTROL_1, &val);
val &= ~SGMII_AN_ENABLE;
regmap_write(ss->regmap[id], SGMSYS_PCS_CONTROL_1, val);
regmap_write(mpcs->regmap, SGMSYS_PCS_CONTROL_1, val);
/* SGMII force mode setting */
regmap_read(ss->regmap[id], SGMSYS_SGMII_MODE, &val);
val &= ~SGMII_IF_MODE_MASK;
switch (state->speed) {
case SPEED_10:
val |= SGMII_SPEED_10;
break;
case SPEED_100:
val |= SGMII_SPEED_100;
break;
case SPEED_2500:
case SPEED_1000:
/* 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;
break;
}
if (state->duplex == DUPLEX_FULL)
val |= SGMII_DUPLEX_FULL;
regmap_write(ss->regmap[id], SGMSYS_SGMII_MODE, val);
regmap_write(mpcs->regmap, SGMSYS_SGMII_MODE, val);
/* Release PHYA power down state */
regmap_read(ss->regmap[id], SGMSYS_QPHY_PWR_STATE_CTRL, &val);
regmap_read(mpcs->regmap, SGMSYS_QPHY_PWR_STATE_CTRL, &val);
val &= ~SGMII_PHYA_PWD;
regmap_write(ss->regmap[id], SGMSYS_QPHY_PWR_STATE_CTRL, val);
regmap_write(mpcs->regmap, SGMSYS_QPHY_PWR_STATE_CTRL, val);
return 0;
}
void mtk_sgmii_restart_an(struct mtk_eth *eth, int mac_id)
static int mtk_pcs_config(struct phylink_pcs *pcs, unsigned int mode,
phy_interface_t interface,
const unsigned long *advertising,
bool permit_pause_to_mac)
{
struct mtk_sgmii *ss = eth->sgmii;
unsigned int val, sid;
struct mtk_pcs *mpcs = pcs_to_mtk_pcs(pcs);
int err = 0;
/* Decide how GMAC and SGMIISYS be mapped */
sid = (MTK_HAS_CAPS(eth->soc->caps, MTK_SHARED_SGMII)) ?
0 : mac_id;
/* 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 (!ss->regmap[sid])
return;
return err;
}
static void mtk_pcs_restart_an(struct phylink_pcs *pcs)
{
struct mtk_pcs *mpcs = pcs_to_mtk_pcs(pcs);
unsigned int val;
regmap_read(ss->regmap[sid], SGMSYS_PCS_CONTROL_1, &val);
regmap_read(mpcs->regmap, SGMSYS_PCS_CONTROL_1, &val);
val |= SGMII_AN_RESTART;
regmap_write(ss->regmap[sid], SGMSYS_PCS_CONTROL_1, val);
regmap_write(mpcs->regmap, SGMSYS_PCS_CONTROL_1, val);
}
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;
if (!phy_interface_mode_is_8023z(interface))
return;
/* SGMII force duplex setting */
regmap_read(mpcs->regmap, SGMSYS_SGMII_MODE, &val);
val &= ~SGMII_DUPLEX_FULL;
if (duplex == DUPLEX_FULL)
val |= SGMII_DUPLEX_FULL;
regmap_write(mpcs->regmap, SGMSYS_SGMII_MODE, val);
}
static const struct phylink_pcs_ops mtk_pcs_ops = {
.pcs_config = mtk_pcs_config,
.pcs_an_restart = mtk_pcs_restart_an,
.pcs_link_up = mtk_pcs_link_up,
};
int mtk_sgmii_init(struct mtk_sgmii *ss, struct device_node *r, u32 ana_rgc3)
{
struct device_node *np;
int i;
for (i = 0; i < MTK_MAX_DEVS; i++) {
np = of_parse_phandle(r, "mediatek,sgmiisys", i);
if (!np)
break;
ss->pcs[i].ana_rgc3 = ana_rgc3;
ss->pcs[i].regmap = syscon_node_to_regmap(np);
of_node_put(np);
if (IS_ERR(ss->pcs[i].regmap))
return PTR_ERR(ss->pcs[i].regmap);
ss->pcs[i].pcs.ops = &mtk_pcs_ops;
}
return 0;
}
struct phylink_pcs *mtk_sgmii_select_pcs(struct mtk_sgmii *ss, int id)
{
if (!ss->pcs[id].regmap)
return NULL;
return &ss->pcs[id].pcs;
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment