net: mscc: ocelot: transmit the "native VLAN" error via extack
[platform/kernel/linux-rpi.git] / drivers / net / ethernet / mscc / ocelot.c
index adfb978..ccb8a98 100644 (file)
@@ -259,16 +259,15 @@ int ocelot_port_vlan_filtering(struct ocelot *ocelot, int port,
 EXPORT_SYMBOL(ocelot_port_vlan_filtering);
 
 int ocelot_vlan_prepare(struct ocelot *ocelot, int port, u16 vid, bool pvid,
-                       bool untagged)
+                       bool untagged, struct netlink_ext_ack *extack)
 {
        struct ocelot_port *ocelot_port = ocelot->ports[port];
 
        /* Deny changing the native VLAN, but always permit deleting it */
        if (untagged && ocelot_port->native_vlan.vid != vid &&
            ocelot_port->native_vlan.valid) {
-               dev_err(ocelot->dev,
-                       "Port already has a native VLAN: %d\n",
-                       ocelot_port->native_vlan.vid);
+               NL_SET_ERR_MSG_MOD(extack,
+                                  "Port already has a native VLAN");
                return -EBUSY;
        }
 
@@ -377,7 +376,7 @@ static u32 ocelot_read_eq_avail(struct ocelot *ocelot, int port)
        return ocelot_read_rix(ocelot, QSYS_SW_STATUS, port);
 }
 
-int ocelot_port_flush(struct ocelot *ocelot, int port)
+static int ocelot_port_flush(struct ocelot *ocelot, int port)
 {
        unsigned int pause_ena;
        int err, val;
@@ -429,63 +428,118 @@ int ocelot_port_flush(struct ocelot *ocelot, int port)
 
        return err;
 }
-EXPORT_SYMBOL(ocelot_port_flush);
 
