[PATCH net-next v2 7/8] dpaa2-mac: configure the SerDes phy on a protocol change
Russell King (Oracle)
linux at armlinux.org.uk
Wed Mar 9 10:46:26 PST 2022
Hi Ioana,
On Wed, Mar 09, 2022 at 07:27:47PM +0200, Ioana Ciornei wrote:
> This patch integrates the dpaa2-eth driver with the generic PHY
> infrastructure in order to search, find and reconfigure the SerDes lanes
> in case of a protocol change.
>
> On the .mac_config() callback, the phy_set_mode_ext() API is called so
> that the Lynx 28G SerDes PHY driver can change the lane's configuration.
> In the same phylink callback the MC firmware is called so that it
> reconfigures the MAC side to run using the new protocol.
>
> The consumer drivers - dpaa2-eth and dpaa2-switch - are updated to call
> the dpaa2_mac_start/stop functions newly added which will
> power_on/power_off the associated SerDes lane.
>
> Signed-off-by: Ioana Ciornei <ioana.ciornei at nxp.com>
> ---
> Changes in v2:
> - none
>
> .../net/ethernet/freescale/dpaa2/dpaa2-eth.c | 5 +-
> .../net/ethernet/freescale/dpaa2/dpaa2-mac.c | 91 +++++++++++++++++++
> .../net/ethernet/freescale/dpaa2/dpaa2-mac.h | 6 ++
> .../ethernet/freescale/dpaa2/dpaa2-switch.c | 5 +-
> 4 files changed, 105 insertions(+), 2 deletions(-)
>
> diff --git a/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c b/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c
> index 939fa9db6a2e..b87369f0605f 100644
> --- a/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c
> +++ b/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c
> @@ -2077,8 +2077,10 @@ static int dpaa2_eth_open(struct net_device *net_dev)
> goto enable_err;
> }
>
> - if (dpaa2_eth_is_type_phy(priv))
> + if (dpaa2_eth_is_type_phy(priv)) {
> phylink_start(priv->mac->phylink);
> + dpaa2_mac_start(priv->mac);
Is this safe? Shouldn't dpaa2_mac_start() come before phylink_start()
in case phylink determines that the link is somehow already up? I'm
a big fan of teardown being in the reverse order of setup so having
the start and stop below in the same order just doesn't look right.
> + }
>
> return 0;
>
> @@ -2153,6 +2155,7 @@ static int dpaa2_eth_stop(struct net_device *net_dev)
>
> if (dpaa2_eth_is_type_phy(priv)) {
> phylink_stop(priv->mac->phylink);
> + dpaa2_mac_stop(priv->mac);
> } else {
> netif_tx_stop_all_queues(net_dev);
> netif_carrier_off(net_dev);
> diff --git a/drivers/net/ethernet/freescale/dpaa2/dpaa2-mac.c b/drivers/net/ethernet/freescale/dpaa2/dpaa2-mac.c
> index e6e758eaafea..bd90acc49cdb 100644
> --- a/drivers/net/ethernet/freescale/dpaa2/dpaa2-mac.c
> +++ b/drivers/net/ethernet/freescale/dpaa2/dpaa2-mac.c
> @@ -3,6 +3,7 @@
>
> #include <linux/acpi.h>
> #include <linux/pcs-lynx.h>
> +#include <linux/phy/phy.h>
> #include <linux/property.h>
>
> #include "dpaa2-eth.h"
> @@ -60,6 +61,26 @@ static int phy_mode(enum dpmac_eth_if eth_if, phy_interface_t *if_mode)
> return 0;
> }
>
> +static enum dpmac_eth_if dpmac_eth_if_mode(phy_interface_t if_mode)
> +{
> + switch (if_mode) {
> + case PHY_INTERFACE_MODE_RGMII:
Shouldn't this also include the other RGMII modes (which, from the MAC
point of view, are all synonymous?
> + return DPMAC_ETH_IF_RGMII;
> + case PHY_INTERFACE_MODE_USXGMII:
> + return DPMAC_ETH_IF_USXGMII;
> + case PHY_INTERFACE_MODE_QSGMII:
> + return DPMAC_ETH_IF_QSGMII;
> + case PHY_INTERFACE_MODE_SGMII:
> + return DPMAC_ETH_IF_SGMII;
> + case PHY_INTERFACE_MODE_10GBASER:
> + return DPMAC_ETH_IF_XFI;
> + case PHY_INTERFACE_MODE_1000BASEX:
> + return DPMAC_ETH_IF_1000BASEX;
> + default:
> + return DPMAC_ETH_IF_MII;
> + }
> +}
> +
> static struct fwnode_handle *dpaa2_mac_get_node(struct device *dev,
> u16 dpmac_id)
> {
> @@ -147,6 +168,19 @@ static void dpaa2_mac_config(struct phylink_config *config, unsigned int mode,
> if (err)
> netdev_err(mac->net_dev, "%s: dpmac_set_link_state() = %d\n",
> __func__, err);
> +
> + if (!mac->serdes_phy)
> + return;
> +
> + /* This happens only if we support changing of protocol at runtime */
> + err = dpmac_set_protocol(mac->mc_io, 0, mac->mc_dev->mc_handle,
> + dpmac_eth_if_mode(state->interface));
> + if (err)
> + netdev_err(mac->net_dev, "dpmac_set_protocol() = %d\n", err);
> +
> + err = phy_set_mode_ext(mac->serdes_phy, PHY_MODE_ETHERNET, state->interface);
> + if (err)
> + netdev_err(mac->net_dev, "phy_set_mode_ext() = %d\n", err);
> }
>
> static void dpaa2_mac_link_up(struct phylink_config *config,
> @@ -200,12 +234,21 @@ static void dpaa2_mac_link_down(struct phylink_config *config,
> netdev_err(mac->net_dev, "dpmac_set_link_state() = %d\n", err);
> }
>
> +static int dpaa2_mac_prepare(struct phylink_config *config, unsigned int mode,
> + phy_interface_t interface)
> +{
> + dpaa2_mac_link_down(config, mode, interface);
You should never see a reconfiguration while the link is up. However,
if the link is in in-band mode, then obviously the link could come up
at any moment, and in that case, forcing it down in mac_prepare() is
a good idea - but that forcing needs to be removed in mac_finish()
to allow in-band to work again. Not sure that your firmware allows
that though, and I'm not convinced that calling the above function
achieves any of those guarantees.
> +
> + return 0;
> +}
> +
> static const struct phylink_mac_ops dpaa2_mac_phylink_ops = {
> .validate = phylink_generic_validate,
> .mac_select_pcs = dpaa2_mac_select_pcs,
> .mac_config = dpaa2_mac_config,
> .mac_link_up = dpaa2_mac_link_up,
> .mac_link_down = dpaa2_mac_link_down,
> + .mac_prepare = dpaa2_mac_prepare,
> };
>
> static int dpaa2_pcs_create(struct dpaa2_mac *mac,
> @@ -259,6 +302,8 @@ static void dpaa2_pcs_destroy(struct dpaa2_mac *mac)
>
> static void dpaa2_mac_set_supported_interfaces(struct dpaa2_mac *mac)
> {
> + int intf, err;
> +
> /* We support the current interface mode, and if we have a PCS
> * similar interface modes that do not require the SerDes lane to be
> * reconfigured.
> @@ -278,12 +323,40 @@ static void dpaa2_mac_set_supported_interfaces(struct dpaa2_mac *mac)
> break;
> }
> }
> +
> + if (!mac->serdes_phy)
> + return;
> +
> + /* In case we have access to the SerDes phy/lane, then ask the SerDes
> + * driver what interfaces are supported based on the current PLL
> + * configuration.
> + */
> + for (intf = 0; intf < PHY_INTERFACE_MODE_MAX; intf++) {
You probably want to avoid PHY_INTERFACE_MODE_NA here, even though your
driver may reject it anyway.
> + err = phy_validate(mac->serdes_phy, PHY_MODE_ETHERNET, intf, NULL);
> + if (err)
> + continue;
> +
> + __set_bit(intf, mac->phylink_config.supported_interfaces);
> + }
> +}
> +
> +void dpaa2_mac_start(struct dpaa2_mac *mac)
> +{
> + if (mac->serdes_phy)
> + phy_power_on(mac->serdes_phy);
> +}
> +
> +void dpaa2_mac_stop(struct dpaa2_mac *mac)
> +{
> + if (mac->serdes_phy)
> + phy_power_off(mac->serdes_phy);
> }
>
> int dpaa2_mac_connect(struct dpaa2_mac *mac)
> {
> struct net_device *net_dev = mac->net_dev;
> struct fwnode_handle *dpmac_node;
> + struct phy *serdes_phy = NULL;
> struct phylink *phylink;
> int err;
>
> @@ -300,6 +373,22 @@ int dpaa2_mac_connect(struct dpaa2_mac *mac)
> return -EINVAL;
> mac->if_mode = err;
>
> + if (mac->features & DPAA2_MAC_FEATURE_PROTOCOL_CHANGE &&
> + !phy_interface_mode_is_rgmii(mac->if_mode) &&
> + is_of_node(dpmac_node)) {
> + serdes_phy = of_phy_get(to_of_node(dpmac_node), NULL);
> +
> + if (IS_ERR(serdes_phy)) {
> + if (PTR_ERR(serdes_phy) == -ENODEV)
> + serdes_phy = NULL;
> + else
> + return PTR_ERR(serdes_phy);
> + } else {
> + phy_init(serdes_phy);
> + }
> + }
> + mac->serdes_phy = serdes_phy;
> +
> /* The MAC does not have the capability to add RGMII delays so
> * error out if the interface mode requests them and there is no PHY
> * to act upon them
> @@ -363,6 +452,8 @@ void dpaa2_mac_disconnect(struct dpaa2_mac *mac)
> phylink_disconnect_phy(mac->phylink);
> phylink_destroy(mac->phylink);
> dpaa2_pcs_destroy(mac);
> + of_phy_put(mac->serdes_phy);
> + mac->serdes_phy = NULL;
> }
>
> int dpaa2_mac_open(struct dpaa2_mac *mac)
> diff --git a/drivers/net/ethernet/freescale/dpaa2/dpaa2-mac.h b/drivers/net/ethernet/freescale/dpaa2/dpaa2-mac.h
> index d2e51d21c80c..a58cab188a99 100644
> --- a/drivers/net/ethernet/freescale/dpaa2/dpaa2-mac.h
> +++ b/drivers/net/ethernet/freescale/dpaa2/dpaa2-mac.h
> @@ -26,6 +26,8 @@ struct dpaa2_mac {
> enum dpmac_link_type if_link_type;
> struct phylink_pcs *pcs;
> struct fwnode_handle *fw_node;
> +
> + struct phy *serdes_phy;
> };
>
> bool dpaa2_mac_is_type_fixed(struct fsl_mc_device *dpmac_dev,
> @@ -45,4 +47,8 @@ void dpaa2_mac_get_strings(u8 *data);
>
> void dpaa2_mac_get_ethtool_stats(struct dpaa2_mac *mac, u64 *data);
>
> +void dpaa2_mac_start(struct dpaa2_mac *mac);
> +
> +void dpaa2_mac_stop(struct dpaa2_mac *mac);
> +
> #endif /* DPAA2_MAC_H */
> diff --git a/drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.c b/drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.c
> index 9a561072aa4a..e4f8f927e223 100644
> --- a/drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.c
> +++ b/drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.c
> @@ -703,8 +703,10 @@ static int dpaa2_switch_port_open(struct net_device *netdev)
>
> dpaa2_switch_enable_ctrl_if_napi(ethsw);
>
> - if (dpaa2_switch_port_is_type_phy(port_priv))
> + if (dpaa2_switch_port_is_type_phy(port_priv)) {
> phylink_start(port_priv->mac->phylink);
> + dpaa2_mac_start(port_priv->mac);
Same comments as for dpaa2-mac.
> + }
>
> return 0;
> }
> @@ -717,6 +719,7 @@ static int dpaa2_switch_port_stop(struct net_device *netdev)
>
> if (dpaa2_switch_port_is_type_phy(port_priv)) {
> phylink_stop(port_priv->mac->phylink);
> + dpaa2_mac_stop(port_priv->mac);
> } else {
> netif_tx_stop_all_queues(netdev);
> netif_carrier_off(netdev);
> --
> 2.33.1
>
>
Thanks!
--
RMK's Patch system: https://www.armlinux.org.uk/developer/patches/
FTTP is here! 40Mbps down 10Mbps up. Decent connectivity at last!
More information about the linux-phy
mailing list