[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <D6759987A7968C4889FDA6FA91D5CBC8146EF0A8@PGSMSX103.gar.corp.intel.com>
Date: Thu, 25 Apr 2019 07:06:43 +0000
From: "Voon, Weifeng" <weifeng.voon@...el.com>
To: "David S. Miller" <davem@...emloft.net>
CC: "netdev@...r.kernel.org" <netdev@...r.kernel.org>,
"linux-kernel@...r.kernel.org" <linux-kernel@...r.kernel.org>,
"Ong, Boon Leong" <boon.leong.ong@...el.com>,
"Kweh, Hock Leong" <hock.leong.kweh@...el.com>,
Florian Fainelli <f.fainelli@...il.com>,
"Andrew Lunn" <andrew@...n.ch>,
Maxime Coquelin <mcoquelin.stm32@...il.com>,
"Giuseppe Cavallaro" <peppe.cavallaro@...com>,
Jose Abreu <joabreu@...opsys.com>,
"Voon, Weifeng" <weifeng.voon@...el.com>
Subject: RE: [PATCH 4/7] net: stmmac: introducing support for DWC xPCS logics
> From: Ong Boon Leong <boon.leong.ong@...el.com>
>
> xPCS is DWC Ethernet Physical Coding Sublayer that may be integrated into a
> GbE controller that uses DWC EQoS MAC controller. An example of HW
> configuration is shown below:-
>
> <-----------------GBE Controller---------->|<--External PHY chip-->
>
> +----------+ +----+ +---+ +--------------+
> | EQoS | <-GMII->|xPCS|<-->|L1 | <-- SGMII --> | External GbE |
> | MAC | | | |PHY| | PHY Chip |
> +----------+ +----+ +---+ +--------------+
> ^ ^ ^
> | | |
> +---------------------MDIO-------------------------+
>
> xPCS is a Clause-45 MDIO Manageable Device (MMD) and we need a way to
> differentiate it from external PHY chip that is discovered over MDIO.
> Therefore, xpcs_phy_addr is introduced in stmmac platform data
> (plat_stmmacenet_data) for differentiating xPCS from 'phy_addr' that
> belongs to external PHY.
>
> Basic functionalities for initializing xPCS and configuring auto negotiation (AN),
> loopback, link status, AN advertisement and Link Partner ability are
> implemented.
>
> xPCS interrupt handling for C37 AN complete is also implemented.
>
> Tested-by: Kweh Hock Leong <hock.leong.kweh@...el.com>
> Reviewed-by: Chuah Kim Tatt <kim.tatt.chuah@...el.com>
> Reviewed-by: Voon Weifeng <weifeng.voon@...el.com>
> Reviewed-by: Kweh Hock Leong <hock.leong.kweh@...el.com>
> Reviewed-by: Baoli Zhang <baoli.zhang@...el.com>
> Signed-off-by: Ong Boon Leong <boon.leong.ong@...el.com>
> ---
> drivers/net/ethernet/stmicro/stmmac/dw_xpcs.h | 288
> ++++++++++++++++++++++++++
> drivers/net/ethernet/stmicro/stmmac/hwif.h | 17 ++
> include/linux/stmmac.h | 1 +
> 3 files changed, 306 insertions(+)
> create mode 100644 drivers/net/ethernet/stmicro/stmmac/dw_xpcs.h
>
> diff --git a/drivers/net/ethernet/stmicro/stmmac/dw_xpcs.h
> b/drivers/net/ethernet/stmicro/stmmac/dw_xpcs.h
> new file mode 100644
> index 0000000..446b714
> --- /dev/null
> +++ b/drivers/net/ethernet/stmicro/stmmac/dw_xpcs.h
> @@ -0,0 +1,288 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/* dw_xpcs.h: DWC Ethernet Physical Coding Sublayer Header
> + *
> + * Copyright (c) 2019, Intel Corporation.
> + *
> + * This program is free software; you can redistribute it and/or modify
> +it
> + * under the terms and conditions of the GNU General Public License,
> + * version 2, as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope it will be useful, but
> +WITHOUT
> + * ANY WARRANTY; without even the implied warranty of
> MERCHANTABILITY
> +or
> + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
> +License for
> + * more details.
> + */
> +#ifndef __DW_XPCS_H__
> +#define __DW_XPCS_H__
> +
> +#include <linux/mdio.h>
> +#include <linux/bitops.h>
> +#include "stmmac.h"
> +
> +/* XPCS Control & MII MMD Device Addresses */
> +#define XPCS_MDIO_CONTROL_MMD MDIO_MMD_VEND1
> +#define XPCS_MDIO_MII_MMD MDIO_MMD_VEND2
> +
> +/* Control MMD register offsets */
> +#define MDIO_CONTROL_MMD_CTRL 0x9 /* Control */
> +
> +/* Control MMD Control defines */
> +#define MDIO_CONTROL_MMD_CTRL_MII_MMD_EN 1 /* MII MMD
> Enable */
> +
> +/* MII MMD registers offsets */
> +#define MDIO_MII_MMD_CTRL 0x0 /* Control */
> +#define MDIO_MII_MMD_ADV 0x4 /* AN Advertisement
> */
> +#define MDIO_MII_MMD_LPA 0x5 /* Link Partner Ability
> */
> +#define MDIO_MII_MMD_DIGITAL_CTRL_1 0x8000 /* Digital Control 1 */
> +#define MDIO_MII_MMD_AN_CTRL 0x8001 /* AN Control */
> +#define MDIO_MII_MMD_AN_STAT 0x8002 /* AN Status */
> +
> +/* MII MMD Control defines */
> +#define MDIO_MII_MMD_CTRL_ANE BIT(12) /* AN Enable */
> +#define MDIO_MII_MMD_CTRL_LBE BIT(14) /* Loopback Enable
> */
> +#define MDIO_MII_MMD_CTRL_RANE BIT(9) /* Restart AN
> */
> +
> +/* MII MMD AN Advertisement & Link Partner Ability */
> +#define MDIO_MII_MMD_HD BIT(6) /* Half duplex
> */
> +#define MDIO_MII_MMD_FD BIT(5) /* Full duplex
> */
> +#define MDIO_MII_MMD_PSE_SHIFT 7 /* Pause
> Ability shift */
> +#define MDIO_MII_MMD_PSE GENMASK(8, 7) /* Pause
> Ability */
> +
> +/* MII MMD Digital Control 1 defines */
> +#define MDIO_MII_MMD_DIGI_CTRL_1_SGMII_PHY_MD BIT(0) /*
> SGMII PHY mode */
> +
> +/* MII MMD AN Control defines */
> +#define MDIO_MII_MMD_AN_CTRL_TX_CONFIG_SHIFT 3 /* TX Config
> shift */
> +#define AN_CTRL_TX_CONF_PHY_SIDE_SGMII 0x1 /* PHY
> side SGMII mode */
> +#define AN_CTRL_TX_CONF_MAC_SIDE_SGMII 0x0 /* MAC
> side SGMII mode */
> +#define MDIO_MII_MMD_AN_CTRL_PCS_MD_SHIFT 1 /* PCS Mode shift
> */
> +#define MDIO_MII_MMD_AN_CTRL_PCS_MD GENMASK(2, 1) /*
> PCS Mode */
> +#define AN_CTRL_PCS_MD_C37_1000BASEX 0x0 /* C37 AN for
> 1000BASE-X */
> +#define AN_CTRL_PCS_MD_C37_SGMII 0x2 /* C37 AN for SGMII
> */
> +#define MDIO_MII_MMD_AN_CTRL_AN_INTR_EN BIT(0) /* AN
> Complete Intr Enable */
> +
> +/* MII MMD AN Status defines for C37 AN SGMII Status */
> +#define AN_STAT_C37_AN_CMPLT BIT(0) /* AN Complete Intr
> */
> +#define AN_STAT_C37_AN_FD BIT(1) /* Full Duplex */
> +#define AN_STAT_C37_AN_SPEED_SHIFT 2 /* AN Speed shift */
> +#define AN_STAT_C37_AN_SPEED GENMASK(3, 2) /* AN
> Speed */
> +#define AN_STAT_C37_AN_10MBPS 0x0 /* 10 Mbps */
> +#define AN_STAT_C37_AN_100MBPS 0x1 /* 100 Mbps */
> +#define AN_STAT_C37_AN_1000MBPS 0x2 /* 1000 Mbps
> */
> +#define AN_STAT_C37_AN_LNKSTS BIT(4) /* Link Status */
> +
> +/**
> + * dw_xpcs_init - To initialize xPCS
> + * @ndev: network device pointer
> + * @mode: PCS mode
> + * Description: this is to initialize xPCS */ static inline void
> +dw_xpcs_init(struct net_device *ndev, int pcs_mode) {
> + struct stmmac_priv *priv = netdev_priv(ndev);
> + int xpcs_phy_addr = priv->plat->xpcs_phy_addr;
> +
> + /* Set SGMII PHY mode control */
> + u16 phydata = (u16)mdiobus_read(priv->mii, xpcs_phy_addr,
> + (MII_ADDR_C45 |
> + (XPCS_MDIO_MII_MMD <<
> + MII_DEVADDR_C45_SHIFT) |
> + MDIO_MII_MMD_DIGITAL_CTRL_1));
> +
> + phydata |= MDIO_MII_MMD_DIGI_CTRL_1_SGMII_PHY_MD;
> +
> + mdiobus_write(priv->mii, xpcs_phy_addr,
> + (MII_ADDR_C45 | (XPCS_MDIO_MII_MMD <<
> + MII_DEVADDR_C45_SHIFT) |
> MDIO_MII_MMD_DIGITAL_CTRL_1),
> + (int)phydata);
> +
> + /* Set PHY side SGMII, PCS Mode & Enable C37 AN complete
> interrupt */
> + phydata = (u16)mdiobus_read(priv->mii, xpcs_phy_addr,
> + (MII_ADDR_C45 |
> + (XPCS_MDIO_MII_MMD <<
> + MII_DEVADDR_C45_SHIFT) |
> + MDIO_MII_MMD_AN_CTRL));
> +
> + phydata &= ~MDIO_MII_MMD_AN_CTRL_PCS_MD;
> + phydata |= (((pcs_mode <<
> MDIO_MII_MMD_AN_CTRL_PCS_MD_SHIFT) &
> + MDIO_MII_MMD_AN_CTRL_PCS_MD) |
> + (AN_CTRL_TX_CONF_PHY_SIDE_SGMII <<
> + MDIO_MII_MMD_AN_CTRL_TX_CONFIG_SHIFT) |
> + MDIO_MII_MMD_AN_CTRL_AN_INTR_EN);
> +
> + mdiobus_write(priv->mii, xpcs_phy_addr,
> + (MII_ADDR_C45 | (XPCS_MDIO_MII_MMD <<
> + MII_DEVADDR_C45_SHIFT) |
> MDIO_MII_MMD_AN_CTRL),
> + (int)phydata);
> +}
> +
> +/**
> + * dw_xpcs_rane - To restart Auto Negotiation (AN)
> + * @ndev: network device pointer
> + * @restart: to restart AN
> + * Description: this is to restart AN.
> + */
> +static inline void dw_xpcs_rane(struct net_device *ndev, bool restart)
> +{
> + struct stmmac_priv *priv = netdev_priv(ndev);
> + int xpcs_phy_addr = priv->plat->xpcs_phy_addr;
> +
> + u16 phydata = (u16)mdiobus_read(priv->mii, xpcs_phy_addr,
> + (MII_ADDR_C45 | (XPCS_MDIO_MII_MMD
> <<
> + MII_DEVADDR_C45_SHIFT) |
> + MDIO_MII_MMD_CTRL));
> +
> + if (restart)
> + phydata |= MDIO_MII_MMD_CTRL_RANE;
> +
> + mdiobus_write(priv->mii, xpcs_phy_addr,
> + (MII_ADDR_C45 | (XPCS_MDIO_MII_MMD <<
> + MII_DEVADDR_C45_SHIFT) | MDIO_MII_MMD_CTRL),
> + (int)phydata);
> +}
> +
> +/**
> + * dw_xpcs_ctrl_ane - To program the MII MMD Control Register.
> + * @ndev: network device pointer
> + * @ane: to enable the Auto Negotiation
> + * @loopback: to cause the PHY to loopback Tx data into Rx path.
> + * Description: this is the main function to configure the MII MMD
> + * control register and init the AN Enable and select loopback.
> + */
> +static inline void dw_xpcs_ctrl_ane(struct net_device *ndev, bool ane,
> + bool loopback)
> +{
> + struct stmmac_priv *priv = netdev_priv(ndev);
> + int xpcs_phy_addr = priv->plat->xpcs_phy_addr;
> +
> + u16 phydata = (u16)mdiobus_read(priv->mii, xpcs_phy_addr,
> + (MII_ADDR_C45 | (XPCS_MDIO_MII_MMD
> <<
> + MII_DEVADDR_C45_SHIFT) |
> + MDIO_MII_MMD_CTRL));
> +
> + if (ane)
> + phydata |= (MDIO_MII_MMD_CTRL_ANE |
> MDIO_MII_MMD_CTRL_RANE);
> +
> + if (loopback)
> + phydata |= MDIO_MII_MMD_CTRL_LBE;
> +
> + mdiobus_write(priv->mii, xpcs_phy_addr,
> + (MII_ADDR_C45 | (XPCS_MDIO_MII_MMD <<
> + MII_DEVADDR_C45_SHIFT) | MDIO_MII_MMD_CTRL),
> + (int)phydata);
> +}
> +
> +/**
> + * dw_xpcs_get_adv_lp - Get AN Advertisement and Link Partner Ability
> + * @ndev: network device pointer
> + * @adv_lp: structure to store the adv, lp status
> + * Description: this is to expose the Auto Negotiation Advertisement
> +and
> + * Link partner ability status to ethtool support.
> + */
> +static inline void dw_xpcs_get_adv_lp(struct net_device *ndev,
> + struct rgmii_adv *adv_lp)
> +{
> + struct stmmac_priv *priv = netdev_priv(ndev);
> + int xpcs_phy_addr = priv->plat->xpcs_phy_addr;
> +
> + /* AN Advertisement Ability */
> + u16 value = (u16)mdiobus_read(priv->mii, xpcs_phy_addr,
> + (MII_ADDR_C45 | (XPCS_MDIO_MII_MMD
> <<
> + MII_DEVADDR_C45_SHIFT) |
> + MDIO_MII_MMD_ADV));
> +
> + if (value & MDIO_MII_MMD_FD)
> + adv_lp->duplex = DUPLEX_FULL;
> + if (value & MDIO_MII_MMD_HD)
> + adv_lp->duplex = DUPLEX_HALF;
> + adv_lp->pause = (u32)((value & MDIO_MII_MMD_PSE) >>
> + MDIO_MII_MMD_PSE_SHIFT);
> +
> + /* Link Partner Ability */
> + value = (u16)mdiobus_read(priv->mii, xpcs_phy_addr,
> + (MII_ADDR_C45 | (XPCS_MDIO_MII_MMD
> <<
> + MII_DEVADDR_C45_SHIFT) |
> + MDIO_MII_MMD_LPA));
> +
> + if (value & MDIO_MII_MMD_FD)
> + adv_lp->lp_duplex = DUPLEX_FULL;
> + if (value & MDIO_MII_MMD_HD)
> + adv_lp->lp_duplex = DUPLEX_HALF;
> + adv_lp->lp_pause = (u32)((value & MDIO_MII_MMD_PSE) >>
> + MDIO_MII_MMD_PSE_SHIFT);
> +}
> +
> +/**
> + * dw_xpcs_get_linkstatus - Get Link Status
> + * @an_stat: C37 AN status value
> + * @x: stmmac extra status
> + * Description: this is to read the link extra status from <C37 AN
> +SGMII
> + * status> field of MII MMD AN Status register.
> + */
> +static inline void dw_xpcs_get_linkstatus(u16 an_stat,
> + struct stmmac_extra_stats *x)
> +{
> + /* Check the link status */
> + if (an_stat & AN_STAT_C37_AN_LNKSTS) {
> + int speed_value;
> +
> + x->pcs_link = 1;
> +
> + speed_value = ((an_stat & AN_STAT_C37_AN_SPEED) >>
> + AN_STAT_C37_AN_SPEED_SHIFT);
> + if (speed_value == AN_STAT_C37_AN_1000MBPS)
> + x->pcs_speed = SPEED_1000;
> + else if (speed_value == AN_STAT_C37_AN_100MBPS)
> + x->pcs_speed = SPEED_100;
> + else
> + x->pcs_speed = SPEED_10;
> +
> + if (an_stat & AN_STAT_C37_AN_FD)
> + x->pcs_duplex = 1;
> + else
> + x->pcs_duplex = 0;
> +
> + pr_info("Link is Up - %d/%s\n", (int)x->pcs_speed,
> + x->pcs_duplex ? "Full" : "Half");
> + } else {
> + x->pcs_link = 0;
> + pr_info("Link is Down\n");
> + }
> +}
> +
> +/**
> + * dw_xpcs_irq_status - Get xPCS IRQ Status
> + * @ndev: network device pointer
> + * @x: stmmac extra status
> + * Description: this is to read the xPCS IRQ status.
> + */
> +static inline int dw_xpcs_irq_status(struct net_device *ndev,
> + struct stmmac_extra_stats *x)
> +{
> + struct stmmac_priv *priv = netdev_priv(ndev);
> + int xpcs_phy_addr = priv->plat->xpcs_phy_addr;
> + int ret = IRQ_NONE;
> +
> + /* C37 AN status */
> + u16 an_stat = (u16)mdiobus_read(priv->mii, xpcs_phy_addr,
> + (MII_ADDR_C45 |
> + (XPCS_MDIO_MII_MMD <<
> + MII_DEVADDR_C45_SHIFT) |
> + MDIO_MII_MMD_AN_STAT));
> +
> + if (an_stat & AN_STAT_C37_AN_CMPLT) {
> + x->irq_pcs_ane_n++;
> + dw_xpcs_get_linkstatus(an_stat, x);
> +
> + /* Clear C37 AN complete status by writing zero */
> + mdiobus_write(priv->mii, xpcs_phy_addr,
> + (MII_ADDR_C45 | (XPCS_MDIO_MII_MMD <<
> + MII_DEVADDR_C45_SHIFT) |
> + MDIO_MII_MMD_AN_STAT),
> + 0);
> + ret = IRQ_HANDLED;
> + }
> +
> + return ret;
> +}
> +#endif /* __DW_XPCS_H__ */
> diff --git a/drivers/net/ethernet/stmicro/stmmac/hwif.h
> b/drivers/net/ethernet/stmicro/stmmac/hwif.h
> index 5bb0023..cb7eb48 100644
> --- a/drivers/net/ethernet/stmicro/stmmac/hwif.h
> +++ b/drivers/net/ethernet/stmicro/stmmac/hwif.h
> @@ -310,6 +310,13 @@ struct stmmac_ops {
> bool loopback);
> void (*pcs_rane)(void __iomem *ioaddr, bool restart);
> void (*pcs_get_adv_lp)(void __iomem *ioaddr, struct rgmii_adv
> *adv);
> + /* xPCS calls */
> + void (*xpcs_init)(struct net_device *ndev, int pcs_mode);
> + void (*xpcs_ctrl_ane)(struct net_device *ndev, bool ane, bool
> loopback);
> + void (*xpcs_rane)(struct net_device *ndev, bool restart);
> + void (*xpcs_get_adv_lp)(struct net_device *ndev, struct rgmii_adv
> *adv);
> + int (*xpcs_irq_status)(struct net_device *ndev,
> + struct stmmac_extra_stats *x);
> /* Safety Features */
> int (*safety_feat_config)(void __iomem *ioaddr, unsigned int asp);
> int (*safety_feat_irq_status)(struct net_device *ndev, @@ -382,6
> +389,16 @@ struct stmmac_ops {
> stmmac_do_void_callback(__priv, mac, pcs_rane, __args) #define
> stmmac_pcs_get_adv_lp(__priv, __args...) \
> stmmac_do_void_callback(__priv, mac, pcs_get_adv_lp, __args)
> +#define stmmac_xpcs_init(__priv, __args...) \
> + stmmac_do_void_callback(__priv, mac, xpcs_init, __args) #define
> +stmmac_xpcs_ctrl_ane(__priv, __args...) \
> + stmmac_do_void_callback(__priv, mac, xpcs_ctrl_ane, __args)
> #define
> +stmmac_xpcs_rane(__priv, __args...) \
> + stmmac_do_void_callback(__priv, mac, xpcs_rane, __args) #define
> +stmmac_xpcs_get_adv_lp(__priv, __args...) \
> + stmmac_do_void_callback(__priv, mac, xpcs_get_adv_lp, __args)
> #define
> +stmmac_xpcs_irq_status(__priv, __args...) \
> + stmmac_do_callback(__priv, mac, xpcs_irq_status, __args)
> #define stmmac_safety_feat_config(__priv, __args...) \
> stmmac_do_callback(__priv, mac, safety_feat_config, __args)
> #define stmmac_safety_feat_irq_status(__priv, __args...) \ diff --git
> a/include/linux/stmmac.h b/include/linux/stmmac.h index 4335bd7..b00e795
> 100644
> --- a/include/linux/stmmac.h
> +++ b/include/linux/stmmac.h
> @@ -148,6 +148,7 @@ struct stmmac_txq_cfg { struct
> plat_stmmacenet_data {
> int bus_id;
> int phy_addr;
> + int xpcs_phy_addr;
> int interface;
> struct stmmac_mdio_bus_data *mdio_bus_data;
> struct device_node *phy_node;
> --
> 1.9.1
++ stmmac maintainers and c45 experts
Powered by blists - more mailing lists