|
@@ -29,6 +29,7 @@
|
|
|
#include <linux/of_address.h>
|
|
|
#include <linux/of_device.h>
|
|
|
#include <linux/phy.h>
|
|
|
+#include <linux/phylink.h>
|
|
|
#include <linux/phy/phy.h>
|
|
|
#include <linux/clk.h>
|
|
|
#include <linux/hrtimer.h>
|
|
@@ -359,15 +360,23 @@
|
|
|
#define MVPP2_GMAC_FORCE_LINK_PASS BIT(1)
|
|
|
#define MVPP2_GMAC_IN_BAND_AUTONEG BIT(2)
|
|
|
#define MVPP2_GMAC_IN_BAND_AUTONEG_BYPASS BIT(3)
|
|
|
+#define MVPP2_GMAC_IN_BAND_RESTART_AN BIT(4)
|
|
|
#define MVPP2_GMAC_CONFIG_MII_SPEED BIT(5)
|
|
|
#define MVPP2_GMAC_CONFIG_GMII_SPEED BIT(6)
|
|
|
#define MVPP2_GMAC_AN_SPEED_EN BIT(7)
|
|
|
#define MVPP2_GMAC_FC_ADV_EN BIT(9)
|
|
|
+#define MVPP2_GMAC_FC_ADV_ASM_EN BIT(10)
|
|
|
#define MVPP2_GMAC_FLOW_CTRL_AUTONEG BIT(11)
|
|
|
#define MVPP2_GMAC_CONFIG_FULL_DUPLEX BIT(12)
|
|
|
#define MVPP2_GMAC_AN_DUPLEX_EN BIT(13)
|
|
|
#define MVPP2_GMAC_STATUS0 0x10
|
|
|
#define MVPP2_GMAC_STATUS0_LINK_UP BIT(0)
|
|
|
+#define MVPP2_GMAC_STATUS0_GMII_SPEED BIT(1)
|
|
|
+#define MVPP2_GMAC_STATUS0_MII_SPEED BIT(2)
|
|
|
+#define MVPP2_GMAC_STATUS0_FULL_DUPLEX BIT(3)
|
|
|
+#define MVPP2_GMAC_STATUS0_RX_PAUSE BIT(6)
|
|
|
+#define MVPP2_GMAC_STATUS0_TX_PAUSE BIT(7)
|
|
|
+#define MVPP2_GMAC_STATUS0_AN_COMPLETE BIT(11)
|
|
|
#define MVPP2_GMAC_PORT_FIFO_CFG_1_REG 0x1c
|
|
|
#define MVPP2_GMAC_TX_FIFO_MIN_TH_OFFS 6
|
|
|
#define MVPP2_GMAC_TX_FIFO_MIN_TH_ALL_MASK 0x1fc0
|
|
@@ -379,6 +388,8 @@
|
|
|
#define MVPP22_GMAC_INT_MASK_LINK_STAT BIT(1)
|
|
|
#define MVPP22_GMAC_CTRL_4_REG 0x90
|
|
|
#define MVPP22_CTRL4_EXT_PIN_GMII_SEL BIT(0)
|
|
|
+#define MVPP22_CTRL4_RX_FC_EN BIT(3)
|
|
|
+#define MVPP22_CTRL4_TX_FC_EN BIT(4)
|
|
|
#define MVPP22_CTRL4_DP_CLK_SEL BIT(5)
|
|
|
#define MVPP22_CTRL4_SYNC_BYPASS_DIS BIT(6)
|
|
|
#define MVPP22_CTRL4_QSGMII_BYPASS_ACTIVE BIT(7)
|
|
@@ -392,6 +403,7 @@
|
|
|
#define MVPP22_XLG_CTRL0_PORT_EN BIT(0)
|
|
|
#define MVPP22_XLG_CTRL0_MAC_RESET_DIS BIT(1)
|
|
|
#define MVPP22_XLG_CTRL0_RX_FLOW_CTRL_EN BIT(7)
|
|
|
+#define MVPP22_XLG_CTRL0_TX_FLOW_CTRL_EN BIT(8)
|
|
|
#define MVPP22_XLG_CTRL0_MIB_CNT_DIS BIT(14)
|
|
|
#define MVPP22_XLG_CTRL1_REG 0x104
|
|
|
#define MVPP22_XLG_CTRL1_FRAMESIZELIMIT_OFFS 0
|
|
@@ -413,6 +425,7 @@
|
|
|
#define MVPP22_XLG_CTRL4_FWD_FC BIT(5)
|
|
|
#define MVPP22_XLG_CTRL4_FWD_PFC BIT(6)
|
|
|
#define MVPP22_XLG_CTRL4_MACMODSELECT_GMAC BIT(12)
|
|
|
+#define MVPP22_XLG_CTRL4_EN_IDLE_CHECK BIT(14)
|
|
|
|
|
|
/* SMI registers. PPv2.2 only, relative to priv->iface_base. */
|
|
|
#define MVPP22_SMI_MISC_CFG_REG 0x1204
|
|
@@ -1017,6 +1030,9 @@ struct mvpp2_port {
|
|
|
/* Firmware node associated to the port */
|
|
|
struct fwnode_handle *fwnode;
|
|
|
|
|
|
+ /* Is a PHY always connected to the port */
|
|
|
+ bool has_phy;
|
|
|
+
|
|
|
/* Per-port registers' base address */
|
|
|
void __iomem *base;
|
|
|
void __iomem *stats_base;
|
|
@@ -1044,12 +1060,11 @@ struct mvpp2_port {
|
|
|
struct mutex gather_stats_lock;
|
|
|
struct delayed_work stats_work;
|
|
|
|
|
|
+ struct device_node *of_node;
|
|
|
+
|
|
|
phy_interface_t phy_interface;
|
|
|
- struct device_node *phy_node;
|
|
|
+ struct phylink *phylink;
|
|
|
struct phy *comphy;
|
|
|
- unsigned int link;
|
|
|
- unsigned int duplex;
|
|
|
- unsigned int speed;
|
|
|
|
|
|
struct mvpp2_bm_pool *pool_long;
|
|
|
struct mvpp2_bm_pool *pool_short;
|
|
@@ -1338,6 +1353,12 @@ struct mvpp2_bm_pool {
|
|
|
(addr) < (txq_pcpu)->tso_headers_dma + \
|
|
|
(txq_pcpu)->size * TSO_HEADER_SIZE)
|
|
|
|
|
|
+/* The prototype is added here to be used in start_dev when using ACPI. This
|
|
|
+ * will be removed once phylink is used for all modes (dt+ACPI).
|
|
|
+ */
|
|
|
+static void mvpp2_mac_config(struct net_device *dev, unsigned int mode,
|
|
|
+ const struct phylink_link_state *state);
|
|
|
+
|
|
|
/* Queue modes */
|
|
|
#define MVPP2_QDIST_SINGLE_MODE 0
|
|
|
#define MVPP2_QDIST_MULTI_MODE 1
|
|
@@ -4969,133 +4990,6 @@ static int mvpp22_comphy_init(struct mvpp2_port *port)
|
|
|
return phy_power_on(port->comphy);
|
|
|
}
|
|
|
|
|
|
-static void mvpp2_port_mii_gmac_configure_mode(struct mvpp2_port *port)
|
|
|
-{
|
|
|
- u32 val;
|
|
|
-
|
|
|
- if (port->phy_interface == PHY_INTERFACE_MODE_SGMII) {
|
|
|
- val = readl(port->base + MVPP22_GMAC_CTRL_4_REG);
|
|
|
- val |= MVPP22_CTRL4_SYNC_BYPASS_DIS | MVPP22_CTRL4_DP_CLK_SEL |
|
|
|
- MVPP22_CTRL4_QSGMII_BYPASS_ACTIVE;
|
|
|
- val &= ~MVPP22_CTRL4_EXT_PIN_GMII_SEL;
|
|
|
- writel(val, port->base + MVPP22_GMAC_CTRL_4_REG);
|
|
|
- } else if (phy_interface_mode_is_rgmii(port->phy_interface)) {
|
|
|
- val = readl(port->base + MVPP22_GMAC_CTRL_4_REG);
|
|
|
- val |= MVPP22_CTRL4_EXT_PIN_GMII_SEL |
|
|
|
- MVPP22_CTRL4_SYNC_BYPASS_DIS |
|
|
|
- MVPP22_CTRL4_QSGMII_BYPASS_ACTIVE;
|
|
|
- val &= ~MVPP22_CTRL4_DP_CLK_SEL;
|
|
|
- writel(val, port->base + MVPP22_GMAC_CTRL_4_REG);
|
|
|
- }
|
|
|
-
|
|
|
- /* The port is connected to a copper PHY */
|
|
|
- val = readl(port->base + MVPP2_GMAC_CTRL_0_REG);
|
|
|
- val &= ~MVPP2_GMAC_PORT_TYPE_MASK;
|
|
|
- writel(val, port->base + MVPP2_GMAC_CTRL_0_REG);
|
|
|
-
|
|
|
- val = readl(port->base + MVPP2_GMAC_AUTONEG_CONFIG);
|
|
|
- val |= MVPP2_GMAC_IN_BAND_AUTONEG_BYPASS |
|
|
|
- MVPP2_GMAC_AN_SPEED_EN | MVPP2_GMAC_FLOW_CTRL_AUTONEG |
|
|
|
- MVPP2_GMAC_AN_DUPLEX_EN;
|
|
|
- if (port->phy_interface == PHY_INTERFACE_MODE_SGMII)
|
|
|
- val |= MVPP2_GMAC_IN_BAND_AUTONEG;
|
|
|
- writel(val, port->base + MVPP2_GMAC_AUTONEG_CONFIG);
|
|
|
-}
|
|
|
-
|
|
|
-static void mvpp2_port_mii_gmac_configure(struct mvpp2_port *port)
|
|
|
-{
|
|
|
- u32 val;
|
|
|
-
|
|
|
- /* Force link down */
|
|
|
- val = readl(port->base + MVPP2_GMAC_AUTONEG_CONFIG);
|
|
|
- val &= ~MVPP2_GMAC_FORCE_LINK_PASS;
|
|
|
- val |= MVPP2_GMAC_FORCE_LINK_DOWN;
|
|
|
- writel(val, port->base + MVPP2_GMAC_AUTONEG_CONFIG);
|
|
|
-
|
|
|
- /* Set the GMAC in a reset state */
|
|
|
- val = readl(port->base + MVPP2_GMAC_CTRL_2_REG);
|
|
|
- val |= MVPP2_GMAC_PORT_RESET_MASK;
|
|
|
- writel(val, port->base + MVPP2_GMAC_CTRL_2_REG);
|
|
|
-
|
|
|
- /* Configure the PCS and in-band AN */
|
|
|
- val = readl(port->base + MVPP2_GMAC_CTRL_2_REG);
|
|
|
- if (port->phy_interface == PHY_INTERFACE_MODE_SGMII) {
|
|
|
- val |= MVPP2_GMAC_INBAND_AN_MASK | MVPP2_GMAC_PCS_ENABLE_MASK;
|
|
|
- } else if (phy_interface_mode_is_rgmii(port->phy_interface)) {
|
|
|
- val &= ~MVPP2_GMAC_PCS_ENABLE_MASK;
|
|
|
- }
|
|
|
- writel(val, port->base + MVPP2_GMAC_CTRL_2_REG);
|
|
|
-
|
|
|
- mvpp2_port_mii_gmac_configure_mode(port);
|
|
|
-
|
|
|
- /* Unset the GMAC reset state */
|
|
|
- val = readl(port->base + MVPP2_GMAC_CTRL_2_REG);
|
|
|
- val &= ~MVPP2_GMAC_PORT_RESET_MASK;
|
|
|
- writel(val, port->base + MVPP2_GMAC_CTRL_2_REG);
|
|
|
-
|
|
|
- /* Stop forcing link down */
|
|
|
- val = readl(port->base + MVPP2_GMAC_AUTONEG_CONFIG);
|
|
|
- val &= ~MVPP2_GMAC_FORCE_LINK_DOWN;
|
|
|
- writel(val, port->base + MVPP2_GMAC_AUTONEG_CONFIG);
|
|
|
-}
|
|
|
-
|
|
|
-static void mvpp2_port_mii_xlg_configure(struct mvpp2_port *port)
|
|
|
-{
|
|
|
- u32 val;
|
|
|
-
|
|
|
- if (port->gop_id != 0)
|
|
|
- return;
|
|
|
-
|
|
|
- val = readl(port->base + MVPP22_XLG_CTRL0_REG);
|
|
|
- val |= MVPP22_XLG_CTRL0_RX_FLOW_CTRL_EN;
|
|
|
- writel(val, port->base + MVPP22_XLG_CTRL0_REG);
|
|
|
-
|
|
|
- val = readl(port->base + MVPP22_XLG_CTRL4_REG);
|
|
|
- val &= ~MVPP22_XLG_CTRL4_MACMODSELECT_GMAC;
|
|
|
- val |= MVPP22_XLG_CTRL4_FWD_FC | MVPP22_XLG_CTRL4_FWD_PFC;
|
|
|
- writel(val, port->base + MVPP22_XLG_CTRL4_REG);
|
|
|
-}
|
|
|
-
|
|
|
-static void mvpp22_port_mii_set(struct mvpp2_port *port)
|
|
|
-{
|
|
|
- u32 val;
|
|
|
-
|
|
|
- /* Only GOP port 0 has an XLG MAC */
|
|
|
- if (port->gop_id == 0) {
|
|
|
- val = readl(port->base + MVPP22_XLG_CTRL3_REG);
|
|
|
- val &= ~MVPP22_XLG_CTRL3_MACMODESELECT_MASK;
|
|
|
-
|
|
|
- if (port->phy_interface == PHY_INTERFACE_MODE_XAUI ||
|
|
|
- port->phy_interface == PHY_INTERFACE_MODE_10GKR)
|
|
|
- val |= MVPP22_XLG_CTRL3_MACMODESELECT_10G;
|
|
|
- else
|
|
|
- val |= MVPP22_XLG_CTRL3_MACMODESELECT_GMAC;
|
|
|
-
|
|
|
- writel(val, port->base + MVPP22_XLG_CTRL3_REG);
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-static void mvpp2_port_mii_set(struct mvpp2_port *port)
|
|
|
-{
|
|
|
- if (port->priv->hw_version == MVPP22)
|
|
|
- mvpp22_port_mii_set(port);
|
|
|
-
|
|
|
- if (phy_interface_mode_is_rgmii(port->phy_interface) ||
|
|
|
- port->phy_interface == PHY_INTERFACE_MODE_SGMII)
|
|
|
- mvpp2_port_mii_gmac_configure(port);
|
|
|
- else if (port->phy_interface == PHY_INTERFACE_MODE_10GKR)
|
|
|
- mvpp2_port_mii_xlg_configure(port);
|
|
|
-}
|
|
|
-
|
|
|
-static void mvpp2_port_fc_adv_enable(struct mvpp2_port *port)
|
|
|
-{
|
|
|
- u32 val;
|
|
|
-
|
|
|
- val = readl(port->base + MVPP2_GMAC_AUTONEG_CONFIG);
|
|
|
- val |= MVPP2_GMAC_FC_ADV_EN;
|
|
|
- writel(val, port->base + MVPP2_GMAC_AUTONEG_CONFIG);
|
|
|
-}
|
|
|
-
|
|
|
static void mvpp2_port_enable(struct mvpp2_port *port)
|
|
|
{
|
|
|
u32 val;
|
|
@@ -5126,8 +5020,11 @@ static void mvpp2_port_disable(struct mvpp2_port *port)
|
|
|
(port->phy_interface == PHY_INTERFACE_MODE_XAUI ||
|
|
|
port->phy_interface == PHY_INTERFACE_MODE_10GKR)) {
|
|
|
val = readl(port->base + MVPP22_XLG_CTRL0_REG);
|
|
|
- val &= ~(MVPP22_XLG_CTRL0_PORT_EN |
|
|
|
- MVPP22_XLG_CTRL0_MAC_RESET_DIS);
|
|
|
+ val &= ~MVPP22_XLG_CTRL0_PORT_EN;
|
|
|
+ writel(val, port->base + MVPP22_XLG_CTRL0_REG);
|
|
|
+
|
|
|
+ /* Disable & reset should be done separately */
|
|
|
+ val &= ~MVPP22_XLG_CTRL0_MAC_RESET_DIS;
|
|
|
writel(val, port->base + MVPP22_XLG_CTRL0_REG);
|
|
|
} else {
|
|
|
val = readl(port->base + MVPP2_GMAC_CTRL_0_REG);
|
|
@@ -5147,13 +5044,14 @@ static void mvpp2_port_periodic_xon_disable(struct mvpp2_port *port)
|
|
|
}
|
|
|
|
|
|
/* Configure loopback port */
|
|
|
-static void mvpp2_port_loopback_set(struct mvpp2_port *port)
|
|
|
+static void mvpp2_port_loopback_set(struct mvpp2_port *port,
|
|
|
+ const struct phylink_link_state *state)
|
|
|
{
|
|
|
u32 val;
|
|
|
|
|
|
val = readl(port->base + MVPP2_GMAC_CTRL_1_REG);
|
|
|
|
|
|
- if (port->speed == 1000)
|
|
|
+ if (state->speed == 1000)
|
|
|
val |= MVPP2_GMAC_GMII_LB_EN_MASK;
|
|
|
else
|
|
|
val &= ~MVPP2_GMAC_GMII_LB_EN_MASK;
|
|
@@ -5331,10 +5229,6 @@ static void mvpp2_defaults_set(struct mvpp2_port *port)
|
|
|
int tx_port_num, val, queue, ptxq, lrxq;
|
|
|
|
|
|
if (port->priv->hw_version == MVPP21) {
|
|
|
- /* Configure port to loopback if needed */
|
|
|
- if (port->flags & MVPP2_F_LOOPBACK)
|
|
|
- mvpp2_port_loopback_set(port);
|
|
|
-
|
|
|
/* Update TX FIFO MIN Threshold */
|
|
|
val = readl(port->base + MVPP2_GMAC_PORT_FIFO_CFG_1_REG);
|
|
|
val &= ~MVPP2_GMAC_TX_FIFO_MIN_TH_ALL_MASK;
|
|
@@ -6382,6 +6276,11 @@ static irqreturn_t mvpp2_link_status_isr(int irq, void *dev_id)
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ if (port->phylink) {
|
|
|
+ phylink_mac_change(port->phylink, link);
|
|
|
+ goto handled;
|
|
|
+ }
|
|
|
+
|
|
|
if (!netif_running(dev) || !event)
|
|
|
goto handled;
|
|
|
|
|
@@ -6406,111 +6305,6 @@ handled:
|
|
|
return IRQ_HANDLED;
|
|
|
}
|
|
|
|
|
|
-static void mvpp2_gmac_set_autoneg(struct mvpp2_port *port,
|
|
|
- struct phy_device *phydev)
|
|
|
-{
|
|
|
- u32 val;
|
|
|
-
|
|
|
- if (port->phy_interface != PHY_INTERFACE_MODE_RGMII &&
|
|
|
- port->phy_interface != PHY_INTERFACE_MODE_RGMII_ID &&
|
|
|
- port->phy_interface != PHY_INTERFACE_MODE_RGMII_RXID &&
|
|
|
- port->phy_interface != PHY_INTERFACE_MODE_RGMII_TXID &&
|
|
|
- port->phy_interface != PHY_INTERFACE_MODE_SGMII)
|
|
|
- return;
|
|
|
-
|
|
|
- val = readl(port->base + MVPP2_GMAC_AUTONEG_CONFIG);
|
|
|
- val &= ~(MVPP2_GMAC_CONFIG_MII_SPEED |
|
|
|
- MVPP2_GMAC_CONFIG_GMII_SPEED |
|
|
|
- MVPP2_GMAC_CONFIG_FULL_DUPLEX |
|
|
|
- MVPP2_GMAC_AN_SPEED_EN |
|
|
|
- MVPP2_GMAC_AN_DUPLEX_EN);
|
|
|
-
|
|
|
- if (phydev->duplex)
|
|
|
- val |= MVPP2_GMAC_CONFIG_FULL_DUPLEX;
|
|
|
-
|
|
|
- if (phydev->speed == SPEED_1000)
|
|
|
- val |= MVPP2_GMAC_CONFIG_GMII_SPEED;
|
|
|
- else if (phydev->speed == SPEED_100)
|
|
|
- val |= MVPP2_GMAC_CONFIG_MII_SPEED;
|
|
|
-
|
|
|
- writel(val, port->base + MVPP2_GMAC_AUTONEG_CONFIG);
|
|
|
-}
|
|
|
-
|
|
|
-/* Adjust link */
|
|
|
-static void mvpp2_link_event(struct net_device *dev)
|
|
|
-{
|
|
|
- struct mvpp2_port *port = netdev_priv(dev);
|
|
|
- struct phy_device *phydev = dev->phydev;
|
|
|
- bool link_reconfigured = false;
|
|
|
- u32 val;
|
|
|
-
|
|
|
- if (phydev->link) {
|
|
|
- if (port->phy_interface != phydev->interface && port->comphy) {
|
|
|
- /* disable current port for reconfiguration */
|
|
|
- mvpp2_interrupts_disable(port);
|
|
|
- netif_carrier_off(port->dev);
|
|
|
- mvpp2_port_disable(port);
|
|
|
- phy_power_off(port->comphy);
|
|
|
-
|
|
|
- /* comphy reconfiguration */
|
|
|
- port->phy_interface = phydev->interface;
|
|
|
- mvpp22_comphy_init(port);
|
|
|
-
|
|
|
- /* gop/mac reconfiguration */
|
|
|
- mvpp22_gop_init(port);
|
|
|
- mvpp2_port_mii_set(port);
|
|
|
-
|
|
|
- link_reconfigured = true;
|
|
|
- }
|
|
|
-
|
|
|
- if ((port->speed != phydev->speed) ||
|
|
|
- (port->duplex != phydev->duplex)) {
|
|
|
- mvpp2_gmac_set_autoneg(port, phydev);
|
|
|
-
|
|
|
- port->duplex = phydev->duplex;
|
|
|
- port->speed = phydev->speed;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- if (phydev->link != port->link || link_reconfigured) {
|
|
|
- port->link = phydev->link;
|
|
|
-
|
|
|
- if (phydev->link) {
|
|
|
- if (port->phy_interface == PHY_INTERFACE_MODE_RGMII ||
|
|
|
- port->phy_interface == PHY_INTERFACE_MODE_RGMII_ID ||
|
|
|
- port->phy_interface == PHY_INTERFACE_MODE_RGMII_RXID ||
|
|
|
- port->phy_interface == PHY_INTERFACE_MODE_RGMII_TXID ||
|
|
|
- port->phy_interface == PHY_INTERFACE_MODE_SGMII) {
|
|
|
- val = readl(port->base + MVPP2_GMAC_AUTONEG_CONFIG);
|
|
|
- val |= (MVPP2_GMAC_FORCE_LINK_PASS |
|
|
|
- MVPP2_GMAC_FORCE_LINK_DOWN);
|
|
|
- writel(val, port->base + MVPP2_GMAC_AUTONEG_CONFIG);
|
|
|
- }
|
|
|
-
|
|
|
- mvpp2_interrupts_enable(port);
|
|
|
- mvpp2_port_enable(port);
|
|
|
-
|
|
|
- mvpp2_egress_enable(port);
|
|
|
- mvpp2_ingress_enable(port);
|
|
|
- netif_carrier_on(dev);
|
|
|
- netif_tx_wake_all_queues(dev);
|
|
|
- } else {
|
|
|
- port->duplex = -1;
|
|
|
- port->speed = 0;
|
|
|
-
|
|
|
- netif_tx_stop_all_queues(dev);
|
|
|
- netif_carrier_off(dev);
|
|
|
- mvpp2_ingress_disable(port);
|
|
|
- mvpp2_egress_disable(port);
|
|
|
-
|
|
|
- mvpp2_port_disable(port);
|
|
|
- mvpp2_interrupts_disable(port);
|
|
|
- }
|
|
|
-
|
|
|
- phy_print_status(phydev);
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
static void mvpp2_timer_set(struct mvpp2_port_pcpu *port_pcpu)
|
|
|
{
|
|
|
ktime_t interval;
|
|
@@ -7118,11 +6912,29 @@ static int mvpp2_poll(struct napi_struct *napi, int budget)
|
|
|
return rx_done;
|
|
|
}
|
|
|
|
|
|
-/* Set hw internals when starting port */
|
|
|
-static void mvpp2_start_dev(struct mvpp2_port *port)
|
|
|
+static void mvpp22_mode_reconfigure(struct mvpp2_port *port)
|
|
|
{
|
|
|
- struct net_device *ndev = port->dev;
|
|
|
- int i;
|
|
|
+ u32 ctrl3;
|
|
|
+
|
|
|
+ /* comphy reconfiguration */
|
|
|
+ mvpp22_comphy_init(port);
|
|
|
+
|
|
|
+ /* gop reconfiguration */
|
|
|
+ mvpp22_gop_init(port);
|
|
|
+
|
|
|
+ /* Only GOP port 0 has an XLG MAC */
|
|
|
+ if (port->gop_id == 0) {
|
|
|
+ ctrl3 = readl(port->base + MVPP22_XLG_CTRL3_REG);
|
|
|
+ ctrl3 &= ~MVPP22_XLG_CTRL3_MACMODESELECT_MASK;
|
|
|
+
|
|
|
+ if (port->phy_interface == PHY_INTERFACE_MODE_XAUI ||
|
|
|
+ port->phy_interface == PHY_INTERFACE_MODE_10GKR)
|
|
|
+ ctrl3 |= MVPP22_XLG_CTRL3_MACMODESELECT_10G;
|
|
|
+ else
|
|
|
+ ctrl3 |= MVPP22_XLG_CTRL3_MACMODESELECT_GMAC;
|
|
|
+
|
|
|
+ writel(ctrl3, port->base + MVPP22_XLG_CTRL3_REG);
|
|
|
+ }
|
|
|
|
|
|
if (port->gop_id == 0 &&
|
|
|
(port->phy_interface == PHY_INTERFACE_MODE_XAUI ||
|
|
@@ -7130,6 +6942,12 @@ static void mvpp2_start_dev(struct mvpp2_port *port)
|
|
|
mvpp2_xlg_max_rx_size_set(port);
|
|
|
else
|
|
|
mvpp2_gmac_max_rx_size_set(port);
|
|
|
+}
|
|
|
+
|
|
|
+/* Set hw internals when starting port */
|
|
|
+static void mvpp2_start_dev(struct mvpp2_port *port)
|
|
|
+{
|
|
|
+ int i;
|
|
|
|
|
|
mvpp2_txp_max_tx_size_set(port);
|
|
|
|
|
@@ -7139,42 +6957,39 @@ static void mvpp2_start_dev(struct mvpp2_port *port)
|
|
|
/* Enable interrupts on all CPUs */
|
|
|
mvpp2_interrupts_enable(port);
|
|
|
|
|
|
- if (port->priv->hw_version == MVPP22) {
|
|
|
- mvpp22_comphy_init(port);
|
|
|
- mvpp22_gop_init(port);
|
|
|
+ if (port->priv->hw_version == MVPP22)
|
|
|
+ mvpp22_mode_reconfigure(port);
|
|
|
+
|
|
|
+ if (port->phylink) {
|
|
|
+ phylink_start(port->phylink);
|
|
|
+ } else {
|
|
|
+ /* Phylink isn't used as of now for ACPI, so the MAC has to be
|
|
|
+ * configured manually when the interface is started. This will
|
|
|
+ * be removed as soon as the phylink ACPI support lands in.
|
|
|
+ */
|
|
|
+ struct phylink_link_state state = {
|
|
|
+ .interface = port->phy_interface,
|
|
|
+ .link = 1,
|
|
|
+ };
|
|
|
+ mvpp2_mac_config(port->dev, MLO_AN_INBAND, &state);
|
|
|
}
|
|
|
|
|
|
- mvpp2_port_mii_set(port);
|
|
|
- mvpp2_port_enable(port);
|
|
|
- if (ndev->phydev)
|
|
|
- phy_start(ndev->phydev);
|
|
|
netif_tx_start_all_queues(port->dev);
|
|
|
}
|
|
|
|
|
|
/* Set hw internals when stopping port */
|
|
|
static void mvpp2_stop_dev(struct mvpp2_port *port)
|
|
|
{
|
|
|
- struct net_device *ndev = port->dev;
|
|
|
int i;
|
|
|
|
|
|
- /* Stop new packets from arriving to RXQs */
|
|
|
- mvpp2_ingress_disable(port);
|
|
|
-
|
|
|
- mdelay(10);
|
|
|
-
|
|
|
/* Disable interrupts on all CPUs */
|
|
|
mvpp2_interrupts_disable(port);
|
|
|
|
|
|
for (i = 0; i < port->nqvecs; i++)
|
|
|
napi_disable(&port->qvecs[i].napi);
|
|
|
|
|
|
- netif_carrier_off(port->dev);
|
|
|
- netif_tx_stop_all_queues(port->dev);
|
|
|
-
|
|
|
- mvpp2_egress_disable(port);
|
|
|
- mvpp2_port_disable(port);
|
|
|
- if (ndev->phydev)
|
|
|
- phy_stop(ndev->phydev);
|
|
|
+ if (port->phylink)
|
|
|
+ phylink_stop(port->phylink);
|
|
|
phy_power_off(port->comphy);
|
|
|
}
|
|
|
|
|
@@ -7233,40 +7048,6 @@ static void mvpp21_get_mac_address(struct mvpp2_port *port, unsigned char *addr)
|
|
|
addr[5] = (mac_addr_l >> MVPP2_GMAC_SA_LOW_OFFS) & 0xFF;
|
|
|
}
|
|
|
|
|
|
-static int mvpp2_phy_connect(struct mvpp2_port *port)
|
|
|
-{
|
|
|
- struct phy_device *phy_dev;
|
|
|
-
|
|
|
- /* No PHY is attached */
|
|
|
- if (!port->phy_node)
|
|
|
- return 0;
|
|
|
-
|
|
|
- phy_dev = of_phy_connect(port->dev, port->phy_node, mvpp2_link_event, 0,
|
|
|
- port->phy_interface);
|
|
|
- if (!phy_dev) {
|
|
|
- netdev_err(port->dev, "cannot connect to phy\n");
|
|
|
- return -ENODEV;
|
|
|
- }
|
|
|
- phy_dev->supported &= PHY_GBIT_FEATURES;
|
|
|
- phy_dev->advertising = phy_dev->supported;
|
|
|
-
|
|
|
- port->link = 0;
|
|
|
- port->duplex = 0;
|
|
|
- port->speed = 0;
|
|
|
-
|
|
|
- return 0;
|
|
|
-}
|
|
|
-
|
|
|
-static void mvpp2_phy_disconnect(struct mvpp2_port *port)
|
|
|
-{
|
|
|
- struct net_device *ndev = port->dev;
|
|
|
-
|
|
|
- if (!ndev->phydev)
|
|
|
- return;
|
|
|
-
|
|
|
- phy_disconnect(ndev->phydev);
|
|
|
-}
|
|
|
-
|
|
|
static int mvpp2_irqs_init(struct mvpp2_port *port)
|
|
|
{
|
|
|
int err, i;
|
|
@@ -7350,6 +7131,7 @@ static int mvpp2_open(struct net_device *dev)
|
|
|
struct mvpp2 *priv = port->priv;
|
|
|
unsigned char mac_bcast[ETH_ALEN] = {
|
|
|
0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
|
|
|
+ bool valid = false;
|
|
|
int err;
|
|
|
|
|
|
err = mvpp2_prs_mac_da_accept(port, mac_bcast, true);
|
|
@@ -7392,7 +7174,19 @@ static int mvpp2_open(struct net_device *dev)
|
|
|
goto err_cleanup_txqs;
|
|
|
}
|
|
|
|
|
|
- if (priv->hw_version == MVPP22 && !port->phy_node && port->link_irq) {
|
|
|
+ /* Phylink isn't supported yet in ACPI mode */
|
|
|
+ if (port->of_node) {
|
|
|
+ err = phylink_of_phy_connect(port->phylink, port->of_node, 0);
|
|
|
+ if (err) {
|
|
|
+ netdev_err(port->dev, "could not attach PHY (%d)\n",
|
|
|
+ err);
|
|
|
+ goto err_free_irq;
|
|
|
+ }
|
|
|
+
|
|
|
+ valid = true;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (priv->hw_version == MVPP22 && port->link_irq && !port->phylink) {
|
|
|
err = request_irq(port->link_irq, mvpp2_link_status_isr, 0,
|
|
|
dev->name, port);
|
|
|
if (err) {
|
|
@@ -7402,14 +7196,20 @@ static int mvpp2_open(struct net_device *dev)
|
|
|
}
|
|
|
|
|
|
mvpp22_gop_setup_irq(port);
|
|
|
- }
|
|
|
|
|
|
- /* In default link is down */
|
|
|
- netif_carrier_off(port->dev);
|
|
|
+ /* In default link is down */
|
|
|
+ netif_carrier_off(port->dev);
|
|
|
|
|
|
- err = mvpp2_phy_connect(port);
|
|
|
- if (err < 0)
|
|
|
- goto err_free_link_irq;
|
|
|
+ valid = true;
|
|
|
+ } else {
|
|
|
+ port->link_irq = 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!valid) {
|
|
|
+ netdev_err(port->dev,
|
|
|
+ "invalid configuration: no dt or link IRQ");
|
|
|
+ goto err_free_irq;
|
|
|
+ }
|
|
|
|
|
|
/* Unmask interrupts on all CPUs */
|
|
|
on_each_cpu(mvpp2_interrupts_unmask, port, 1);
|
|
@@ -7426,9 +7226,6 @@ static int mvpp2_open(struct net_device *dev)
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
-err_free_link_irq:
|
|
|
- if (priv->hw_version == MVPP22 && !port->phy_node && port->link_irq)
|
|
|
- free_irq(port->link_irq, port);
|
|
|
err_free_irq:
|
|
|
mvpp2_irqs_deinit(port);
|
|
|
err_cleanup_txqs:
|
|
@@ -7442,17 +7239,17 @@ static int mvpp2_stop(struct net_device *dev)
|
|
|
{
|
|
|
struct mvpp2_port *port = netdev_priv(dev);
|
|
|
struct mvpp2_port_pcpu *port_pcpu;
|
|
|
- struct mvpp2 *priv = port->priv;
|
|
|
int cpu;
|
|
|
|
|
|
mvpp2_stop_dev(port);
|
|
|
- mvpp2_phy_disconnect(port);
|
|
|
|
|
|
/* Mask interrupts on all CPUs */
|
|
|
on_each_cpu(mvpp2_interrupts_mask, port, 1);
|
|
|
mvpp2_shared_interrupt_mask_unmask(port, true);
|
|
|
|
|
|
- if (priv->hw_version == MVPP22 && !port->phy_node && port->link_irq)
|
|
|
+ if (port->phylink)
|
|
|
+ phylink_disconnect_phy(port->phylink);
|
|
|
+ if (port->link_irq)
|
|
|
free_irq(port->link_irq, port);
|
|
|
|
|
|
mvpp2_irqs_deinit(port);
|
|
@@ -7658,16 +7455,12 @@ mvpp2_get_stats64(struct net_device *dev, struct rtnl_link_stats64 *stats)
|
|
|
|
|
|
static int mvpp2_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
|
|
|
{
|
|
|
- int ret;
|
|
|
+ struct mvpp2_port *port = netdev_priv(dev);
|
|
|
|
|
|
- if (!dev->phydev)
|
|
|
+ if (!port->phylink)
|
|
|
return -ENOTSUPP;
|
|
|
|
|
|
- ret = phy_mii_ioctl(dev->phydev, ifr, cmd);
|
|
|
- if (!ret)
|
|
|
- mvpp2_link_event(dev);
|
|
|
-
|
|
|
- return ret;
|
|
|
+ return phylink_mii_ioctl(port->phylink, ifr, cmd);
|
|
|
}
|
|
|
|
|
|
static int mvpp2_vlan_rx_add_vid(struct net_device *dev, __be16 proto, u16 vid)
|
|
@@ -7714,6 +7507,16 @@ static int mvpp2_set_features(struct net_device *dev,
|
|
|
|
|
|
/* Ethtool methods */
|
|
|
|
|
|
+static int mvpp2_ethtool_nway_reset(struct net_device *dev)
|
|
|
+{
|
|
|
+ struct mvpp2_port *port = netdev_priv(dev);
|
|
|
+
|
|
|
+ if (!port->phylink)
|
|
|
+ return -ENOTSUPP;
|
|
|
+
|
|
|
+ return phylink_ethtool_nway_reset(port->phylink);
|
|
|
+}
|
|
|
+
|
|
|
/* Set interrupt coalescing for ethtools */
|
|
|
static int mvpp2_ethtool_set_coalesce(struct net_device *dev,
|
|
|
struct ethtool_coalesce *c)
|
|
@@ -7842,6 +7645,50 @@ err_out:
|
|
|
return err;
|
|
|
}
|
|
|
|
|
|
+static void mvpp2_ethtool_get_pause_param(struct net_device *dev,
|
|
|
+ struct ethtool_pauseparam *pause)
|
|
|
+{
|
|
|
+ struct mvpp2_port *port = netdev_priv(dev);
|
|
|
+
|
|
|
+ if (!port->phylink)
|
|
|
+ return;
|
|
|
+
|
|
|
+ phylink_ethtool_get_pauseparam(port->phylink, pause);
|
|
|
+}
|
|
|
+
|
|
|
+static int mvpp2_ethtool_set_pause_param(struct net_device *dev,
|
|
|
+ struct ethtool_pauseparam *pause)
|
|
|
+{
|
|
|
+ struct mvpp2_port *port = netdev_priv(dev);
|
|
|
+
|
|
|
+ if (!port->phylink)
|
|
|
+ return -ENOTSUPP;
|
|
|
+
|
|
|
+ return phylink_ethtool_set_pauseparam(port->phylink, pause);
|
|
|
+}
|
|
|
+
|
|
|
+static int mvpp2_ethtool_get_link_ksettings(struct net_device *dev,
|
|
|
+ struct ethtool_link_ksettings *cmd)
|
|
|
+{
|
|
|
+ struct mvpp2_port *port = netdev_priv(dev);
|
|
|
+
|
|
|
+ if (!port->phylink)
|
|
|
+ return -ENOTSUPP;
|
|
|
+
|
|
|
+ return phylink_ethtool_ksettings_get(port->phylink, cmd);
|
|
|
+}
|
|
|
+
|
|
|
+static int mvpp2_ethtool_set_link_ksettings(struct net_device *dev,
|
|
|
+ const struct ethtool_link_ksettings *cmd)
|
|
|
+{
|
|
|
+ struct mvpp2_port *port = netdev_priv(dev);
|
|
|
+
|
|
|
+ if (!port->phylink)
|
|
|
+ return -ENOTSUPP;
|
|
|
+
|
|
|
+ return phylink_ethtool_ksettings_set(port->phylink, cmd);
|
|
|
+}
|
|
|
+
|
|
|
/* Device ops */
|
|
|
|
|
|
static const struct net_device_ops mvpp2_netdev_ops = {
|
|
@@ -7859,7 +7706,7 @@ static const struct net_device_ops mvpp2_netdev_ops = {
|
|
|
};
|
|
|
|
|
|
static const struct ethtool_ops mvpp2_eth_tool_ops = {
|
|
|
- .nway_reset = phy_ethtool_nway_reset,
|
|
|
+ .nway_reset = mvpp2_ethtool_nway_reset,
|
|
|
.get_link = ethtool_op_get_link,
|
|
|
.set_coalesce = mvpp2_ethtool_set_coalesce,
|
|
|
.get_coalesce = mvpp2_ethtool_get_coalesce,
|
|
@@ -7869,8 +7716,10 @@ static const struct ethtool_ops mvpp2_eth_tool_ops = {
|
|
|
.get_strings = mvpp2_ethtool_get_strings,
|
|
|
.get_ethtool_stats = mvpp2_ethtool_get_stats,
|
|
|
.get_sset_count = mvpp2_ethtool_get_sset_count,
|
|
|
- .get_link_ksettings = phy_ethtool_get_link_ksettings,
|
|
|
- .set_link_ksettings = phy_ethtool_set_link_ksettings,
|
|
|
+ .get_pauseparam = mvpp2_ethtool_get_pause_param,
|
|
|
+ .set_pauseparam = mvpp2_ethtool_set_pause_param,
|
|
|
+ .get_link_ksettings = mvpp2_ethtool_get_link_ksettings,
|
|
|
+ .set_link_ksettings = mvpp2_ethtool_set_link_ksettings,
|
|
|
};
|
|
|
|
|
|
/* Used for PPv2.1, or PPv2.2 with the old Device Tree binding that
|
|
@@ -8172,18 +8021,330 @@ static void mvpp2_port_copy_mac_addr(struct net_device *dev, struct mvpp2 *priv,
|
|
|
eth_hw_addr_random(dev);
|
|
|
}
|
|
|
|
|
|
+static void mvpp2_phylink_validate(struct net_device *dev,
|
|
|
+ unsigned long *supported,
|
|
|
+ struct phylink_link_state *state)
|
|
|
+{
|
|
|
+ __ETHTOOL_DECLARE_LINK_MODE_MASK(mask) = { 0, };
|
|
|
+
|
|
|
+ phylink_set(mask, Autoneg);
|
|
|
+ phylink_set_port_modes(mask);
|
|
|
+ phylink_set(mask, Pause);
|
|
|
+ phylink_set(mask, Asym_Pause);
|
|
|
+
|
|
|
+ phylink_set(mask, 10baseT_Half);
|
|
|
+ phylink_set(mask, 10baseT_Full);
|
|
|
+ phylink_set(mask, 100baseT_Half);
|
|
|
+ phylink_set(mask, 100baseT_Full);
|
|
|
+ phylink_set(mask, 1000baseT_Full);
|
|
|
+ phylink_set(mask, 10000baseT_Full);
|
|
|
+
|
|
|
+ if (state->interface == PHY_INTERFACE_MODE_10GKR) {
|
|
|
+ phylink_set(mask, 10000baseCR_Full);
|
|
|
+ phylink_set(mask, 10000baseSR_Full);
|
|
|
+ phylink_set(mask, 10000baseLR_Full);
|
|
|
+ phylink_set(mask, 10000baseLRM_Full);
|
|
|
+ phylink_set(mask, 10000baseER_Full);
|
|
|
+ phylink_set(mask, 10000baseKR_Full);
|
|
|
+ }
|
|
|
+
|
|
|
+ bitmap_and(supported, supported, mask, __ETHTOOL_LINK_MODE_MASK_NBITS);
|
|
|
+ bitmap_and(state->advertising, state->advertising, mask,
|
|
|
+ __ETHTOOL_LINK_MODE_MASK_NBITS);
|
|
|
+}
|
|
|
+
|
|
|
+static void mvpp22_xlg_link_state(struct mvpp2_port *port,
|
|
|
+ struct phylink_link_state *state)
|
|
|
+{
|
|
|
+ u32 val;
|
|
|
+
|
|
|
+ state->speed = SPEED_10000;
|
|
|
+ state->duplex = 1;
|
|
|
+ state->an_complete = 1;
|
|
|
+
|
|
|
+ val = readl(port->base + MVPP22_XLG_STATUS);
|
|
|
+ state->link = !!(val & MVPP22_XLG_STATUS_LINK_UP);
|
|
|
+
|
|
|
+ state->pause = 0;
|
|
|
+ val = readl(port->base + MVPP22_XLG_CTRL0_REG);
|
|
|
+ if (val & MVPP22_XLG_CTRL0_TX_FLOW_CTRL_EN)
|
|
|
+ state->pause |= MLO_PAUSE_TX;
|
|
|
+ if (val & MVPP22_XLG_CTRL0_RX_FLOW_CTRL_EN)
|
|
|
+ state->pause |= MLO_PAUSE_RX;
|
|
|
+}
|
|
|
+
|
|
|
+static void mvpp2_gmac_link_state(struct mvpp2_port *port,
|
|
|
+ struct phylink_link_state *state)
|
|
|
+{
|
|
|
+ u32 val;
|
|
|
+
|
|
|
+ val = readl(port->base + MVPP2_GMAC_STATUS0);
|
|
|
+
|
|
|
+ state->an_complete = !!(val & MVPP2_GMAC_STATUS0_AN_COMPLETE);
|
|
|
+ state->link = !!(val & MVPP2_GMAC_STATUS0_LINK_UP);
|
|
|
+ state->duplex = !!(val & MVPP2_GMAC_STATUS0_FULL_DUPLEX);
|
|
|
+
|
|
|
+ if (val & MVPP2_GMAC_STATUS0_GMII_SPEED)
|
|
|
+ state->speed = SPEED_1000;
|
|
|
+ else if (val & MVPP2_GMAC_STATUS0_MII_SPEED)
|
|
|
+ state->speed = SPEED_100;
|
|
|
+ else
|
|
|
+ state->speed = SPEED_10;
|
|
|
+
|
|
|
+ state->pause = 0;
|
|
|
+ if (val & MVPP2_GMAC_STATUS0_RX_PAUSE)
|
|
|
+ state->pause |= MLO_PAUSE_RX;
|
|
|
+ if (val & MVPP2_GMAC_STATUS0_TX_PAUSE)
|
|
|
+ state->pause |= MLO_PAUSE_TX;
|
|
|
+}
|
|
|
+
|
|
|
+static int mvpp2_phylink_mac_link_state(struct net_device *dev,
|
|
|
+ struct phylink_link_state *state)
|
|
|
+{
|
|
|
+ struct mvpp2_port *port = netdev_priv(dev);
|
|
|
+
|
|
|
+ if (port->priv->hw_version == MVPP22 && port->gop_id == 0) {
|
|
|
+ u32 mode = readl(port->base + MVPP22_XLG_CTRL3_REG);
|
|
|
+ mode &= MVPP22_XLG_CTRL3_MACMODESELECT_MASK;
|
|
|
+
|
|
|
+ if (mode == MVPP22_XLG_CTRL3_MACMODESELECT_10G) {
|
|
|
+ mvpp22_xlg_link_state(port, state);
|
|
|
+ return 1;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ mvpp2_gmac_link_state(port, state);
|
|
|
+ return 1;
|
|
|
+}
|
|
|
+
|
|
|
+static void mvpp2_mac_an_restart(struct net_device *dev)
|
|
|
+{
|
|
|
+ struct mvpp2_port *port = netdev_priv(dev);
|
|
|
+ u32 val;
|
|
|
+
|
|
|
+ if (port->phy_interface != PHY_INTERFACE_MODE_SGMII)
|
|
|
+ return;
|
|
|
+
|
|
|
+ val = readl(port->base + MVPP2_GMAC_AUTONEG_CONFIG);
|
|
|
+ /* The RESTART_AN bit is cleared by the h/w after restarting the AN
|
|
|
+ * process.
|
|
|
+ */
|
|
|
+ val |= MVPP2_GMAC_IN_BAND_RESTART_AN | MVPP2_GMAC_IN_BAND_AUTONEG;
|
|
|
+ writel(val, port->base + MVPP2_GMAC_AUTONEG_CONFIG);
|
|
|
+}
|
|
|
+
|
|
|
+static void mvpp2_xlg_config(struct mvpp2_port *port, unsigned int mode,
|
|
|
+ const struct phylink_link_state *state)
|
|
|
+{
|
|
|
+ u32 ctrl0, ctrl4;
|
|
|
+
|
|
|
+ ctrl0 = readl(port->base + MVPP22_XLG_CTRL0_REG);
|
|
|
+ ctrl4 = readl(port->base + MVPP22_XLG_CTRL4_REG);
|
|
|
+
|
|
|
+ if (state->pause & MLO_PAUSE_TX)
|
|
|
+ ctrl0 |= MVPP22_XLG_CTRL0_TX_FLOW_CTRL_EN;
|
|
|
+ if (state->pause & MLO_PAUSE_RX)
|
|
|
+ ctrl0 |= MVPP22_XLG_CTRL0_RX_FLOW_CTRL_EN;
|
|
|
+
|
|
|
+ ctrl4 &= ~MVPP22_XLG_CTRL4_MACMODSELECT_GMAC;
|
|
|
+ ctrl4 |= MVPP22_XLG_CTRL4_FWD_FC | MVPP22_XLG_CTRL4_FWD_PFC |
|
|
|
+ MVPP22_XLG_CTRL4_EN_IDLE_CHECK;
|
|
|
+
|
|
|
+ writel(ctrl0, port->base + MVPP22_XLG_CTRL0_REG);
|
|
|
+ writel(ctrl4, port->base + MVPP22_XLG_CTRL4_REG);
|
|
|
+}
|
|
|
+
|
|
|
+static void mvpp2_gmac_config(struct mvpp2_port *port, unsigned int mode,
|
|
|
+ const struct phylink_link_state *state)
|
|
|
+{
|
|
|
+ u32 an, ctrl0, ctrl2, ctrl4;
|
|
|
+
|
|
|
+ an = readl(port->base + MVPP2_GMAC_AUTONEG_CONFIG);
|
|
|
+ ctrl0 = readl(port->base + MVPP2_GMAC_CTRL_0_REG);
|
|
|
+ ctrl2 = readl(port->base + MVPP2_GMAC_CTRL_2_REG);
|
|
|
+ ctrl4 = readl(port->base + MVPP22_GMAC_CTRL_4_REG);
|
|
|
+
|
|
|
+ /* Force link down */
|
|
|
+ an &= ~MVPP2_GMAC_FORCE_LINK_PASS;
|
|
|
+ an |= MVPP2_GMAC_FORCE_LINK_DOWN;
|
|
|
+ writel(an, port->base + MVPP2_GMAC_AUTONEG_CONFIG);
|
|
|
+
|
|
|
+ /* Set the GMAC in a reset state */
|
|
|
+ ctrl2 |= MVPP2_GMAC_PORT_RESET_MASK;
|
|
|
+ writel(ctrl2, port->base + MVPP2_GMAC_CTRL_2_REG);
|
|
|
+
|
|
|
+ an &= ~(MVPP2_GMAC_CONFIG_MII_SPEED | MVPP2_GMAC_CONFIG_GMII_SPEED |
|
|
|
+ MVPP2_GMAC_AN_SPEED_EN | MVPP2_GMAC_FC_ADV_EN |
|
|
|
+ MVPP2_GMAC_FC_ADV_ASM_EN | MVPP2_GMAC_FLOW_CTRL_AUTONEG |
|
|
|
+ MVPP2_GMAC_CONFIG_FULL_DUPLEX | MVPP2_GMAC_AN_DUPLEX_EN |
|
|
|
+ MVPP2_GMAC_FORCE_LINK_DOWN);
|
|
|
+ ctrl0 &= ~MVPP2_GMAC_PORT_TYPE_MASK;
|
|
|
+ ctrl2 &= ~(MVPP2_GMAC_PORT_RESET_MASK | MVPP2_GMAC_PCS_ENABLE_MASK);
|
|
|
+
|
|
|
+ if (!phy_interface_mode_is_rgmii(state->interface))
|
|
|
+ an |= MVPP2_GMAC_AN_SPEED_EN | MVPP2_GMAC_FLOW_CTRL_AUTONEG;
|
|
|
+
|
|
|
+ if (state->duplex)
|
|
|
+ an |= MVPP2_GMAC_CONFIG_FULL_DUPLEX;
|
|
|
+ if (phylink_test(state->advertising, Pause))
|
|
|
+ an |= MVPP2_GMAC_FC_ADV_EN;
|
|
|
+ if (phylink_test(state->advertising, Asym_Pause))
|
|
|
+ an |= MVPP2_GMAC_FC_ADV_ASM_EN;
|
|
|
+
|
|
|
+ if (state->interface == PHY_INTERFACE_MODE_SGMII) {
|
|
|
+ an |= MVPP2_GMAC_IN_BAND_AUTONEG;
|
|
|
+ ctrl2 |= MVPP2_GMAC_INBAND_AN_MASK | MVPP2_GMAC_PCS_ENABLE_MASK;
|
|
|
+
|
|
|
+ ctrl4 &= ~(MVPP22_CTRL4_EXT_PIN_GMII_SEL |
|
|
|
+ MVPP22_CTRL4_RX_FC_EN | MVPP22_CTRL4_TX_FC_EN);
|
|
|
+ ctrl4 |= MVPP22_CTRL4_SYNC_BYPASS_DIS |
|
|
|
+ MVPP22_CTRL4_DP_CLK_SEL |
|
|
|
+ MVPP22_CTRL4_QSGMII_BYPASS_ACTIVE;
|
|
|
+
|
|
|
+ if (state->pause & MLO_PAUSE_TX)
|
|
|
+ ctrl4 |= MVPP22_CTRL4_TX_FC_EN;
|
|
|
+ if (state->pause & MLO_PAUSE_RX)
|
|
|
+ ctrl4 |= MVPP22_CTRL4_RX_FC_EN;
|
|
|
+ } else if (phy_interface_mode_is_rgmii(state->interface)) {
|
|
|
+ an |= MVPP2_GMAC_IN_BAND_AUTONEG_BYPASS;
|
|
|
+
|
|
|
+ if (state->speed == SPEED_1000)
|
|
|
+ an |= MVPP2_GMAC_CONFIG_GMII_SPEED;
|
|
|
+ else if (state->speed == SPEED_100)
|
|
|
+ an |= MVPP2_GMAC_CONFIG_MII_SPEED;
|
|
|
+
|
|
|
+ ctrl4 &= ~MVPP22_CTRL4_DP_CLK_SEL;
|
|
|
+ ctrl4 |= MVPP22_CTRL4_EXT_PIN_GMII_SEL |
|
|
|
+ MVPP22_CTRL4_SYNC_BYPASS_DIS |
|
|
|
+ MVPP22_CTRL4_QSGMII_BYPASS_ACTIVE;
|
|
|
+ }
|
|
|
+
|
|
|
+ writel(ctrl0, port->base + MVPP2_GMAC_CTRL_0_REG);
|
|
|
+ writel(ctrl2, port->base + MVPP2_GMAC_CTRL_2_REG);
|
|
|
+ writel(ctrl4, port->base + MVPP22_GMAC_CTRL_4_REG);
|
|
|
+ writel(an, port->base + MVPP2_GMAC_AUTONEG_CONFIG);
|
|
|
+}
|
|
|
+
|
|
|
+static void mvpp2_mac_config(struct net_device *dev, unsigned int mode,
|
|
|
+ const struct phylink_link_state *state)
|
|
|
+{
|
|
|
+ struct mvpp2_port *port = netdev_priv(dev);
|
|
|
+
|
|
|
+ /* Check for invalid configuration */
|
|
|
+ if (state->interface == PHY_INTERFACE_MODE_10GKR && port->gop_id != 0) {
|
|
|
+ netdev_err(dev, "Invalid mode on %s\n", dev->name);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ netif_tx_stop_all_queues(port->dev);
|
|
|
+ if (!port->has_phy)
|
|
|
+ netif_carrier_off(port->dev);
|
|
|
+
|
|
|
+ /* Make sure the port is disabled when reconfiguring the mode */
|
|
|
+ mvpp2_port_disable(port);
|
|
|
+
|
|
|
+ if (port->priv->hw_version == MVPP22 &&
|
|
|
+ port->phy_interface != state->interface) {
|
|
|
+ port->phy_interface = state->interface;
|
|
|
+
|
|
|
+ /* Reconfigure the serdes lanes */
|
|
|
+ phy_power_off(port->comphy);
|
|
|
+ mvpp22_mode_reconfigure(port);
|
|
|
+ }
|
|
|
+
|
|
|
+ /* mac (re)configuration */
|
|
|
+ if (state->interface == PHY_INTERFACE_MODE_10GKR)
|
|
|
+ mvpp2_xlg_config(port, mode, state);
|
|
|
+ else if (phy_interface_mode_is_rgmii(state->interface) ||
|
|
|
+ state->interface == PHY_INTERFACE_MODE_SGMII)
|
|
|
+ mvpp2_gmac_config(port, mode, state);
|
|
|
+
|
|
|
+ if (port->priv->hw_version == MVPP21 && port->flags & MVPP2_F_LOOPBACK)
|
|
|
+ mvpp2_port_loopback_set(port, state);
|
|
|
+
|
|
|
+ /* If the port already was up, make sure it's still in the same state */
|
|
|
+ if (state->link || !port->has_phy) {
|
|
|
+ mvpp2_port_enable(port);
|
|
|
+
|
|
|
+ mvpp2_egress_enable(port);
|
|
|
+ mvpp2_ingress_enable(port);
|
|
|
+ if (!port->has_phy)
|
|
|
+ netif_carrier_on(dev);
|
|
|
+ netif_tx_wake_all_queues(dev);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+static void mvpp2_mac_link_up(struct net_device *dev, unsigned int mode,
|
|
|
+ phy_interface_t interface, struct phy_device *phy)
|
|
|
+{
|
|
|
+ struct mvpp2_port *port = netdev_priv(dev);
|
|
|
+ u32 val;
|
|
|
+
|
|
|
+ if (!phylink_autoneg_inband(mode) &&
|
|
|
+ interface != PHY_INTERFACE_MODE_10GKR) {
|
|
|
+ val = readl(port->base + MVPP2_GMAC_AUTONEG_CONFIG);
|
|
|
+ val &= ~MVPP2_GMAC_FORCE_LINK_DOWN;
|
|
|
+ if (phy_interface_mode_is_rgmii(interface))
|
|
|
+ val |= MVPP2_GMAC_FORCE_LINK_PASS;
|
|
|
+ writel(val, port->base + MVPP2_GMAC_AUTONEG_CONFIG);
|
|
|
+ }
|
|
|
+
|
|
|
+ mvpp2_port_enable(port);
|
|
|
+
|
|
|
+ mvpp2_egress_enable(port);
|
|
|
+ mvpp2_ingress_enable(port);
|
|
|
+ netif_tx_wake_all_queues(dev);
|
|
|
+}
|
|
|
+
|
|
|
+static void mvpp2_mac_link_down(struct net_device *dev, unsigned int mode,
|
|
|
+ phy_interface_t interface)
|
|
|
+{
|
|
|
+ struct mvpp2_port *port = netdev_priv(dev);
|
|
|
+ u32 val;
|
|
|
+
|
|
|
+ if (!phylink_autoneg_inband(mode) &&
|
|
|
+ interface != PHY_INTERFACE_MODE_10GKR) {
|
|
|
+ val = readl(port->base + MVPP2_GMAC_AUTONEG_CONFIG);
|
|
|
+ val &= ~MVPP2_GMAC_FORCE_LINK_PASS;
|
|
|
+ val |= MVPP2_GMAC_FORCE_LINK_DOWN;
|
|
|
+ writel(val, port->base + MVPP2_GMAC_AUTONEG_CONFIG);
|
|
|
+ }
|
|
|
+
|
|
|
+ netif_tx_stop_all_queues(dev);
|
|
|
+ mvpp2_egress_disable(port);
|
|
|
+ mvpp2_ingress_disable(port);
|
|
|
+
|
|
|
+ /* When using link interrupts to notify phylink of a MAC state change,
|
|
|
+ * we do not want the port to be disabled (we want to receive further
|
|
|
+ * interrupts, to be notified when the port will have a link later).
|
|
|
+ */
|
|
|
+ if (!port->has_phy)
|
|
|
+ return;
|
|
|
+
|
|
|
+ mvpp2_port_disable(port);
|
|
|
+}
|
|
|
+
|
|
|
+static const struct phylink_mac_ops mvpp2_phylink_ops = {
|
|
|
+ .validate = mvpp2_phylink_validate,
|
|
|
+ .mac_link_state = mvpp2_phylink_mac_link_state,
|
|
|
+ .mac_an_restart = mvpp2_mac_an_restart,
|
|
|
+ .mac_config = mvpp2_mac_config,
|
|
|
+ .mac_link_up = mvpp2_mac_link_up,
|
|
|
+ .mac_link_down = mvpp2_mac_link_down,
|
|
|
+};
|
|
|
+
|
|
|
/* Ports initialization */
|
|
|
static int mvpp2_port_probe(struct platform_device *pdev,
|
|
|
struct fwnode_handle *port_fwnode,
|
|
|
struct mvpp2 *priv)
|
|
|
{
|
|
|
- struct device_node *phy_node;
|
|
|
struct phy *comphy = NULL;
|
|
|
struct mvpp2_port *port;
|
|
|
struct mvpp2_port_pcpu *port_pcpu;
|
|
|
struct device_node *port_node = to_of_node(port_fwnode);
|
|
|
struct net_device *dev;
|
|
|
struct resource *res;
|
|
|
+ struct phylink *phylink;
|
|
|
char *mac_from = "";
|
|
|
unsigned int ntxqs, nrxqs;
|
|
|
bool has_tx_irqs;
|
|
@@ -8212,11 +8373,6 @@ static int mvpp2_port_probe(struct platform_device *pdev,
|
|
|
if (!dev)
|
|
|
return -ENOMEM;
|
|
|
|
|
|
- if (port_node)
|
|
|
- phy_node = of_parse_phandle(port_node, "phy", 0);
|
|
|
- else
|
|
|
- phy_node = NULL;
|
|
|
-
|
|
|
phy_mode = fwnode_get_phy_mode(port_fwnode);
|
|
|
if (phy_mode < 0) {
|
|
|
dev_err(&pdev->dev, "incorrect phy mode\n");
|
|
@@ -8249,6 +8405,7 @@ static int mvpp2_port_probe(struct platform_device *pdev,
|
|
|
port = netdev_priv(dev);
|
|
|
port->dev = dev;
|
|
|
port->fwnode = port_fwnode;
|
|
|
+ port->has_phy = !!of_find_property(port_node, "phy", NULL);
|
|
|
port->ntxqs = ntxqs;
|
|
|
port->nrxqs = nrxqs;
|
|
|
port->priv = priv;
|
|
@@ -8279,7 +8436,7 @@ static int mvpp2_port_probe(struct platform_device *pdev,
|
|
|
else
|
|
|
port->first_rxq = port->id * priv->max_port_rxqs;
|
|
|
|
|
|
- port->phy_node = phy_node;
|
|
|
+ port->of_node = port_node;
|
|
|
port->phy_interface = phy_mode;
|
|
|
port->comphy = comphy;
|
|
|
|
|
@@ -8340,9 +8497,6 @@ static int mvpp2_port_probe(struct platform_device *pdev,
|
|
|
|
|
|
mvpp2_port_periodic_xon_disable(port);
|
|
|
|
|
|
- if (priv->hw_version == MVPP21)
|
|
|
- mvpp2_port_fc_adv_enable(port);
|
|
|
-
|
|
|
mvpp2_port_reset(port);
|
|
|
|
|
|
port->pcpu = alloc_percpu(struct mvpp2_port_pcpu);
|
|
@@ -8386,10 +8540,23 @@ static int mvpp2_port_probe(struct platform_device *pdev,
|
|
|
/* 9704 == 9728 - 20 and rounding to 8 */
|
|
|
dev->max_mtu = MVPP2_BM_JUMBO_PKT_SIZE;
|
|
|
|
|
|
+ /* Phylink isn't used w/ ACPI as of now */
|
|
|
+ if (port_node) {
|
|
|
+ phylink = phylink_create(dev, port_fwnode, phy_mode,
|
|
|
+ &mvpp2_phylink_ops);
|
|
|
+ if (IS_ERR(phylink)) {
|
|
|
+ err = PTR_ERR(phylink);
|
|
|
+ goto err_free_port_pcpu;
|
|
|
+ }
|
|
|
+ port->phylink = phylink;
|
|
|
+ } else {
|
|
|
+ port->phylink = NULL;
|
|
|
+ }
|
|
|
+
|
|
|
err = register_netdev(dev);
|
|
|
if (err < 0) {
|
|
|
dev_err(&pdev->dev, "failed to register netdev\n");
|
|
|
- goto err_free_port_pcpu;
|
|
|
+ goto err_phylink;
|
|
|
}
|
|
|
netdev_info(dev, "Using %s mac address %pM\n", mac_from, dev->dev_addr);
|
|
|
|
|
@@ -8397,6 +8564,9 @@ static int mvpp2_port_probe(struct platform_device *pdev,
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
+err_phylink:
|
|
|
+ if (port->phylink)
|
|
|
+ phylink_destroy(port->phylink);
|
|
|
err_free_port_pcpu:
|
|
|
free_percpu(port->pcpu);
|
|
|
err_free_txq_pcpu:
|
|
@@ -8410,7 +8580,6 @@ err_free_irq:
|
|
|
err_deinit_qvecs:
|
|
|
mvpp2_queue_vectors_deinit(port);
|
|
|
err_free_netdev:
|
|
|
- of_node_put(phy_node);
|
|
|
free_netdev(dev);
|
|
|
return err;
|
|
|
}
|
|
@@ -8421,7 +8590,8 @@ static void mvpp2_port_remove(struct mvpp2_port *port)
|
|
|
int i;
|
|
|
|
|
|
unregister_netdev(port->dev);
|
|
|
- of_node_put(port->phy_node);
|
|
|
+ if (port->phylink)
|
|
|
+ phylink_destroy(port->phylink);
|
|
|
free_percpu(port->pcpu);
|
|
|
free_percpu(port->stats);
|
|
|
for (i = 0; i < port->ntxqs; i++)
|