-void ocelot_adjust_link(struct ocelot *ocelot, int port,
-                       struct phy_device *phydev)
+void ocelot_phylink_mac_link_down(struct ocelot *ocelot, int port,
+                                 unsigned int link_an_mode,
+                                 phy_interface_t interface,
+                                 unsigned long quirks)
+{
+       struct ocelot_port *ocelot_port = ocelot->ports[port];
+       int err;
+
+       ocelot_port_rmwl(ocelot_port, 0, DEV_MAC_ENA_CFG_RX_ENA,
+                        DEV_MAC_ENA_CFG);
+
+       ocelot_fields_write(ocelot, port, QSYS_SWITCH_PORT_MODE_PORT_ENA, 0);
+
+       err = ocelot_port_flush(ocelot, port);
+       if (err)
+               dev_err(ocelot->dev, "failed to flush port %d: %d\n",
+                       port, err);
+
+       /* Put the port in reset. */
+       if (interface != PHY_INTERFACE_MODE_QSGMII ||
+           !(quirks & OCELOT_QUIRK_QSGMII_PORTS_MUST_BE_UP))
+               ocelot_port_rmwl(ocelot_port,
+                                DEV_CLOCK_CFG_MAC_TX_RST |
+                                DEV_CLOCK_CFG_MAC_TX_RST,
+                                DEV_CLOCK_CFG_MAC_TX_RST |
+                                DEV_CLOCK_CFG_MAC_TX_RST,
+                                DEV_CLOCK_CFG);
+}
+EXPORT_SYMBOL_GPL(ocelot_phylink_mac_link_down);
+
+void ocelot_phylink_mac_link_up(struct ocelot *ocelot, int port,
+                               struct phy_device *phydev,
+                               unsigned int link_an_mode,
+                               phy_interface_t interface,
+                               int speed, int duplex,
+                               bool tx_pause, bool rx_pause,
+                               unsigned long quirks)
 {
        struct ocelot_port *ocelot_port = ocelot->ports[port];
-       int speed, mode = 0;
+       int mac_speed, mode = 0;
+       u32 mac_fc_cfg;
 
-       switch (phydev->speed) {
+       /* The MAC might be integrated in systems where the MAC speed is fixed
+        * and it's the PCS who is performing the rate adaptation, so we have
+        * to write "1000Mbps" into the LINK_SPEED field of DEV_CLOCK_CFG
+        * (which is also its default value).
+        */
+       if ((quirks & OCELOT_QUIRK_PCS_PERFORMS_RATE_ADAPTATION) ||
+           speed == SPEED_1000) {
+               mac_speed = OCELOT_SPEED_1000;
+               mode = DEV_MAC_MODE_CFG_GIGA_MODE_ENA;
+       } else if (speed == SPEED_2500) {
+               mac_speed = OCELOT_SPEED_2500;
+               mode = DEV_MAC_MODE_CFG_GIGA_MODE_ENA;
+       } else if (speed == SPEED_100) {
+               mac_speed = OCELOT_SPEED_100;
+       } else {
+               mac_speed = OCELOT_SPEED_10;
+       }
+
+       if (duplex == DUPLEX_FULL)
+               mode |= DEV_MAC_MODE_CFG_FDX_ENA;
+
+       ocelot_port_writel(ocelot_port, mode, DEV_MAC_MODE_CFG);
+
+       /* Take port out of reset by clearing the MAC_TX_RST, MAC_RX_RST and
+        * PORT_RST bits in DEV_CLOCK_CFG.
+        */
+       ocelot_port_writel(ocelot_port, DEV_CLOCK_CFG_LINK_SPEED(mac_speed),
+                          DEV_CLOCK_CFG);
+
+       switch (speed) {
        case SPEED_10:
-               speed = OCELOT_SPEED_10;
+               mac_fc_cfg = SYS_MAC_FC_CFG_FC_LINK_SPEED(OCELOT_SPEED_10);
                break;
        case SPEED_100:
-               speed = OCELOT_SPEED_100;
+               mac_fc_cfg = SYS_MAC_FC_CFG_FC_LINK_SPEED(OCELOT_SPEED_100);
                break;
        case SPEED_1000:
-               speed = OCELOT_SPEED_1000;
-               mode = DEV_MAC_MODE_CFG_GIGA_MODE_ENA;
-               break;
        case SPEED_2500:
-               speed = OCELOT_SPEED_2500;
-               mode = DEV_MAC_MODE_CFG_GIGA_MODE_ENA;
+               mac_fc_cfg = SYS_MAC_FC_CFG_FC_LINK_SPEED(OCELOT_SPEED_1000);
                break;
        default:
-               dev_err(ocelot->dev, "Unsupported PHY speed on port %d: %d\n",
-                       port, phydev->speed);
+               dev_err(ocelot->dev, "Unsupported speed on port %d: %d\n",
+                       port, speed);
                return;
        }
 
-       phy_print_status(phydev);
-
-       if (!phydev->link)
-               return;
-
-       /* Only full duplex supported for now */
-       ocelot_port_writel(ocelot_port, DEV_MAC_MODE_CFG_FDX_ENA |
-                          mode, DEV_MAC_MODE_CFG);
-
-       /* Disable HDX fast control */
-       ocelot_port_writel(ocelot_port, DEV_PORT_MISC_HDX_FAST_DIS,
-                          DEV_PORT_MISC);
+       /* Handle RX pause in all cases, with 2500base-X this is used for rate
+        * adaptation.
+        */
+       mac_fc_cfg |= SYS_MAC_FC_CFG_RX_FC_ENA;
 
-       /* SGMII only for now */
-       ocelot_port_writel(ocelot_port, PCS1G_MODE_CFG_SGMII_MODE_ENA,
-                          PCS1G_MODE_CFG);
-       ocelot_port_writel(ocelot_port, PCS1G_SD_CFG_SD_SEL, PCS1G_SD_CFG);
+       if (tx_pause)
+               mac_fc_cfg |= SYS_MAC_FC_CFG_TX_FC_ENA |
+                             SYS_MAC_FC_CFG_PAUSE_VAL_CFG(0xffff) |
+                             SYS_MAC_FC_CFG_FC_LATENCY_CFG(0x7) |
+                             SYS_MAC_FC_CFG_ZERO_PAUSE_ENA;
 
-       /* Enable PCS */
-       ocelot_port_writel(ocelot_port, PCS1G_CFG_PCS_ENA, PCS1G_CFG);
+       /* Flow control. Link speed is only used here to evaluate the time
+        * specification in incoming pause frames.
+        */
+       ocelot_write_rix(ocelot, mac_fc_cfg, SYS_MAC_FC_CFG, port);
 
-       /* No aneg on SGMII */
-       ocelot_port_writel(ocelot_port, 0, PCS1G_ANEG_CFG);
+       ocelot_write_rix(ocelot, 0, ANA_POL_FLOWC, port);
 
-       /* No loopback */
-       ocelot_port_writel(ocelot_port, 0, PCS1G_LB_CFG);
+       ocelot_fields_write(ocelot, port, SYS_PAUSE_CFG_PAUSE_ENA, tx_pause);
 
-       /* Enable MAC module */
+       /* Undo the effects of ocelot_phylink_mac_link_down:
+        * enable MAC module
+        */
        ocelot_port_writel(ocelot_port, DEV_MAC_ENA_CFG_RX_ENA |
                           DEV_MAC_ENA_CFG_TX_ENA, DEV_MAC_ENA_CFG);
 
@@ -502,39 +556,8 @@ void ocelot_adjust_link(struct ocelot *ocelot, int port,
        /* Core: Enable port for frame transfer */
        ocelot_fields_write(ocelot, port,
                            QSYS_SWITCH_PORT_MODE_PORT_ENA, 1);
-
-       /* Flow control */
-       ocelot_write_rix(ocelot, SYS_MAC_FC_CFG_PAUSE_VAL_CFG(0xffff) |
-                        SYS_MAC_FC_CFG_RX_FC_ENA | SYS_MAC_FC_CFG_TX_FC_ENA |
-                        SYS_MAC_FC_CFG_ZERO_PAUSE_ENA |
-                        SYS_MAC_FC_CFG_FC_LATENCY_CFG(0x7) |
-                        SYS_MAC_FC_CFG_FC_LINK_SPEED(speed),
-                        SYS_MAC_FC_CFG, port);
-       ocelot_write_rix(ocelot, 0, ANA_POL_FLOWC, port);
-}
-EXPORT_SYMBOL(ocelot_adjust_link);
-
-void ocelot_port_enable(struct ocelot *ocelot, int port,
-                       struct phy_device *phy)
-{
-       /* Enable receiving frames on the port, and activate auto-learning of
-        * MAC addresses.
-        */
-       ocelot_write_gix(ocelot, ANA_PORT_PORT_CFG_LEARNAUTO |
-                        ANA_PORT_PORT_CFG_RECV_ENA |
-                        ANA_PORT_PORT_CFG_PORTID_VAL(port),
-                        ANA_PORT_PORT_CFG, port);
-}
-EXPORT_SYMBOL(ocelot_port_enable);
-
-void ocelot_port_disable(struct ocelot *ocelot, int port)
-{
-       struct ocelot_port *ocelot_port = ocelot->ports[port];
-
-       ocelot_port_writel(ocelot_port, 0, DEV_MAC_ENA_CFG);
-       ocelot_fields_write(ocelot, port, QSYS_SWITCH_PORT_MODE_PORT_ENA, 0);
 }
-EXPORT_SYMBOL(ocelot_port_disable);
+EXPORT_SYMBOL_GPL(ocelot_phylink_mac_link_up);
 
 static void ocelot_port_add_txtstamp_skb(struct ocelot *ocelot, int port,
                                         struct sk_buff *clone)
@@ -1334,6 +1357,7 @@ void ocelot_apply_bridge_fwd_mask(struct ocelot *ocelot)
                        struct net_device *bond = ocelot_port->bond;
 
                        mask = ocelot_get_bridge_fwd_mask(ocelot, bridge);
+                       mask |= cpu_fwd_mask;
                        mask &= ~BIT(port);
                        if (bond) {
                                mask &= ~ocelot_get_bond_mask(ocelot, bond,
@@ -1956,6 +1980,15 @@ void ocelot_init_port(struct ocelot *ocelot, int port)
        /* Disable source address learning for standalone mode */
        ocelot_port_set_learning(ocelot, port, false);
 
+       /* Set the port's initial logical port ID value, enable receiving
+        * frames on it, and configure the MAC address learning type to
+        * automatic.
+        */
+       ocelot_write_gix(ocelot, ANA_PORT_PORT_CFG_LEARNAUTO |
+                        ANA_PORT_PORT_CFG_RECV_ENA |
+                        ANA_PORT_PORT_CFG_PORTID_VAL(port),
+                        ANA_PORT_PORT_CFG, port);
+
        /* Enable vcap lookups */
        ocelot_vcap_enable(ocelot, port);
 }