From b22da3d8526a935aa31e086e63f60ff3246cb61c Mon Sep 17 00:00:00 2001 From: hc <hc@nodka.com> Date: Sat, 09 Dec 2023 07:24:11 +0000 Subject: [PATCH] add stmac read mac form eeprom --- kernel/drivers/staging/fsl-dpaa2/ethsw/ethsw.c | 949 ++++++++++++++++++++++++++++++++++++++++------------------ 1 files changed, 646 insertions(+), 303 deletions(-) diff --git a/kernel/drivers/staging/fsl-dpaa2/ethsw/ethsw.c b/kernel/drivers/staging/fsl-dpaa2/ethsw/ethsw.c index 8549e80..20c6326 100644 --- a/kernel/drivers/staging/fsl-dpaa2/ethsw/ethsw.c +++ b/kernel/drivers/staging/fsl-dpaa2/ethsw/ethsw.c @@ -18,26 +18,19 @@ #include "ethsw.h" -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 -static int ethsw_add_vlan(struct ethsw_core *ethsw, u16 vid) +static int dpaa2_switch_add_vlan(struct ethsw_core *ethsw, u16 vid) { int err; struct dpsw_vlan_cfg vcfg = { .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); @@ -50,12 +43,31 @@ return 0; } -static int ethsw_port_set_pvid(struct ethsw_port_priv *port_priv, u16 pvid) +static bool dpaa2_switch_port_is_up(struct ethsw_port_priv *port_priv) +{ + struct net_device *netdev = port_priv->netdev; + struct dpsw_link_state state; + int err; + + err = dpsw_if_get_link_state(port_priv->ethsw_data->mc_io, 0, + port_priv->ethsw_data->dpsw_handle, + port_priv->idx, &state); + if (err) { + netdev_err(netdev, "dpsw_if_get_link_state() err %d\n", err); + return true; + } + + WARN_ONCE(state.up > 1, "Garbage read into link_state"); + + return state.up ? true : false; +} + +static int dpaa2_switch_port_set_pvid(struct ethsw_port_priv *port_priv, u16 pvid) { struct ethsw_core *ethsw = port_priv->ethsw_data; struct net_device *netdev = port_priv->netdev; struct dpsw_tci_cfg tci_cfg = { 0 }; - bool is_oper; + bool up; int err, ret; err = dpsw_if_get_tci(ethsw->mc_io, 0, ethsw->dpsw_handle, @@ -68,8 +80,8 @@ tci_cfg.vlan_id = pvid; /* Interface needs to be down to change PVID */ - is_oper = netif_oper_up(netdev); - if (is_oper) { + up = dpaa2_switch_port_is_up(port_priv); + if (up) { err = dpsw_if_disable(ethsw->mc_io, 0, ethsw->dpsw_handle, port_priv->idx); @@ -92,7 +104,7 @@ port_priv->pvid = pvid; set_tci_error: - if (is_oper) { + if (up) { ret = dpsw_if_enable(ethsw->mc_io, 0, ethsw->dpsw_handle, port_priv->idx); @@ -105,8 +117,8 @@ return err; } -static int ethsw_port_add_vlan(struct ethsw_port_priv *port_priv, - u16 vid, u16 flags) +static int dpaa2_switch_port_add_vlan(struct ethsw_port_priv *port_priv, + u16 vid, u16 flags) { struct ethsw_core *ethsw = port_priv->ethsw_data; struct net_device *netdev = port_priv->netdev; @@ -141,7 +153,7 @@ } if (flags & BRIDGE_VLAN_INFO_PVID) { - err = ethsw_port_set_pvid(port_priv, vid); + err = dpaa2_switch_port_set_pvid(port_priv, vid); if (err) return err; } @@ -149,12 +161,12 @@ return 0; } -static int ethsw_set_learning(struct ethsw_core *ethsw, u8 flag) +static int dpaa2_switch_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,46 +177,51 @@ 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 dpaa2_switch_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; } -static int ethsw_port_set_stp_state(struct ethsw_port_priv *port_priv, u8 state) +static int dpaa2_switch_port_set_stp_state(struct ethsw_port_priv *port_priv, u8 state) { struct dpsw_stp_cfg stp_cfg = { - .vlan_id = DEFAULT_VLAN_ID, .state = state, }; int err; + u16 vid; - if (!netif_oper_up(port_priv->netdev) || state == port_priv->stp_state) + if (!netif_running(port_priv->netdev) || state == port_priv->stp_state) return 0; /* Nothing to do */ - err = dpsw_if_set_stp(port_priv->ethsw_data->mc_io, 0, - port_priv->ethsw_data->dpsw_handle, - port_priv->idx, &stp_cfg); - if (err) { - netdev_err(port_priv->netdev, - "dpsw_if_set_stp err %d\n", err); - return err; + for (vid = 0; vid <= VLAN_VID_MASK; vid++) { + if (port_priv->vlans[vid] & ETHSW_VLAN_MEMBER) { + stp_cfg.vlan_id = vid; + err = dpsw_if_set_stp(port_priv->ethsw_data->mc_io, 0, + port_priv->ethsw_data->dpsw_handle, + port_priv->idx, &stp_cfg); + if (err) { + netdev_err(port_priv->netdev, + "dpsw_if_set_stp err %d\n", err); + return err; + } + } } port_priv->stp_state = state; @@ -212,7 +229,7 @@ return 0; } -static int ethsw_dellink_switch(struct ethsw_core *ethsw, u16 vid) +static int dpaa2_switch_dellink(struct ethsw_core *ethsw, u16 vid) { struct ethsw_port_priv *ppriv_local = NULL; int i, err; @@ -235,8 +252,8 @@ return 0; } -static int ethsw_port_fdb_add_uc(struct ethsw_port_priv *port_priv, - const unsigned char *addr) +static int dpaa2_switch_port_fdb_add_uc(struct ethsw_port_priv *port_priv, + const unsigned char *addr) { struct dpsw_fdb_unicast_cfg entry = {0}; int err; @@ -254,8 +271,8 @@ return err; } -static int ethsw_port_fdb_del_uc(struct ethsw_port_priv *port_priv, - const unsigned char *addr) +static int dpaa2_switch_port_fdb_del_uc(struct ethsw_port_priv *port_priv, + const unsigned char *addr) { struct dpsw_fdb_unicast_cfg entry = {0}; int err; @@ -274,8 +291,8 @@ return err; } -static int ethsw_port_fdb_add_mc(struct ethsw_port_priv *port_priv, - const unsigned char *addr) +static int dpaa2_switch_port_fdb_add_mc(struct ethsw_port_priv *port_priv, + const unsigned char *addr) { struct dpsw_fdb_multicast_cfg entry = {0}; int err; @@ -295,8 +312,8 @@ return err; } -static int ethsw_port_fdb_del_mc(struct ethsw_port_priv *port_priv, - const unsigned char *addr) +static int dpaa2_switch_port_fdb_del_mc(struct ethsw_port_priv *port_priv, + const unsigned char *addr) { struct dpsw_fdb_multicast_cfg entry = {0}; int err; @@ -316,8 +333,33 @@ return err; } -static void port_get_stats(struct net_device *netdev, - struct rtnl_link_stats64 *stats) +static int dpaa2_switch_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 dpaa2_switch_port_fdb_add_uc(netdev_priv(dev), + addr); + else + return dpaa2_switch_port_fdb_add_mc(netdev_priv(dev), + addr); +} + +static int dpaa2_switch_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 dpaa2_switch_port_fdb_del_uc(netdev_priv(dev), + addr); + else + return dpaa2_switch_port_fdb_del_mc(netdev_priv(dev), + addr); +} + +static void dpaa2_switch_port_get_stats(struct net_device *netdev, + struct rtnl_link_stats64 *stats) { struct ethsw_port_priv *port_priv = netdev_priv(netdev); u64 tmp; @@ -382,26 +424,26 @@ netdev_err(netdev, "dpsw_if_get_counter err %d\n", err); } -static bool port_has_offload_stats(const struct net_device *netdev, - int attr_id) +static bool dpaa2_switch_port_has_offload_stats(const struct net_device *netdev, + int attr_id) { return (attr_id == IFLA_OFFLOAD_XSTATS_CPU_HIT); } -static int port_get_offload_stats(int attr_id, - const struct net_device *netdev, - void *sp) +static int dpaa2_switch_port_get_offload_stats(int attr_id, + const struct net_device *netdev, + void *sp) { switch (attr_id) { case IFLA_OFFLOAD_XSTATS_CPU_HIT: - port_get_stats((struct net_device *)netdev, sp); + dpaa2_switch_port_get_stats((struct net_device *)netdev, sp); return 0; } return -EINVAL; } -static int port_change_mtu(struct net_device *netdev, int mtu) +static int dpaa2_switch_port_change_mtu(struct net_device *netdev, int mtu) { struct ethsw_port_priv *port_priv = netdev_priv(netdev); int err; @@ -421,11 +463,17 @@ return 0; } -static int port_carrier_state_sync(struct net_device *netdev) +static int dpaa2_switch_port_carrier_state_sync(struct net_device *netdev) { struct ethsw_port_priv *port_priv = netdev_priv(netdev); struct dpsw_link_state state; int err; + + /* Interrupts are received even though no one issued an 'ifconfig up' + * on the switch interface. Ignore these link state update interrupts + */ + if (!netif_running(netdev)) + return 0; err = dpsw_if_get_link_state(port_priv->ethsw_data->mc_io, 0, port_priv->ethsw_data->dpsw_handle, @@ -444,16 +492,24 @@ netif_carrier_off(netdev); port_priv->link_state = state.up; } + return 0; } -static int port_open(struct net_device *netdev) +static int dpaa2_switch_port_open(struct net_device *netdev) { struct ethsw_port_priv *port_priv = netdev_priv(netdev); int err; /* No need to allow Tx as control interface is disabled */ netif_tx_stop_all_queues(netdev); + + /* Explicitly set carrier off, otherwise + * netif_carrier_ok() will return true and cause 'ip link show' + * to report the LOWER_UP flag, even though the link + * notification wasn't even received. + */ + netif_carrier_off(netdev); err = dpsw_if_enable(port_priv->ethsw_data->mc_io, 0, port_priv->ethsw_data->dpsw_handle, @@ -464,10 +520,10 @@ } /* sync carrier state */ - err = port_carrier_state_sync(netdev); + err = dpaa2_switch_port_carrier_state_sync(netdev); if (err) { netdev_err(netdev, - "port_carrier_state_sync err %d\n", err); + "dpaa2_switch_port_carrier_state_sync err %d\n", err); goto err_carrier_sync; } @@ -480,7 +536,7 @@ return err; } -static int port_stop(struct net_device *netdev) +static int dpaa2_switch_port_stop(struct net_device *netdev) { struct ethsw_port_priv *port_priv = netdev_priv(netdev); int err; @@ -496,8 +552,8 @@ return 0; } -static netdev_tx_t port_dropframe(struct sk_buff *skb, - struct net_device *netdev) +static netdev_tx_t dpaa2_switch_port_dropframe(struct sk_buff *skb, + struct net_device *netdev) { /* we don't support I/O for now, drop the frame */ dev_kfree_skb_any(skb); @@ -505,27 +561,243 @@ return NETDEV_TX_OK; } -static const struct net_device_ops ethsw_port_ops = { - .ndo_open = port_open, - .ndo_stop = port_stop, +static int dpaa2_switch_port_parent_id(struct net_device *dev, + struct netdev_phys_item_id *ppid) +{ + struct ethsw_port_priv *port_priv = netdev_priv(dev); - .ndo_set_mac_address = eth_mac_addr, - .ndo_change_mtu = port_change_mtu, - .ndo_has_offload_stats = port_has_offload_stats, - .ndo_get_offload_stats = port_get_offload_stats, + ppid->id_len = 1; + ppid->id[0] = port_priv->ethsw_data->dev_id; - .ndo_start_xmit = port_dropframe, + return 0; +} + +static int dpaa2_switch_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 void ethsw_links_state_update(struct ethsw_core *ethsw) +static int dpaa2_switch_fdb_dump_nl(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 dpaa2_switch_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 dpaa2_switch_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 (!dpaa2_switch_port_fdb_valid_entry(&fdb_entry, port_priv)) + continue; + + err = dpaa2_switch_fdb_dump_nl(&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 int dpaa2_switch_port_set_mac_addr(struct ethsw_port_priv *port_priv) +{ + struct ethsw_core *ethsw = port_priv->ethsw_data; + struct net_device *net_dev = port_priv->netdev; + struct device *dev = net_dev->dev.parent; + u8 mac_addr[ETH_ALEN]; + int err; + + if (!(ethsw->features & ETHSW_FEATURE_MAC_ADDR)) + return 0; + + /* Get firmware address, if any */ + err = dpsw_if_get_port_mac_addr(ethsw->mc_io, 0, ethsw->dpsw_handle, + port_priv->idx, mac_addr); + if (err) { + dev_err(dev, "dpsw_if_get_port_mac_addr() failed\n"); + return err; + } + + /* First check if firmware has any address configured by bootloader */ + if (!is_zero_ether_addr(mac_addr)) { + memcpy(net_dev->dev_addr, mac_addr, net_dev->addr_len); + } else { + /* No MAC address configured, fill in net_dev->dev_addr + * with a random one + */ + eth_hw_addr_random(net_dev); + dev_dbg_once(dev, "device(s) have all-zero hwaddr, replaced with random\n"); + + /* Override NET_ADDR_RANDOM set by eth_hw_addr_random(); for all + * practical purposes, this will be our "permanent" mac address, + * at least until the next reboot. This move will also permit + * register_netdevice() to properly fill up net_dev->perm_addr. + */ + net_dev->addr_assign_type = NET_ADDR_PERM; + } + + return 0; +} + +static const struct net_device_ops dpaa2_switch_port_ops = { + .ndo_open = dpaa2_switch_port_open, + .ndo_stop = dpaa2_switch_port_stop, + + .ndo_set_mac_address = eth_mac_addr, + .ndo_get_stats64 = dpaa2_switch_port_get_stats, + .ndo_change_mtu = dpaa2_switch_port_change_mtu, + .ndo_has_offload_stats = dpaa2_switch_port_has_offload_stats, + .ndo_get_offload_stats = dpaa2_switch_port_get_offload_stats, + .ndo_fdb_add = dpaa2_switch_port_fdb_add, + .ndo_fdb_del = dpaa2_switch_port_fdb_del, + .ndo_fdb_dump = dpaa2_switch_port_fdb_dump, + + .ndo_start_xmit = dpaa2_switch_port_dropframe, + .ndo_get_port_parent_id = dpaa2_switch_port_parent_id, + .ndo_get_phys_port_name = dpaa2_switch_port_get_phys_name, +}; + +static bool dpaa2_switch_port_dev_check(const struct net_device *netdev, + struct notifier_block *nb) +{ + struct ethsw_port_priv *port_priv = netdev_priv(netdev); + + if (netdev->netdev_ops == &dpaa2_switch_port_ops && + (!nb || &port_priv->ethsw_data->port_nb == nb || + &port_priv->ethsw_data->port_switchdev_nb == nb || + &port_priv->ethsw_data->port_switchdevb_nb == nb)) + return true; + + return false; +} + +static void dpaa2_switch_links_state_update(struct ethsw_core *ethsw) { int i; - for (i = 0; i < ethsw->sw_attr.num_ifs; i++) - port_carrier_state_sync(ethsw->ports[i]->netdev); + for (i = 0; i < ethsw->sw_attr.num_ifs; i++) { + dpaa2_switch_port_carrier_state_sync(ethsw->ports[i]->netdev); + dpaa2_switch_port_set_mac_addr(ethsw->ports[i]); + } } -static irqreturn_t ethsw_irq0_handler_thread(int irq_num, void *arg) +static irqreturn_t dpaa2_switch_irq0_handler_thread(int irq_num, void *arg) { struct device *dev = (struct device *)arg; struct ethsw_core *ethsw = dev_get_drvdata(dev); @@ -537,23 +809,23 @@ 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; } if (status & DPSW_IRQ_EVENT_LINK_CHANGED) - ethsw_links_state_update(ethsw); + dpaa2_switch_links_state_update(ethsw); out: return IRQ_HANDLED; } -static int ethsw_setup_irqs(struct fsl_mc_device *sw_dev) +static int dpaa2_switch_setup_irqs(struct fsl_mc_device *sw_dev) { struct device *dev = &sw_dev->dev; struct ethsw_core *ethsw = dev_get_drvdata(dev); @@ -583,25 +855,25 @@ err = devm_request_threaded_irq(dev, irq->msi_desc->irq, NULL, - ethsw_irq0_handler_thread, + dpaa2_switch_irq0_handler_thread, 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; } @@ -614,7 +886,7 @@ return err; } -static void ethsw_teardown_irqs(struct fsl_mc_device *sw_dev) +static void dpaa2_switch_teardown_irqs(struct fsl_mc_device *sw_dev) { struct device *dev = &sw_dev->dev; struct ethsw_core *ethsw = dev_get_drvdata(dev); @@ -628,46 +900,31 @@ fsl_mc_free_irqs(sw_dev); } -static int swdev_port_attr_get(struct net_device *netdev, - struct switchdev_attr *attr) -{ - struct ethsw_port_priv *port_priv = netdev_priv(netdev); - - switch (attr->id) { - case SWITCHDEV_ATTR_ID_PORT_PARENT_ID: - attr->u.ppid.id_len = 1; - attr->u.ppid.id[0] = port_priv->ethsw_data->dev_id; - break; - case SWITCHDEV_ATTR_ID_PORT_BRIDGE_FLAGS: - attr->u.brport_flags = - (port_priv->ethsw_data->learning ? BR_LEARNING : 0) | - (port_priv->flood ? BR_FLOOD : 0); - break; - case SWITCHDEV_ATTR_ID_PORT_BRIDGE_FLAGS_SUPPORT: - attr->u.brport_flags_support = BR_LEARNING | BR_FLOOD; - break; - default: - return -EOPNOTSUPP; - } - - return 0; -} - -static int port_attr_stp_state_set(struct net_device *netdev, - struct switchdev_trans *trans, - u8 state) +static int dpaa2_switch_port_attr_stp_state_set(struct net_device *netdev, + struct switchdev_trans *trans, + u8 state) { struct ethsw_port_priv *port_priv = netdev_priv(netdev); if (switchdev_trans_ph_prepare(trans)) return 0; - return ethsw_port_set_stp_state(port_priv, state); + return dpaa2_switch_port_set_stp_state(port_priv, state); } -static int port_attr_br_flags_set(struct net_device *netdev, - struct switchdev_trans *trans, - unsigned long flags) +static int dpaa2_switch_port_attr_br_flags_pre_set(struct net_device *netdev, + struct switchdev_trans *trans, + unsigned long flags) +{ + if (flags & ~(BR_LEARNING | BR_FLOOD)) + return -EINVAL; + + return 0; +} + +static int dpaa2_switch_port_attr_br_flags_set(struct net_device *netdev, + struct switchdev_trans *trans, + unsigned long flags) { struct ethsw_port_priv *port_priv = netdev_priv(netdev); int err = 0; @@ -676,30 +933,35 @@ return 0; /* Learning is enabled per switch */ - err = ethsw_set_learning(port_priv->ethsw_data, flags & BR_LEARNING); + err = dpaa2_switch_set_learning(port_priv->ethsw_data, + !!(flags & BR_LEARNING)); if (err) goto exit; - err = ethsw_port_set_flood(port_priv, flags & BR_FLOOD); + err = dpaa2_switch_port_set_flood(port_priv, !!(flags & BR_FLOOD)); exit: return err; } -static int swdev_port_attr_set(struct net_device *netdev, - const struct switchdev_attr *attr, - struct switchdev_trans *trans) +static int dpaa2_switch_port_attr_set(struct net_device *netdev, + const struct switchdev_attr *attr, + struct switchdev_trans *trans) { int err = 0; switch (attr->id) { case SWITCHDEV_ATTR_ID_PORT_STP_STATE: - err = port_attr_stp_state_set(netdev, trans, - attr->u.stp_state); + err = dpaa2_switch_port_attr_stp_state_set(netdev, trans, + attr->u.stp_state); + break; + case SWITCHDEV_ATTR_ID_PORT_PRE_BRIDGE_FLAGS: + err = dpaa2_switch_port_attr_br_flags_pre_set(netdev, trans, + attr->u.brport_flags); break; case SWITCHDEV_ATTR_ID_PORT_BRIDGE_FLAGS: - err = port_attr_br_flags_set(netdev, trans, - attr->u.brport_flags); + err = dpaa2_switch_port_attr_br_flags_set(netdev, trans, + attr->u.brport_flags); break; case SWITCHDEV_ATTR_ID_BRIDGE_VLAN_FILTERING: /* VLANs are supported by default */ @@ -712,29 +974,43 @@ return err; } -static int port_vlans_add(struct net_device *netdev, - const struct switchdev_obj_port_vlan *vlan, - struct switchdev_trans *trans) +static int dpaa2_switch_port_vlans_add(struct net_device *netdev, + const struct switchdev_obj_port_vlan *vlan, + struct switchdev_trans *trans) { struct ethsw_port_priv *port_priv = netdev_priv(netdev); - int vid, err; + struct ethsw_core *ethsw = port_priv->ethsw_data; + struct dpsw_attr *attr = ðsw->sw_attr; + int vid, err = 0, new_vlans = 0; - if (netif_is_bridge_master(vlan->obj.orig_dev)) - return -EOPNOTSUPP; + if (switchdev_trans_ph_prepare(trans)) { + for (vid = vlan->vid_begin; vid <= vlan->vid_end; vid++) + if (!port_priv->ethsw_data->vlans[vid]) + new_vlans++; - if (switchdev_trans_ph_prepare(trans)) + /* Check if there is space for a new VLAN */ + err = dpsw_get_attributes(ethsw->mc_io, 0, ethsw->dpsw_handle, + ðsw->sw_attr); + if (err) { + netdev_err(netdev, "dpsw_get_attributes err %d\n", err); + return err; + } + if (attr->max_vlans - attr->num_vlans < new_vlans) + return -ENOSPC; + return 0; + } for (vid = vlan->vid_begin; vid <= vlan->vid_end; vid++) { if (!port_priv->ethsw_data->vlans[vid]) { /* this is a new VLAN */ - err = ethsw_add_vlan(port_priv->ethsw_data, vid); + err = dpaa2_switch_add_vlan(port_priv->ethsw_data, vid); if (err) return err; port_priv->ethsw_data->vlans[vid] |= ETHSW_VLAN_GLOBAL; } - err = ethsw_port_add_vlan(port_priv, vid, vlan->flags); + err = dpaa2_switch_port_add_vlan(port_priv, vid, vlan->flags); if (err) break; } @@ -742,8 +1018,8 @@ return err; } -static int port_lookup_address(struct net_device *netdev, int is_uc, - const unsigned char *addr) +static int dpaa2_switch_port_lookup_address(struct net_device *netdev, int is_uc, + const unsigned char *addr) { struct netdev_hw_addr_list *list = (is_uc) ? &netdev->uc : &netdev->mc; struct netdev_hw_addr *ha; @@ -759,9 +1035,9 @@ return 0; } -static int port_mdb_add(struct net_device *netdev, - const struct switchdev_obj_port_mdb *mdb, - struct switchdev_trans *trans) +static int dpaa2_switch_port_mdb_add(struct net_device *netdev, + const struct switchdev_obj_port_mdb *mdb, + struct switchdev_trans *trans) { struct ethsw_port_priv *port_priv = netdev_priv(netdev); int err; @@ -770,38 +1046,38 @@ return 0; /* Check if address is already set on this port */ - if (port_lookup_address(netdev, 0, mdb->addr)) + if (dpaa2_switch_port_lookup_address(netdev, 0, mdb->addr)) return -EEXIST; - err = ethsw_port_fdb_add_mc(port_priv, mdb->addr); + err = dpaa2_switch_port_fdb_add_mc(port_priv, mdb->addr); if (err) return err; err = dev_mc_add(netdev, mdb->addr); if (err) { netdev_err(netdev, "dev_mc_add err %d\n", err); - ethsw_port_fdb_del_mc(port_priv, mdb->addr); + dpaa2_switch_port_fdb_del_mc(port_priv, mdb->addr); } return err; } -static int swdev_port_obj_add(struct net_device *netdev, - const struct switchdev_obj *obj, - struct switchdev_trans *trans) +static int dpaa2_switch_port_obj_add(struct net_device *netdev, + const struct switchdev_obj *obj, + struct switchdev_trans *trans) { int err; switch (obj->id) { case SWITCHDEV_OBJ_ID_PORT_VLAN: - err = port_vlans_add(netdev, - SWITCHDEV_OBJ_PORT_VLAN(obj), - trans); + err = dpaa2_switch_port_vlans_add(netdev, + SWITCHDEV_OBJ_PORT_VLAN(obj), + trans); break; case SWITCHDEV_OBJ_ID_PORT_MDB: - err = port_mdb_add(netdev, - SWITCHDEV_OBJ_PORT_MDB(obj), - trans); + err = dpaa2_switch_port_mdb_add(netdev, + SWITCHDEV_OBJ_PORT_MDB(obj), + trans); break; default: err = -EOPNOTSUPP; @@ -811,7 +1087,7 @@ return err; } -static int ethsw_port_del_vlan(struct ethsw_port_priv *port_priv, u16 vid) +static int dpaa2_switch_port_del_vlan(struct ethsw_port_priv *port_priv, u16 vid) { struct ethsw_core *ethsw = port_priv->ethsw_data; struct net_device *netdev = port_priv->netdev; @@ -822,7 +1098,7 @@ return -ENOENT; if (port_priv->vlans[vid] & ETHSW_VLAN_PVID) { - err = ethsw_port_set_pvid(port_priv, 0); + err = dpaa2_switch_port_set_pvid(port_priv, 0); if (err) return err; } @@ -860,7 +1136,7 @@ ethsw->vlans[vid] &= ~ETHSW_VLAN_GLOBAL; - err = ethsw_dellink_switch(ethsw, vid); + err = dpaa2_switch_dellink(ethsw, vid); if (err) return err; } @@ -868,17 +1144,17 @@ return 0; } -static int port_vlans_del(struct net_device *netdev, - const struct switchdev_obj_port_vlan *vlan) +static int dpaa2_switch_port_vlans_del(struct net_device *netdev, + const struct switchdev_obj_port_vlan *vlan) { struct ethsw_port_priv *port_priv = netdev_priv(netdev); - int vid, err; + int vid, err = 0; if (netif_is_bridge_master(vlan->obj.orig_dev)) return -EOPNOTSUPP; for (vid = vlan->vid_begin; vid <= vlan->vid_end; vid++) { - err = ethsw_port_del_vlan(port_priv, vid); + err = dpaa2_switch_port_del_vlan(port_priv, vid); if (err) break; } @@ -886,16 +1162,16 @@ return err; } -static int port_mdb_del(struct net_device *netdev, - const struct switchdev_obj_port_mdb *mdb) +static int dpaa2_switch_port_mdb_del(struct net_device *netdev, + const struct switchdev_obj_port_mdb *mdb) { struct ethsw_port_priv *port_priv = netdev_priv(netdev); int err; - if (!port_lookup_address(netdev, 0, mdb->addr)) + if (!dpaa2_switch_port_lookup_address(netdev, 0, mdb->addr)) return -ENOENT; - err = ethsw_port_fdb_del_mc(port_priv, mdb->addr); + err = dpaa2_switch_port_fdb_del_mc(port_priv, mdb->addr); if (err) return err; @@ -908,17 +1184,17 @@ return err; } -static int swdev_port_obj_del(struct net_device *netdev, - const struct switchdev_obj *obj) +static int dpaa2_switch_port_obj_del(struct net_device *netdev, + const struct switchdev_obj *obj) { int err; switch (obj->id) { case SWITCHDEV_OBJ_ID_PORT_VLAN: - err = port_vlans_del(netdev, SWITCHDEV_OBJ_PORT_VLAN(obj)); + err = dpaa2_switch_port_vlans_del(netdev, SWITCHDEV_OBJ_PORT_VLAN(obj)); break; case SWITCHDEV_OBJ_ID_PORT_MDB: - err = port_mdb_del(netdev, SWITCHDEV_OBJ_PORT_MDB(obj)); + err = dpaa2_switch_port_mdb_del(netdev, SWITCHDEV_OBJ_PORT_MDB(obj)); break; default: err = -EOPNOTSUPP; @@ -927,60 +1203,80 @@ return err; } -static const struct switchdev_ops ethsw_port_switchdev_ops = { - .switchdev_port_attr_get = swdev_port_attr_get, - .switchdev_port_attr_set = swdev_port_attr_set, - .switchdev_port_obj_add = swdev_port_obj_add, - .switchdev_port_obj_del = swdev_port_obj_del, -}; +static int dpaa2_switch_port_attr_set_event(struct net_device *netdev, + struct switchdev_notifier_port_attr_info + *port_attr_info) +{ + int err; + + err = dpaa2_switch_port_attr_set(netdev, port_attr_info->attr, + port_attr_info->trans); + + port_attr_info->handled = true; + return notifier_from_errno(err); +} /* For the moment, only flood setting needs to be updated */ -static int port_bridge_join(struct net_device *netdev, - struct net_device *upper_dev) +static int dpaa2_switch_port_bridge_join(struct net_device *netdev, + struct net_device *upper_dev) { struct ethsw_port_priv *port_priv = netdev_priv(netdev); struct ethsw_core *ethsw = port_priv->ethsw_data; + struct ethsw_port_priv *other_port_priv; + struct net_device *other_dev; + struct list_head *iter; int i, err; for (i = 0; i < ethsw->sw_attr.num_ifs; i++) 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; } + netdev_for_each_lower_dev(upper_dev, other_dev, iter) { + if (!dpaa2_switch_port_dev_check(other_dev, NULL)) + continue; + + other_port_priv = netdev_priv(other_dev); + if (other_port_priv->ethsw_data != port_priv->ethsw_data) { + netdev_err(netdev, + "Interface from a different DPSW is in the bridge already!\n"); + return -EINVAL; + } + } + /* Enable flooding */ - err = ethsw_port_set_flood(port_priv, 1); + err = dpaa2_switch_port_set_flood(port_priv, 1); if (!err) port_priv->bridge_dev = upper_dev; return err; } -static int port_bridge_leave(struct net_device *netdev) +static int dpaa2_switch_port_bridge_leave(struct net_device *netdev) { struct ethsw_port_priv *port_priv = netdev_priv(netdev); int err; /* Disable flooding */ - err = ethsw_port_set_flood(port_priv, 0); + err = dpaa2_switch_port_set_flood(port_priv, 0); if (!err) port_priv->bridge_dev = NULL; return err; } -static int port_netdevice_event(struct notifier_block *unused, - unsigned long event, void *ptr) +static int dpaa2_switch_port_netdevice_event(struct notifier_block *nb, + unsigned long event, void *ptr) { struct net_device *netdev = netdev_notifier_info_to_dev(ptr); struct netdev_notifier_changeupper_info *info = ptr; struct net_device *upper_dev; int err = 0; - if (netdev->netdev_ops != ðsw_port_ops) + if (!dpaa2_switch_port_dev_check(netdev, nb)) return NOTIFY_DONE; /* Handle just upper dev link/unlink for the moment */ @@ -988,18 +1284,14 @@ upper_dev = info->upper_dev; if (netif_is_bridge_master(upper_dev)) { if (info->linking) - err = port_bridge_join(netdev, upper_dev); + err = dpaa2_switch_port_bridge_join(netdev, upper_dev); else - err = port_bridge_leave(netdev); + err = dpaa2_switch_port_bridge_leave(netdev); } } return notifier_from_errno(err); } - -static struct notifier_block port_nb __read_mostly = { - .notifier_call = port_netdevice_event, -}; struct ethsw_switchdev_event_work { struct work_struct work; @@ -1008,30 +1300,40 @@ unsigned long event; }; -static void ethsw_switchdev_event_work(struct work_struct *work) +static void dpaa2_switch_event_work(struct work_struct *work) { struct ethsw_switchdev_event_work *switchdev_work = container_of(work, struct ethsw_switchdev_event_work, work); struct net_device *dev = switchdev_work->dev; struct switchdev_notifier_fdb_info *fdb_info; - struct ethsw_port_priv *port_priv; + int err; rtnl_lock(); - port_priv = netdev_priv(dev); 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 = dpaa2_switch_port_fdb_add_uc(netdev_priv(dev), + fdb_info->addr); else - ethsw_port_fdb_add_mc(netdev_priv(dev), fdb_info->addr); + err = dpaa2_switch_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); + dpaa2_switch_port_fdb_del_uc(netdev_priv(dev), fdb_info->addr); else - ethsw_port_fdb_del_mc(netdev_priv(dev), fdb_info->addr); + dpaa2_switch_port_fdb_del_mc(netdev_priv(dev), fdb_info->addr); break; } @@ -1042,18 +1344,26 @@ } /* Called under rcu_read_lock() */ -static int port_switchdev_event(struct notifier_block *unused, - unsigned long event, void *ptr) +static int dpaa2_switch_port_event(struct notifier_block *nb, + unsigned long event, void *ptr) { struct net_device *dev = switchdev_notifier_info_to_dev(ptr); + struct ethsw_port_priv *port_priv = netdev_priv(dev); struct ethsw_switchdev_event_work *switchdev_work; struct switchdev_notifier_fdb_info *fdb_info = ptr; + struct ethsw_core *ethsw = port_priv->ethsw_data; + + if (!dpaa2_switch_port_dev_check(dev, nb)) + return NOTIFY_DONE; + + if (event == SWITCHDEV_PORT_ATTR_SET) + return dpaa2_switch_port_attr_set_event(dev, ptr); switchdev_work = kzalloc(sizeof(*switchdev_work), GFP_ATOMIC); if (!switchdev_work) return NOTIFY_BAD; - INIT_WORK(&switchdev_work->work, ethsw_switchdev_event_work); + INIT_WORK(&switchdev_work->work, dpaa2_switch_event_work); switchdev_work->dev = dev; switchdev_work->event = event; @@ -1077,7 +1387,7 @@ return NOTIFY_DONE; } - queue_work(ethsw_owq, &switchdev_work->work); + queue_work(ethsw->workqueue, &switchdev_work->work); return NOTIFY_DONE; @@ -1086,82 +1396,95 @@ return NOTIFY_BAD; } -static struct notifier_block port_switchdev_nb = { - .notifier_call = port_switchdev_event, -}; - -static int ethsw_register_notifier(struct device *dev) +static int dpaa2_switch_port_obj_event(unsigned long event, + struct net_device *netdev, + struct switchdev_notifier_port_obj_info *port_obj_info) { + int err = -EOPNOTSUPP; + + switch (event) { + case SWITCHDEV_PORT_OBJ_ADD: + err = dpaa2_switch_port_obj_add(netdev, port_obj_info->obj, + port_obj_info->trans); + break; + case SWITCHDEV_PORT_OBJ_DEL: + err = dpaa2_switch_port_obj_del(netdev, port_obj_info->obj); + break; + } + + port_obj_info->handled = true; + return notifier_from_errno(err); +} + +static int dpaa2_switch_port_blocking_event(struct notifier_block *nb, + unsigned long event, void *ptr) +{ + struct net_device *dev = switchdev_notifier_info_to_dev(ptr); + + if (!dpaa2_switch_port_dev_check(dev, nb)) + return NOTIFY_DONE; + + switch (event) { + case SWITCHDEV_PORT_OBJ_ADD: + case SWITCHDEV_PORT_OBJ_DEL: + return dpaa2_switch_port_obj_event(event, dev, ptr); + case SWITCHDEV_PORT_ATTR_SET: + return dpaa2_switch_port_attr_set_event(dev, ptr); + } + + return NOTIFY_DONE; +} + +static int dpaa2_switch_register_notifier(struct device *dev) +{ + struct ethsw_core *ethsw = dev_get_drvdata(dev); int err; - err = register_netdevice_notifier(&port_nb); + ethsw->port_nb.notifier_call = dpaa2_switch_port_netdevice_event; + err = register_netdevice_notifier(ðsw->port_nb); if (err) { dev_err(dev, "Failed to register netdev notifier\n"); return err; } - err = register_switchdev_notifier(&port_switchdev_nb); + ethsw->port_switchdev_nb.notifier_call = dpaa2_switch_port_event; + err = register_switchdev_notifier(ðsw->port_switchdev_nb); if (err) { dev_err(dev, "Failed to register switchdev notifier\n"); goto err_switchdev_nb; } + ethsw->port_switchdevb_nb.notifier_call = dpaa2_switch_port_blocking_event; + err = register_switchdev_blocking_notifier(ðsw->port_switchdevb_nb); + if (err) { + dev_err(dev, "Failed to register switchdev blocking notifier\n"); + goto err_switchdev_blocking_nb; + } + return 0; +err_switchdev_blocking_nb: + unregister_switchdev_notifier(ðsw->port_switchdev_nb); err_switchdev_nb: - unregister_netdevice_notifier(&port_nb); + unregister_netdevice_notifier(ðsw->port_nb); return err; } -static int ethsw_open(struct ethsw_core *ethsw) +static void dpaa2_switch_detect_features(struct ethsw_core *ethsw) { - struct ethsw_port_priv *port_priv = NULL; - int i, err; + ethsw->features = 0; - 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); - if (err) { - netdev_err(port_priv->netdev, "dev_open err %d\n", err); - return err; - } - } - - return 0; + if (ethsw->major > 8 || (ethsw->major == 8 && ethsw->minor >= 6)) + ethsw->features |= ETHSW_FEATURE_MAC_ADDR; } -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) +static int dpaa2_switch_init(struct fsl_mc_device *sw_dev) { struct device *dev = &sw_dev->dev; struct ethsw_core *ethsw = dev_get_drvdata(dev); - u16 version_major, version_minor, i; struct dpsw_stp_cfg stp_cfg; int err; + u16 i; ethsw->dev_id = sw_dev->obj_desc.id; @@ -1179,24 +1502,26 @@ } err = dpsw_get_api_version(ethsw->mc_io, 0, - &version_major, - &version_minor); + ðsw->major, + ðsw->minor); if (err) { dev_err(dev, "dpsw_get_api_version err %d\n", err); goto err_close; } /* Minimum supported DPSW version check */ - if (version_major < DPSW_MIN_VER_MAJOR || - (version_major == DPSW_MIN_VER_MAJOR && - version_minor < DPSW_MIN_VER_MINOR)) { + if (ethsw->major < DPSW_MIN_VER_MAJOR || + (ethsw->major == DPSW_MIN_VER_MAJOR && + ethsw->minor < DPSW_MIN_VER_MINOR)) { dev_err(dev, "DPSW version %d:%d not supported. Use %d.%d or greater.\n", - version_major, - version_minor, + ethsw->major, + ethsw->minor, DPSW_MIN_VER_MAJOR, DPSW_MIN_VER_MINOR); err = -ENOTSUPP; goto err_close; } + + dpaa2_switch_detect_features(ethsw); err = dpsw_reset(ethsw->mc_io, 0, ethsw->dpsw_handle); if (err) { @@ -1233,30 +1558,30 @@ } } - ethsw_owq = alloc_ordered_workqueue("%s_ordered", WQ_MEM_RECLAIM, - "ethsw"); - if (!ethsw_owq) { + ethsw->workqueue = alloc_ordered_workqueue("%s_%d_ordered", + WQ_MEM_RECLAIM, "ethsw", + ethsw->sw_attr.id); + if (!ethsw->workqueue) { err = -ENOMEM; goto err_close; } - err = ethsw_register_notifier(dev); + err = dpaa2_switch_register_notifier(dev); if (err) goto err_destroy_ordered_workqueue; return 0; err_destroy_ordered_workqueue: - destroy_workqueue(ethsw_owq); + destroy_workqueue(ethsw->workqueue); err_close: dpsw_close(ethsw->mc_io, 0, ethsw->dpsw_handle); return err; } -static int ethsw_port_init(struct ethsw_port_priv *port_priv, u16 port) +static int dpaa2_switch_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; @@ -1276,51 +1601,56 @@ return err; } - err = ethsw_port_set_pvid(port_priv, 0); + err = dpaa2_switch_port_set_pvid(port_priv, 0); if (err) return err; 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; - } - - err = ethsw_port_fdb_add_mc(port_priv, def_mcast); return err; } -static void ethsw_unregister_notifier(struct device *dev) +static void dpaa2_switch_unregister_notifier(struct device *dev) { + struct ethsw_core *ethsw = dev_get_drvdata(dev); + struct notifier_block *nb; int err; - err = unregister_switchdev_notifier(&port_switchdev_nb); + nb = ðsw->port_switchdevb_nb; + err = unregister_switchdev_blocking_notifier(nb); + if (err) + dev_err(dev, + "Failed to unregister switchdev blocking notifier (%d)\n", + err); + + err = unregister_switchdev_notifier(ðsw->port_switchdev_nb); if (err) dev_err(dev, "Failed to unregister switchdev notifier (%d)\n", err); - err = unregister_netdevice_notifier(&port_nb); + err = unregister_netdevice_notifier(ðsw->port_nb); if (err) dev_err(dev, "Failed to unregister netdev notifier (%d)\n", err); } -static void ethsw_takedown(struct fsl_mc_device *sw_dev) +static void dpaa2_switch_takedown(struct fsl_mc_device *sw_dev) { struct device *dev = &sw_dev->dev; struct ethsw_core *ethsw = dev_get_drvdata(dev); int err; - ethsw_unregister_notifier(dev); + dpaa2_switch_unregister_notifier(dev); err = dpsw_close(ethsw->mc_io, 0, ethsw->dpsw_handle); if (err) dev_warn(dev, "dpsw_close err %d\n", err); } -static int ethsw_remove(struct fsl_mc_device *sw_dev) +static int dpaa2_switch_remove(struct fsl_mc_device *sw_dev) { struct ethsw_port_priv *port_priv; struct ethsw_core *ethsw; @@ -1330,13 +1660,9 @@ dev = &sw_dev->dev; ethsw = dev_get_drvdata(dev); - ethsw_teardown_irqs(sw_dev); + dpaa2_switch_teardown_irqs(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]; @@ -1345,7 +1671,10 @@ } kfree(ethsw->ports); - ethsw_takedown(sw_dev); + dpaa2_switch_takedown(sw_dev); + + destroy_workqueue(ethsw->workqueue); + fsl_mc_portal_free(ethsw->mc_io); kfree(ethsw); @@ -1355,7 +1684,8 @@ return 0; } -static int ethsw_probe_port(struct ethsw_core *ethsw, u16 port_idx) +static int dpaa2_switch_probe_port(struct ethsw_core *ethsw, + u16 port_idx) { struct ethsw_port_priv *port_priv; struct device *dev = ethsw->dev; @@ -1379,27 +1709,38 @@ port_priv->flood = true; SET_NETDEV_DEV(port_netdev, dev); - port_netdev->netdev_ops = ðsw_port_ops; - port_netdev->ethtool_ops = ðsw_port_ethtool_ops; - port_netdev->switchdev_ops = ðsw_port_switchdev_ops; + port_netdev->netdev_ops = &dpaa2_switch_port_ops; + port_netdev->ethtool_ops = &dpaa2_switch_port_ethtool_ops; /* Set MTU limits */ port_netdev->min_mtu = ETH_MIN_MTU; port_netdev->max_mtu = ETHSW_MAX_FRAME_LENGTH; + err = dpaa2_switch_port_init(port_priv, port_idx); + if (err) + goto err_port_probe; + + err = dpaa2_switch_port_set_mac_addr(port_priv); + 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) +static int dpaa2_switch_probe(struct fsl_mc_device *sw_dev) { struct device *dev = &sw_dev->dev; struct ethsw_core *ethsw; @@ -1414,7 +1755,8 @@ ethsw->dev = dev; dev_set_drvdata(dev, ethsw); - err = fsl_mc_portal_allocate(sw_dev, 0, ðsw->mc_io); + err = fsl_mc_portal_allocate(sw_dev, FSL_MC_IO_ATOMIC_CONTEXT_PORTAL, + ðsw->mc_io); if (err) { if (err == -ENXIO) err = -EPROBE_DEFER; @@ -1423,7 +1765,7 @@ goto err_free_drvdata; } - err = ethsw_init(sw_dev); + err = dpaa2_switch_init(sw_dev); if (err) goto err_free_cmdport; @@ -1441,20 +1783,23 @@ } for (i = 0; i < ethsw->sw_attr.num_ifs; i++) { - err = ethsw_probe_port(ethsw, i); + err = dpaa2_switch_probe_port(ethsw, i); if (err) 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; + } + + /* Make sure the switch ports are disabled at probe time */ + for (i = 0; i < ethsw->sw_attr.num_ifs; i++) + dpsw_if_disable(ethsw->mc_io, 0, ethsw->dpsw_handle, i); /* Setup IRQs */ - err = ethsw_setup_irqs(sw_dev); + err = dpaa2_switch_setup_irqs(sw_dev); if (err) goto err_stop; @@ -1462,9 +1807,7 @@ 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 */ @@ -1475,7 +1818,7 @@ kfree(ethsw->ports); err_takedown: - ethsw_takedown(sw_dev); + dpaa2_switch_takedown(sw_dev); err_free_cmdport: fsl_mc_portal_free(ethsw->mc_io); @@ -1487,26 +1830,26 @@ return err; } -static const struct fsl_mc_device_id ethsw_match_id_table[] = { +static const struct fsl_mc_device_id dpaa2_switch_match_id_table[] = { { .vendor = FSL_MC_VENDOR_FREESCALE, .obj_type = "dpsw", }, { .vendor = 0x0 } }; -MODULE_DEVICE_TABLE(fslmc, ethsw_match_id_table); +MODULE_DEVICE_TABLE(fslmc, dpaa2_switch_match_id_table); -static struct fsl_mc_driver eth_sw_drv = { +static struct fsl_mc_driver dpaa2_switch_drv = { .driver = { .name = KBUILD_MODNAME, .owner = THIS_MODULE, }, - .probe = ethsw_probe, - .remove = ethsw_remove, - .match_id_table = ethsw_match_id_table + .probe = dpaa2_switch_probe, + .remove = dpaa2_switch_remove, + .match_id_table = dpaa2_switch_match_id_table }; -module_fsl_mc_driver(eth_sw_drv); +module_fsl_mc_driver(dpaa2_switch_drv); MODULE_LICENSE("GPL v2"); MODULE_DESCRIPTION("DPAA2 Ethernet Switch Driver"); -- Gitblit v1.6.2