diff options
Diffstat (limited to 'drivers/net/ethernet/mscc/ocelot.c')
-rw-r--r-- | drivers/net/ethernet/mscc/ocelot.c | 246 |
1 files changed, 149 insertions, 97 deletions
diff --git a/drivers/net/ethernet/mscc/ocelot.c b/drivers/net/ethernet/mscc/ocelot.c index 2948d731a1c1..c581b955efb3 100644 --- a/drivers/net/ethernet/mscc/ocelot.c +++ b/drivers/net/ethernet/mscc/ocelot.c @@ -222,8 +222,35 @@ static void ocelot_port_set_pvid(struct ocelot *ocelot, int port, ANA_PORT_DROP_CFG, port); } +static int ocelot_vlan_member_set(struct ocelot *ocelot, u32 vlan_mask, u16 vid) +{ + int err; + + err = ocelot_vlant_set_mask(ocelot, vid, vlan_mask); + if (err) + return err; + + ocelot->vlan_mask[vid] = vlan_mask; + + return 0; +} + +static int ocelot_vlan_member_add(struct ocelot *ocelot, int port, u16 vid) +{ + return ocelot_vlan_member_set(ocelot, + ocelot->vlan_mask[vid] | BIT(port), + vid); +} + +static int ocelot_vlan_member_del(struct ocelot *ocelot, int port, u16 vid) +{ + return ocelot_vlan_member_set(ocelot, + ocelot->vlan_mask[vid] & ~BIT(port), + vid); +} + int ocelot_port_vlan_filtering(struct ocelot *ocelot, int port, - bool vlan_aware) + bool vlan_aware, struct netlink_ext_ack *extack) { struct ocelot_vcap_block *block = &ocelot->block[VCAP_IS1]; struct ocelot_port *ocelot_port = ocelot->ports[port]; @@ -233,8 +260,8 @@ int ocelot_port_vlan_filtering(struct ocelot *ocelot, int port, list_for_each_entry(filter, &block->rules, list) { if (filter->ingress_port_mask & BIT(port) && filter->action.vid_replace_ena) { - dev_err(ocelot->dev, - "Cannot change VLAN state with vlan modify rules active\n"); + NL_SET_ERR_MSG_MOD(extack, + "Cannot change VLAN state with vlan modify rules active"); return -EBUSY; } } @@ -259,16 +286,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; } @@ -279,13 +305,11 @@ EXPORT_SYMBOL(ocelot_vlan_prepare); int ocelot_vlan_add(struct ocelot *ocelot, int port, u16 vid, bool pvid, bool untagged) { - int ret; + int err; - /* Make the port a member of the VLAN */ - ocelot->vlan_mask[vid] |= BIT(port); - ret = ocelot_vlant_set_mask(ocelot, vid, ocelot->vlan_mask[vid]); - if (ret) - return ret; + err = ocelot_vlan_member_add(ocelot, port, vid); + if (err) + return err; /* Default ingress vlan classification */ if (pvid) { @@ -312,13 +336,11 @@ EXPORT_SYMBOL(ocelot_vlan_add); int ocelot_vlan_del(struct ocelot *ocelot, int port, u16 vid) { struct ocelot_port *ocelot_port = ocelot->ports[port]; - int ret; + int err; - /* Stop the port from being a member of the vlan */ - ocelot->vlan_mask[vid] &= ~BIT(port); - ret = ocelot_vlant_set_mask(ocelot, vid, ocelot->vlan_mask[vid]); - if (ret) - return ret; + err = ocelot_vlan_member_del(ocelot, port, vid); + if (err) + return err; /* Ingress */ if (ocelot_port->pvid_vlan.vid == vid) { @@ -340,6 +362,7 @@ EXPORT_SYMBOL(ocelot_vlan_del); static void ocelot_vlan_init(struct ocelot *ocelot) { + unsigned long all_ports = GENMASK(ocelot->num_phys_ports - 1, 0); u16 port, vid; /* Clear VLAN table, by default all ports are members of all VLANs */ @@ -348,23 +371,19 @@ static void ocelot_vlan_init(struct ocelot *ocelot) ocelot_vlant_wait_for_completion(ocelot); /* Configure the port VLAN memberships */ - for (vid = 1; vid < VLAN_N_VID; vid++) { - ocelot->vlan_mask[vid] = 0; - ocelot_vlant_set_mask(ocelot, vid, ocelot->vlan_mask[vid]); - } + for (vid = 1; vid < VLAN_N_VID; vid++) + ocelot_vlan_member_set(ocelot, 0, vid); /* Because VLAN filtering is enabled, we need VID 0 to get untagged * traffic. It is added automatically if 8021q module is loaded, but * we can't rely on it since module may be not loaded. */ - ocelot->vlan_mask[0] = GENMASK(ocelot->num_phys_ports - 1, 0); - ocelot_vlant_set_mask(ocelot, 0, ocelot->vlan_mask[0]); + ocelot_vlan_member_set(ocelot, all_ports, 0); /* Set vlan ingress filter mask to all ports but the CPU port by * default. */ - ocelot_write(ocelot, GENMASK(ocelot->num_phys_ports - 1, 0), - ANA_VLANMASK); + ocelot_write(ocelot, all_ports, ANA_VLANMASK); for (port = 0; port < ocelot->num_phys_ports; port++) { ocelot_write_gix(ocelot, 0, REW_PORT_VLAN_CFG, port); @@ -377,7 +396,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 +448,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 speed, mode = 0; + 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 mac_speed, mode = 0; + u32 mac_fc_cfg; + + /* 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; + } - switch (phydev->speed) { + 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 +576,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) @@ -1957,6 +2000,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); } |