diff options
Diffstat (limited to 'drivers/net/ethernet/mscc/ocelot.c')
-rw-r--r-- | drivers/net/ethernet/mscc/ocelot.c | 1175 |
1 files changed, 729 insertions, 446 deletions
diff --git a/drivers/net/ethernet/mscc/ocelot.c b/drivers/net/ethernet/mscc/ocelot.c index 4d1bce4389c7..985b46d7e3d1 100644 --- a/drivers/net/ethernet/mscc/ocelot.c +++ b/drivers/net/ethernet/mscc/ocelot.c @@ -132,11 +132,11 @@ static void ocelot_mact_init(struct ocelot *ocelot) ocelot_write(ocelot, MACACCESS_CMD_INIT, ANA_TABLES_MACACCESS); } -static void ocelot_vcap_enable(struct ocelot *ocelot, struct ocelot_port *port) +static void ocelot_vcap_enable(struct ocelot *ocelot, int port) { ocelot_write_gix(ocelot, ANA_PORT_VCAP_S2_CFG_S2_ENA | ANA_PORT_VCAP_S2_CFG_S2_IP6_CFG(0xa), - ANA_PORT_VCAP_S2_CFG, port->chip_port); + ANA_PORT_VCAP_S2_CFG, port); } static inline u32 ocelot_vlant_read_vlanaccess(struct ocelot *ocelot) @@ -169,110 +169,178 @@ static int ocelot_vlant_set_mask(struct ocelot *ocelot, u16 vid, u32 mask) return ocelot_vlant_wait_for_completion(ocelot); } -static void ocelot_vlan_mode(struct ocelot_port *port, +static void ocelot_vlan_mode(struct ocelot *ocelot, int port, netdev_features_t features) { - struct ocelot *ocelot = port->ocelot; - u8 p = port->chip_port; u32 val; /* Filtering */ val = ocelot_read(ocelot, ANA_VLANMASK); if (features & NETIF_F_HW_VLAN_CTAG_FILTER) - val |= BIT(p); + val |= BIT(port); else - val &= ~BIT(p); + val &= ~BIT(port); ocelot_write(ocelot, val, ANA_VLANMASK); } -static void ocelot_vlan_port_apply(struct ocelot *ocelot, - struct ocelot_port *port) +void ocelot_port_vlan_filtering(struct ocelot *ocelot, int port, + bool vlan_aware) { + struct ocelot_port *ocelot_port = ocelot->ports[port]; u32 val; - /* Ingress clasification (ANA_PORT_VLAN_CFG) */ - /* Default vlan to clasify for untagged frames (may be zero) */ - val = ANA_PORT_VLAN_CFG_VLAN_VID(port->pvid); - if (port->vlan_aware) - val |= ANA_PORT_VLAN_CFG_VLAN_AWARE_ENA | - ANA_PORT_VLAN_CFG_VLAN_POP_CNT(1); - + if (vlan_aware) + val = ANA_PORT_VLAN_CFG_VLAN_AWARE_ENA | + ANA_PORT_VLAN_CFG_VLAN_POP_CNT(1); + else + val = 0; ocelot_rmw_gix(ocelot, val, - ANA_PORT_VLAN_CFG_VLAN_VID_M | ANA_PORT_VLAN_CFG_VLAN_AWARE_ENA | ANA_PORT_VLAN_CFG_VLAN_POP_CNT_M, - ANA_PORT_VLAN_CFG, port->chip_port); + ANA_PORT_VLAN_CFG, port); - /* Drop frames with multicast source address */ - val = ANA_PORT_DROP_CFG_DROP_MC_SMAC_ENA; - if (port->vlan_aware && !port->vid) + if (vlan_aware && !ocelot_port->vid) /* If port is vlan-aware and tagged, drop untagged and priority * tagged frames. */ - val |= ANA_PORT_DROP_CFG_DROP_UNTAGGED_ENA | + val = ANA_PORT_DROP_CFG_DROP_UNTAGGED_ENA | + ANA_PORT_DROP_CFG_DROP_PRIO_S_TAGGED_ENA | + ANA_PORT_DROP_CFG_DROP_PRIO_C_TAGGED_ENA; + else + val = 0; + ocelot_rmw_gix(ocelot, val, + ANA_PORT_DROP_CFG_DROP_UNTAGGED_ENA | ANA_PORT_DROP_CFG_DROP_PRIO_S_TAGGED_ENA | - ANA_PORT_DROP_CFG_DROP_PRIO_C_TAGGED_ENA; - ocelot_write_gix(ocelot, val, ANA_PORT_DROP_CFG, port->chip_port); - - /* Egress configuration (REW_TAG_CFG): VLAN tag type to 8021Q. */ - val = REW_TAG_CFG_TAG_TPID_CFG(0); + ANA_PORT_DROP_CFG_DROP_PRIO_C_TAGGED_ENA, + ANA_PORT_DROP_CFG, port); - if (port->vlan_aware) { - if (port->vid) + if (vlan_aware) { + if (ocelot_port->vid) /* Tag all frames except when VID == DEFAULT_VLAN */ val |= REW_TAG_CFG_TAG_CFG(1); else /* Tag all frames */ val |= REW_TAG_CFG_TAG_CFG(3); + } else { + /* Port tagging disabled. */ + val = REW_TAG_CFG_TAG_CFG(0); } ocelot_rmw_gix(ocelot, val, - REW_TAG_CFG_TAG_TPID_CFG_M | REW_TAG_CFG_TAG_CFG_M, - REW_TAG_CFG, port->chip_port); + REW_TAG_CFG, port); +} +EXPORT_SYMBOL(ocelot_port_vlan_filtering); - /* Set default VLAN and tag type to 8021Q. */ - val = REW_PORT_VLAN_CFG_PORT_TPID(ETH_P_8021Q) | - REW_PORT_VLAN_CFG_PORT_VID(port->vid); - ocelot_rmw_gix(ocelot, val, - REW_PORT_VLAN_CFG_PORT_TPID_M | +static int ocelot_port_set_native_vlan(struct ocelot *ocelot, int port, + u16 vid) +{ + struct ocelot_port *ocelot_port = ocelot->ports[port]; + + if (ocelot_port->vid != vid) { + /* Always permit deleting the native VLAN (vid = 0) */ + if (ocelot_port->vid && vid) { + dev_err(ocelot->dev, + "Port already has a native VLAN: %d\n", + ocelot_port->vid); + return -EBUSY; + } + ocelot_port->vid = vid; + } + + ocelot_rmw_gix(ocelot, REW_PORT_VLAN_CFG_PORT_VID(vid), REW_PORT_VLAN_CFG_PORT_VID_M, - REW_PORT_VLAN_CFG, port->chip_port); + REW_PORT_VLAN_CFG, port); + + return 0; } +/* Default vlan to clasify for untagged frames (may be zero) */ +static void ocelot_port_set_pvid(struct ocelot *ocelot, int port, u16 pvid) +{ + struct ocelot_port *ocelot_port = ocelot->ports[port]; + + ocelot_rmw_gix(ocelot, + ANA_PORT_VLAN_CFG_VLAN_VID(pvid), + ANA_PORT_VLAN_CFG_VLAN_VID_M, + ANA_PORT_VLAN_CFG, port); + + ocelot_port->pvid = pvid; +} + +int ocelot_vlan_add(struct ocelot *ocelot, int port, u16 vid, bool pvid, + bool untagged) +{ + int ret; + + /* 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; + + /* Default ingress vlan classification */ + if (pvid) + ocelot_port_set_pvid(ocelot, port, vid); + + /* Untagged egress vlan clasification */ + if (untagged) { + ret = ocelot_port_set_native_vlan(ocelot, port, vid); + if (ret) + return ret; + } + + return 0; +} +EXPORT_SYMBOL(ocelot_vlan_add); + static int ocelot_vlan_vid_add(struct net_device *dev, u16 vid, bool pvid, bool untagged) { - struct ocelot_port *port = netdev_priv(dev); - struct ocelot *ocelot = port->ocelot; + struct ocelot_port_private *priv = netdev_priv(dev); + struct ocelot_port *ocelot_port = &priv->port; + struct ocelot *ocelot = ocelot_port->ocelot; + int port = priv->chip_port; int ret; + ret = ocelot_vlan_add(ocelot, port, vid, pvid, untagged); + if (ret) + return ret; + /* Add the port MAC address to with the right VLAN information */ ocelot_mact_learn(ocelot, PGID_CPU, dev->dev_addr, vid, ENTRYTYPE_LOCKED); - /* Make the port a member of the VLAN */ - ocelot->vlan_mask[vid] |= BIT(port->chip_port); + return 0; +} + +int ocelot_vlan_del(struct ocelot *ocelot, int port, u16 vid) +{ + struct ocelot_port *ocelot_port = ocelot->ports[port]; + int ret; + + /* 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; - /* Default ingress vlan classification */ - if (pvid) - port->pvid = vid; - - /* Untagged egress vlan clasification */ - if (untagged) - port->vid = vid; + /* Ingress */ + if (ocelot_port->pvid == vid) + ocelot_port_set_pvid(ocelot, port, 0); - ocelot_vlan_port_apply(ocelot, port); + /* Egress */ + if (ocelot_port->vid == vid) + ocelot_port_set_native_vlan(ocelot, port, 0); return 0; } +EXPORT_SYMBOL(ocelot_vlan_del); static int ocelot_vlan_vid_del(struct net_device *dev, u16 vid) { - struct ocelot_port *port = netdev_priv(dev); - struct ocelot *ocelot = port->ocelot; + struct ocelot_port_private *priv = netdev_priv(dev); + struct ocelot *ocelot = priv->port.ocelot; + int port = priv->chip_port; int ret; /* 8021q removes VID 0 on module unload for all interfaces @@ -282,24 +350,12 @@ static int ocelot_vlan_vid_del(struct net_device *dev, u16 vid) if (vid == 0) return 0; - /* Del the port MAC address to with the right VLAN information */ - ocelot_mact_forget(ocelot, dev->dev_addr, vid); - - /* Stop the port from being a member of the vlan */ - ocelot->vlan_mask[vid] &= ~BIT(port->chip_port); - ret = ocelot_vlant_set_mask(ocelot, vid, ocelot->vlan_mask[vid]); + ret = ocelot_vlan_del(ocelot, port, vid); if (ret) return ret; - /* Ingress */ - if (port->pvid == vid) - port->pvid = 0; - - /* Egress */ - if (port->vid == vid) - port->vid = 0; - - ocelot_vlan_port_apply(ocelot, port); + /* Del the port MAC address to with the right VLAN information */ + ocelot_mact_forget(ocelot, dev->dev_addr, vid); return 0; } @@ -326,16 +382,11 @@ static void ocelot_vlan_init(struct ocelot *ocelot) ocelot->vlan_mask[0] = GENMASK(ocelot->num_phys_ports - 1, 0); ocelot_vlant_set_mask(ocelot, 0, ocelot->vlan_mask[0]); - /* Configure the CPU port to be VLAN aware */ - ocelot_write_gix(ocelot, ANA_PORT_VLAN_CFG_VLAN_VID(0) | - ANA_PORT_VLAN_CFG_VLAN_AWARE_ENA | - ANA_PORT_VLAN_CFG_VLAN_POP_CNT(1), - ANA_PORT_VLAN_CFG, ocelot->num_phys_ports); - /* Set vlan ingress filter mask to all ports but the CPU port by * default. */ - ocelot_write(ocelot, GENMASK(9, 0), ANA_VLANMASK); + ocelot_write(ocelot, GENMASK(ocelot->num_phys_ports - 1, 0), + ANA_VLANMASK); for (port = 0; port < ocelot->num_phys_ports; port++) { ocelot_write_gix(ocelot, 0, REW_PORT_VLAN_CFG, port); @@ -355,14 +406,13 @@ static u16 ocelot_wm_enc(u16 value) return value; } -static void ocelot_port_adjust_link(struct net_device *dev) +void ocelot_adjust_link(struct ocelot *ocelot, int port, + struct phy_device *phydev) { - struct ocelot_port *port = netdev_priv(dev); - struct ocelot *ocelot = port->ocelot; - u8 p = port->chip_port; - int speed, atop_wm, mode = 0; + struct ocelot_port *ocelot_port = ocelot->ports[port]; + int speed, mode = 0; - switch (dev->phydev->speed) { + switch (phydev->speed) { case SPEED_10: speed = OCELOT_SPEED_10; break; @@ -378,87 +428,41 @@ static void ocelot_port_adjust_link(struct net_device *dev) mode = DEV_MAC_MODE_CFG_GIGA_MODE_ENA; break; default: - netdev_err(dev, "Unsupported PHY speed: %d\n", - dev->phydev->speed); + dev_err(ocelot->dev, "Unsupported PHY speed on port %d: %d\n", + port, phydev->speed); return; } - phy_print_status(dev->phydev); + phy_print_status(phydev); - if (!dev->phydev->link) + if (!phydev->link) return; /* Only full duplex supported for now */ - ocelot_port_writel(port, DEV_MAC_MODE_CFG_FDX_ENA | + ocelot_port_writel(ocelot_port, DEV_MAC_MODE_CFG_FDX_ENA | mode, DEV_MAC_MODE_CFG); - /* Set MAC IFG Gaps - * FDX: TX_IFG = 5, RX_IFG1 = RX_IFG2 = 0 - * !FDX: TX_IFG = 5, RX_IFG1 = RX_IFG2 = 5 - */ - ocelot_port_writel(port, DEV_MAC_IFG_CFG_TX_IFG(5), DEV_MAC_IFG_CFG); - - /* Load seed (0) and set MAC HDX late collision */ - ocelot_port_writel(port, DEV_MAC_HDX_CFG_LATE_COL_POS(67) | - DEV_MAC_HDX_CFG_SEED_LOAD, - DEV_MAC_HDX_CFG); - mdelay(1); - ocelot_port_writel(port, DEV_MAC_HDX_CFG_LATE_COL_POS(67), - DEV_MAC_HDX_CFG); - - /* Disable HDX fast control */ - ocelot_port_writel(port, DEV_PORT_MISC_HDX_FAST_DIS, DEV_PORT_MISC); - - /* SGMII only for now */ - ocelot_port_writel(port, PCS1G_MODE_CFG_SGMII_MODE_ENA, PCS1G_MODE_CFG); - ocelot_port_writel(port, PCS1G_SD_CFG_SD_SEL, PCS1G_SD_CFG); - - /* Enable PCS */ - ocelot_port_writel(port, PCS1G_CFG_PCS_ENA, PCS1G_CFG); - - /* No aneg on SGMII */ - ocelot_port_writel(port, 0, PCS1G_ANEG_CFG); - - /* No loopback */ - ocelot_port_writel(port, 0, PCS1G_LB_CFG); - - /* Set Max Length and maximum tags allowed */ - ocelot_port_writel(port, VLAN_ETH_FRAME_LEN, DEV_MAC_MAXLEN_CFG); - ocelot_port_writel(port, DEV_MAC_TAGS_CFG_TAG_ID(ETH_P_8021AD) | - DEV_MAC_TAGS_CFG_VLAN_AWR_ENA | - DEV_MAC_TAGS_CFG_VLAN_LEN_AWR_ENA, - DEV_MAC_TAGS_CFG); + if (ocelot->ops->pcs_init) + ocelot->ops->pcs_init(ocelot, port); /* Enable MAC module */ - ocelot_port_writel(port, DEV_MAC_ENA_CFG_RX_ENA | + ocelot_port_writel(ocelot_port, DEV_MAC_ENA_CFG_RX_ENA | DEV_MAC_ENA_CFG_TX_ENA, DEV_MAC_ENA_CFG); /* Take MAC, Port, Phy (intern) and PCS (SGMII/Serdes) clock out of * reset */ - ocelot_port_writel(port, DEV_CLOCK_CFG_LINK_SPEED(speed), + ocelot_port_writel(ocelot_port, DEV_CLOCK_CFG_LINK_SPEED(speed), DEV_CLOCK_CFG); - /* Set SMAC of Pause frame (00:00:00:00:00:00) */ - ocelot_port_writel(port, 0, DEV_MAC_FC_MAC_HIGH_CFG); - ocelot_port_writel(port, 0, DEV_MAC_FC_MAC_LOW_CFG); - /* No PFC */ ocelot_write_gix(ocelot, ANA_PFC_PFC_CFG_FC_LINK_SPEED(speed), - ANA_PFC_PFC_CFG, p); - - /* Set Pause WM hysteresis - * 152 = 6 * VLAN_ETH_FRAME_LEN / OCELOT_BUFFER_CELL_SZ - * 101 = 4 * VLAN_ETH_FRAME_LEN / OCELOT_BUFFER_CELL_SZ - */ - ocelot_write_rix(ocelot, SYS_PAUSE_CFG_PAUSE_ENA | - SYS_PAUSE_CFG_PAUSE_STOP(101) | - SYS_PAUSE_CFG_PAUSE_START(152), SYS_PAUSE_CFG, p); + ANA_PFC_PFC_CFG, port); /* Core: Enable port for frame transfer */ ocelot_write_rix(ocelot, QSYS_SWITCH_PORT_MODE_INGRESS_DROP_MODE | QSYS_SWITCH_PORT_MODE_SCH_NEXT_CFG(1) | QSYS_SWITCH_PORT_MODE_PORT_ENA, - QSYS_SWITCH_PORT_MODE, p); + QSYS_SWITCH_PORT_MODE, port); /* Flow control */ ocelot_write_rix(ocelot, SYS_MAC_FC_CFG_PAUSE_VAL_CFG(0xffff) | @@ -466,64 +470,88 @@ static void ocelot_port_adjust_link(struct net_device *dev) 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, p); - ocelot_write_rix(ocelot, 0, ANA_POL_FLOWC, p); - - /* Tail dropping watermark */ - atop_wm = (ocelot->shared_queue_sz - 9 * VLAN_ETH_FRAME_LEN) / OCELOT_BUFFER_CELL_SZ; - ocelot_write_rix(ocelot, ocelot_wm_enc(9 * VLAN_ETH_FRAME_LEN), - SYS_ATOP, p); - ocelot_write(ocelot, ocelot_wm_enc(atop_wm), SYS_ATOP_TOT_CFG); + SYS_MAC_FC_CFG, port); + ocelot_write_rix(ocelot, 0, ANA_POL_FLOWC, port); } +EXPORT_SYMBOL(ocelot_adjust_link); -static int ocelot_port_open(struct net_device *dev) +static void ocelot_port_adjust_link(struct net_device *dev) { - struct ocelot_port *port = netdev_priv(dev); - struct ocelot *ocelot = port->ocelot; - int err; + struct ocelot_port_private *priv = netdev_priv(dev); + struct ocelot *ocelot = priv->port.ocelot; + int port = priv->chip_port; + ocelot_adjust_link(ocelot, port, dev->phydev); +} + +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->chip_port), - ANA_PORT_PORT_CFG, port->chip_port); + ANA_PORT_PORT_CFG_PORTID_VAL(port), + ANA_PORT_PORT_CFG, port); +} +EXPORT_SYMBOL(ocelot_port_enable); + +static int ocelot_port_open(struct net_device *dev) +{ + struct ocelot_port_private *priv = netdev_priv(dev); + struct ocelot *ocelot = priv->port.ocelot; + int port = priv->chip_port; + int err; - if (port->serdes) { - err = phy_set_mode_ext(port->serdes, PHY_MODE_ETHERNET, - port->phy_mode); + if (priv->serdes) { + err = phy_set_mode_ext(priv->serdes, PHY_MODE_ETHERNET, + priv->phy_mode); if (err) { netdev_err(dev, "Could not set mode of SerDes\n"); return err; } } - err = phy_connect_direct(dev, port->phy, &ocelot_port_adjust_link, - port->phy_mode); + err = phy_connect_direct(dev, priv->phy, &ocelot_port_adjust_link, + priv->phy_mode); if (err) { netdev_err(dev, "Could not attach to PHY\n"); return err; } - dev->phydev = port->phy; + dev->phydev = priv->phy; + + phy_attached_info(priv->phy); + phy_start(priv->phy); + + ocelot_port_enable(ocelot, port, priv->phy); - phy_attached_info(port->phy); - phy_start(port->phy); return 0; } +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_rmw_rix(ocelot, 0, QSYS_SWITCH_PORT_MODE_PORT_ENA, + QSYS_SWITCH_PORT_MODE, port); +} +EXPORT_SYMBOL(ocelot_port_disable); + static int ocelot_port_stop(struct net_device *dev) { - struct ocelot_port *port = netdev_priv(dev); + struct ocelot_port_private *priv = netdev_priv(dev); + struct ocelot *ocelot = priv->port.ocelot; + int port = priv->chip_port; - phy_disconnect(port->phy); + phy_disconnect(priv->phy); dev->phydev = NULL; - ocelot_port_writel(port, 0, DEV_MAC_ENA_CFG); - ocelot_rmw_rix(port->ocelot, 0, QSYS_SWITCH_PORT_MODE_PORT_ENA, - QSYS_SWITCH_PORT_MODE, port->chip_port); + ocelot_port_disable(ocelot, port); + return 0; } @@ -547,15 +575,35 @@ static int ocelot_gen_ifh(u32 *ifh, struct frame_info *info) return 0; } +int ocelot_port_add_txtstamp_skb(struct ocelot_port *ocelot_port, + struct sk_buff *skb) +{ + struct skb_shared_info *shinfo = skb_shinfo(skb); + struct ocelot *ocelot = ocelot_port->ocelot; + + if (ocelot->ptp && shinfo->tx_flags & SKBTX_HW_TSTAMP && + ocelot_port->ptp_cmd == IFH_REW_OP_TWO_STEP_PTP) { + shinfo->tx_flags |= SKBTX_IN_PROGRESS; + /* Store timestamp ID in cb[0] of sk_buff */ + skb->cb[0] = ocelot_port->ts_id % 4; + skb_queue_tail(&ocelot_port->tx_skbs, skb); + return 0; + } + return -ENODATA; +} +EXPORT_SYMBOL(ocelot_port_add_txtstamp_skb); + static int ocelot_port_xmit(struct sk_buff *skb, struct net_device *dev) { + struct ocelot_port_private *priv = netdev_priv(dev); struct skb_shared_info *shinfo = skb_shinfo(skb); - struct ocelot_port *port = netdev_priv(dev); - struct ocelot *ocelot = port->ocelot; - u32 val, ifh[IFH_LEN]; + struct ocelot_port *ocelot_port = &priv->port; + struct ocelot *ocelot = ocelot_port->ocelot; + u32 val, ifh[OCELOT_TAG_LEN / 4]; struct frame_info info = {}; u8 grp = 0; /* Send everything on CPU group 0 */ unsigned int i, count, last; + int port = priv->chip_port; val = ocelot_read(ocelot, QS_INJ_STATUS); if (!(val & QS_INJ_STATUS_FIFO_RDY(BIT(grp))) || @@ -565,20 +613,20 @@ static int ocelot_port_xmit(struct sk_buff *skb, struct net_device *dev) ocelot_write_rix(ocelot, QS_INJ_CTRL_GAP_SIZE(1) | QS_INJ_CTRL_SOF, QS_INJ_CTRL, grp); - info.port = BIT(port->chip_port); + info.port = BIT(port); info.tag_type = IFH_TAG_TYPE_C; info.vid = skb_vlan_tag_get(skb); /* Check if timestamping is needed */ if (ocelot->ptp && shinfo->tx_flags & SKBTX_HW_TSTAMP) { - info.rew_op = port->ptp_cmd; - if (port->ptp_cmd == IFH_REW_OP_TWO_STEP_PTP) - info.rew_op |= (port->ts_id % 4) << 3; + info.rew_op = ocelot_port->ptp_cmd; + if (ocelot_port->ptp_cmd == IFH_REW_OP_TWO_STEP_PTP) + info.rew_op |= (ocelot_port->ts_id % 4) << 3; } ocelot_gen_ifh(ifh, &info); - for (i = 0; i < IFH_LEN; i++) + for (i = 0; i < OCELOT_TAG_LEN / 4; i++) ocelot_write_rix(ocelot, (__force u32)cpu_to_be32(ifh[i]), QS_INJ_WR, grp); @@ -607,31 +655,17 @@ static int ocelot_port_xmit(struct sk_buff *skb, struct net_device *dev) dev->stats.tx_packets++; dev->stats.tx_bytes += skb->len; - if (ocelot->ptp && shinfo->tx_flags & SKBTX_HW_TSTAMP && - port->ptp_cmd == IFH_REW_OP_TWO_STEP_PTP) { - struct ocelot_skb *oskb = - kzalloc(sizeof(struct ocelot_skb), GFP_ATOMIC); - - if (unlikely(!oskb)) - goto out; - - skb_shinfo(skb)->tx_flags |= SKBTX_IN_PROGRESS; - - oskb->skb = skb; - oskb->id = port->ts_id % 4; - port->ts_id++; - - list_add_tail(&oskb->head, &port->skbs); - + if (!ocelot_port_add_txtstamp_skb(ocelot_port, skb)) { + ocelot_port->ts_id++; return NETDEV_TX_OK; } -out: dev_kfree_skb_any(skb); return NETDEV_TX_OK; } -void ocelot_get_hwtimestamp(struct ocelot *ocelot, struct timespec64 *ts) +static void ocelot_get_hwtimestamp(struct ocelot *ocelot, + struct timespec64 *ts) { unsigned long flags; u32 val; @@ -656,29 +690,90 @@ void ocelot_get_hwtimestamp(struct ocelot *ocelot, struct timespec64 *ts) spin_unlock_irqrestore(&ocelot->ptp_clock_lock, flags); } -EXPORT_SYMBOL(ocelot_get_hwtimestamp); + +void ocelot_get_txtstamp(struct ocelot *ocelot) +{ + int budget = OCELOT_PTP_QUEUE_SZ; + + while (budget--) { + struct sk_buff *skb, *skb_tmp, *skb_match = NULL; + struct skb_shared_hwtstamps shhwtstamps; + struct ocelot_port *port; + struct timespec64 ts; + unsigned long flags; + u32 val, id, txport; + + val = ocelot_read(ocelot, SYS_PTP_STATUS); + + /* Check if a timestamp can be retrieved */ + if (!(val & SYS_PTP_STATUS_PTP_MESS_VLD)) + break; + + WARN_ON(val & SYS_PTP_STATUS_PTP_OVFL); + + /* Retrieve the ts ID and Tx port */ + id = SYS_PTP_STATUS_PTP_MESS_ID_X(val); + txport = SYS_PTP_STATUS_PTP_MESS_TXPORT_X(val); + + /* Retrieve its associated skb */ + port = ocelot->ports[txport]; + + spin_lock_irqsave(&port->tx_skbs.lock, flags); + + skb_queue_walk_safe(&port->tx_skbs, skb, skb_tmp) { + if (skb->cb[0] != id) + continue; + __skb_unlink(skb, &port->tx_skbs); + skb_match = skb; + break; + } + + spin_unlock_irqrestore(&port->tx_skbs.lock, flags); + + /* Next ts */ + ocelot_write(ocelot, SYS_PTP_NXT_PTP_NXT, SYS_PTP_NXT); + + if (unlikely(!skb_match)) + continue; + + /* Get the h/w timestamp */ + ocelot_get_hwtimestamp(ocelot, &ts); + + /* Set the timestamp into the skb */ + memset(&shhwtstamps, 0, sizeof(shhwtstamps)); + shhwtstamps.hwtstamp = ktime_set(ts.tv_sec, ts.tv_nsec); + skb_tstamp_tx(skb_match, &shhwtstamps); + + dev_kfree_skb_any(skb_match); + } +} +EXPORT_SYMBOL(ocelot_get_txtstamp); static int ocelot_mc_unsync(struct net_device *dev, const unsigned char *addr) { - struct ocelot_port *port = netdev_priv(dev); + struct ocelot_port_private *priv = netdev_priv(dev); + struct ocelot_port *ocelot_port = &priv->port; + struct ocelot *ocelot = ocelot_port->ocelot; - return ocelot_mact_forget(port->ocelot, addr, port->pvid); + return ocelot_mact_forget(ocelot, addr, ocelot_port->pvid); } static int ocelot_mc_sync(struct net_device *dev, const unsigned char *addr) { - struct ocelot_port *port = netdev_priv(dev); + struct ocelot_port_private *priv = netdev_priv(dev); + struct ocelot_port *ocelot_port = &priv->port; + struct ocelot *ocelot = ocelot_port->ocelot; - return ocelot_mact_learn(port->ocelot, PGID_CPU, addr, port->pvid, + return ocelot_mact_learn(ocelot, PGID_CPU, addr, ocelot_port->pvid, ENTRYTYPE_LOCKED); } static void ocelot_set_rx_mode(struct net_device *dev) { - struct ocelot_port *port = netdev_priv(dev); - struct ocelot *ocelot = port->ocelot; - int i; + struct ocelot_port_private *priv = netdev_priv(dev); + struct ocelot *ocelot = priv->port.ocelot; u32 val; + int i; /* This doesn't handle promiscuous mode because the bridge core is * setting IFF_PROMISC on all slave interfaces and all frames would be @@ -694,10 +789,11 @@ static void ocelot_set_rx_mode(struct net_device *dev) static int ocelot_port_get_phys_port_name(struct net_device *dev, char *buf, size_t len) { - struct ocelot_port *port = netdev_priv(dev); + struct ocelot_port_private *priv = netdev_priv(dev); + int port = priv->chip_port; int ret; - ret = snprintf(buf, len, "p%d", port->chip_port); + ret = snprintf(buf, len, "p%d", port); if (ret >= len) return -EINVAL; @@ -706,15 +802,16 @@ static int ocelot_port_get_phys_port_name(struct net_device *dev, static int ocelot_port_set_mac_address(struct net_device *dev, void *p) { - struct ocelot_port *port = netdev_priv(dev); - struct ocelot *ocelot = port->ocelot; + struct ocelot_port_private *priv = netdev_priv(dev); + struct ocelot_port *ocelot_port = &priv->port; + struct ocelot *ocelot = ocelot_port->ocelot; const struct sockaddr *addr = p; /* Learn the new net device MAC address in the mac table. */ - ocelot_mact_learn(ocelot, PGID_CPU, addr->sa_data, port->pvid, + ocelot_mact_learn(ocelot, PGID_CPU, addr->sa_data, ocelot_port->pvid, ENTRYTYPE_LOCKED); /* Then forget the previous one. */ - ocelot_mact_forget(ocelot, dev->dev_addr, port->pvid); + ocelot_mact_forget(ocelot, dev->dev_addr, ocelot_port->pvid); ether_addr_copy(dev->dev_addr, addr->sa_data); return 0; @@ -723,11 +820,12 @@ static int ocelot_port_set_mac_address(struct net_device *dev, void *p) static void ocelot_get_stats64(struct net_device *dev, struct rtnl_link_stats64 *stats) { - struct ocelot_port *port = netdev_priv(dev); - struct ocelot *ocelot = port->ocelot; + struct ocelot_port_private *priv = netdev_priv(dev); + struct ocelot *ocelot = priv->port.ocelot; + int port = priv->chip_port; /* Configure the port to read the stats from */ - ocelot_write(ocelot, SYS_STAT_CFG_STAT_VIEW(port->chip_port), + ocelot_write(ocelot, SYS_STAT_CFG_STAT_VIEW(port), SYS_STAT_CFG); /* Get Rx stats */ @@ -758,21 +856,18 @@ static void ocelot_get_stats64(struct net_device *dev, stats->collisions = ocelot_read(ocelot, SYS_COUNT_TX_COLLISION); } -static int ocelot_fdb_add(struct ndmsg *ndm, struct nlattr *tb[], - struct net_device *dev, const unsigned char *addr, - u16 vid, u16 flags, - struct netlink_ext_ack *extack) +int ocelot_fdb_add(struct ocelot *ocelot, int port, + const unsigned char *addr, u16 vid, bool vlan_aware) { - struct ocelot_port *port = netdev_priv(dev); - struct ocelot *ocelot = port->ocelot; + struct ocelot_port *ocelot_port = ocelot->ports[port]; if (!vid) { - if (!port->vlan_aware) + if (!vlan_aware) /* If the bridge is not VLAN aware and no VID was * provided, set it to pvid to ensure the MAC entry * matches incoming untagged packets */ - vid = port->pvid; + vid = ocelot_port->pvid; else /* If the bridge is VLAN aware a VID must be provided as * otherwise the learnt entry wouldn't match any frame. @@ -780,19 +875,40 @@ static int ocelot_fdb_add(struct ndmsg *ndm, struct nlattr *tb[], return -EINVAL; } - return ocelot_mact_learn(ocelot, port->chip_port, addr, vid, - ENTRYTYPE_LOCKED); + return ocelot_mact_learn(ocelot, port, addr, vid, ENTRYTYPE_LOCKED); } +EXPORT_SYMBOL(ocelot_fdb_add); -static int ocelot_fdb_del(struct ndmsg *ndm, struct nlattr *tb[], - struct net_device *dev, - const unsigned char *addr, u16 vid) +static int ocelot_port_fdb_add(struct ndmsg *ndm, struct nlattr *tb[], + struct net_device *dev, + const unsigned char *addr, + u16 vid, u16 flags, + struct netlink_ext_ack *extack) { - struct ocelot_port *port = netdev_priv(dev); - struct ocelot *ocelot = port->ocelot; + struct ocelot_port_private *priv = netdev_priv(dev); + struct ocelot *ocelot = priv->port.ocelot; + int port = priv->chip_port; + return ocelot_fdb_add(ocelot, port, addr, vid, priv->vlan_aware); +} + +int ocelot_fdb_del(struct ocelot *ocelot, int port, + const unsigned char *addr, u16 vid) +{ return ocelot_mact_forget(ocelot, addr, vid); } +EXPORT_SYMBOL(ocelot_fdb_del); + +static int ocelot_port_fdb_del(struct ndmsg *ndm, struct nlattr *tb[], + struct net_device *dev, + const unsigned char *addr, u16 vid) +{ + struct ocelot_port_private *priv = netdev_priv(dev); + struct ocelot *ocelot = priv->port.ocelot; + int port = priv->chip_port; + + return ocelot_fdb_del(ocelot, port, addr, vid); +} struct ocelot_dump_ctx { struct net_device *dev; @@ -801,9 +917,10 @@ struct ocelot_dump_ctx { int idx; }; -static int ocelot_fdb_do_dump(struct ocelot_mact_entry *entry, - struct ocelot_dump_ctx *dump) +static int ocelot_port_fdb_do_dump(const unsigned char *addr, u16 vid, + bool is_static, void *data) { + struct ocelot_dump_ctx *dump = data; u32 portid = NETLINK_CB(dump->cb->skb).portid; u32 seq = dump->cb->nlh->nlmsg_seq; struct nlmsghdr *nlh; @@ -824,12 +941,12 @@ static int ocelot_fdb_do_dump(struct ocelot_mact_entry *entry, ndm->ndm_flags = NTF_SELF; ndm->ndm_type = 0; ndm->ndm_ifindex = dump->dev->ifindex; - ndm->ndm_state = NUD_REACHABLE; + ndm->ndm_state = is_static ? NUD_NOARP : NUD_REACHABLE; - if (nla_put(dump->skb, NDA_LLADDR, ETH_ALEN, entry->mac)) + if (nla_put(dump->skb, NDA_LLADDR, ETH_ALEN, addr)) goto nla_put_failure; - if (entry->vid && nla_put_u16(dump->skb, NDA_VLAN, entry->vid)) + if (vid && nla_put_u16(dump->skb, NDA_VLAN, vid)) goto nla_put_failure; nlmsg_end(dump->skb, nlh); @@ -843,12 +960,11 @@ nla_put_failure: return -EMSGSIZE; } -static inline int ocelot_mact_read(struct ocelot_port *port, int row, int col, - struct ocelot_mact_entry *entry) +static int ocelot_mact_read(struct ocelot *ocelot, int port, int row, int col, + struct ocelot_mact_entry *entry) { - struct ocelot *ocelot = port->ocelot; - char mac[ETH_ALEN]; u32 val, dst, macl, mach; + char mac[ETH_ALEN]; /* Set row and column to read from */ ocelot_field_write(ocelot, ANA_TABLES_MACTINDX_M_INDEX, row); @@ -871,7 +987,7 @@ static inline int ocelot_mact_read(struct ocelot_port *port, int row, int col, * do not report it. */ dst = (val & ANA_TABLES_MACACCESS_DEST_IDX_M) >> 3; - if (dst != port->chip_port) + if (dst != port) return -EINVAL; /* Get the entry's MAC address and VLAN id */ @@ -891,50 +1007,68 @@ static inline int ocelot_mact_read(struct ocelot_port *port, int row, int col, return 0; } -static int ocelot_fdb_dump(struct sk_buff *skb, struct netlink_callback *cb, - struct net_device *dev, - struct net_device *filter_dev, int *idx) +int ocelot_fdb_dump(struct ocelot *ocelot, int port, + dsa_fdb_dump_cb_t *cb, void *data) { - struct ocelot_port *port = netdev_priv(dev); - int i, j, ret = 0; - struct ocelot_dump_ctx dump = { - .dev = dev, - .skb = skb, - .cb = cb, - .idx = *idx, - }; - - struct ocelot_mact_entry entry; + int i, j; /* Loop through all the mac tables entries. There are 1024 rows of 4 * entries. */ for (i = 0; i < 1024; i++) { for (j = 0; j < 4; j++) { - ret = ocelot_mact_read(port, i, j, &entry); + struct ocelot_mact_entry entry; + bool is_static; + int ret; + + ret = ocelot_mact_read(ocelot, port, i, j, &entry); /* If the entry is invalid (wrong port, invalid...), * skip it. */ if (ret == -EINVAL) continue; else if (ret) - goto end; + return ret; + + is_static = (entry.type == ENTRYTYPE_LOCKED); - ret = ocelot_fdb_do_dump(&entry, &dump); + ret = cb(entry.mac, entry.vid, is_static, data); if (ret) - goto end; + return ret; } } -end: + return 0; +} +EXPORT_SYMBOL(ocelot_fdb_dump); + +static int ocelot_port_fdb_dump(struct sk_buff *skb, + struct netlink_callback *cb, + struct net_device *dev, + struct net_device *filter_dev, int *idx) +{ + struct ocelot_port_private *priv = netdev_priv(dev); + struct ocelot *ocelot = priv->port.ocelot; + struct ocelot_dump_ctx dump = { + .dev = dev, + .skb = skb, + .cb = cb, + .idx = *idx, + }; + int port = priv->chip_port; + int ret; + + ret = ocelot_fdb_dump(ocelot, port, ocelot_port_fdb_do_dump, &dump); + *idx = dump.idx; + return ret; } static int ocelot_vlan_rx_add_vid(struct net_device *dev, __be16 proto, u16 vid) { - return ocelot_vlan_vid_add(dev, vid, false, true); + return ocelot_vlan_vid_add(dev, vid, false, false); } static int ocelot_vlan_rx_kill_vid(struct net_device *dev, __be16 proto, @@ -946,18 +1080,20 @@ static int ocelot_vlan_rx_kill_vid(struct net_device *dev, __be16 proto, static int ocelot_set_features(struct net_device *dev, netdev_features_t features) { - struct ocelot_port *port = netdev_priv(dev); netdev_features_t changed = dev->features ^ features; + struct ocelot_port_private *priv = netdev_priv(dev); + struct ocelot *ocelot = priv->port.ocelot; + int port = priv->chip_port; if ((dev->features & NETIF_F_HW_TC) > (features & NETIF_F_HW_TC) && - port->tc.offload_cnt) { + priv->tc.offload_cnt) { netdev_err(dev, "Cannot disable HW TC offload while offloads active\n"); return -EBUSY; } if (changed & NETIF_F_HW_VLAN_CTAG_FILTER) - ocelot_vlan_mode(port, features); + ocelot_vlan_mode(ocelot, port, features); return 0; } @@ -965,8 +1101,8 @@ static int ocelot_set_features(struct net_device *dev, static int ocelot_get_port_parent_id(struct net_device *dev, struct netdev_phys_item_id *ppid) { - struct ocelot_port *ocelot_port = netdev_priv(dev); - struct ocelot *ocelot = ocelot_port->ocelot; + struct ocelot_port_private *priv = netdev_priv(dev); + struct ocelot *ocelot = priv->port.ocelot; ppid->id_len = sizeof(ocelot->base_mac); memcpy(&ppid->id, &ocelot->base_mac, ppid->id_len); @@ -974,17 +1110,16 @@ static int ocelot_get_port_parent_id(struct net_device *dev, return 0; } -static int ocelot_hwstamp_get(struct ocelot_port *port, struct ifreq *ifr) +int ocelot_hwstamp_get(struct ocelot *ocelot, int port, struct ifreq *ifr) { - struct ocelot *ocelot = port->ocelot; - return copy_to_user(ifr->ifr_data, &ocelot->hwtstamp_config, sizeof(ocelot->hwtstamp_config)) ? -EFAULT : 0; } +EXPORT_SYMBOL(ocelot_hwstamp_get); -static int ocelot_hwstamp_set(struct ocelot_port *port, struct ifreq *ifr) +int ocelot_hwstamp_set(struct ocelot *ocelot, int port, struct ifreq *ifr) { - struct ocelot *ocelot = port->ocelot; + struct ocelot_port *ocelot_port = ocelot->ports[port]; struct hwtstamp_config cfg; if (copy_from_user(&cfg, ifr->ifr_data, sizeof(cfg))) @@ -997,16 +1132,16 @@ static int ocelot_hwstamp_set(struct ocelot_port *port, struct ifreq *ifr) /* Tx type sanity check */ switch (cfg.tx_type) { case HWTSTAMP_TX_ON: - port->ptp_cmd = IFH_REW_OP_TWO_STEP_PTP; + ocelot_port->ptp_cmd = IFH_REW_OP_TWO_STEP_PTP; break; case HWTSTAMP_TX_ONESTEP_SYNC: /* IFH_REW_OP_ONE_STEP_PTP updates the correctional field, we * need to update the origin time. */ - port->ptp_cmd = IFH_REW_OP_ORIGIN_PTP; + ocelot_port->ptp_cmd = IFH_REW_OP_ORIGIN_PTP; break; case HWTSTAMP_TX_OFF: - port->ptp_cmd = 0; + ocelot_port->ptp_cmd = 0; break; default: return -ERANGE; @@ -1045,11 +1180,13 @@ static int ocelot_hwstamp_set(struct ocelot_port *port, struct ifreq *ifr) return copy_to_user(ifr->ifr_data, &cfg, sizeof(cfg)) ? -EFAULT : 0; } +EXPORT_SYMBOL(ocelot_hwstamp_set); static int ocelot_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) { - struct ocelot_port *port = netdev_priv(dev); - struct ocelot *ocelot = port->ocelot; + struct ocelot_port_private *priv = netdev_priv(dev); + struct ocelot *ocelot = priv->port.ocelot; + int port = priv->chip_port; /* The function is only used for PTP operations for now */ if (!ocelot->ptp) @@ -1057,9 +1194,9 @@ static int ocelot_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) switch (cmd) { case SIOCSHWTSTAMP: - return ocelot_hwstamp_set(port, ifr); + return ocelot_hwstamp_set(ocelot, port, ifr); case SIOCGHWTSTAMP: - return ocelot_hwstamp_get(port, ifr); + return ocelot_hwstamp_get(ocelot, port, ifr); default: return -EOPNOTSUPP; } @@ -1073,9 +1210,9 @@ static const struct net_device_ops ocelot_port_netdev_ops = { .ndo_get_phys_port_name = ocelot_port_get_phys_port_name, .ndo_set_mac_address = ocelot_port_set_mac_address, .ndo_get_stats64 = ocelot_get_stats64, - .ndo_fdb_add = ocelot_fdb_add, - .ndo_fdb_del = ocelot_fdb_del, - .ndo_fdb_dump = ocelot_fdb_dump, + .ndo_fdb_add = ocelot_port_fdb_add, + .ndo_fdb_del = ocelot_port_fdb_del, + .ndo_fdb_dump = ocelot_port_fdb_dump, .ndo_vlan_rx_add_vid = ocelot_vlan_rx_add_vid, .ndo_vlan_rx_kill_vid = ocelot_vlan_rx_kill_vid, .ndo_set_features = ocelot_set_features, @@ -1084,10 +1221,8 @@ static const struct net_device_ops ocelot_port_netdev_ops = { .ndo_do_ioctl = ocelot_ioctl, }; -static void ocelot_get_strings(struct net_device *netdev, u32 sset, u8 *data) +void ocelot_get_strings(struct ocelot *ocelot, int port, u32 sset, u8 *data) { - struct ocelot_port *port = netdev_priv(netdev); - struct ocelot *ocelot = port->ocelot; int i; if (sset != ETH_SS_STATS) @@ -1097,6 +1232,17 @@ static void ocelot_get_strings(struct net_device *netdev, u32 sset, u8 *data) memcpy(data + i * ETH_GSTRING_LEN, ocelot->stats_layout[i].name, ETH_GSTRING_LEN); } +EXPORT_SYMBOL(ocelot_get_strings); + +static void ocelot_port_get_strings(struct net_device *netdev, u32 sset, + u8 *data) +{ + struct ocelot_port_private *priv = netdev_priv(netdev); + struct ocelot *ocelot = priv->port.ocelot; + int port = priv->chip_port; + + ocelot_get_strings(ocelot, port, sset, data); +} static void ocelot_update_stats(struct ocelot *ocelot) { @@ -1138,11 +1284,8 @@ static void ocelot_check_stats_work(struct work_struct *work) OCELOT_STATS_CHECK_DELAY); } -static void ocelot_get_ethtool_stats(struct net_device *dev, - struct ethtool_stats *stats, u64 *data) +void ocelot_get_ethtool_stats(struct ocelot *ocelot, int port, u64 *data) { - struct ocelot_port *port = netdev_priv(dev); - struct ocelot *ocelot = port->ocelot; int i; /* check and update now */ @@ -1150,28 +1293,42 @@ static void ocelot_get_ethtool_stats(struct net_device *dev, /* Copy all counters */ for (i = 0; i < ocelot->num_stats; i++) - *data++ = ocelot->stats[port->chip_port * ocelot->num_stats + i]; + *data++ = ocelot->stats[port * ocelot->num_stats + i]; } +EXPORT_SYMBOL(ocelot_get_ethtool_stats); -static int ocelot_get_sset_count(struct net_device *dev, int sset) +static void ocelot_port_get_ethtool_stats(struct net_device *dev, + struct ethtool_stats *stats, + u64 *data) { - struct ocelot_port *port = netdev_priv(dev); - struct ocelot *ocelot = port->ocelot; + struct ocelot_port_private *priv = netdev_priv(dev); + struct ocelot *ocelot = priv->port.ocelot; + int port = priv->chip_port; + + ocelot_get_ethtool_stats(ocelot, port, data); +} +int ocelot_get_sset_count(struct ocelot *ocelot, int port, int sset) +{ if (sset != ETH_SS_STATS) return -EOPNOTSUPP; + return ocelot->num_stats; } +EXPORT_SYMBOL(ocelot_get_sset_count); -static int ocelot_get_ts_info(struct net_device *dev, - struct ethtool_ts_info *info) +static int ocelot_port_get_sset_count(struct net_device *dev, int sset) { - struct ocelot_port *ocelot_port = netdev_priv(dev); - struct ocelot *ocelot = ocelot_port->ocelot; + struct ocelot_port_private *priv = netdev_priv(dev); + struct ocelot *ocelot = priv->port.ocelot; + int port = priv->chip_port; - if (!ocelot->ptp) - return ethtool_op_get_ts_info(dev, info); + return ocelot_get_sset_count(ocelot, port, sset); +} +int ocelot_get_ts_info(struct ocelot *ocelot, int port, + struct ethtool_ts_info *info) +{ info->phc_index = ocelot->ptp_clock ? ptp_clock_index(ocelot->ptp_clock) : -1; info->so_timestamping |= SOF_TIMESTAMPING_TX_SOFTWARE | @@ -1186,36 +1343,43 @@ static int ocelot_get_ts_info(struct net_device *dev, return 0; } +EXPORT_SYMBOL(ocelot_get_ts_info); + +static int ocelot_port_get_ts_info(struct net_device *dev, + struct ethtool_ts_info *info) +{ + struct ocelot_port_private *priv = netdev_priv(dev); + struct ocelot *ocelot = priv->port.ocelot; + int port = priv->chip_port; + + if (!ocelot->ptp) + return ethtool_op_get_ts_info(dev, info); + + return ocelot_get_ts_info(ocelot, port, info); +} static const struct ethtool_ops ocelot_ethtool_ops = { - .get_strings = ocelot_get_strings, - .get_ethtool_stats = ocelot_get_ethtool_stats, - .get_sset_count = ocelot_get_sset_count, + .get_strings = ocelot_port_get_strings, + .get_ethtool_stats = ocelot_port_get_ethtool_stats, + .get_sset_count = ocelot_port_get_sset_count, .get_link_ksettings = phy_ethtool_get_link_ksettings, .set_link_ksettings = phy_ethtool_set_link_ksettings, - .get_ts_info = ocelot_get_ts_info, + .get_ts_info = ocelot_port_get_ts_info, }; -static int ocelot_port_attr_stp_state_set(struct ocelot_port *ocelot_port, - struct switchdev_trans *trans, - u8 state) +void ocelot_bridge_stp_state_set(struct ocelot *ocelot, int port, u8 state) { - struct ocelot *ocelot = ocelot_port->ocelot; u32 port_cfg; - int port, i; + int p, i; - if (switchdev_trans_ph_prepare(trans)) - return 0; - - if (!(BIT(ocelot_port->chip_port) & ocelot->bridge_mask)) - return 0; + if (!(BIT(port) & ocelot->bridge_mask)) + return; - port_cfg = ocelot_read_gix(ocelot, ANA_PORT_PORT_CFG, - ocelot_port->chip_port); + port_cfg = ocelot_read_gix(ocelot, ANA_PORT_PORT_CFG, port); switch (state) { case BR_STATE_FORWARDING: - ocelot->bridge_fwd_mask |= BIT(ocelot_port->chip_port); + ocelot->bridge_fwd_mask |= BIT(port); /* Fallthrough */ case BR_STATE_LEARNING: port_cfg |= ANA_PORT_PORT_CFG_LEARN_ENA; @@ -1223,19 +1387,18 @@ static int ocelot_port_attr_stp_state_set(struct ocelot_port *ocelot_port, default: port_cfg &= ~ANA_PORT_PORT_CFG_LEARN_ENA; - ocelot->bridge_fwd_mask &= ~BIT(ocelot_port->chip_port); + ocelot->bridge_fwd_mask &= ~BIT(port); break; } - ocelot_write_gix(ocelot, port_cfg, ANA_PORT_PORT_CFG, - ocelot_port->chip_port); + ocelot_write_gix(ocelot, port_cfg, ANA_PORT_PORT_CFG, port); /* Apply FWD mask. The loop is needed to add/remove the current port as * a source for the other ports. */ - for (port = 0; port < ocelot->num_phys_ports; port++) { - if (ocelot->bridge_fwd_mask & BIT(port)) { - unsigned long mask = ocelot->bridge_fwd_mask & ~BIT(port); + for (p = 0; p < ocelot->num_phys_ports; p++) { + if (p == ocelot->cpu || (ocelot->bridge_fwd_mask & BIT(p))) { + unsigned long mask = ocelot->bridge_fwd_mask & ~BIT(p); for (i = 0; i < ocelot->num_phys_ports; i++) { unsigned long bond_mask = ocelot->lags[i]; @@ -1243,78 +1406,93 @@ static int ocelot_port_attr_stp_state_set(struct ocelot_port *ocelot_port, if (!bond_mask) continue; - if (bond_mask & BIT(port)) { + if (bond_mask & BIT(p)) { mask &= ~bond_mask; break; } } - ocelot_write_rix(ocelot, - BIT(ocelot->num_phys_ports) | mask, - ANA_PGID_PGID, PGID_SRC + port); + /* Avoid the NPI port from looping back to itself */ + if (p != ocelot->cpu) + mask |= BIT(ocelot->cpu); + + ocelot_write_rix(ocelot, mask, + ANA_PGID_PGID, PGID_SRC + p); } else { /* Only the CPU port, this is compatible with link * aggregation. */ ocelot_write_rix(ocelot, - BIT(ocelot->num_phys_ports), - ANA_PGID_PGID, PGID_SRC + port); + BIT(ocelot->cpu), + ANA_PGID_PGID, PGID_SRC + p); } } +} +EXPORT_SYMBOL(ocelot_bridge_stp_state_set); - return 0; +static void ocelot_port_attr_stp_state_set(struct ocelot *ocelot, int port, + struct switchdev_trans *trans, + u8 state) +{ + if (switchdev_trans_ph_prepare(trans)) + return; + + ocelot_bridge_stp_state_set(ocelot, port, state); } -static void ocelot_port_attr_ageing_set(struct ocelot_port *ocelot_port, +void ocelot_set_ageing_time(struct ocelot *ocelot, unsigned int msecs) +{ + ocelot_write(ocelot, ANA_AUTOAGE_AGE_PERIOD(msecs / 2), + ANA_AUTOAGE); +} +EXPORT_SYMBOL(ocelot_set_ageing_time); + +static void ocelot_port_attr_ageing_set(struct ocelot *ocelot, int port, unsigned long ageing_clock_t) { - struct ocelot *ocelot = ocelot_port->ocelot; unsigned long ageing_jiffies = clock_t_to_jiffies(ageing_clock_t); u32 ageing_time = jiffies_to_msecs(ageing_jiffies) / 1000; - ocelot_write(ocelot, ANA_AUTOAGE_AGE_PERIOD(ageing_time / 2), - ANA_AUTOAGE); + ocelot_set_ageing_time(ocelot, ageing_time); } -static void ocelot_port_attr_mc_set(struct ocelot_port *port, bool mc) +static void ocelot_port_attr_mc_set(struct ocelot *ocelot, int port, bool mc) { - struct ocelot *ocelot = port->ocelot; - u32 val = ocelot_read_gix(ocelot, ANA_PORT_CPU_FWD_CFG, - port->chip_port); + u32 cpu_fwd_mcast = ANA_PORT_CPU_FWD_CFG_CPU_IGMP_REDIR_ENA | + ANA_PORT_CPU_FWD_CFG_CPU_MLD_REDIR_ENA | + ANA_PORT_CPU_FWD_CFG_CPU_IPMC_CTRL_COPY_ENA; + u32 val = 0; if (mc) - val |= ANA_PORT_CPU_FWD_CFG_CPU_IGMP_REDIR_ENA | - ANA_PORT_CPU_FWD_CFG_CPU_MLD_REDIR_ENA | - ANA_PORT_CPU_FWD_CFG_CPU_IPMC_CTRL_COPY_ENA; - else - val &= ~(ANA_PORT_CPU_FWD_CFG_CPU_IGMP_REDIR_ENA | - ANA_PORT_CPU_FWD_CFG_CPU_MLD_REDIR_ENA | - ANA_PORT_CPU_FWD_CFG_CPU_IPMC_CTRL_COPY_ENA); + val = cpu_fwd_mcast; - ocelot_write_gix(ocelot, val, ANA_PORT_CPU_FWD_CFG, port->chip_port); + ocelot_rmw_gix(ocelot, val, cpu_fwd_mcast, + ANA_PORT_CPU_FWD_CFG, port); } static int ocelot_port_attr_set(struct net_device *dev, const struct switchdev_attr *attr, struct switchdev_trans *trans) { - struct ocelot_port *ocelot_port = netdev_priv(dev); + struct ocelot_port_private *priv = netdev_priv(dev); + struct ocelot *ocelot = priv->port.ocelot; + int port = priv->chip_port; int err = 0; switch (attr->id) { case SWITCHDEV_ATTR_ID_PORT_STP_STATE: - ocelot_port_attr_stp_state_set(ocelot_port, trans, + ocelot_port_attr_stp_state_set(ocelot, port, trans, attr->u.stp_state); break; case SWITCHDEV_ATTR_ID_BRIDGE_AGEING_TIME: - ocelot_port_attr_ageing_set(ocelot_port, attr->u.ageing_time); + ocelot_port_attr_ageing_set(ocelot, port, attr->u.ageing_time); break; case SWITCHDEV_ATTR_ID_BRIDGE_VLAN_FILTERING: - ocelot_port->vlan_aware = attr->u.vlan_filtering; - ocelot_vlan_port_apply(ocelot_port->ocelot, ocelot_port); + priv->vlan_aware = attr->u.vlan_filtering; + ocelot_port_vlan_filtering(ocelot, port, priv->vlan_aware); break; case SWITCHDEV_ATTR_ID_BRIDGE_MC_DISABLED: - ocelot_port_attr_mc_set(ocelot_port, !attr->u.mc_disabled); + ocelot_port_attr_mc_set(ocelot, port, !attr->u.mc_disabled); break; default: err = -EOPNOTSUPP; @@ -1376,15 +1554,17 @@ static int ocelot_port_obj_add_mdb(struct net_device *dev, const struct switchdev_obj_port_mdb *mdb, struct switchdev_trans *trans) { - struct ocelot_port *port = netdev_priv(dev); - struct ocelot *ocelot = port->ocelot; - struct ocelot_multicast *mc; + struct ocelot_port_private *priv = netdev_priv(dev); + struct ocelot_port *ocelot_port = &priv->port; + struct ocelot *ocelot = ocelot_port->ocelot; unsigned char addr[ETH_ALEN]; + struct ocelot_multicast *mc; + int port = priv->chip_port; u16 vid = mdb->vid; bool new = false; if (!vid) - vid = port->pvid; + vid = ocelot_port->pvid; mc = ocelot_multicast_get(ocelot, mdb->addr, vid); if (!mc) { @@ -1408,7 +1588,7 @@ static int ocelot_port_obj_add_mdb(struct net_device *dev, ocelot_mact_forget(ocelot, addr, vid); } - mc->ports |= BIT(port->chip_port); + mc->ports |= BIT(port); addr[2] = mc->ports << 0; addr[1] = mc->ports << 8; @@ -1418,14 +1598,16 @@ static int ocelot_port_obj_add_mdb(struct net_device *dev, static int ocelot_port_obj_del_mdb(struct net_device *dev, const struct switchdev_obj_port_mdb *mdb) { - struct ocelot_port *port = netdev_priv(dev); - struct ocelot *ocelot = port->ocelot; - struct ocelot_multicast *mc; + struct ocelot_port_private *priv = netdev_priv(dev); + struct ocelot_port *ocelot_port = &priv->port; + struct ocelot *ocelot = ocelot_port->ocelot; unsigned char addr[ETH_ALEN]; + struct ocelot_multicast *mc; + int port = priv->chip_port; u16 vid = mdb->vid; if (!vid) - vid = port->pvid; + vid = ocelot_port->pvid; mc = ocelot_multicast_get(ocelot, mdb->addr, vid); if (!mc) @@ -1437,7 +1619,7 @@ static int ocelot_port_obj_del_mdb(struct net_device *dev, addr[0] = 0; ocelot_mact_forget(ocelot, addr, vid); - mc->ports &= ~BIT(port->chip_port); + mc->ports &= ~BIT(port); if (!mc->ports) { list_del(&mc->list); devm_kfree(ocelot->dev, mc); @@ -1494,11 +1676,9 @@ static int ocelot_port_obj_del(struct net_device *dev, return ret; } -static int ocelot_port_bridge_join(struct ocelot_port *ocelot_port, - struct net_device *bridge) +int ocelot_port_bridge_join(struct ocelot *ocelot, int port, + struct net_device *bridge) { - struct ocelot *ocelot = ocelot_port->ocelot; - if (!ocelot->bridge_mask) { ocelot->hw_bridge_dev = bridge; } else { @@ -1508,26 +1688,25 @@ static int ocelot_port_bridge_join(struct ocelot_port *ocelot_port, return -ENODEV; } - ocelot->bridge_mask |= BIT(ocelot_port->chip_port); + ocelot->bridge_mask |= BIT(port); return 0; } +EXPORT_SYMBOL(ocelot_port_bridge_join); -static void ocelot_port_bridge_leave(struct ocelot_port *ocelot_port, - struct net_device *bridge) +int ocelot_port_bridge_leave(struct ocelot *ocelot, int port, + struct net_device *bridge) { - struct ocelot *ocelot = ocelot_port->ocelot; - - ocelot->bridge_mask &= ~BIT(ocelot_port->chip_port); + ocelot->bridge_mask &= ~BIT(port); if (!ocelot->bridge_mask) ocelot->hw_bridge_dev = NULL; - /* Clear bridge vlan settings before calling ocelot_vlan_port_apply */ - ocelot_port->vlan_aware = 0; - ocelot_port->pvid = 0; - ocelot_port->vid = 0; + ocelot_port_vlan_filtering(ocelot, port, 0); + ocelot_port_set_pvid(ocelot, port, 0); + return ocelot_port_set_native_vlan(ocelot, port, 0); } +EXPORT_SYMBOL(ocelot_port_bridge_leave); static void ocelot_set_aggr_pgids(struct ocelot *ocelot) { @@ -1587,20 +1766,18 @@ static void ocelot_setup_lag(struct ocelot *ocelot, int lag) } } -static int ocelot_port_lag_join(struct ocelot_port *ocelot_port, +static int ocelot_port_lag_join(struct ocelot *ocelot, int port, struct net_device *bond) { - struct ocelot *ocelot = ocelot_port->ocelot; - int p = ocelot_port->chip_port; - int lag, lp; struct net_device *ndev; u32 bond_mask = 0; + int lag, lp; rcu_read_lock(); for_each_netdev_in_bond_rcu(bond, ndev) { - struct ocelot_port *port = netdev_priv(ndev); + struct ocelot_port_private *priv = netdev_priv(ndev); - bond_mask |= BIT(port->chip_port); + bond_mask |= BIT(priv->chip_port); } rcu_read_unlock(); @@ -1609,17 +1786,17 @@ static int ocelot_port_lag_join(struct ocelot_port *ocelot_port, /* If the new port is the lowest one, use it as the logical port from * now on */ - if (p == lp) { - lag = p; - ocelot->lags[p] = bond_mask; - bond_mask &= ~BIT(p); + if (port == lp) { + lag = port; + ocelot->lags[port] = bond_mask; + bond_mask &= ~BIT(port); if (bond_mask) { lp = __ffs(bond_mask); ocelot->lags[lp] = 0; } } else { lag = lp; - ocelot->lags[lp] |= BIT(p); + ocelot->lags[lp] |= BIT(port); } ocelot_setup_lag(ocelot, lag); @@ -1628,34 +1805,32 @@ static int ocelot_port_lag_join(struct ocelot_port *ocelot_port, return 0; } -static void ocelot_port_lag_leave(struct ocelot_port *ocelot_port, +static void ocelot_port_lag_leave(struct ocelot *ocelot, int port, struct net_device *bond) { - struct ocelot *ocelot = ocelot_port->ocelot; - int p = ocelot_port->chip_port; u32 port_cfg; int i; /* Remove port from any lag */ for (i = 0; i < ocelot->num_phys_ports; i++) - ocelot->lags[i] &= ~BIT(ocelot_port->chip_port); + ocelot->lags[i] &= ~BIT(port); /* if it was the logical port of the lag, move the lag config to the * next port */ - if (ocelot->lags[p]) { - int n = __ffs(ocelot->lags[p]); + if (ocelot->lags[port]) { + int n = __ffs(ocelot->lags[port]); - ocelot->lags[n] = ocelot->lags[p]; - ocelot->lags[p] = 0; + ocelot->lags[n] = ocelot->lags[port]; + ocelot->lags[port] = 0; ocelot_setup_lag(ocelot, n); } - port_cfg = ocelot_read_gix(ocelot, ANA_PORT_PORT_CFG, p); + port_cfg = ocelot_read_gix(ocelot, ANA_PORT_PORT_CFG, port); port_cfg &= ~ANA_PORT_PORT_CFG_PORTID_VAL_M; - ocelot_write_gix(ocelot, port_cfg | ANA_PORT_PORT_CFG_PORTID_VAL(p), - ANA_PORT_PORT_CFG, p); + ocelot_write_gix(ocelot, port_cfg | ANA_PORT_PORT_CFG_PORTID_VAL(port), + ANA_PORT_PORT_CFG, port); ocelot_set_aggr_pgids(ocelot); } @@ -1670,31 +1845,30 @@ static int ocelot_netdevice_port_event(struct net_device *dev, unsigned long event, struct netdev_notifier_changeupper_info *info) { - struct ocelot_port *ocelot_port = netdev_priv(dev); + struct ocelot_port_private *priv = netdev_priv(dev); + struct ocelot_port *ocelot_port = &priv->port; + struct ocelot *ocelot = ocelot_port->ocelot; + int port = priv->chip_port; int err = 0; - if (!ocelot_netdevice_dev_check(dev)) - return 0; - switch (event) { case NETDEV_CHANGEUPPER: if (netif_is_bridge_master(info->upper_dev)) { - if (info->linking) - err = ocelot_port_bridge_join(ocelot_port, + if (info->linking) { + err = ocelot_port_bridge_join(ocelot, port, info->upper_dev); - else - ocelot_port_bridge_leave(ocelot_port, - info->upper_dev); - - ocelot_vlan_port_apply(ocelot_port->ocelot, - ocelot_port); + } else { + err = ocelot_port_bridge_leave(ocelot, port, + info->upper_dev); + priv->vlan_aware = false; + } } if (netif_is_lag_master(info->upper_dev)) { if (info->linking) - err = ocelot_port_lag_join(ocelot_port, + err = ocelot_port_lag_join(ocelot, port, info->upper_dev); else - ocelot_port_lag_leave(ocelot_port, + ocelot_port_lag_leave(ocelot, port, info->upper_dev); } break; @@ -1712,12 +1886,16 @@ static int ocelot_netdevice_event(struct notifier_block *unused, struct net_device *dev = netdev_notifier_info_to_dev(ptr); int ret = 0; + if (!ocelot_netdevice_dev_check(dev)) + return 0; + if (event == NETDEV_PRECHANGEUPPER && netif_is_lag_master(info->upper_dev)) { struct netdev_lag_upper_info *lag_upper_info = info->upper_info; struct netlink_ext_ack *extack; - if (lag_upper_info->tx_type != NETDEV_LAG_TX_TYPE_HASH) { + if (lag_upper_info && + lag_upper_info->tx_type != NETDEV_LAG_TX_TYPE_HASH) { extack = netdev_notifier_info_to_extack(&info->info); NL_SET_ERR_MSG_MOD(extack, "LAG device using unsupported Tx type"); @@ -1971,14 +2149,18 @@ static struct ptp_clock_info ocelot_ptp_clock_info = { static int ocelot_init_timestamp(struct ocelot *ocelot) { + struct ptp_clock *ptp_clock; + ocelot->ptp_info = ocelot_ptp_clock_info; - ocelot->ptp_clock = ptp_clock_register(&ocelot->ptp_info, ocelot->dev); - if (IS_ERR(ocelot->ptp_clock)) - return PTR_ERR(ocelot->ptp_clock); + ptp_clock = ptp_clock_register(&ocelot->ptp_info, ocelot->dev); + if (IS_ERR(ptp_clock)) + return PTR_ERR(ptp_clock); /* Check if PHC support is missing at the configuration level */ - if (!ocelot->ptp_clock) + if (!ptp_clock) return 0; + ocelot->ptp_clock = ptp_clock; + ocelot_write(ocelot, SYS_PTP_CFG_PTP_STAMP_WID(30), SYS_PTP_CFG); ocelot_write(ocelot, 0xffffffff, ANA_TABLES_PTP_ID_LOW); ocelot_write(ocelot, 0xffffffff, ANA_TABLES_PTP_ID_HIGH); @@ -1993,24 +2175,97 @@ static int ocelot_init_timestamp(struct ocelot *ocelot) return 0; } +static void ocelot_port_set_mtu(struct ocelot *ocelot, int port, size_t mtu) +{ + struct ocelot_port *ocelot_port = ocelot->ports[port]; + int atop_wm; + + ocelot_port_writel(ocelot_port, mtu, DEV_MAC_MAXLEN_CFG); + + /* Set Pause WM hysteresis + * 152 = 6 * mtu / OCELOT_BUFFER_CELL_SZ + * 101 = 4 * mtu / OCELOT_BUFFER_CELL_SZ + */ + ocelot_write_rix(ocelot, SYS_PAUSE_CFG_PAUSE_ENA | + SYS_PAUSE_CFG_PAUSE_STOP(101) | + SYS_PAUSE_CFG_PAUSE_START(152), SYS_PAUSE_CFG, port); + + /* Tail dropping watermark */ + atop_wm = (ocelot->shared_queue_sz - 9 * mtu) / OCELOT_BUFFER_CELL_SZ; + ocelot_write_rix(ocelot, ocelot_wm_enc(9 * mtu), + SYS_ATOP, port); + ocelot_write(ocelot, ocelot_wm_enc(atop_wm), SYS_ATOP_TOT_CFG); +} + +void ocelot_init_port(struct ocelot *ocelot, int port) +{ + struct ocelot_port *ocelot_port = ocelot->ports[port]; + + skb_queue_head_init(&ocelot_port->tx_skbs); + + /* Basic L2 initialization */ + + /* Set MAC IFG Gaps + * FDX: TX_IFG = 5, RX_IFG1 = RX_IFG2 = 0 + * !FDX: TX_IFG = 5, RX_IFG1 = RX_IFG2 = 5 + */ + ocelot_port_writel(ocelot_port, DEV_MAC_IFG_CFG_TX_IFG(5), + DEV_MAC_IFG_CFG); + + /* Load seed (0) and set MAC HDX late collision */ + ocelot_port_writel(ocelot_port, DEV_MAC_HDX_CFG_LATE_COL_POS(67) | + DEV_MAC_HDX_CFG_SEED_LOAD, + DEV_MAC_HDX_CFG); + mdelay(1); + ocelot_port_writel(ocelot_port, DEV_MAC_HDX_CFG_LATE_COL_POS(67), + DEV_MAC_HDX_CFG); + + /* Set Max Length and maximum tags allowed */ + ocelot_port_set_mtu(ocelot, port, VLAN_ETH_FRAME_LEN); + ocelot_port_writel(ocelot_port, DEV_MAC_TAGS_CFG_TAG_ID(ETH_P_8021AD) | + DEV_MAC_TAGS_CFG_VLAN_AWR_ENA | + DEV_MAC_TAGS_CFG_VLAN_LEN_AWR_ENA, + DEV_MAC_TAGS_CFG); + + /* Set SMAC of Pause frame (00:00:00:00:00:00) */ + ocelot_port_writel(ocelot_port, 0, DEV_MAC_FC_MAC_HIGH_CFG); + ocelot_port_writel(ocelot_port, 0, DEV_MAC_FC_MAC_LOW_CFG); + + /* Drop frames with multicast source address */ + ocelot_rmw_gix(ocelot, ANA_PORT_DROP_CFG_DROP_MC_SMAC_ENA, + ANA_PORT_DROP_CFG_DROP_MC_SMAC_ENA, + ANA_PORT_DROP_CFG, port); + + /* Set default VLAN and tag type to 8021Q. */ + ocelot_rmw_gix(ocelot, REW_PORT_VLAN_CFG_PORT_TPID(ETH_P_8021Q), + REW_PORT_VLAN_CFG_PORT_TPID_M, + REW_PORT_VLAN_CFG, port); + + /* Enable vcap lookups */ + ocelot_vcap_enable(ocelot, port); +} +EXPORT_SYMBOL(ocelot_init_port); + int ocelot_probe_port(struct ocelot *ocelot, u8 port, void __iomem *regs, struct phy_device *phy) { + struct ocelot_port_private *priv; struct ocelot_port *ocelot_port; struct net_device *dev; int err; - dev = alloc_etherdev(sizeof(struct ocelot_port)); + dev = alloc_etherdev(sizeof(struct ocelot_port_private)); if (!dev) return -ENOMEM; SET_NETDEV_DEV(dev, ocelot->dev); - ocelot_port = netdev_priv(dev); - ocelot_port->dev = dev; + priv = netdev_priv(dev); + priv->dev = dev; + priv->phy = phy; + priv->chip_port = port; + ocelot_port = &priv->port; ocelot_port->ocelot = ocelot; ocelot_port->regs = regs; - ocelot_port->chip_port = port; - ocelot_port->phy = phy; ocelot->ports[port] = ocelot_port; dev->netdev_ops = &ocelot_port_netdev_ops; @@ -2025,33 +2280,81 @@ int ocelot_probe_port(struct ocelot *ocelot, u8 port, ocelot_mact_learn(ocelot, PGID_CPU, dev->dev_addr, ocelot_port->pvid, ENTRYTYPE_LOCKED); - INIT_LIST_HEAD(&ocelot_port->skbs); + ocelot_init_port(ocelot, port); err = register_netdev(dev); if (err) { dev_err(ocelot->dev, "register_netdev failed\n"); - goto err_register_netdev; + free_netdev(dev); } - /* Basic L2 initialization */ - ocelot_vlan_port_apply(ocelot, ocelot_port); + return err; +} +EXPORT_SYMBOL(ocelot_probe_port); - /* Enable vcap lookups */ - ocelot_vcap_enable(ocelot, ocelot_port); +void ocelot_set_cpu_port(struct ocelot *ocelot, int cpu, + enum ocelot_tag_prefix injection, + enum ocelot_tag_prefix extraction) +{ + /* Configure and enable the CPU port. */ + ocelot_write_rix(ocelot, 0, ANA_PGID_PGID, cpu); + ocelot_write_rix(ocelot, BIT(cpu), ANA_PGID_PGID, PGID_CPU); + ocelot_write_gix(ocelot, ANA_PORT_PORT_CFG_RECV_ENA | + ANA_PORT_PORT_CFG_PORTID_VAL(cpu), + ANA_PORT_PORT_CFG, cpu); - return 0; + /* If the CPU port is a physical port, set up the port in Node + * Processor Interface (NPI) mode. This is the mode through which + * frames can be injected from and extracted to an external CPU. + * Only one port can be an NPI at the same time. + */ + if (cpu < ocelot->num_phys_ports) { + int mtu = VLAN_ETH_FRAME_LEN + OCELOT_TAG_LEN; -err_register_netdev: - free_netdev(dev); - return err; + ocelot_write(ocelot, QSYS_EXT_CPU_CFG_EXT_CPUQ_MSK_M | + QSYS_EXT_CPU_CFG_EXT_CPU_PORT(cpu), + QSYS_EXT_CPU_CFG); + + if (injection == OCELOT_TAG_PREFIX_SHORT) + mtu += OCELOT_SHORT_PREFIX_LEN; + else if (injection == OCELOT_TAG_PREFIX_LONG) + mtu += OCELOT_LONG_PREFIX_LEN; + + ocelot_port_set_mtu(ocelot, cpu, mtu); + } + + /* CPU port Injection/Extraction configuration */ + ocelot_write_rix(ocelot, QSYS_SWITCH_PORT_MODE_INGRESS_DROP_MODE | + QSYS_SWITCH_PORT_MODE_SCH_NEXT_CFG(1) | + QSYS_SWITCH_PORT_MODE_PORT_ENA, + QSYS_SWITCH_PORT_MODE, cpu); + ocelot_write_rix(ocelot, SYS_PORT_MODE_INCL_XTR_HDR(extraction) | + SYS_PORT_MODE_INCL_INJ_HDR(injection), + SYS_PORT_MODE, cpu); + + /* Configure the CPU port to be VLAN aware */ + ocelot_write_gix(ocelot, ANA_PORT_VLAN_CFG_VLAN_VID(0) | + ANA_PORT_VLAN_CFG_VLAN_AWARE_ENA | + ANA_PORT_VLAN_CFG_VLAN_POP_CNT(1), + ANA_PORT_VLAN_CFG, cpu); + + ocelot->cpu = cpu; } -EXPORT_SYMBOL(ocelot_probe_port); +EXPORT_SYMBOL(ocelot_set_cpu_port); int ocelot_init(struct ocelot *ocelot) { - u32 port; - int i, ret, cpu = ocelot->num_phys_ports; char queue_name[32]; + int i, ret; + u32 port; + + if (ocelot->ops->reset) { + ret = ocelot->ops->reset(ocelot); + if (ret) { + dev_err(ocelot->dev, "Switch reset failed\n"); + return ret; + } + } ocelot->lags = devm_kcalloc(ocelot->dev, ocelot->num_phys_ports, sizeof(u32), GFP_KERNEL); @@ -2073,6 +2376,7 @@ int ocelot_init(struct ocelot *ocelot) if (!ocelot->stats_queue) return -ENOMEM; + INIT_LIST_HEAD(&ocelot->multicast); ocelot_mact_init(ocelot); ocelot_vlan_init(ocelot); ocelot_ace_init(ocelot); @@ -2130,13 +2434,6 @@ int ocelot_init(struct ocelot *ocelot) ocelot_write_rix(ocelot, 0, ANA_PGID_PGID, PGID_SRC + port); } - /* Configure and enable the CPU port. */ - ocelot_write_rix(ocelot, 0, ANA_PGID_PGID, cpu); - ocelot_write_rix(ocelot, BIT(cpu), ANA_PGID_PGID, PGID_CPU); - ocelot_write_gix(ocelot, ANA_PORT_PORT_CFG_RECV_ENA | - ANA_PORT_PORT_CFG_PORTID_VAL(cpu), - ANA_PORT_PORT_CFG, cpu); - /* Allow broadcast MAC frames. */ for (i = ocelot->num_phys_ports + 1; i < PGID_CPU; i++) { u32 val = ANA_PGID_PGID_PGID(GENMASK(ocelot->num_phys_ports - 1, 0)); @@ -2149,13 +2446,6 @@ int ocelot_init(struct ocelot *ocelot) ocelot_write_rix(ocelot, 0, ANA_PGID_PGID, PGID_MCIPV4); ocelot_write_rix(ocelot, 0, ANA_PGID_PGID, PGID_MCIPV6); - /* CPU port Injection/Extraction configuration */ - ocelot_write_rix(ocelot, QSYS_SWITCH_PORT_MODE_INGRESS_DROP_MODE | - QSYS_SWITCH_PORT_MODE_SCH_NEXT_CFG(1) | - QSYS_SWITCH_PORT_MODE_PORT_ENA, - QSYS_SWITCH_PORT_MODE, cpu); - ocelot_write_rix(ocelot, SYS_PORT_MODE_INCL_XTR_HDR(1) | - SYS_PORT_MODE_INCL_INJ_HDR(1), SYS_PORT_MODE, cpu); /* Allow manual injection via DEVCPU_QS registers, and byte swap these * registers endianness. */ @@ -2196,26 +2486,19 @@ EXPORT_SYMBOL(ocelot_init); void ocelot_deinit(struct ocelot *ocelot) { - struct list_head *pos, *tmp; struct ocelot_port *port; - struct ocelot_skb *entry; int i; cancel_delayed_work(&ocelot->stats_work); destroy_workqueue(ocelot->stats_queue); mutex_destroy(&ocelot->stats_lock); ocelot_ace_deinit(); + if (ocelot->ptp_clock) + ptp_clock_unregister(ocelot->ptp_clock); for (i = 0; i < ocelot->num_phys_ports; i++) { port = ocelot->ports[i]; - - list_for_each_safe(pos, tmp, &port->skbs) { - entry = list_entry(pos, struct ocelot_skb, head); - - list_del(pos); - dev_kfree_skb_any(entry->skb); - kfree(entry); - } + skb_queue_purge(&port->tx_skbs); } } EXPORT_SYMBOL(ocelot_deinit); |