diff --git a/drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.c b/drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.c index 2fd05dd18d46..a9b30a72ddad 100644 --- a/drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.c +++ b/drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.c @@ -110,6 +110,63 @@ static u16 dpaa2_switch_port_set_fdb(struct ethsw_port_priv *port_priv, return 0; } +static void dpaa2_switch_fdb_get_flood_cfg(struct ethsw_core *ethsw, u16 fdb_id, + enum dpsw_flood_type type, + struct dpsw_egress_flood_cfg *cfg) +{ + int i = 0, j; + + memset(cfg, 0, sizeof(*cfg)); + + /* Add all the DPAA2 switch ports found in the same bridging domain to + * the egress flooding domain + */ + for (j = 0; j < ethsw->sw_attr.num_ifs; j++) { + if (!ethsw->ports[j]) + continue; + if (ethsw->ports[j]->fdb->fdb_id != fdb_id) + continue; + + if (type == DPSW_BROADCAST && ethsw->ports[j]->bcast_flood) + cfg->if_id[i++] = ethsw->ports[j]->idx; + else if (type == DPSW_FLOODING && ethsw->ports[j]->ucast_flood) + cfg->if_id[i++] = ethsw->ports[j]->idx; + } + + /* Add the CTRL interface to the egress flooding domain */ + cfg->if_id[i++] = ethsw->sw_attr.num_ifs; + + cfg->fdb_id = fdb_id; + cfg->flood_type = type; + cfg->num_ifs = i; +} + +static int dpaa2_switch_fdb_set_egress_flood(struct ethsw_core *ethsw, u16 fdb_id) +{ + struct dpsw_egress_flood_cfg flood_cfg; + int err; + + /* Setup broadcast flooding domain */ + dpaa2_switch_fdb_get_flood_cfg(ethsw, fdb_id, DPSW_BROADCAST, &flood_cfg); + err = dpsw_set_egress_flood(ethsw->mc_io, 0, ethsw->dpsw_handle, + &flood_cfg); + if (err) { + dev_err(ethsw->dev, "dpsw_set_egress_flood() = %d\n", err); + return err; + } + + /* Setup unknown flooding domain */ + dpaa2_switch_fdb_get_flood_cfg(ethsw, fdb_id, DPSW_FLOODING, &flood_cfg); + err = dpsw_set_egress_flood(ethsw->mc_io, 0, ethsw->dpsw_handle, + &flood_cfg); + if (err) { + dev_err(ethsw->dev, "dpsw_set_egress_flood() = %d\n", err); + return err; + } + + return 0; +} + static void *dpaa2_iova_to_virt(struct iommu_domain *domain, dma_addr_t iova_addr) { @@ -1184,6 +1241,88 @@ static int dpaa2_switch_port_attr_stp_state_set(struct net_device *netdev, return dpaa2_switch_port_set_stp_state(port_priv, state); } +static int dpaa2_switch_port_set_learning(struct ethsw_port_priv *port_priv, bool enable) +{ + struct ethsw_core *ethsw = port_priv->ethsw_data; + enum dpsw_learning_mode learn_mode; + int err; + + if (enable) + learn_mode = DPSW_LEARNING_MODE_HW; + else + learn_mode = DPSW_LEARNING_MODE_DIS; + + err = dpsw_if_set_learning_mode(ethsw->mc_io, 0, ethsw->dpsw_handle, + port_priv->idx, learn_mode); + if (err) + netdev_err(port_priv->netdev, "dpsw_if_set_learning_mode err %d\n", err); + + if (!enable) + dpaa2_switch_port_fast_age(port_priv); + + return err; +} + +static int dpaa2_switch_port_flood(struct ethsw_port_priv *port_priv, + struct switchdev_brport_flags flags) +{ + struct ethsw_core *ethsw = port_priv->ethsw_data; + + if (flags.mask & BR_BCAST_FLOOD) + port_priv->bcast_flood = !!(flags.val & BR_BCAST_FLOOD); + + if (flags.mask & BR_FLOOD) + port_priv->ucast_flood = !!(flags.val & BR_FLOOD); + + return dpaa2_switch_fdb_set_egress_flood(ethsw, port_priv->fdb->fdb_id); +} + +static int dpaa2_switch_port_pre_bridge_flags(struct net_device *netdev, + struct switchdev_brport_flags flags, + struct netlink_ext_ack *extack) +{ + if (flags.mask & ~(BR_LEARNING | BR_BCAST_FLOOD | BR_FLOOD | + BR_MCAST_FLOOD)) + return -EINVAL; + + if (flags.mask & (BR_FLOOD | BR_MCAST_FLOOD)) { + bool multicast = !!(flags.val & BR_MCAST_FLOOD); + bool unicast = !!(flags.val & BR_FLOOD); + + if (unicast != multicast) { + NL_SET_ERR_MSG_MOD(extack, + "Cannot configure multicast flooding independently of unicast"); + return -EINVAL; + } + } + + return 0; +} + +static int dpaa2_switch_port_bridge_flags(struct net_device *netdev, + struct switchdev_brport_flags flags, + struct netlink_ext_ack *extack) +{ + struct ethsw_port_priv *port_priv = netdev_priv(netdev); + int err; + + if (flags.mask & BR_LEARNING) { + bool learn_ena = !!(flags.val & BR_LEARNING); + + err = dpaa2_switch_port_set_learning(port_priv, learn_ena); + if (err) + return err; + } + + if (flags.mask & (BR_BCAST_FLOOD | BR_FLOOD | BR_MCAST_FLOOD)) { + err = dpaa2_switch_port_flood(port_priv, flags); + if (err) + return err; + } + + return 0; +} + static int dpaa2_switch_port_attr_set(struct net_device *netdev, const struct switchdev_attr *attr, struct netlink_ext_ack *extack) @@ -1202,6 +1341,12 @@ static int dpaa2_switch_port_attr_set(struct net_device *netdev, return -EOPNOTSUPP; } break; + case SWITCHDEV_ATTR_ID_PORT_PRE_BRIDGE_FLAGS: + err = dpaa2_switch_port_pre_bridge_flags(netdev, attr->u.brport_flags, extack); + break; + case SWITCHDEV_ATTR_ID_PORT_BRIDGE_FLAGS: + err = dpaa2_switch_port_bridge_flags(netdev, attr->u.brport_flags, extack); + break; default: err = -EOPNOTSUPP; break; @@ -1442,48 +1587,6 @@ static int dpaa2_switch_port_attr_set_event(struct net_device *netdev, return notifier_from_errno(err); } -static int dpaa2_switch_fdb_set_egress_flood(struct ethsw_core *ethsw, u16 fdb_id) -{ - struct dpsw_egress_flood_cfg flood_cfg; - int i = 0, j; - int err; - - /* Add all the DPAA2 switch ports found in the same bridging domain to - * the egress flooding domain - */ - for (j = 0; j < ethsw->sw_attr.num_ifs; j++) - if (ethsw->ports[j] && ethsw->ports[j]->fdb->fdb_id == fdb_id) - flood_cfg.if_id[i++] = ethsw->ports[j]->idx; - - /* Add the CTRL interface to the egress flooding domain */ - flood_cfg.if_id[i++] = ethsw->sw_attr.num_ifs; - - /* Use the FDB of the first dpaa2 switch port added to the bridge */ - flood_cfg.fdb_id = fdb_id; - - /* Setup broadcast flooding domain */ - flood_cfg.flood_type = DPSW_BROADCAST; - flood_cfg.num_ifs = i; - err = dpsw_set_egress_flood(ethsw->mc_io, 0, ethsw->dpsw_handle, - &flood_cfg); - if (err) { - dev_err(ethsw->dev, "dpsw_set_egress_flood() = %d\n", err); - return err; - } - - /* Setup unknown flooding domain */ - flood_cfg.flood_type = DPSW_FLOODING; - flood_cfg.num_ifs = i; - err = dpsw_set_egress_flood(ethsw->mc_io, 0, ethsw->dpsw_handle, - &flood_cfg); - if (err) { - dev_err(ethsw->dev, "dpsw_set_egress_flood() = %d\n", err); - return err; - } - - return 0; -} - static int dpaa2_switch_port_bridge_join(struct net_device *netdev, struct net_device *upper_dev) { @@ -1492,6 +1595,7 @@ static int dpaa2_switch_port_bridge_join(struct net_device *netdev, struct ethsw_port_priv *other_port_priv; struct net_device *other_dev; struct list_head *iter; + bool learn_ena; int err; netdev_for_each_lower_dev(upper_dev, other_dev, iter) { @@ -1513,6 +1617,10 @@ static int dpaa2_switch_port_bridge_join(struct net_device *netdev, dpaa2_switch_port_set_fdb(port_priv, upper_dev); + /* Inherit the initial bridge port learning state */ + learn_ena = br_port_flag_is_set(netdev, BR_LEARNING); + err = dpaa2_switch_port_set_learning(port_priv, learn_ena); + /* Setup the egress flood policy (broadcast, unknown unicast) */ err = dpaa2_switch_fdb_set_egress_flood(ethsw, port_priv->fdb->fdb_id); if (err) @@ -1570,6 +1678,13 @@ static int dpaa2_switch_port_bridge_leave(struct net_device *netdev) if (err) netdev_err(netdev, "Unable to restore RX VLANs to the new FDB, err (%d)\n", err); + /* Reset the flooding state to denote that this port can send any + * packet in standalone mode. With this, we are also ensuring that any + * later bridge join will have the flooding flag on. + */ + port_priv->bcast_flood = true; + port_priv->ucast_flood = true; + /* Setup the egress flood policy (broadcast, unknown unicast). * When the port is not under a bridge, only the CTRL interface is part * of the flooding domain besides the actual port @@ -1583,6 +1698,11 @@ static int dpaa2_switch_port_bridge_leave(struct net_device *netdev) if (err) return err; + /* No HW learning when not under a bridge */ + err = dpaa2_switch_port_set_learning(port_priv, false); + if (err) + return err; + /* Add the VLAN 1 as PVID when not under a bridge. We need this since * the dpaa2 switch interfaces are not capable to be VLAN unaware */ @@ -1885,6 +2005,9 @@ static void dpaa2_switch_rx(struct dpaa2_switch_fq *fq, skb->dev = netdev; skb->protocol = eth_type_trans(skb, skb->dev); + /* Setup the offload_fwd_mark only if the port is under a bridge */ + skb->offload_fwd_mark = !!(port_priv->fdb->bridge_dev); + netif_receive_skb(skb); return; @@ -2650,6 +2773,9 @@ static int dpaa2_switch_probe_port(struct ethsw_core *ethsw, port_netdev->needed_headroom = DPAA2_SWITCH_NEEDED_HEADROOM; + port_priv->bcast_flood = true; + port_priv->ucast_flood = true; + /* Set MTU limits */ port_netdev->min_mtu = ETH_MIN_MTU; port_netdev->max_mtu = ETHSW_MAX_FRAME_LENGTH; @@ -2672,6 +2798,10 @@ static int dpaa2_switch_probe_port(struct ethsw_core *ethsw, if (err) goto err_port_probe; + err = dpaa2_switch_port_set_learning(port_priv, false); + if (err) + goto err_port_probe; + return 0; err_port_probe: diff --git a/drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.h b/drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.h index 933563064015..549218994243 100644 --- a/drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.h +++ b/drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.h @@ -105,13 +105,14 @@ struct ethsw_port_priv { struct ethsw_core *ethsw_data; u8 link_state; u8 stp_state; - bool flood; u8 vlans[VLAN_VID_MASK + 1]; u16 pvid; u16 tx_qdid; struct dpaa2_switch_fdb *fdb; + bool bcast_flood; + bool ucast_flood; }; /* Switch data */ diff --git a/drivers/net/ethernet/freescale/dpaa2/dpsw-cmd.h b/drivers/net/ethernet/freescale/dpaa2/dpsw-cmd.h index 996a59dcd01d..24b17d6e09af 100644 --- a/drivers/net/ethernet/freescale/dpaa2/dpsw-cmd.h +++ b/drivers/net/ethernet/freescale/dpaa2/dpsw-cmd.h @@ -83,6 +83,7 @@ #define DPSW_CMDID_CTRL_IF_SET_QUEUE DPSW_CMD_ID(0x0A6) #define DPSW_CMDID_SET_EGRESS_FLOOD DPSW_CMD_ID(0x0AC) +#define DPSW_CMDID_IF_SET_LEARNING_MODE DPSW_CMD_ID(0x0AD) /* Macros for accessing command fields smaller than 1byte */ #define DPSW_MASK(field) \ @@ -447,5 +448,14 @@ struct dpsw_cmd_set_egress_flood { u8 pad[5]; __le64 if_id; }; + +#define DPSW_LEARNING_MODE_SHIFT 0 +#define DPSW_LEARNING_MODE_SIZE 4 + +struct dpsw_cmd_if_set_learning_mode { + __le16 if_id; + /* only the first 4 bits from LSB */ + u8 mode; +}; #pragma pack(pop) #endif /* __FSL_DPSW_CMD_H */ diff --git a/drivers/net/ethernet/freescale/dpaa2/dpsw.c b/drivers/net/ethernet/freescale/dpaa2/dpsw.c index 56f3c23fce07..6c787d4b85f9 100644 --- a/drivers/net/ethernet/freescale/dpaa2/dpsw.c +++ b/drivers/net/ethernet/freescale/dpaa2/dpsw.c @@ -1327,3 +1327,30 @@ int dpsw_set_egress_flood(struct fsl_mc_io *mc_io, u32 cmd_flags, u16 token, return mc_send_command(mc_io, &cmd); } + +/** + * dpsw_if_set_learning_mode() - Configure the learning mode on an interface. + * If this API is used, it will take precedence over the FDB configuration. + * @mc_io: Pointer to MC portal's I/O object + * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' + * @token: Token of DPSW object + * @if_id: InterfaceID + * @mode: Learning mode + * + * Return: Completion status. '0' on Success; Error code otherwise. + */ +int dpsw_if_set_learning_mode(struct fsl_mc_io *mc_io, u32 cmd_flags, u16 token, + u16 if_id, enum dpsw_learning_mode mode) +{ + struct dpsw_cmd_if_set_learning_mode *cmd_params; + struct fsl_mc_command cmd = { 0 }; + + cmd.header = mc_encode_cmd_header(DPSW_CMDID_IF_SET_LEARNING_MODE, + cmd_flags, + token); + cmd_params = (struct dpsw_cmd_if_set_learning_mode *)cmd.params; + cmd_params->if_id = cpu_to_le16(if_id); + dpsw_set_field(cmd_params->mode, LEARNING_MODE, mode); + + return mc_send_command(mc_io, &cmd); +} diff --git a/drivers/net/ethernet/freescale/dpaa2/dpsw.h b/drivers/net/ethernet/freescale/dpaa2/dpsw.h index f108cc61bb27..96837b10cc94 100644 --- a/drivers/net/ethernet/freescale/dpaa2/dpsw.h +++ b/drivers/net/ethernet/freescale/dpaa2/dpsw.h @@ -532,11 +532,11 @@ int dpsw_fdb_remove_multicast(struct fsl_mc_io *mc_io, u32 cmd_flags, u16 token, u16 fdb_id, const struct dpsw_fdb_multicast_cfg *cfg); /** - * enum dpsw_fdb_learning_mode - Auto-learning modes - * @DPSW_FDB_LEARNING_MODE_DIS: Disable Auto-learning - * @DPSW_FDB_LEARNING_MODE_HW: Enable HW auto-Learning - * @DPSW_FDB_LEARNING_MODE_NON_SECURE: Enable None secure learning by CPU - * @DPSW_FDB_LEARNING_MODE_SECURE: Enable secure learning by CPU + * enum dpsw_learning_mode - Auto-learning modes + * @DPSW_LEARNING_MODE_DIS: Disable Auto-learning + * @DPSW_LEARNING_MODE_HW: Enable HW auto-Learning + * @DPSW_LEARNING_MODE_NON_SECURE: Enable None secure learning by CPU + * @DPSW_LEARNING_MODE_SECURE: Enable secure learning by CPU * * NONE - SECURE LEARNING * SMAC found DMAC found CTLU Action @@ -561,11 +561,11 @@ int dpsw_fdb_remove_multicast(struct fsl_mc_io *mc_io, u32 cmd_flags, u16 token, * - - Forward frame to * 1. Control interface */ -enum dpsw_fdb_learning_mode { - DPSW_FDB_LEARNING_MODE_DIS = 0, - DPSW_FDB_LEARNING_MODE_HW = 1, - DPSW_FDB_LEARNING_MODE_NON_SECURE = 2, - DPSW_FDB_LEARNING_MODE_SECURE = 3 +enum dpsw_learning_mode { + DPSW_LEARNING_MODE_DIS = 0, + DPSW_LEARNING_MODE_HW = 1, + DPSW_LEARNING_MODE_NON_SECURE = 2, + DPSW_LEARNING_MODE_SECURE = 3 }; /** @@ -579,7 +579,7 @@ enum dpsw_fdb_learning_mode { struct dpsw_fdb_attr { u16 max_fdb_entries; u16 fdb_ageing_time; - enum dpsw_fdb_learning_mode learning_mode; + enum dpsw_learning_mode learning_mode; u16 num_fdb_mc_groups; u16 max_fdb_mc_groups; }; @@ -625,4 +625,7 @@ struct dpsw_egress_flood_cfg { int dpsw_set_egress_flood(struct fsl_mc_io *mc_io, u32 cmd_flags, u16 token, const struct dpsw_egress_flood_cfg *cfg); +int dpsw_if_set_learning_mode(struct fsl_mc_io *mc_io, u32 cmd_flags, u16 token, + u16 if_id, enum dpsw_learning_mode mode); + #endif /* __FSL_DPSW_H */