aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/staging/fsl-dpaa2
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/staging/fsl-dpaa2')
-rw-r--r--drivers/staging/fsl-dpaa2/ethsw/TODO1
-rw-r--r--drivers/staging/fsl-dpaa2/ethsw/dpsw-cmd.h15
-rw-r--r--drivers/staging/fsl-dpaa2/ethsw/dpsw.c51
-rw-r--r--drivers/staging/fsl-dpaa2/ethsw/dpsw.h56
-rw-r--r--drivers/staging/fsl-dpaa2/ethsw/ethsw-ethtool.c44
-rw-r--r--drivers/staging/fsl-dpaa2/ethsw/ethsw.c309
-rw-r--r--drivers/staging/fsl-dpaa2/ethsw/ethsw.h4
7 files changed, 342 insertions, 138 deletions
diff --git a/drivers/staging/fsl-dpaa2/ethsw/TODO b/drivers/staging/fsl-dpaa2/ethsw/TODO
index 24b5e95a96f8..4d46857b0b2b 100644
--- a/drivers/staging/fsl-dpaa2/ethsw/TODO
+++ b/drivers/staging/fsl-dpaa2/ethsw/TODO
@@ -1,7 +1,6 @@
* Add I/O capabilities on switch port netdevices. This will allow control
traffic to reach the CPU.
* Add ACL to redirect control traffic to CPU.
-* Add support for displaying learned FDB entries
* Add support for multiple FDBs and switch port partitioning
* MC firmware uprev; the DPAA2 objects used by the Ethernet Switch driver
need to be kept in sync with binary interface changes in MC
diff --git a/drivers/staging/fsl-dpaa2/ethsw/dpsw-cmd.h b/drivers/staging/fsl-dpaa2/ethsw/dpsw-cmd.h
index 14b974defa3a..5e1339daa7c7 100644
--- a/drivers/staging/fsl-dpaa2/ethsw/dpsw-cmd.h
+++ b/drivers/staging/fsl-dpaa2/ethsw/dpsw-cmd.h
@@ -10,7 +10,7 @@
/* DPSW Version */
#define DPSW_VER_MAJOR 8
-#define DPSW_VER_MINOR 0
+#define DPSW_VER_MINOR 1
#define DPSW_CMD_BASE_VERSION 1
#define DPSW_CMD_ID_OFFSET 4
@@ -67,6 +67,7 @@
#define DPSW_CMDID_FDB_ADD_MULTICAST DPSW_CMD_ID(0x086)
#define DPSW_CMDID_FDB_REMOVE_MULTICAST DPSW_CMD_ID(0x087)
#define DPSW_CMDID_FDB_SET_LEARNING_MODE DPSW_CMD_ID(0x088)
+#define DPSW_CMDID_FDB_DUMP DPSW_CMD_ID(0x08A)
/* Macros for accessing command fields smaller than 1byte */
#define DPSW_MASK(field) \
@@ -351,6 +352,18 @@ struct dpsw_cmd_fdb_set_learning_mode {
u8 mode;
};
+struct dpsw_cmd_fdb_dump {
+ __le16 fdb_id;
+ __le16 pad0;
+ __le32 pad1;
+ __le64 iova_addr;
+ __le32 iova_size;
+};
+
+struct dpsw_rsp_fdb_dump {
+ __le16 num_entries;
+};
+
struct dpsw_rsp_get_api_version {
__le16 version_major;
__le16 version_minor;
diff --git a/drivers/staging/fsl-dpaa2/ethsw/dpsw.c b/drivers/staging/fsl-dpaa2/ethsw/dpsw.c
index cabed77b445d..56b0fa789a67 100644
--- a/drivers/staging/fsl-dpaa2/ethsw/dpsw.c
+++ b/drivers/staging/fsl-dpaa2/ethsw/dpsw.c
@@ -981,6 +981,57 @@ int dpsw_fdb_add_unicast(struct fsl_mc_io *mc_io,
}
/**
+ * dpsw_fdb_dump() - Dump the content of FDB table into memory.
+ * @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
+ * @fdb_id: Forwarding Database Identifier
+ * @iova_addr: Data will be stored here as an array of struct fdb_dump_entry
+ * @iova_size: Memory size allocated at iova_addr
+ * @num_entries:Number of entries written at iova_addr
+ *
+ * Return: Completion status. '0' on Success; Error code otherwise.
+ *
+ * The memory allocated at iova_addr must be initialized with zero before
+ * command execution. If the FDB table does not fit into memory MC will stop
+ * after the memory is filled up.
+ * The struct fdb_dump_entry array must be parsed until the end of memory
+ * area or until an entry with mac_addr set to zero is found.
+ */
+int dpsw_fdb_dump(struct fsl_mc_io *mc_io,
+ u32 cmd_flags,
+ u16 token,
+ u16 fdb_id,
+ u64 iova_addr,
+ u32 iova_size,
+ u16 *num_entries)
+{
+ struct dpsw_cmd_fdb_dump *cmd_params;
+ struct dpsw_rsp_fdb_dump *rsp_params;
+ struct fsl_mc_command cmd = { 0 };
+ int err;
+
+ /* prepare command */
+ cmd.header = mc_encode_cmd_header(DPSW_CMDID_FDB_DUMP,
+ cmd_flags,
+ token);
+ cmd_params = (struct dpsw_cmd_fdb_dump *)cmd.params;
+ cmd_params->fdb_id = cpu_to_le16(fdb_id);
+ cmd_params->iova_addr = cpu_to_le64(iova_addr);
+ cmd_params->iova_size = cpu_to_le32(iova_size);
+
+ /* send command to mc */
+ err = mc_send_command(mc_io, &cmd);
+ if (err)
+ return err;
+
+ rsp_params = (struct dpsw_rsp_fdb_dump *)cmd.params;
+ *num_entries = le16_to_cpu(rsp_params->num_entries);
+
+ return 0;
+}
+
+/**
* dpsw_fdb_remove_unicast() - removes an entry from MAC lookup table
* @mc_io: Pointer to MC portal's I/O object
* @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_'
diff --git a/drivers/staging/fsl-dpaa2/ethsw/dpsw.h b/drivers/staging/fsl-dpaa2/ethsw/dpsw.h
index 25635259ce44..25b45850925c 100644
--- a/drivers/staging/fsl-dpaa2/ethsw/dpsw.h
+++ b/drivers/staging/fsl-dpaa2/ethsw/dpsw.h
@@ -75,37 +75,6 @@ enum dpsw_component_type {
DPSW_COMPONENT_TYPE_S_VLAN
};
-/**
- * struct dpsw_cfg - DPSW configuration
- * @num_ifs: Number of external and internal interfaces
- * @adv: Advanced parameters; default is all zeros;
- * use this structure to change default settings
- * @adv.options: Enable/Disable DPSW features (bitmap)
- * @adv.max_vlans: Maximum Number of VLAN's; 0 - indicates default 16
- * @adv.max_meters_per_if: Number of meters per interface
- * @adv.max_fdbs: Maximum Number of FDB's; 0 - indicates default 16
- * @adv.max_fdb_entries: Number of FDB entries for default FDB table;
- * 0 - indicates default 1024 entries.
- * @adv.fdb_aging_time: Default FDB aging time for default FDB table;
- * 0 - indicates default 300 seconds
- * @adv.max_fdb_mc_groups: Number of multicast groups in each FDB table;
- * 0 - indicates default 32
- * @adv.component_type: Indicates the component type of this bridge
- */
-struct dpsw_cfg {
- u16 num_ifs;
- struct {
- u64 options;
- u16 max_vlans;
- u8 max_meters_per_if;
- u8 max_fdbs;
- u16 max_fdb_entries;
- u16 fdb_aging_time;
- u16 max_fdb_mc_groups;
- enum dpsw_component_type component_type;
- } adv;
-};
-
int dpsw_enable(struct fsl_mc_io *mc_io,
u32 cmd_flags,
u16 token);
@@ -496,6 +465,31 @@ int dpsw_fdb_remove_unicast(struct fsl_mc_io *mc_io,
u16 fdb_id,
const struct dpsw_fdb_unicast_cfg *cfg);
+#define DPSW_FDB_ENTRY_TYPE_DYNAMIC BIT(0)
+#define DPSW_FDB_ENTRY_TYPE_UNICAST BIT(1)
+
+/**
+ * struct fdb_dump_entry - fdb snapshot entry
+ * @mac_addr: MAC address
+ * @type: bit0 - DINAMIC(1)/STATIC(0), bit1 - UNICAST(1)/MULTICAST(0)
+ * @if_info: unicast - egress interface, multicast - number of egress interfaces
+ * @if_mask: multicast - egress interface mask
+ */
+struct fdb_dump_entry {
+ u8 mac_addr[6];
+ u8 type;
+ u8 if_info;
+ u8 if_mask[8];
+};
+
+int dpsw_fdb_dump(struct fsl_mc_io *mc_io,
+ u32 cmd_flags,
+ u16 token,
+ u16 fdb_id,
+ u64 iova_addr,
+ u32 iova_size,
+ u16 *num_entries);
+
/**
* struct dpsw_fdb_multicast_cfg - Multi-cast entry configuration
* @type: Select static or dynamic entry
diff --git a/drivers/staging/fsl-dpaa2/ethsw/ethsw-ethtool.c b/drivers/staging/fsl-dpaa2/ethsw/ethsw-ethtool.c
index 926a0c053e18..4f0bff86e43e 100644
--- a/drivers/staging/fsl-dpaa2/ethsw/ethsw-ethtool.c
+++ b/drivers/staging/fsl-dpaa2/ethsw/ethsw-ethtool.c
@@ -65,7 +65,7 @@ ethsw_get_link_ksettings(struct net_device *netdev,
port_priv->idx,
&state);
if (err) {
- netdev_err(netdev, "ERROR %d getting link state", err);
+ netdev_err(netdev, "ERROR %d getting link state\n", err);
goto out;
}
@@ -88,18 +88,21 @@ ethsw_set_link_ksettings(struct net_device *netdev,
const struct ethtool_link_ksettings *link_ksettings)
{
struct ethsw_port_priv *port_priv = netdev_priv(netdev);
+ struct ethsw_core *ethsw = port_priv->ethsw_data;
struct dpsw_link_cfg cfg = {0};
- int err = 0;
-
- netdev_dbg(netdev, "Setting link parameters...");
-
- /* Due to a temporary MC limitation, the DPSW port must be down
- * in order to be able to change link settings. Taking steps to let
- * the user know that.
- */
- if (netif_running(netdev)) {
- netdev_info(netdev, "Sorry, interface must be brought down first.\n");
- return -EACCES;
+ bool if_running;
+ int err = 0, ret;
+
+ /* Interface needs to be down to change link settings */
+ if_running = netif_running(netdev);
+ if (if_running) {
+ err = dpsw_if_disable(ethsw->mc_io, 0,
+ ethsw->dpsw_handle,
+ port_priv->idx);
+ if (err) {
+ netdev_err(netdev, "dpsw_if_disable err %d\n", err);
+ return err;
+ }
}
cfg.rate = link_ksettings->base.speed;
@@ -116,12 +119,16 @@ ethsw_set_link_ksettings(struct net_device *netdev,
port_priv->ethsw_data->dpsw_handle,
port_priv->idx,
&cfg);
- if (err)
- /* ethtool will be loud enough if we return an error; no point
- * in putting our own error message on the console by default
- */
- netdev_dbg(netdev, "ERROR %d setting link cfg", err);
+ if (if_running) {
+ ret = dpsw_if_enable(ethsw->mc_io, 0,
+ ethsw->dpsw_handle,
+ port_priv->idx);
+ if (ret) {
+ netdev_err(netdev, "dpsw_if_enable err %d\n", ret);
+ return ret;
+ }
+ }
return err;
}
@@ -156,9 +163,6 @@ static void ethsw_ethtool_get_stats(struct net_device *netdev,
struct ethsw_port_priv *port_priv = netdev_priv(netdev);
int i, err;
- memset(data, 0,
- sizeof(u64) * ETHSW_NUM_COUNTERS);
-
for (i = 0; i < ETHSW_NUM_COUNTERS; i++) {
err = dpsw_if_get_counter(port_priv->ethsw_data->mc_io, 0,
port_priv->ethsw_data->dpsw_handle,
diff --git a/drivers/staging/fsl-dpaa2/ethsw/ethsw.c b/drivers/staging/fsl-dpaa2/ethsw/ethsw.c
index f73edaf6ce87..14a9eebf687e 100644
--- a/drivers/staging/fsl-dpaa2/ethsw/ethsw.c
+++ b/drivers/staging/fsl-dpaa2/ethsw/ethsw.c
@@ -22,7 +22,7 @@ static struct workqueue_struct *ethsw_owq;
/* Minimal supported DPSW version */
#define DPSW_MIN_VER_MAJOR 8
-#define DPSW_MIN_VER_MINOR 0
+#define DPSW_MIN_VER_MINOR 1
#define DEFAULT_VLAN_ID 1
@@ -34,11 +34,6 @@ static int ethsw_add_vlan(struct ethsw_core *ethsw, u16 vid)
.fdb_id = 0,
};
- if (ethsw->vlans[vid]) {
- dev_err(ethsw->dev, "VLAN already configured\n");
- return -EEXIST;
- }
-
err = dpsw_vlan_add(ethsw->mc_io, 0,
ethsw->dpsw_handle, vid, &vcfg);
if (err) {
@@ -149,12 +144,12 @@ static int ethsw_port_add_vlan(struct ethsw_port_priv *port_priv,
return 0;
}
-static int ethsw_set_learning(struct ethsw_core *ethsw, u8 flag)
+static int ethsw_set_learning(struct ethsw_core *ethsw, bool enable)
{
enum dpsw_fdb_learning_mode learn_mode;
int err;
- if (flag)
+ if (enable)
learn_mode = DPSW_FDB_LEARNING_MODE_HW;
else
learn_mode = DPSW_FDB_LEARNING_MODE_DIS;
@@ -165,24 +160,24 @@ static int ethsw_set_learning(struct ethsw_core *ethsw, u8 flag)
dev_err(ethsw->dev, "dpsw_fdb_set_learning_mode err %d\n", err);
return err;
}
- ethsw->learning = !!flag;
+ ethsw->learning = enable;
return 0;
}
-static int ethsw_port_set_flood(struct ethsw_port_priv *port_priv, u8 flag)
+static int ethsw_port_set_flood(struct ethsw_port_priv *port_priv, bool enable)
{
int err;
err = dpsw_if_set_flooding(port_priv->ethsw_data->mc_io, 0,
port_priv->ethsw_data->dpsw_handle,
- port_priv->idx, flag);
+ port_priv->idx, enable);
if (err) {
netdev_err(port_priv->netdev,
"dpsw_if_set_flooding err %d\n", err);
return err;
}
- port_priv->flood = !!flag;
+ port_priv->flood = enable;
return 0;
}
@@ -316,6 +311,31 @@ static int ethsw_port_fdb_del_mc(struct ethsw_port_priv *port_priv,
return err;
}
+static int 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)
+{
+ if (is_unicast_ether_addr(addr))
+ return ethsw_port_fdb_add_uc(netdev_priv(dev),
+ addr);
+ else
+ return ethsw_port_fdb_add_mc(netdev_priv(dev),
+ addr);
+}
+
+static int port_fdb_del(struct ndmsg *ndm, struct nlattr *tb[],
+ struct net_device *dev,
+ const unsigned char *addr, u16 vid)
+{
+ if (is_unicast_ether_addr(addr))
+ return ethsw_port_fdb_del_uc(netdev_priv(dev),
+ addr);
+ else
+ return ethsw_port_fdb_del_mc(netdev_priv(dev),
+ addr);
+}
+
static void port_get_stats(struct net_device *netdev,
struct rtnl_link_stats64 *stats)
{
@@ -516,17 +536,165 @@ static int swdev_get_port_parent_id(struct net_device *dev,
return 0;
}
+static int port_get_phys_name(struct net_device *netdev, char *name,
+ size_t len)
+{
+ struct ethsw_port_priv *port_priv = netdev_priv(netdev);
+ int err;
+
+ err = snprintf(name, len, "p%d", port_priv->idx);
+ if (err >= len)
+ return -EINVAL;
+
+ return 0;
+}
+
+struct ethsw_dump_ctx {
+ struct net_device *dev;
+ struct sk_buff *skb;
+ struct netlink_callback *cb;
+ int idx;
+};
+
+static int ethsw_fdb_do_dump(struct fdb_dump_entry *entry,
+ struct ethsw_dump_ctx *dump)
+{
+ int is_dynamic = entry->type & DPSW_FDB_ENTRY_DINAMIC;
+ u32 portid = NETLINK_CB(dump->cb->skb).portid;
+ u32 seq = dump->cb->nlh->nlmsg_seq;
+ struct nlmsghdr *nlh;
+ struct ndmsg *ndm;
+
+ if (dump->idx < dump->cb->args[2])
+ goto skip;
+
+ nlh = nlmsg_put(dump->skb, portid, seq, RTM_NEWNEIGH,
+ sizeof(*ndm), NLM_F_MULTI);
+ if (!nlh)
+ return -EMSGSIZE;
+
+ ndm = nlmsg_data(nlh);
+ ndm->ndm_family = AF_BRIDGE;
+ ndm->ndm_pad1 = 0;
+ ndm->ndm_pad2 = 0;
+ ndm->ndm_flags = NTF_SELF;
+ ndm->ndm_type = 0;
+ ndm->ndm_ifindex = dump->dev->ifindex;
+ ndm->ndm_state = is_dynamic ? NUD_REACHABLE : NUD_NOARP;
+
+ if (nla_put(dump->skb, NDA_LLADDR, ETH_ALEN, entry->mac_addr))
+ goto nla_put_failure;
+
+ nlmsg_end(dump->skb, nlh);
+
+skip:
+ dump->idx++;
+ return 0;
+
+nla_put_failure:
+ nlmsg_cancel(dump->skb, nlh);
+ return -EMSGSIZE;
+}
+
+static int port_fdb_valid_entry(struct fdb_dump_entry *entry,
+ struct ethsw_port_priv *port_priv)
+{
+ int idx = port_priv->idx;
+ int valid;
+
+ if (entry->type & DPSW_FDB_ENTRY_TYPE_UNICAST)
+ valid = entry->if_info == port_priv->idx;
+ else
+ valid = entry->if_mask[idx / 8] & BIT(idx % 8);
+
+ return valid;
+}
+
+static int port_fdb_dump(struct sk_buff *skb, struct netlink_callback *cb,
+ struct net_device *net_dev,
+ struct net_device *filter_dev, int *idx)
+{
+ struct ethsw_port_priv *port_priv = netdev_priv(net_dev);
+ struct ethsw_core *ethsw = port_priv->ethsw_data;
+ struct device *dev = net_dev->dev.parent;
+ struct fdb_dump_entry *fdb_entries;
+ struct fdb_dump_entry fdb_entry;
+ struct ethsw_dump_ctx dump = {
+ .dev = net_dev,
+ .skb = skb,
+ .cb = cb,
+ .idx = *idx,
+ };
+ dma_addr_t fdb_dump_iova;
+ u16 num_fdb_entries;
+ u32 fdb_dump_size;
+ int err = 0, i;
+ u8 *dma_mem;
+
+ fdb_dump_size = ethsw->sw_attr.max_fdb_entries * sizeof(fdb_entry);
+ dma_mem = kzalloc(fdb_dump_size, GFP_KERNEL);
+ if (!dma_mem)
+ return -ENOMEM;
+
+ fdb_dump_iova = dma_map_single(dev, dma_mem, fdb_dump_size,
+ DMA_FROM_DEVICE);
+ if (dma_mapping_error(dev, fdb_dump_iova)) {
+ netdev_err(net_dev, "dma_map_single() failed\n");
+ err = -ENOMEM;
+ goto err_map;
+ }
+
+ err = dpsw_fdb_dump(ethsw->mc_io, 0, ethsw->dpsw_handle, 0,
+ fdb_dump_iova, fdb_dump_size, &num_fdb_entries);
+ if (err) {
+ netdev_err(net_dev, "dpsw_fdb_dump() = %d\n", err);
+ goto err_dump;
+ }
+
+ dma_unmap_single(dev, fdb_dump_iova, fdb_dump_size, DMA_FROM_DEVICE);
+
+ fdb_entries = (struct fdb_dump_entry *)dma_mem;
+ for (i = 0; i < num_fdb_entries; i++) {
+ fdb_entry = fdb_entries[i];
+
+ if (!port_fdb_valid_entry(&fdb_entry, port_priv))
+ continue;
+
+ err = ethsw_fdb_do_dump(&fdb_entry, &dump);
+ if (err)
+ goto end;
+ }
+
+end:
+ *idx = dump.idx;
+
+ kfree(dma_mem);
+
+ return 0;
+
+err_dump:
+ dma_unmap_single(dev, fdb_dump_iova, fdb_dump_size, DMA_TO_DEVICE);
+err_map:
+ kfree(dma_mem);
+ return err;
+}
+
static const struct net_device_ops ethsw_port_ops = {
.ndo_open = port_open,
.ndo_stop = port_stop,
.ndo_set_mac_address = eth_mac_addr,
+ .ndo_get_stats64 = port_get_stats,
.ndo_change_mtu = port_change_mtu,
.ndo_has_offload_stats = port_has_offload_stats,
.ndo_get_offload_stats = port_get_offload_stats,
+ .ndo_fdb_add = port_fdb_add,
+ .ndo_fdb_del = port_fdb_del,
+ .ndo_fdb_dump = port_fdb_dump,
.ndo_start_xmit = port_dropframe,
.ndo_get_port_parent_id = swdev_get_port_parent_id,
+ .ndo_get_phys_port_name = port_get_phys_name,
};
static void ethsw_links_state_update(struct ethsw_core *ethsw)
@@ -549,12 +717,12 @@ static irqreturn_t ethsw_irq0_handler_thread(int irq_num, void *arg)
err = dpsw_get_irq_status(ethsw->mc_io, 0, ethsw->dpsw_handle,
DPSW_IRQ_INDEX_IF, &status);
if (err) {
- dev_err(dev, "Can't get irq status (err %d)", err);
+ dev_err(dev, "Can't get irq status (err %d)\n", err);
err = dpsw_clear_irq_status(ethsw->mc_io, 0, ethsw->dpsw_handle,
DPSW_IRQ_INDEX_IF, 0xFFFFFFFF);
if (err)
- dev_err(dev, "Can't clear irq status (err %d)", err);
+ dev_err(dev, "Can't clear irq status (err %d)\n", err);
goto out;
}
@@ -599,21 +767,21 @@ static int ethsw_setup_irqs(struct fsl_mc_device *sw_dev)
IRQF_NO_SUSPEND | IRQF_ONESHOT,
dev_name(dev), dev);
if (err) {
- dev_err(dev, "devm_request_threaded_irq(): %d", err);
+ dev_err(dev, "devm_request_threaded_irq(): %d\n", err);
goto free_irq;
}
err = dpsw_set_irq_mask(ethsw->mc_io, 0, ethsw->dpsw_handle,
DPSW_IRQ_INDEX_IF, mask);
if (err) {
- dev_err(dev, "dpsw_set_irq_mask(): %d", err);
+ dev_err(dev, "dpsw_set_irq_mask(): %d\n", err);
goto free_devm_irq;
}
err = dpsw_set_irq_enable(ethsw->mc_io, 0, ethsw->dpsw_handle,
DPSW_IRQ_INDEX_IF, 1);
if (err) {
- dev_err(dev, "dpsw_set_irq_enable(): %d", err);
+ dev_err(dev, "dpsw_set_irq_enable(): %d\n", err);
goto free_devm_irq;
}
@@ -673,11 +841,12 @@ static int port_attr_br_flags_set(struct net_device *netdev,
return 0;
/* Learning is enabled per switch */
- err = ethsw_set_learning(port_priv->ethsw_data, flags & BR_LEARNING);
+ err = ethsw_set_learning(port_priv->ethsw_data,
+ !!(flags & BR_LEARNING));
if (err)
goto exit;
- err = ethsw_port_set_flood(port_priv, flags & BR_FLOOD);
+ err = ethsw_port_set_flood(port_priv, !!(flags & BR_FLOOD));
exit:
return err;
@@ -950,8 +1119,7 @@ static int port_bridge_join(struct net_device *netdev,
if (ethsw->ports[i]->bridge_dev &&
(ethsw->ports[i]->bridge_dev != upper_dev)) {
netdev_err(netdev,
- "Another switch port is connected to %s\n",
- ethsw->ports[i]->bridge_dev->name);
+ "Only one bridge supported per DPSW object!\n");
return -EINVAL;
}
@@ -1023,18 +1191,30 @@ static void ethsw_switchdev_event_work(struct work_struct *work)
container_of(work, struct ethsw_switchdev_event_work, work);
struct net_device *dev = switchdev_work->dev;
struct switchdev_notifier_fdb_info *fdb_info;
+ int err;
rtnl_lock();
fdb_info = &switchdev_work->fdb_info;
switch (switchdev_work->event) {
case SWITCHDEV_FDB_ADD_TO_DEVICE:
+ if (!fdb_info->added_by_user)
+ break;
if (is_unicast_ether_addr(fdb_info->addr))
- ethsw_port_fdb_add_uc(netdev_priv(dev), fdb_info->addr);
+ err = ethsw_port_fdb_add_uc(netdev_priv(dev),
+ fdb_info->addr);
else
- ethsw_port_fdb_add_mc(netdev_priv(dev), fdb_info->addr);
+ err = ethsw_port_fdb_add_mc(netdev_priv(dev),
+ fdb_info->addr);
+ if (err)
+ break;
+ fdb_info->offloaded = true;
+ call_switchdev_notifiers(SWITCHDEV_FDB_OFFLOADED, dev,
+ &fdb_info->info, NULL);
break;
case SWITCHDEV_FDB_DEL_TO_DEVICE:
+ if (!fdb_info->added_by_user)
+ break;
if (is_unicast_ether_addr(fdb_info->addr))
ethsw_port_fdb_del_uc(netdev_priv(dev), fdb_info->addr);
else
@@ -1177,48 +1357,6 @@ err_switchdev_nb:
return err;
}
-static int ethsw_open(struct ethsw_core *ethsw)
-{
- struct ethsw_port_priv *port_priv = NULL;
- int i, err;
-
- err = dpsw_enable(ethsw->mc_io, 0, ethsw->dpsw_handle);
- if (err) {
- dev_err(ethsw->dev, "dpsw_enable err %d\n", err);
- return err;
- }
-
- for (i = 0; i < ethsw->sw_attr.num_ifs; i++) {
- port_priv = ethsw->ports[i];
- err = dev_open(port_priv->netdev, NULL);
- if (err) {
- netdev_err(port_priv->netdev, "dev_open err %d\n", err);
- return err;
- }
- }
-
- return 0;
-}
-
-static int ethsw_stop(struct ethsw_core *ethsw)
-{
- struct ethsw_port_priv *port_priv = NULL;
- int i, err;
-
- for (i = 0; i < ethsw->sw_attr.num_ifs; i++) {
- port_priv = ethsw->ports[i];
- dev_close(port_priv->netdev);
- }
-
- err = dpsw_disable(ethsw->mc_io, 0, ethsw->dpsw_handle);
- if (err) {
- dev_err(ethsw->dev, "dpsw_disable err %d\n", err);
- return err;
- }
-
- return 0;
-}
-
static int ethsw_init(struct fsl_mc_device *sw_dev)
{
struct device *dev = &sw_dev->dev;
@@ -1320,7 +1458,6 @@ err_close:
static int ethsw_port_init(struct ethsw_port_priv *port_priv, u16 port)
{
- const char def_mcast[ETH_ALEN] = {0x01, 0x00, 0x5e, 0x00, 0x00, 0x01};
struct net_device *netdev = port_priv->netdev;
struct ethsw_core *ethsw = port_priv->ethsw_data;
struct dpsw_vlan_if_cfg vcfg;
@@ -1346,12 +1483,10 @@ static int ethsw_port_init(struct ethsw_port_priv *port_priv, u16 port)
err = dpsw_vlan_remove_if(ethsw->mc_io, 0, ethsw->dpsw_handle,
DEFAULT_VLAN_ID, &vcfg);
- if (err) {
+ if (err)
netdev_err(netdev, "dpsw_vlan_remove_if err %d\n", err);
- return err;
- }
- return ethsw_port_fdb_add_mc(port_priv, def_mcast);
+ return err;
}
static void ethsw_unregister_notifier(struct device *dev)
@@ -1403,9 +1538,7 @@ static int ethsw_remove(struct fsl_mc_device *sw_dev)
destroy_workqueue(ethsw_owq);
- rtnl_lock();
- ethsw_stop(ethsw);
- rtnl_unlock();
+ dpsw_disable(ethsw->mc_io, 0, ethsw->dpsw_handle);
for (i = 0; i < ethsw->sw_attr.num_ifs; i++) {
port_priv = ethsw->ports[i];
@@ -1455,16 +1588,24 @@ static int ethsw_probe_port(struct ethsw_core *ethsw, u16 port_idx)
port_netdev->min_mtu = ETH_MIN_MTU;
port_netdev->max_mtu = ETHSW_MAX_FRAME_LENGTH;
+ err = ethsw_port_init(port_priv, port_idx);
+ if (err)
+ goto err_port_probe;
+
err = register_netdev(port_netdev);
if (err < 0) {
dev_err(dev, "register_netdev error %d\n", err);
- free_netdev(port_netdev);
- return err;
+ goto err_port_probe;
}
ethsw->ports[port_idx] = port_priv;
- return ethsw_port_init(port_priv, port_idx);
+ return 0;
+
+err_port_probe:
+ free_netdev(port_netdev);
+
+ return err;
}
static int ethsw_probe(struct fsl_mc_device *sw_dev)
@@ -1482,7 +1623,8 @@ static int ethsw_probe(struct fsl_mc_device *sw_dev)
ethsw->dev = dev;
dev_set_drvdata(dev, ethsw);
- err = fsl_mc_portal_allocate(sw_dev, 0, &ethsw->mc_io);
+ err = fsl_mc_portal_allocate(sw_dev, FSL_MC_IO_ATOMIC_CONTEXT_PORTAL,
+ &ethsw->mc_io);
if (err) {
if (err == -ENXIO)
err = -EPROBE_DEFER;
@@ -1514,12 +1656,11 @@ static int ethsw_probe(struct fsl_mc_device *sw_dev)
goto err_free_ports;
}
- /* Switch starts up enabled */
- rtnl_lock();
- err = ethsw_open(ethsw);
- rtnl_unlock();
- if (err)
+ err = dpsw_enable(ethsw->mc_io, 0, ethsw->dpsw_handle);
+ if (err) {
+ dev_err(ethsw->dev, "dpsw_enable err %d\n", err);
goto err_free_ports;
+ }
/* Setup IRQs */
err = ethsw_setup_irqs(sw_dev);
@@ -1530,9 +1671,7 @@ static int ethsw_probe(struct fsl_mc_device *sw_dev)
return 0;
err_stop:
- rtnl_lock();
- ethsw_stop(ethsw);
- rtnl_unlock();
+ dpsw_disable(ethsw->mc_io, 0, ethsw->dpsw_handle);
err_free_ports:
/* Cleanup registered ports only */
diff --git a/drivers/staging/fsl-dpaa2/ethsw/ethsw.h b/drivers/staging/fsl-dpaa2/ethsw/ethsw.h
index c48783680a05..3ea8a0ad8c10 100644
--- a/drivers/staging/fsl-dpaa2/ethsw/ethsw.h
+++ b/drivers/staging/fsl-dpaa2/ethsw/ethsw.h
@@ -23,9 +23,13 @@
/* Number of IRQs supported */
#define DPSW_IRQ_NUM 2
+/* Port is member of VLAN */
#define ETHSW_VLAN_MEMBER 1
+/* VLAN to be treated as untagged on egress */
#define ETHSW_VLAN_UNTAGGED 2
+/* Untagged frames will be assigned to this VLAN */
#define ETHSW_VLAN_PVID 4
+/* VLAN configured on the switch */
#define ETHSW_VLAN_GLOBAL 8
/* Maximum Frame Length supported by HW (currently 10k) */