162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * DPAA2 Ethernet Switch driver 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright 2014-2016 Freescale Semiconductor Inc. 662306a36Sopenharmony_ci * Copyright 2017-2021 NXP 762306a36Sopenharmony_ci * 862306a36Sopenharmony_ci */ 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#include <linux/module.h> 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ci#include <linux/interrupt.h> 1362306a36Sopenharmony_ci#include <linux/kthread.h> 1462306a36Sopenharmony_ci#include <linux/workqueue.h> 1562306a36Sopenharmony_ci#include <linux/iommu.h> 1662306a36Sopenharmony_ci#include <net/pkt_cls.h> 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_ci#include <linux/fsl/mc.h> 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci#include "dpaa2-switch.h" 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci/* Minimal supported DPSW version */ 2362306a36Sopenharmony_ci#define DPSW_MIN_VER_MAJOR 8 2462306a36Sopenharmony_ci#define DPSW_MIN_VER_MINOR 9 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci#define DEFAULT_VLAN_ID 1 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_cistatic u16 dpaa2_switch_port_get_fdb_id(struct ethsw_port_priv *port_priv) 2962306a36Sopenharmony_ci{ 3062306a36Sopenharmony_ci return port_priv->fdb->fdb_id; 3162306a36Sopenharmony_ci} 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_cistatic struct dpaa2_switch_fdb *dpaa2_switch_fdb_get_unused(struct ethsw_core *ethsw) 3462306a36Sopenharmony_ci{ 3562306a36Sopenharmony_ci int i; 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci for (i = 0; i < ethsw->sw_attr.num_ifs; i++) 3862306a36Sopenharmony_ci if (!ethsw->fdbs[i].in_use) 3962306a36Sopenharmony_ci return ðsw->fdbs[i]; 4062306a36Sopenharmony_ci return NULL; 4162306a36Sopenharmony_ci} 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_cistatic struct dpaa2_switch_filter_block * 4462306a36Sopenharmony_cidpaa2_switch_filter_block_get_unused(struct ethsw_core *ethsw) 4562306a36Sopenharmony_ci{ 4662306a36Sopenharmony_ci int i; 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_ci for (i = 0; i < ethsw->sw_attr.num_ifs; i++) 4962306a36Sopenharmony_ci if (!ethsw->filter_blocks[i].in_use) 5062306a36Sopenharmony_ci return ðsw->filter_blocks[i]; 5162306a36Sopenharmony_ci return NULL; 5262306a36Sopenharmony_ci} 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_cistatic u16 dpaa2_switch_port_set_fdb(struct ethsw_port_priv *port_priv, 5562306a36Sopenharmony_ci struct net_device *bridge_dev) 5662306a36Sopenharmony_ci{ 5762306a36Sopenharmony_ci struct ethsw_port_priv *other_port_priv = NULL; 5862306a36Sopenharmony_ci struct dpaa2_switch_fdb *fdb; 5962306a36Sopenharmony_ci struct net_device *other_dev; 6062306a36Sopenharmony_ci struct list_head *iter; 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci /* If we leave a bridge (bridge_dev is NULL), find an unused 6362306a36Sopenharmony_ci * FDB and use that. 6462306a36Sopenharmony_ci */ 6562306a36Sopenharmony_ci if (!bridge_dev) { 6662306a36Sopenharmony_ci fdb = dpaa2_switch_fdb_get_unused(port_priv->ethsw_data); 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ci /* If there is no unused FDB, we must be the last port that 6962306a36Sopenharmony_ci * leaves the last bridge, all the others are standalone. We 7062306a36Sopenharmony_ci * can just keep the FDB that we already have. 7162306a36Sopenharmony_ci */ 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci if (!fdb) { 7462306a36Sopenharmony_ci port_priv->fdb->bridge_dev = NULL; 7562306a36Sopenharmony_ci return 0; 7662306a36Sopenharmony_ci } 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci port_priv->fdb = fdb; 7962306a36Sopenharmony_ci port_priv->fdb->in_use = true; 8062306a36Sopenharmony_ci port_priv->fdb->bridge_dev = NULL; 8162306a36Sopenharmony_ci return 0; 8262306a36Sopenharmony_ci } 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci /* The below call to netdev_for_each_lower_dev() demands the RTNL lock 8562306a36Sopenharmony_ci * being held. Assert on it so that it's easier to catch new code 8662306a36Sopenharmony_ci * paths that reach this point without the RTNL lock. 8762306a36Sopenharmony_ci */ 8862306a36Sopenharmony_ci ASSERT_RTNL(); 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci /* If part of a bridge, use the FDB of the first dpaa2 switch interface 9162306a36Sopenharmony_ci * to be present in that bridge 9262306a36Sopenharmony_ci */ 9362306a36Sopenharmony_ci netdev_for_each_lower_dev(bridge_dev, other_dev, iter) { 9462306a36Sopenharmony_ci if (!dpaa2_switch_port_dev_check(other_dev)) 9562306a36Sopenharmony_ci continue; 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci if (other_dev == port_priv->netdev) 9862306a36Sopenharmony_ci continue; 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci other_port_priv = netdev_priv(other_dev); 10162306a36Sopenharmony_ci break; 10262306a36Sopenharmony_ci } 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci /* The current port is about to change its FDB to the one used by the 10562306a36Sopenharmony_ci * first port that joined the bridge. 10662306a36Sopenharmony_ci */ 10762306a36Sopenharmony_ci if (other_port_priv) { 10862306a36Sopenharmony_ci /* The previous FDB is about to become unused, since the 10962306a36Sopenharmony_ci * interface is no longer standalone. 11062306a36Sopenharmony_ci */ 11162306a36Sopenharmony_ci port_priv->fdb->in_use = false; 11262306a36Sopenharmony_ci port_priv->fdb->bridge_dev = NULL; 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci /* Get a reference to the new FDB */ 11562306a36Sopenharmony_ci port_priv->fdb = other_port_priv->fdb; 11662306a36Sopenharmony_ci } 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci /* Keep track of the new upper bridge device */ 11962306a36Sopenharmony_ci port_priv->fdb->bridge_dev = bridge_dev; 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_ci return 0; 12262306a36Sopenharmony_ci} 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_cistatic void dpaa2_switch_fdb_get_flood_cfg(struct ethsw_core *ethsw, u16 fdb_id, 12562306a36Sopenharmony_ci enum dpsw_flood_type type, 12662306a36Sopenharmony_ci struct dpsw_egress_flood_cfg *cfg) 12762306a36Sopenharmony_ci{ 12862306a36Sopenharmony_ci int i = 0, j; 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci memset(cfg, 0, sizeof(*cfg)); 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci /* Add all the DPAA2 switch ports found in the same bridging domain to 13362306a36Sopenharmony_ci * the egress flooding domain 13462306a36Sopenharmony_ci */ 13562306a36Sopenharmony_ci for (j = 0; j < ethsw->sw_attr.num_ifs; j++) { 13662306a36Sopenharmony_ci if (!ethsw->ports[j]) 13762306a36Sopenharmony_ci continue; 13862306a36Sopenharmony_ci if (ethsw->ports[j]->fdb->fdb_id != fdb_id) 13962306a36Sopenharmony_ci continue; 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci if (type == DPSW_BROADCAST && ethsw->ports[j]->bcast_flood) 14262306a36Sopenharmony_ci cfg->if_id[i++] = ethsw->ports[j]->idx; 14362306a36Sopenharmony_ci else if (type == DPSW_FLOODING && ethsw->ports[j]->ucast_flood) 14462306a36Sopenharmony_ci cfg->if_id[i++] = ethsw->ports[j]->idx; 14562306a36Sopenharmony_ci } 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci /* Add the CTRL interface to the egress flooding domain */ 14862306a36Sopenharmony_ci cfg->if_id[i++] = ethsw->sw_attr.num_ifs; 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci cfg->fdb_id = fdb_id; 15162306a36Sopenharmony_ci cfg->flood_type = type; 15262306a36Sopenharmony_ci cfg->num_ifs = i; 15362306a36Sopenharmony_ci} 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_cistatic int dpaa2_switch_fdb_set_egress_flood(struct ethsw_core *ethsw, u16 fdb_id) 15662306a36Sopenharmony_ci{ 15762306a36Sopenharmony_ci struct dpsw_egress_flood_cfg flood_cfg; 15862306a36Sopenharmony_ci int err; 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci /* Setup broadcast flooding domain */ 16162306a36Sopenharmony_ci dpaa2_switch_fdb_get_flood_cfg(ethsw, fdb_id, DPSW_BROADCAST, &flood_cfg); 16262306a36Sopenharmony_ci err = dpsw_set_egress_flood(ethsw->mc_io, 0, ethsw->dpsw_handle, 16362306a36Sopenharmony_ci &flood_cfg); 16462306a36Sopenharmony_ci if (err) { 16562306a36Sopenharmony_ci dev_err(ethsw->dev, "dpsw_set_egress_flood() = %d\n", err); 16662306a36Sopenharmony_ci return err; 16762306a36Sopenharmony_ci } 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ci /* Setup unknown flooding domain */ 17062306a36Sopenharmony_ci dpaa2_switch_fdb_get_flood_cfg(ethsw, fdb_id, DPSW_FLOODING, &flood_cfg); 17162306a36Sopenharmony_ci err = dpsw_set_egress_flood(ethsw->mc_io, 0, ethsw->dpsw_handle, 17262306a36Sopenharmony_ci &flood_cfg); 17362306a36Sopenharmony_ci if (err) { 17462306a36Sopenharmony_ci dev_err(ethsw->dev, "dpsw_set_egress_flood() = %d\n", err); 17562306a36Sopenharmony_ci return err; 17662306a36Sopenharmony_ci } 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ci return 0; 17962306a36Sopenharmony_ci} 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_cistatic void *dpaa2_iova_to_virt(struct iommu_domain *domain, 18262306a36Sopenharmony_ci dma_addr_t iova_addr) 18362306a36Sopenharmony_ci{ 18462306a36Sopenharmony_ci phys_addr_t phys_addr; 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_ci phys_addr = domain ? iommu_iova_to_phys(domain, iova_addr) : iova_addr; 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci return phys_to_virt(phys_addr); 18962306a36Sopenharmony_ci} 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_cistatic int dpaa2_switch_add_vlan(struct ethsw_port_priv *port_priv, u16 vid) 19262306a36Sopenharmony_ci{ 19362306a36Sopenharmony_ci struct ethsw_core *ethsw = port_priv->ethsw_data; 19462306a36Sopenharmony_ci struct dpsw_vlan_cfg vcfg = {0}; 19562306a36Sopenharmony_ci int err; 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci vcfg.fdb_id = dpaa2_switch_port_get_fdb_id(port_priv); 19862306a36Sopenharmony_ci err = dpsw_vlan_add(ethsw->mc_io, 0, 19962306a36Sopenharmony_ci ethsw->dpsw_handle, vid, &vcfg); 20062306a36Sopenharmony_ci if (err) { 20162306a36Sopenharmony_ci dev_err(ethsw->dev, "dpsw_vlan_add err %d\n", err); 20262306a36Sopenharmony_ci return err; 20362306a36Sopenharmony_ci } 20462306a36Sopenharmony_ci ethsw->vlans[vid] = ETHSW_VLAN_MEMBER; 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ci return 0; 20762306a36Sopenharmony_ci} 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_cistatic bool dpaa2_switch_port_is_up(struct ethsw_port_priv *port_priv) 21062306a36Sopenharmony_ci{ 21162306a36Sopenharmony_ci struct net_device *netdev = port_priv->netdev; 21262306a36Sopenharmony_ci struct dpsw_link_state state; 21362306a36Sopenharmony_ci int err; 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ci err = dpsw_if_get_link_state(port_priv->ethsw_data->mc_io, 0, 21662306a36Sopenharmony_ci port_priv->ethsw_data->dpsw_handle, 21762306a36Sopenharmony_ci port_priv->idx, &state); 21862306a36Sopenharmony_ci if (err) { 21962306a36Sopenharmony_ci netdev_err(netdev, "dpsw_if_get_link_state() err %d\n", err); 22062306a36Sopenharmony_ci return true; 22162306a36Sopenharmony_ci } 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_ci WARN_ONCE(state.up > 1, "Garbage read into link_state"); 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ci return state.up ? true : false; 22662306a36Sopenharmony_ci} 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_cistatic int dpaa2_switch_port_set_pvid(struct ethsw_port_priv *port_priv, u16 pvid) 22962306a36Sopenharmony_ci{ 23062306a36Sopenharmony_ci struct ethsw_core *ethsw = port_priv->ethsw_data; 23162306a36Sopenharmony_ci struct net_device *netdev = port_priv->netdev; 23262306a36Sopenharmony_ci struct dpsw_tci_cfg tci_cfg = { 0 }; 23362306a36Sopenharmony_ci bool up; 23462306a36Sopenharmony_ci int err, ret; 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_ci err = dpsw_if_get_tci(ethsw->mc_io, 0, ethsw->dpsw_handle, 23762306a36Sopenharmony_ci port_priv->idx, &tci_cfg); 23862306a36Sopenharmony_ci if (err) { 23962306a36Sopenharmony_ci netdev_err(netdev, "dpsw_if_get_tci err %d\n", err); 24062306a36Sopenharmony_ci return err; 24162306a36Sopenharmony_ci } 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_ci tci_cfg.vlan_id = pvid; 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_ci /* Interface needs to be down to change PVID */ 24662306a36Sopenharmony_ci up = dpaa2_switch_port_is_up(port_priv); 24762306a36Sopenharmony_ci if (up) { 24862306a36Sopenharmony_ci err = dpsw_if_disable(ethsw->mc_io, 0, 24962306a36Sopenharmony_ci ethsw->dpsw_handle, 25062306a36Sopenharmony_ci port_priv->idx); 25162306a36Sopenharmony_ci if (err) { 25262306a36Sopenharmony_ci netdev_err(netdev, "dpsw_if_disable err %d\n", err); 25362306a36Sopenharmony_ci return err; 25462306a36Sopenharmony_ci } 25562306a36Sopenharmony_ci } 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_ci err = dpsw_if_set_tci(ethsw->mc_io, 0, ethsw->dpsw_handle, 25862306a36Sopenharmony_ci port_priv->idx, &tci_cfg); 25962306a36Sopenharmony_ci if (err) { 26062306a36Sopenharmony_ci netdev_err(netdev, "dpsw_if_set_tci err %d\n", err); 26162306a36Sopenharmony_ci goto set_tci_error; 26262306a36Sopenharmony_ci } 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_ci /* Delete previous PVID info and mark the new one */ 26562306a36Sopenharmony_ci port_priv->vlans[port_priv->pvid] &= ~ETHSW_VLAN_PVID; 26662306a36Sopenharmony_ci port_priv->vlans[pvid] |= ETHSW_VLAN_PVID; 26762306a36Sopenharmony_ci port_priv->pvid = pvid; 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_ciset_tci_error: 27062306a36Sopenharmony_ci if (up) { 27162306a36Sopenharmony_ci ret = dpsw_if_enable(ethsw->mc_io, 0, 27262306a36Sopenharmony_ci ethsw->dpsw_handle, 27362306a36Sopenharmony_ci port_priv->idx); 27462306a36Sopenharmony_ci if (ret) { 27562306a36Sopenharmony_ci netdev_err(netdev, "dpsw_if_enable err %d\n", ret); 27662306a36Sopenharmony_ci return ret; 27762306a36Sopenharmony_ci } 27862306a36Sopenharmony_ci } 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_ci return err; 28162306a36Sopenharmony_ci} 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_cistatic int dpaa2_switch_port_add_vlan(struct ethsw_port_priv *port_priv, 28462306a36Sopenharmony_ci u16 vid, u16 flags) 28562306a36Sopenharmony_ci{ 28662306a36Sopenharmony_ci struct ethsw_core *ethsw = port_priv->ethsw_data; 28762306a36Sopenharmony_ci struct net_device *netdev = port_priv->netdev; 28862306a36Sopenharmony_ci struct dpsw_vlan_if_cfg vcfg = {0}; 28962306a36Sopenharmony_ci int err; 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_ci if (port_priv->vlans[vid]) { 29262306a36Sopenharmony_ci netdev_warn(netdev, "VLAN %d already configured\n", vid); 29362306a36Sopenharmony_ci return -EEXIST; 29462306a36Sopenharmony_ci } 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_ci /* If hit, this VLAN rule will lead the packet into the FDB table 29762306a36Sopenharmony_ci * specified in the vlan configuration below 29862306a36Sopenharmony_ci */ 29962306a36Sopenharmony_ci vcfg.num_ifs = 1; 30062306a36Sopenharmony_ci vcfg.if_id[0] = port_priv->idx; 30162306a36Sopenharmony_ci vcfg.fdb_id = dpaa2_switch_port_get_fdb_id(port_priv); 30262306a36Sopenharmony_ci vcfg.options |= DPSW_VLAN_ADD_IF_OPT_FDB_ID; 30362306a36Sopenharmony_ci err = dpsw_vlan_add_if(ethsw->mc_io, 0, ethsw->dpsw_handle, vid, &vcfg); 30462306a36Sopenharmony_ci if (err) { 30562306a36Sopenharmony_ci netdev_err(netdev, "dpsw_vlan_add_if err %d\n", err); 30662306a36Sopenharmony_ci return err; 30762306a36Sopenharmony_ci } 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_ci port_priv->vlans[vid] = ETHSW_VLAN_MEMBER; 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_ci if (flags & BRIDGE_VLAN_INFO_UNTAGGED) { 31262306a36Sopenharmony_ci err = dpsw_vlan_add_if_untagged(ethsw->mc_io, 0, 31362306a36Sopenharmony_ci ethsw->dpsw_handle, 31462306a36Sopenharmony_ci vid, &vcfg); 31562306a36Sopenharmony_ci if (err) { 31662306a36Sopenharmony_ci netdev_err(netdev, 31762306a36Sopenharmony_ci "dpsw_vlan_add_if_untagged err %d\n", err); 31862306a36Sopenharmony_ci return err; 31962306a36Sopenharmony_ci } 32062306a36Sopenharmony_ci port_priv->vlans[vid] |= ETHSW_VLAN_UNTAGGED; 32162306a36Sopenharmony_ci } 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_ci if (flags & BRIDGE_VLAN_INFO_PVID) { 32462306a36Sopenharmony_ci err = dpaa2_switch_port_set_pvid(port_priv, vid); 32562306a36Sopenharmony_ci if (err) 32662306a36Sopenharmony_ci return err; 32762306a36Sopenharmony_ci } 32862306a36Sopenharmony_ci 32962306a36Sopenharmony_ci return 0; 33062306a36Sopenharmony_ci} 33162306a36Sopenharmony_ci 33262306a36Sopenharmony_cistatic enum dpsw_stp_state br_stp_state_to_dpsw(u8 state) 33362306a36Sopenharmony_ci{ 33462306a36Sopenharmony_ci switch (state) { 33562306a36Sopenharmony_ci case BR_STATE_DISABLED: 33662306a36Sopenharmony_ci return DPSW_STP_STATE_DISABLED; 33762306a36Sopenharmony_ci case BR_STATE_LISTENING: 33862306a36Sopenharmony_ci return DPSW_STP_STATE_LISTENING; 33962306a36Sopenharmony_ci case BR_STATE_LEARNING: 34062306a36Sopenharmony_ci return DPSW_STP_STATE_LEARNING; 34162306a36Sopenharmony_ci case BR_STATE_FORWARDING: 34262306a36Sopenharmony_ci return DPSW_STP_STATE_FORWARDING; 34362306a36Sopenharmony_ci case BR_STATE_BLOCKING: 34462306a36Sopenharmony_ci return DPSW_STP_STATE_BLOCKING; 34562306a36Sopenharmony_ci default: 34662306a36Sopenharmony_ci return DPSW_STP_STATE_DISABLED; 34762306a36Sopenharmony_ci } 34862306a36Sopenharmony_ci} 34962306a36Sopenharmony_ci 35062306a36Sopenharmony_cistatic int dpaa2_switch_port_set_stp_state(struct ethsw_port_priv *port_priv, u8 state) 35162306a36Sopenharmony_ci{ 35262306a36Sopenharmony_ci struct dpsw_stp_cfg stp_cfg = {0}; 35362306a36Sopenharmony_ci int err; 35462306a36Sopenharmony_ci u16 vid; 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_ci if (!netif_running(port_priv->netdev) || state == port_priv->stp_state) 35762306a36Sopenharmony_ci return 0; /* Nothing to do */ 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_ci stp_cfg.state = br_stp_state_to_dpsw(state); 36062306a36Sopenharmony_ci for (vid = 0; vid <= VLAN_VID_MASK; vid++) { 36162306a36Sopenharmony_ci if (port_priv->vlans[vid] & ETHSW_VLAN_MEMBER) { 36262306a36Sopenharmony_ci stp_cfg.vlan_id = vid; 36362306a36Sopenharmony_ci err = dpsw_if_set_stp(port_priv->ethsw_data->mc_io, 0, 36462306a36Sopenharmony_ci port_priv->ethsw_data->dpsw_handle, 36562306a36Sopenharmony_ci port_priv->idx, &stp_cfg); 36662306a36Sopenharmony_ci if (err) { 36762306a36Sopenharmony_ci netdev_err(port_priv->netdev, 36862306a36Sopenharmony_ci "dpsw_if_set_stp err %d\n", err); 36962306a36Sopenharmony_ci return err; 37062306a36Sopenharmony_ci } 37162306a36Sopenharmony_ci } 37262306a36Sopenharmony_ci } 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_ci port_priv->stp_state = state; 37562306a36Sopenharmony_ci 37662306a36Sopenharmony_ci return 0; 37762306a36Sopenharmony_ci} 37862306a36Sopenharmony_ci 37962306a36Sopenharmony_cistatic int dpaa2_switch_dellink(struct ethsw_core *ethsw, u16 vid) 38062306a36Sopenharmony_ci{ 38162306a36Sopenharmony_ci struct ethsw_port_priv *ppriv_local = NULL; 38262306a36Sopenharmony_ci int i, err; 38362306a36Sopenharmony_ci 38462306a36Sopenharmony_ci if (!ethsw->vlans[vid]) 38562306a36Sopenharmony_ci return -ENOENT; 38662306a36Sopenharmony_ci 38762306a36Sopenharmony_ci err = dpsw_vlan_remove(ethsw->mc_io, 0, ethsw->dpsw_handle, vid); 38862306a36Sopenharmony_ci if (err) { 38962306a36Sopenharmony_ci dev_err(ethsw->dev, "dpsw_vlan_remove err %d\n", err); 39062306a36Sopenharmony_ci return err; 39162306a36Sopenharmony_ci } 39262306a36Sopenharmony_ci ethsw->vlans[vid] = 0; 39362306a36Sopenharmony_ci 39462306a36Sopenharmony_ci for (i = 0; i < ethsw->sw_attr.num_ifs; i++) { 39562306a36Sopenharmony_ci ppriv_local = ethsw->ports[i]; 39662306a36Sopenharmony_ci if (ppriv_local) 39762306a36Sopenharmony_ci ppriv_local->vlans[vid] = 0; 39862306a36Sopenharmony_ci } 39962306a36Sopenharmony_ci 40062306a36Sopenharmony_ci return 0; 40162306a36Sopenharmony_ci} 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_cistatic int dpaa2_switch_port_fdb_add_uc(struct ethsw_port_priv *port_priv, 40462306a36Sopenharmony_ci const unsigned char *addr) 40562306a36Sopenharmony_ci{ 40662306a36Sopenharmony_ci struct dpsw_fdb_unicast_cfg entry = {0}; 40762306a36Sopenharmony_ci u16 fdb_id; 40862306a36Sopenharmony_ci int err; 40962306a36Sopenharmony_ci 41062306a36Sopenharmony_ci entry.if_egress = port_priv->idx; 41162306a36Sopenharmony_ci entry.type = DPSW_FDB_ENTRY_STATIC; 41262306a36Sopenharmony_ci ether_addr_copy(entry.mac_addr, addr); 41362306a36Sopenharmony_ci 41462306a36Sopenharmony_ci fdb_id = dpaa2_switch_port_get_fdb_id(port_priv); 41562306a36Sopenharmony_ci err = dpsw_fdb_add_unicast(port_priv->ethsw_data->mc_io, 0, 41662306a36Sopenharmony_ci port_priv->ethsw_data->dpsw_handle, 41762306a36Sopenharmony_ci fdb_id, &entry); 41862306a36Sopenharmony_ci if (err) 41962306a36Sopenharmony_ci netdev_err(port_priv->netdev, 42062306a36Sopenharmony_ci "dpsw_fdb_add_unicast err %d\n", err); 42162306a36Sopenharmony_ci return err; 42262306a36Sopenharmony_ci} 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_cistatic int dpaa2_switch_port_fdb_del_uc(struct ethsw_port_priv *port_priv, 42562306a36Sopenharmony_ci const unsigned char *addr) 42662306a36Sopenharmony_ci{ 42762306a36Sopenharmony_ci struct dpsw_fdb_unicast_cfg entry = {0}; 42862306a36Sopenharmony_ci u16 fdb_id; 42962306a36Sopenharmony_ci int err; 43062306a36Sopenharmony_ci 43162306a36Sopenharmony_ci entry.if_egress = port_priv->idx; 43262306a36Sopenharmony_ci entry.type = DPSW_FDB_ENTRY_STATIC; 43362306a36Sopenharmony_ci ether_addr_copy(entry.mac_addr, addr); 43462306a36Sopenharmony_ci 43562306a36Sopenharmony_ci fdb_id = dpaa2_switch_port_get_fdb_id(port_priv); 43662306a36Sopenharmony_ci err = dpsw_fdb_remove_unicast(port_priv->ethsw_data->mc_io, 0, 43762306a36Sopenharmony_ci port_priv->ethsw_data->dpsw_handle, 43862306a36Sopenharmony_ci fdb_id, &entry); 43962306a36Sopenharmony_ci /* Silently discard error for calling multiple times the del command */ 44062306a36Sopenharmony_ci if (err && err != -ENXIO) 44162306a36Sopenharmony_ci netdev_err(port_priv->netdev, 44262306a36Sopenharmony_ci "dpsw_fdb_remove_unicast err %d\n", err); 44362306a36Sopenharmony_ci return err; 44462306a36Sopenharmony_ci} 44562306a36Sopenharmony_ci 44662306a36Sopenharmony_cistatic int dpaa2_switch_port_fdb_add_mc(struct ethsw_port_priv *port_priv, 44762306a36Sopenharmony_ci const unsigned char *addr) 44862306a36Sopenharmony_ci{ 44962306a36Sopenharmony_ci struct dpsw_fdb_multicast_cfg entry = {0}; 45062306a36Sopenharmony_ci u16 fdb_id; 45162306a36Sopenharmony_ci int err; 45262306a36Sopenharmony_ci 45362306a36Sopenharmony_ci ether_addr_copy(entry.mac_addr, addr); 45462306a36Sopenharmony_ci entry.type = DPSW_FDB_ENTRY_STATIC; 45562306a36Sopenharmony_ci entry.num_ifs = 1; 45662306a36Sopenharmony_ci entry.if_id[0] = port_priv->idx; 45762306a36Sopenharmony_ci 45862306a36Sopenharmony_ci fdb_id = dpaa2_switch_port_get_fdb_id(port_priv); 45962306a36Sopenharmony_ci err = dpsw_fdb_add_multicast(port_priv->ethsw_data->mc_io, 0, 46062306a36Sopenharmony_ci port_priv->ethsw_data->dpsw_handle, 46162306a36Sopenharmony_ci fdb_id, &entry); 46262306a36Sopenharmony_ci /* Silently discard error for calling multiple times the add command */ 46362306a36Sopenharmony_ci if (err && err != -ENXIO) 46462306a36Sopenharmony_ci netdev_err(port_priv->netdev, "dpsw_fdb_add_multicast err %d\n", 46562306a36Sopenharmony_ci err); 46662306a36Sopenharmony_ci return err; 46762306a36Sopenharmony_ci} 46862306a36Sopenharmony_ci 46962306a36Sopenharmony_cistatic int dpaa2_switch_port_fdb_del_mc(struct ethsw_port_priv *port_priv, 47062306a36Sopenharmony_ci const unsigned char *addr) 47162306a36Sopenharmony_ci{ 47262306a36Sopenharmony_ci struct dpsw_fdb_multicast_cfg entry = {0}; 47362306a36Sopenharmony_ci u16 fdb_id; 47462306a36Sopenharmony_ci int err; 47562306a36Sopenharmony_ci 47662306a36Sopenharmony_ci ether_addr_copy(entry.mac_addr, addr); 47762306a36Sopenharmony_ci entry.type = DPSW_FDB_ENTRY_STATIC; 47862306a36Sopenharmony_ci entry.num_ifs = 1; 47962306a36Sopenharmony_ci entry.if_id[0] = port_priv->idx; 48062306a36Sopenharmony_ci 48162306a36Sopenharmony_ci fdb_id = dpaa2_switch_port_get_fdb_id(port_priv); 48262306a36Sopenharmony_ci err = dpsw_fdb_remove_multicast(port_priv->ethsw_data->mc_io, 0, 48362306a36Sopenharmony_ci port_priv->ethsw_data->dpsw_handle, 48462306a36Sopenharmony_ci fdb_id, &entry); 48562306a36Sopenharmony_ci /* Silently discard error for calling multiple times the del command */ 48662306a36Sopenharmony_ci if (err && err != -ENAVAIL) 48762306a36Sopenharmony_ci netdev_err(port_priv->netdev, 48862306a36Sopenharmony_ci "dpsw_fdb_remove_multicast err %d\n", err); 48962306a36Sopenharmony_ci return err; 49062306a36Sopenharmony_ci} 49162306a36Sopenharmony_ci 49262306a36Sopenharmony_cistatic void dpaa2_switch_port_get_stats(struct net_device *netdev, 49362306a36Sopenharmony_ci struct rtnl_link_stats64 *stats) 49462306a36Sopenharmony_ci{ 49562306a36Sopenharmony_ci struct ethsw_port_priv *port_priv = netdev_priv(netdev); 49662306a36Sopenharmony_ci u64 tmp; 49762306a36Sopenharmony_ci int err; 49862306a36Sopenharmony_ci 49962306a36Sopenharmony_ci err = dpsw_if_get_counter(port_priv->ethsw_data->mc_io, 0, 50062306a36Sopenharmony_ci port_priv->ethsw_data->dpsw_handle, 50162306a36Sopenharmony_ci port_priv->idx, 50262306a36Sopenharmony_ci DPSW_CNT_ING_FRAME, &stats->rx_packets); 50362306a36Sopenharmony_ci if (err) 50462306a36Sopenharmony_ci goto error; 50562306a36Sopenharmony_ci 50662306a36Sopenharmony_ci err = dpsw_if_get_counter(port_priv->ethsw_data->mc_io, 0, 50762306a36Sopenharmony_ci port_priv->ethsw_data->dpsw_handle, 50862306a36Sopenharmony_ci port_priv->idx, 50962306a36Sopenharmony_ci DPSW_CNT_EGR_FRAME, &stats->tx_packets); 51062306a36Sopenharmony_ci if (err) 51162306a36Sopenharmony_ci goto error; 51262306a36Sopenharmony_ci 51362306a36Sopenharmony_ci err = dpsw_if_get_counter(port_priv->ethsw_data->mc_io, 0, 51462306a36Sopenharmony_ci port_priv->ethsw_data->dpsw_handle, 51562306a36Sopenharmony_ci port_priv->idx, 51662306a36Sopenharmony_ci DPSW_CNT_ING_BYTE, &stats->rx_bytes); 51762306a36Sopenharmony_ci if (err) 51862306a36Sopenharmony_ci goto error; 51962306a36Sopenharmony_ci 52062306a36Sopenharmony_ci err = dpsw_if_get_counter(port_priv->ethsw_data->mc_io, 0, 52162306a36Sopenharmony_ci port_priv->ethsw_data->dpsw_handle, 52262306a36Sopenharmony_ci port_priv->idx, 52362306a36Sopenharmony_ci DPSW_CNT_EGR_BYTE, &stats->tx_bytes); 52462306a36Sopenharmony_ci if (err) 52562306a36Sopenharmony_ci goto error; 52662306a36Sopenharmony_ci 52762306a36Sopenharmony_ci err = dpsw_if_get_counter(port_priv->ethsw_data->mc_io, 0, 52862306a36Sopenharmony_ci port_priv->ethsw_data->dpsw_handle, 52962306a36Sopenharmony_ci port_priv->idx, 53062306a36Sopenharmony_ci DPSW_CNT_ING_FRAME_DISCARD, 53162306a36Sopenharmony_ci &stats->rx_dropped); 53262306a36Sopenharmony_ci if (err) 53362306a36Sopenharmony_ci goto error; 53462306a36Sopenharmony_ci 53562306a36Sopenharmony_ci err = dpsw_if_get_counter(port_priv->ethsw_data->mc_io, 0, 53662306a36Sopenharmony_ci port_priv->ethsw_data->dpsw_handle, 53762306a36Sopenharmony_ci port_priv->idx, 53862306a36Sopenharmony_ci DPSW_CNT_ING_FLTR_FRAME, 53962306a36Sopenharmony_ci &tmp); 54062306a36Sopenharmony_ci if (err) 54162306a36Sopenharmony_ci goto error; 54262306a36Sopenharmony_ci stats->rx_dropped += tmp; 54362306a36Sopenharmony_ci 54462306a36Sopenharmony_ci err = dpsw_if_get_counter(port_priv->ethsw_data->mc_io, 0, 54562306a36Sopenharmony_ci port_priv->ethsw_data->dpsw_handle, 54662306a36Sopenharmony_ci port_priv->idx, 54762306a36Sopenharmony_ci DPSW_CNT_EGR_FRAME_DISCARD, 54862306a36Sopenharmony_ci &stats->tx_dropped); 54962306a36Sopenharmony_ci if (err) 55062306a36Sopenharmony_ci goto error; 55162306a36Sopenharmony_ci 55262306a36Sopenharmony_ci return; 55362306a36Sopenharmony_ci 55462306a36Sopenharmony_cierror: 55562306a36Sopenharmony_ci netdev_err(netdev, "dpsw_if_get_counter err %d\n", err); 55662306a36Sopenharmony_ci} 55762306a36Sopenharmony_ci 55862306a36Sopenharmony_cistatic bool dpaa2_switch_port_has_offload_stats(const struct net_device *netdev, 55962306a36Sopenharmony_ci int attr_id) 56062306a36Sopenharmony_ci{ 56162306a36Sopenharmony_ci return (attr_id == IFLA_OFFLOAD_XSTATS_CPU_HIT); 56262306a36Sopenharmony_ci} 56362306a36Sopenharmony_ci 56462306a36Sopenharmony_cistatic int dpaa2_switch_port_get_offload_stats(int attr_id, 56562306a36Sopenharmony_ci const struct net_device *netdev, 56662306a36Sopenharmony_ci void *sp) 56762306a36Sopenharmony_ci{ 56862306a36Sopenharmony_ci switch (attr_id) { 56962306a36Sopenharmony_ci case IFLA_OFFLOAD_XSTATS_CPU_HIT: 57062306a36Sopenharmony_ci dpaa2_switch_port_get_stats((struct net_device *)netdev, sp); 57162306a36Sopenharmony_ci return 0; 57262306a36Sopenharmony_ci } 57362306a36Sopenharmony_ci 57462306a36Sopenharmony_ci return -EINVAL; 57562306a36Sopenharmony_ci} 57662306a36Sopenharmony_ci 57762306a36Sopenharmony_cistatic int dpaa2_switch_port_change_mtu(struct net_device *netdev, int mtu) 57862306a36Sopenharmony_ci{ 57962306a36Sopenharmony_ci struct ethsw_port_priv *port_priv = netdev_priv(netdev); 58062306a36Sopenharmony_ci int err; 58162306a36Sopenharmony_ci 58262306a36Sopenharmony_ci err = dpsw_if_set_max_frame_length(port_priv->ethsw_data->mc_io, 58362306a36Sopenharmony_ci 0, 58462306a36Sopenharmony_ci port_priv->ethsw_data->dpsw_handle, 58562306a36Sopenharmony_ci port_priv->idx, 58662306a36Sopenharmony_ci (u16)ETHSW_L2_MAX_FRM(mtu)); 58762306a36Sopenharmony_ci if (err) { 58862306a36Sopenharmony_ci netdev_err(netdev, 58962306a36Sopenharmony_ci "dpsw_if_set_max_frame_length() err %d\n", err); 59062306a36Sopenharmony_ci return err; 59162306a36Sopenharmony_ci } 59262306a36Sopenharmony_ci 59362306a36Sopenharmony_ci netdev->mtu = mtu; 59462306a36Sopenharmony_ci return 0; 59562306a36Sopenharmony_ci} 59662306a36Sopenharmony_ci 59762306a36Sopenharmony_cistatic int dpaa2_switch_port_link_state_update(struct net_device *netdev) 59862306a36Sopenharmony_ci{ 59962306a36Sopenharmony_ci struct ethsw_port_priv *port_priv = netdev_priv(netdev); 60062306a36Sopenharmony_ci struct dpsw_link_state state; 60162306a36Sopenharmony_ci int err; 60262306a36Sopenharmony_ci 60362306a36Sopenharmony_ci /* When we manage the MAC/PHY using phylink there is no need 60462306a36Sopenharmony_ci * to manually update the netif_carrier. 60562306a36Sopenharmony_ci * We can avoid locking because we are called from the "link changed" 60662306a36Sopenharmony_ci * IRQ handler, which is the same as the "endpoint changed" IRQ handler 60762306a36Sopenharmony_ci * (the writer to port_priv->mac), so we cannot race with it. 60862306a36Sopenharmony_ci */ 60962306a36Sopenharmony_ci if (dpaa2_mac_is_type_phy(port_priv->mac)) 61062306a36Sopenharmony_ci return 0; 61162306a36Sopenharmony_ci 61262306a36Sopenharmony_ci /* Interrupts are received even though no one issued an 'ifconfig up' 61362306a36Sopenharmony_ci * on the switch interface. Ignore these link state update interrupts 61462306a36Sopenharmony_ci */ 61562306a36Sopenharmony_ci if (!netif_running(netdev)) 61662306a36Sopenharmony_ci return 0; 61762306a36Sopenharmony_ci 61862306a36Sopenharmony_ci err = dpsw_if_get_link_state(port_priv->ethsw_data->mc_io, 0, 61962306a36Sopenharmony_ci port_priv->ethsw_data->dpsw_handle, 62062306a36Sopenharmony_ci port_priv->idx, &state); 62162306a36Sopenharmony_ci if (err) { 62262306a36Sopenharmony_ci netdev_err(netdev, "dpsw_if_get_link_state() err %d\n", err); 62362306a36Sopenharmony_ci return err; 62462306a36Sopenharmony_ci } 62562306a36Sopenharmony_ci 62662306a36Sopenharmony_ci WARN_ONCE(state.up > 1, "Garbage read into link_state"); 62762306a36Sopenharmony_ci 62862306a36Sopenharmony_ci if (state.up != port_priv->link_state) { 62962306a36Sopenharmony_ci if (state.up) { 63062306a36Sopenharmony_ci netif_carrier_on(netdev); 63162306a36Sopenharmony_ci netif_tx_start_all_queues(netdev); 63262306a36Sopenharmony_ci } else { 63362306a36Sopenharmony_ci netif_carrier_off(netdev); 63462306a36Sopenharmony_ci netif_tx_stop_all_queues(netdev); 63562306a36Sopenharmony_ci } 63662306a36Sopenharmony_ci port_priv->link_state = state.up; 63762306a36Sopenharmony_ci } 63862306a36Sopenharmony_ci 63962306a36Sopenharmony_ci return 0; 64062306a36Sopenharmony_ci} 64162306a36Sopenharmony_ci 64262306a36Sopenharmony_ci/* Manage all NAPI instances for the control interface. 64362306a36Sopenharmony_ci * 64462306a36Sopenharmony_ci * We only have one RX queue and one Tx Conf queue for all 64562306a36Sopenharmony_ci * switch ports. Therefore, we only need to enable the NAPI instance once, the 64662306a36Sopenharmony_ci * first time one of the switch ports runs .dev_open(). 64762306a36Sopenharmony_ci */ 64862306a36Sopenharmony_ci 64962306a36Sopenharmony_cistatic void dpaa2_switch_enable_ctrl_if_napi(struct ethsw_core *ethsw) 65062306a36Sopenharmony_ci{ 65162306a36Sopenharmony_ci int i; 65262306a36Sopenharmony_ci 65362306a36Sopenharmony_ci /* Access to the ethsw->napi_users relies on the RTNL lock */ 65462306a36Sopenharmony_ci ASSERT_RTNL(); 65562306a36Sopenharmony_ci 65662306a36Sopenharmony_ci /* a new interface is using the NAPI instance */ 65762306a36Sopenharmony_ci ethsw->napi_users++; 65862306a36Sopenharmony_ci 65962306a36Sopenharmony_ci /* if there is already a user of the instance, return */ 66062306a36Sopenharmony_ci if (ethsw->napi_users > 1) 66162306a36Sopenharmony_ci return; 66262306a36Sopenharmony_ci 66362306a36Sopenharmony_ci for (i = 0; i < DPAA2_SWITCH_RX_NUM_FQS; i++) 66462306a36Sopenharmony_ci napi_enable(ðsw->fq[i].napi); 66562306a36Sopenharmony_ci} 66662306a36Sopenharmony_ci 66762306a36Sopenharmony_cistatic void dpaa2_switch_disable_ctrl_if_napi(struct ethsw_core *ethsw) 66862306a36Sopenharmony_ci{ 66962306a36Sopenharmony_ci int i; 67062306a36Sopenharmony_ci 67162306a36Sopenharmony_ci /* Access to the ethsw->napi_users relies on the RTNL lock */ 67262306a36Sopenharmony_ci ASSERT_RTNL(); 67362306a36Sopenharmony_ci 67462306a36Sopenharmony_ci /* If we are not the last interface using the NAPI, return */ 67562306a36Sopenharmony_ci ethsw->napi_users--; 67662306a36Sopenharmony_ci if (ethsw->napi_users) 67762306a36Sopenharmony_ci return; 67862306a36Sopenharmony_ci 67962306a36Sopenharmony_ci for (i = 0; i < DPAA2_SWITCH_RX_NUM_FQS; i++) 68062306a36Sopenharmony_ci napi_disable(ðsw->fq[i].napi); 68162306a36Sopenharmony_ci} 68262306a36Sopenharmony_ci 68362306a36Sopenharmony_cistatic int dpaa2_switch_port_open(struct net_device *netdev) 68462306a36Sopenharmony_ci{ 68562306a36Sopenharmony_ci struct ethsw_port_priv *port_priv = netdev_priv(netdev); 68662306a36Sopenharmony_ci struct ethsw_core *ethsw = port_priv->ethsw_data; 68762306a36Sopenharmony_ci int err; 68862306a36Sopenharmony_ci 68962306a36Sopenharmony_ci mutex_lock(&port_priv->mac_lock); 69062306a36Sopenharmony_ci 69162306a36Sopenharmony_ci if (!dpaa2_switch_port_is_type_phy(port_priv)) { 69262306a36Sopenharmony_ci /* Explicitly set carrier off, otherwise 69362306a36Sopenharmony_ci * netif_carrier_ok() will return true and cause 'ip link show' 69462306a36Sopenharmony_ci * to report the LOWER_UP flag, even though the link 69562306a36Sopenharmony_ci * notification wasn't even received. 69662306a36Sopenharmony_ci */ 69762306a36Sopenharmony_ci netif_carrier_off(netdev); 69862306a36Sopenharmony_ci } 69962306a36Sopenharmony_ci 70062306a36Sopenharmony_ci err = dpsw_if_enable(port_priv->ethsw_data->mc_io, 0, 70162306a36Sopenharmony_ci port_priv->ethsw_data->dpsw_handle, 70262306a36Sopenharmony_ci port_priv->idx); 70362306a36Sopenharmony_ci if (err) { 70462306a36Sopenharmony_ci mutex_unlock(&port_priv->mac_lock); 70562306a36Sopenharmony_ci netdev_err(netdev, "dpsw_if_enable err %d\n", err); 70662306a36Sopenharmony_ci return err; 70762306a36Sopenharmony_ci } 70862306a36Sopenharmony_ci 70962306a36Sopenharmony_ci dpaa2_switch_enable_ctrl_if_napi(ethsw); 71062306a36Sopenharmony_ci 71162306a36Sopenharmony_ci if (dpaa2_switch_port_is_type_phy(port_priv)) 71262306a36Sopenharmony_ci dpaa2_mac_start(port_priv->mac); 71362306a36Sopenharmony_ci 71462306a36Sopenharmony_ci mutex_unlock(&port_priv->mac_lock); 71562306a36Sopenharmony_ci 71662306a36Sopenharmony_ci return 0; 71762306a36Sopenharmony_ci} 71862306a36Sopenharmony_ci 71962306a36Sopenharmony_cistatic int dpaa2_switch_port_stop(struct net_device *netdev) 72062306a36Sopenharmony_ci{ 72162306a36Sopenharmony_ci struct ethsw_port_priv *port_priv = netdev_priv(netdev); 72262306a36Sopenharmony_ci struct ethsw_core *ethsw = port_priv->ethsw_data; 72362306a36Sopenharmony_ci int err; 72462306a36Sopenharmony_ci 72562306a36Sopenharmony_ci mutex_lock(&port_priv->mac_lock); 72662306a36Sopenharmony_ci 72762306a36Sopenharmony_ci if (dpaa2_switch_port_is_type_phy(port_priv)) { 72862306a36Sopenharmony_ci dpaa2_mac_stop(port_priv->mac); 72962306a36Sopenharmony_ci } else { 73062306a36Sopenharmony_ci netif_tx_stop_all_queues(netdev); 73162306a36Sopenharmony_ci netif_carrier_off(netdev); 73262306a36Sopenharmony_ci } 73362306a36Sopenharmony_ci 73462306a36Sopenharmony_ci mutex_unlock(&port_priv->mac_lock); 73562306a36Sopenharmony_ci 73662306a36Sopenharmony_ci err = dpsw_if_disable(port_priv->ethsw_data->mc_io, 0, 73762306a36Sopenharmony_ci port_priv->ethsw_data->dpsw_handle, 73862306a36Sopenharmony_ci port_priv->idx); 73962306a36Sopenharmony_ci if (err) { 74062306a36Sopenharmony_ci netdev_err(netdev, "dpsw_if_disable err %d\n", err); 74162306a36Sopenharmony_ci return err; 74262306a36Sopenharmony_ci } 74362306a36Sopenharmony_ci 74462306a36Sopenharmony_ci dpaa2_switch_disable_ctrl_if_napi(ethsw); 74562306a36Sopenharmony_ci 74662306a36Sopenharmony_ci return 0; 74762306a36Sopenharmony_ci} 74862306a36Sopenharmony_ci 74962306a36Sopenharmony_cistatic int dpaa2_switch_port_parent_id(struct net_device *dev, 75062306a36Sopenharmony_ci struct netdev_phys_item_id *ppid) 75162306a36Sopenharmony_ci{ 75262306a36Sopenharmony_ci struct ethsw_port_priv *port_priv = netdev_priv(dev); 75362306a36Sopenharmony_ci 75462306a36Sopenharmony_ci ppid->id_len = 1; 75562306a36Sopenharmony_ci ppid->id[0] = port_priv->ethsw_data->dev_id; 75662306a36Sopenharmony_ci 75762306a36Sopenharmony_ci return 0; 75862306a36Sopenharmony_ci} 75962306a36Sopenharmony_ci 76062306a36Sopenharmony_cistatic int dpaa2_switch_port_get_phys_name(struct net_device *netdev, char *name, 76162306a36Sopenharmony_ci size_t len) 76262306a36Sopenharmony_ci{ 76362306a36Sopenharmony_ci struct ethsw_port_priv *port_priv = netdev_priv(netdev); 76462306a36Sopenharmony_ci int err; 76562306a36Sopenharmony_ci 76662306a36Sopenharmony_ci err = snprintf(name, len, "p%d", port_priv->idx); 76762306a36Sopenharmony_ci if (err >= len) 76862306a36Sopenharmony_ci return -EINVAL; 76962306a36Sopenharmony_ci 77062306a36Sopenharmony_ci return 0; 77162306a36Sopenharmony_ci} 77262306a36Sopenharmony_ci 77362306a36Sopenharmony_cistruct ethsw_dump_ctx { 77462306a36Sopenharmony_ci struct net_device *dev; 77562306a36Sopenharmony_ci struct sk_buff *skb; 77662306a36Sopenharmony_ci struct netlink_callback *cb; 77762306a36Sopenharmony_ci int idx; 77862306a36Sopenharmony_ci}; 77962306a36Sopenharmony_ci 78062306a36Sopenharmony_cistatic int dpaa2_switch_fdb_dump_nl(struct fdb_dump_entry *entry, 78162306a36Sopenharmony_ci struct ethsw_dump_ctx *dump) 78262306a36Sopenharmony_ci{ 78362306a36Sopenharmony_ci int is_dynamic = entry->type & DPSW_FDB_ENTRY_DINAMIC; 78462306a36Sopenharmony_ci u32 portid = NETLINK_CB(dump->cb->skb).portid; 78562306a36Sopenharmony_ci u32 seq = dump->cb->nlh->nlmsg_seq; 78662306a36Sopenharmony_ci struct nlmsghdr *nlh; 78762306a36Sopenharmony_ci struct ndmsg *ndm; 78862306a36Sopenharmony_ci 78962306a36Sopenharmony_ci if (dump->idx < dump->cb->args[2]) 79062306a36Sopenharmony_ci goto skip; 79162306a36Sopenharmony_ci 79262306a36Sopenharmony_ci nlh = nlmsg_put(dump->skb, portid, seq, RTM_NEWNEIGH, 79362306a36Sopenharmony_ci sizeof(*ndm), NLM_F_MULTI); 79462306a36Sopenharmony_ci if (!nlh) 79562306a36Sopenharmony_ci return -EMSGSIZE; 79662306a36Sopenharmony_ci 79762306a36Sopenharmony_ci ndm = nlmsg_data(nlh); 79862306a36Sopenharmony_ci ndm->ndm_family = AF_BRIDGE; 79962306a36Sopenharmony_ci ndm->ndm_pad1 = 0; 80062306a36Sopenharmony_ci ndm->ndm_pad2 = 0; 80162306a36Sopenharmony_ci ndm->ndm_flags = NTF_SELF; 80262306a36Sopenharmony_ci ndm->ndm_type = 0; 80362306a36Sopenharmony_ci ndm->ndm_ifindex = dump->dev->ifindex; 80462306a36Sopenharmony_ci ndm->ndm_state = is_dynamic ? NUD_REACHABLE : NUD_NOARP; 80562306a36Sopenharmony_ci 80662306a36Sopenharmony_ci if (nla_put(dump->skb, NDA_LLADDR, ETH_ALEN, entry->mac_addr)) 80762306a36Sopenharmony_ci goto nla_put_failure; 80862306a36Sopenharmony_ci 80962306a36Sopenharmony_ci nlmsg_end(dump->skb, nlh); 81062306a36Sopenharmony_ci 81162306a36Sopenharmony_ciskip: 81262306a36Sopenharmony_ci dump->idx++; 81362306a36Sopenharmony_ci return 0; 81462306a36Sopenharmony_ci 81562306a36Sopenharmony_cinla_put_failure: 81662306a36Sopenharmony_ci nlmsg_cancel(dump->skb, nlh); 81762306a36Sopenharmony_ci return -EMSGSIZE; 81862306a36Sopenharmony_ci} 81962306a36Sopenharmony_ci 82062306a36Sopenharmony_cistatic int dpaa2_switch_port_fdb_valid_entry(struct fdb_dump_entry *entry, 82162306a36Sopenharmony_ci struct ethsw_port_priv *port_priv) 82262306a36Sopenharmony_ci{ 82362306a36Sopenharmony_ci int idx = port_priv->idx; 82462306a36Sopenharmony_ci int valid; 82562306a36Sopenharmony_ci 82662306a36Sopenharmony_ci if (entry->type & DPSW_FDB_ENTRY_TYPE_UNICAST) 82762306a36Sopenharmony_ci valid = entry->if_info == port_priv->idx; 82862306a36Sopenharmony_ci else 82962306a36Sopenharmony_ci valid = entry->if_mask[idx / 8] & BIT(idx % 8); 83062306a36Sopenharmony_ci 83162306a36Sopenharmony_ci return valid; 83262306a36Sopenharmony_ci} 83362306a36Sopenharmony_ci 83462306a36Sopenharmony_cistatic int dpaa2_switch_fdb_iterate(struct ethsw_port_priv *port_priv, 83562306a36Sopenharmony_ci dpaa2_switch_fdb_cb_t cb, void *data) 83662306a36Sopenharmony_ci{ 83762306a36Sopenharmony_ci struct net_device *net_dev = port_priv->netdev; 83862306a36Sopenharmony_ci struct ethsw_core *ethsw = port_priv->ethsw_data; 83962306a36Sopenharmony_ci struct device *dev = net_dev->dev.parent; 84062306a36Sopenharmony_ci struct fdb_dump_entry *fdb_entries; 84162306a36Sopenharmony_ci struct fdb_dump_entry fdb_entry; 84262306a36Sopenharmony_ci dma_addr_t fdb_dump_iova; 84362306a36Sopenharmony_ci u16 num_fdb_entries; 84462306a36Sopenharmony_ci u32 fdb_dump_size; 84562306a36Sopenharmony_ci int err = 0, i; 84662306a36Sopenharmony_ci u8 *dma_mem; 84762306a36Sopenharmony_ci u16 fdb_id; 84862306a36Sopenharmony_ci 84962306a36Sopenharmony_ci fdb_dump_size = ethsw->sw_attr.max_fdb_entries * sizeof(fdb_entry); 85062306a36Sopenharmony_ci dma_mem = kzalloc(fdb_dump_size, GFP_KERNEL); 85162306a36Sopenharmony_ci if (!dma_mem) 85262306a36Sopenharmony_ci return -ENOMEM; 85362306a36Sopenharmony_ci 85462306a36Sopenharmony_ci fdb_dump_iova = dma_map_single(dev, dma_mem, fdb_dump_size, 85562306a36Sopenharmony_ci DMA_FROM_DEVICE); 85662306a36Sopenharmony_ci if (dma_mapping_error(dev, fdb_dump_iova)) { 85762306a36Sopenharmony_ci netdev_err(net_dev, "dma_map_single() failed\n"); 85862306a36Sopenharmony_ci err = -ENOMEM; 85962306a36Sopenharmony_ci goto err_map; 86062306a36Sopenharmony_ci } 86162306a36Sopenharmony_ci 86262306a36Sopenharmony_ci fdb_id = dpaa2_switch_port_get_fdb_id(port_priv); 86362306a36Sopenharmony_ci err = dpsw_fdb_dump(ethsw->mc_io, 0, ethsw->dpsw_handle, fdb_id, 86462306a36Sopenharmony_ci fdb_dump_iova, fdb_dump_size, &num_fdb_entries); 86562306a36Sopenharmony_ci if (err) { 86662306a36Sopenharmony_ci netdev_err(net_dev, "dpsw_fdb_dump() = %d\n", err); 86762306a36Sopenharmony_ci goto err_dump; 86862306a36Sopenharmony_ci } 86962306a36Sopenharmony_ci 87062306a36Sopenharmony_ci dma_unmap_single(dev, fdb_dump_iova, fdb_dump_size, DMA_FROM_DEVICE); 87162306a36Sopenharmony_ci 87262306a36Sopenharmony_ci fdb_entries = (struct fdb_dump_entry *)dma_mem; 87362306a36Sopenharmony_ci for (i = 0; i < num_fdb_entries; i++) { 87462306a36Sopenharmony_ci fdb_entry = fdb_entries[i]; 87562306a36Sopenharmony_ci 87662306a36Sopenharmony_ci err = cb(port_priv, &fdb_entry, data); 87762306a36Sopenharmony_ci if (err) 87862306a36Sopenharmony_ci goto end; 87962306a36Sopenharmony_ci } 88062306a36Sopenharmony_ci 88162306a36Sopenharmony_ciend: 88262306a36Sopenharmony_ci kfree(dma_mem); 88362306a36Sopenharmony_ci 88462306a36Sopenharmony_ci return 0; 88562306a36Sopenharmony_ci 88662306a36Sopenharmony_cierr_dump: 88762306a36Sopenharmony_ci dma_unmap_single(dev, fdb_dump_iova, fdb_dump_size, DMA_TO_DEVICE); 88862306a36Sopenharmony_cierr_map: 88962306a36Sopenharmony_ci kfree(dma_mem); 89062306a36Sopenharmony_ci return err; 89162306a36Sopenharmony_ci} 89262306a36Sopenharmony_ci 89362306a36Sopenharmony_cistatic int dpaa2_switch_fdb_entry_dump(struct ethsw_port_priv *port_priv, 89462306a36Sopenharmony_ci struct fdb_dump_entry *fdb_entry, 89562306a36Sopenharmony_ci void *data) 89662306a36Sopenharmony_ci{ 89762306a36Sopenharmony_ci if (!dpaa2_switch_port_fdb_valid_entry(fdb_entry, port_priv)) 89862306a36Sopenharmony_ci return 0; 89962306a36Sopenharmony_ci 90062306a36Sopenharmony_ci return dpaa2_switch_fdb_dump_nl(fdb_entry, data); 90162306a36Sopenharmony_ci} 90262306a36Sopenharmony_ci 90362306a36Sopenharmony_cistatic int dpaa2_switch_port_fdb_dump(struct sk_buff *skb, struct netlink_callback *cb, 90462306a36Sopenharmony_ci struct net_device *net_dev, 90562306a36Sopenharmony_ci struct net_device *filter_dev, int *idx) 90662306a36Sopenharmony_ci{ 90762306a36Sopenharmony_ci struct ethsw_port_priv *port_priv = netdev_priv(net_dev); 90862306a36Sopenharmony_ci struct ethsw_dump_ctx dump = { 90962306a36Sopenharmony_ci .dev = net_dev, 91062306a36Sopenharmony_ci .skb = skb, 91162306a36Sopenharmony_ci .cb = cb, 91262306a36Sopenharmony_ci .idx = *idx, 91362306a36Sopenharmony_ci }; 91462306a36Sopenharmony_ci int err; 91562306a36Sopenharmony_ci 91662306a36Sopenharmony_ci err = dpaa2_switch_fdb_iterate(port_priv, dpaa2_switch_fdb_entry_dump, &dump); 91762306a36Sopenharmony_ci *idx = dump.idx; 91862306a36Sopenharmony_ci 91962306a36Sopenharmony_ci return err; 92062306a36Sopenharmony_ci} 92162306a36Sopenharmony_ci 92262306a36Sopenharmony_cistatic int dpaa2_switch_fdb_entry_fast_age(struct ethsw_port_priv *port_priv, 92362306a36Sopenharmony_ci struct fdb_dump_entry *fdb_entry, 92462306a36Sopenharmony_ci void *data __always_unused) 92562306a36Sopenharmony_ci{ 92662306a36Sopenharmony_ci if (!dpaa2_switch_port_fdb_valid_entry(fdb_entry, port_priv)) 92762306a36Sopenharmony_ci return 0; 92862306a36Sopenharmony_ci 92962306a36Sopenharmony_ci if (!(fdb_entry->type & DPSW_FDB_ENTRY_TYPE_DYNAMIC)) 93062306a36Sopenharmony_ci return 0; 93162306a36Sopenharmony_ci 93262306a36Sopenharmony_ci if (fdb_entry->type & DPSW_FDB_ENTRY_TYPE_UNICAST) 93362306a36Sopenharmony_ci dpaa2_switch_port_fdb_del_uc(port_priv, fdb_entry->mac_addr); 93462306a36Sopenharmony_ci else 93562306a36Sopenharmony_ci dpaa2_switch_port_fdb_del_mc(port_priv, fdb_entry->mac_addr); 93662306a36Sopenharmony_ci 93762306a36Sopenharmony_ci return 0; 93862306a36Sopenharmony_ci} 93962306a36Sopenharmony_ci 94062306a36Sopenharmony_cistatic void dpaa2_switch_port_fast_age(struct ethsw_port_priv *port_priv) 94162306a36Sopenharmony_ci{ 94262306a36Sopenharmony_ci dpaa2_switch_fdb_iterate(port_priv, 94362306a36Sopenharmony_ci dpaa2_switch_fdb_entry_fast_age, NULL); 94462306a36Sopenharmony_ci} 94562306a36Sopenharmony_ci 94662306a36Sopenharmony_cistatic int dpaa2_switch_port_vlan_add(struct net_device *netdev, __be16 proto, 94762306a36Sopenharmony_ci u16 vid) 94862306a36Sopenharmony_ci{ 94962306a36Sopenharmony_ci struct switchdev_obj_port_vlan vlan = { 95062306a36Sopenharmony_ci .obj.id = SWITCHDEV_OBJ_ID_PORT_VLAN, 95162306a36Sopenharmony_ci .vid = vid, 95262306a36Sopenharmony_ci .obj.orig_dev = netdev, 95362306a36Sopenharmony_ci /* This API only allows programming tagged, non-PVID VIDs */ 95462306a36Sopenharmony_ci .flags = 0, 95562306a36Sopenharmony_ci }; 95662306a36Sopenharmony_ci 95762306a36Sopenharmony_ci return dpaa2_switch_port_vlans_add(netdev, &vlan); 95862306a36Sopenharmony_ci} 95962306a36Sopenharmony_ci 96062306a36Sopenharmony_cistatic int dpaa2_switch_port_vlan_kill(struct net_device *netdev, __be16 proto, 96162306a36Sopenharmony_ci u16 vid) 96262306a36Sopenharmony_ci{ 96362306a36Sopenharmony_ci struct switchdev_obj_port_vlan vlan = { 96462306a36Sopenharmony_ci .obj.id = SWITCHDEV_OBJ_ID_PORT_VLAN, 96562306a36Sopenharmony_ci .vid = vid, 96662306a36Sopenharmony_ci .obj.orig_dev = netdev, 96762306a36Sopenharmony_ci /* This API only allows programming tagged, non-PVID VIDs */ 96862306a36Sopenharmony_ci .flags = 0, 96962306a36Sopenharmony_ci }; 97062306a36Sopenharmony_ci 97162306a36Sopenharmony_ci return dpaa2_switch_port_vlans_del(netdev, &vlan); 97262306a36Sopenharmony_ci} 97362306a36Sopenharmony_ci 97462306a36Sopenharmony_cistatic int dpaa2_switch_port_set_mac_addr(struct ethsw_port_priv *port_priv) 97562306a36Sopenharmony_ci{ 97662306a36Sopenharmony_ci struct ethsw_core *ethsw = port_priv->ethsw_data; 97762306a36Sopenharmony_ci struct net_device *net_dev = port_priv->netdev; 97862306a36Sopenharmony_ci struct device *dev = net_dev->dev.parent; 97962306a36Sopenharmony_ci u8 mac_addr[ETH_ALEN]; 98062306a36Sopenharmony_ci int err; 98162306a36Sopenharmony_ci 98262306a36Sopenharmony_ci if (!(ethsw->features & ETHSW_FEATURE_MAC_ADDR)) 98362306a36Sopenharmony_ci return 0; 98462306a36Sopenharmony_ci 98562306a36Sopenharmony_ci /* Get firmware address, if any */ 98662306a36Sopenharmony_ci err = dpsw_if_get_port_mac_addr(ethsw->mc_io, 0, ethsw->dpsw_handle, 98762306a36Sopenharmony_ci port_priv->idx, mac_addr); 98862306a36Sopenharmony_ci if (err) { 98962306a36Sopenharmony_ci dev_err(dev, "dpsw_if_get_port_mac_addr() failed\n"); 99062306a36Sopenharmony_ci return err; 99162306a36Sopenharmony_ci } 99262306a36Sopenharmony_ci 99362306a36Sopenharmony_ci /* First check if firmware has any address configured by bootloader */ 99462306a36Sopenharmony_ci if (!is_zero_ether_addr(mac_addr)) { 99562306a36Sopenharmony_ci eth_hw_addr_set(net_dev, mac_addr); 99662306a36Sopenharmony_ci } else { 99762306a36Sopenharmony_ci /* No MAC address configured, fill in net_dev->dev_addr 99862306a36Sopenharmony_ci * with a random one 99962306a36Sopenharmony_ci */ 100062306a36Sopenharmony_ci eth_hw_addr_random(net_dev); 100162306a36Sopenharmony_ci dev_dbg_once(dev, "device(s) have all-zero hwaddr, replaced with random\n"); 100262306a36Sopenharmony_ci 100362306a36Sopenharmony_ci /* Override NET_ADDR_RANDOM set by eth_hw_addr_random(); for all 100462306a36Sopenharmony_ci * practical purposes, this will be our "permanent" mac address, 100562306a36Sopenharmony_ci * at least until the next reboot. This move will also permit 100662306a36Sopenharmony_ci * register_netdevice() to properly fill up net_dev->perm_addr. 100762306a36Sopenharmony_ci */ 100862306a36Sopenharmony_ci net_dev->addr_assign_type = NET_ADDR_PERM; 100962306a36Sopenharmony_ci } 101062306a36Sopenharmony_ci 101162306a36Sopenharmony_ci return 0; 101262306a36Sopenharmony_ci} 101362306a36Sopenharmony_ci 101462306a36Sopenharmony_cistatic void dpaa2_switch_free_fd(const struct ethsw_core *ethsw, 101562306a36Sopenharmony_ci const struct dpaa2_fd *fd) 101662306a36Sopenharmony_ci{ 101762306a36Sopenharmony_ci struct device *dev = ethsw->dev; 101862306a36Sopenharmony_ci unsigned char *buffer_start; 101962306a36Sopenharmony_ci struct sk_buff **skbh, *skb; 102062306a36Sopenharmony_ci dma_addr_t fd_addr; 102162306a36Sopenharmony_ci 102262306a36Sopenharmony_ci fd_addr = dpaa2_fd_get_addr(fd); 102362306a36Sopenharmony_ci skbh = dpaa2_iova_to_virt(ethsw->iommu_domain, fd_addr); 102462306a36Sopenharmony_ci 102562306a36Sopenharmony_ci skb = *skbh; 102662306a36Sopenharmony_ci buffer_start = (unsigned char *)skbh; 102762306a36Sopenharmony_ci 102862306a36Sopenharmony_ci dma_unmap_single(dev, fd_addr, 102962306a36Sopenharmony_ci skb_tail_pointer(skb) - buffer_start, 103062306a36Sopenharmony_ci DMA_TO_DEVICE); 103162306a36Sopenharmony_ci 103262306a36Sopenharmony_ci /* Move on with skb release */ 103362306a36Sopenharmony_ci dev_kfree_skb(skb); 103462306a36Sopenharmony_ci} 103562306a36Sopenharmony_ci 103662306a36Sopenharmony_cistatic int dpaa2_switch_build_single_fd(struct ethsw_core *ethsw, 103762306a36Sopenharmony_ci struct sk_buff *skb, 103862306a36Sopenharmony_ci struct dpaa2_fd *fd) 103962306a36Sopenharmony_ci{ 104062306a36Sopenharmony_ci struct device *dev = ethsw->dev; 104162306a36Sopenharmony_ci struct sk_buff **skbh; 104262306a36Sopenharmony_ci dma_addr_t addr; 104362306a36Sopenharmony_ci u8 *buff_start; 104462306a36Sopenharmony_ci void *hwa; 104562306a36Sopenharmony_ci 104662306a36Sopenharmony_ci buff_start = PTR_ALIGN(skb->data - DPAA2_SWITCH_TX_DATA_OFFSET - 104762306a36Sopenharmony_ci DPAA2_SWITCH_TX_BUF_ALIGN, 104862306a36Sopenharmony_ci DPAA2_SWITCH_TX_BUF_ALIGN); 104962306a36Sopenharmony_ci 105062306a36Sopenharmony_ci /* Clear FAS to have consistent values for TX confirmation. It is 105162306a36Sopenharmony_ci * located in the first 8 bytes of the buffer's hardware annotation 105262306a36Sopenharmony_ci * area 105362306a36Sopenharmony_ci */ 105462306a36Sopenharmony_ci hwa = buff_start + DPAA2_SWITCH_SWA_SIZE; 105562306a36Sopenharmony_ci memset(hwa, 0, 8); 105662306a36Sopenharmony_ci 105762306a36Sopenharmony_ci /* Store a backpointer to the skb at the beginning of the buffer 105862306a36Sopenharmony_ci * (in the private data area) such that we can release it 105962306a36Sopenharmony_ci * on Tx confirm 106062306a36Sopenharmony_ci */ 106162306a36Sopenharmony_ci skbh = (struct sk_buff **)buff_start; 106262306a36Sopenharmony_ci *skbh = skb; 106362306a36Sopenharmony_ci 106462306a36Sopenharmony_ci addr = dma_map_single(dev, buff_start, 106562306a36Sopenharmony_ci skb_tail_pointer(skb) - buff_start, 106662306a36Sopenharmony_ci DMA_TO_DEVICE); 106762306a36Sopenharmony_ci if (unlikely(dma_mapping_error(dev, addr))) 106862306a36Sopenharmony_ci return -ENOMEM; 106962306a36Sopenharmony_ci 107062306a36Sopenharmony_ci /* Setup the FD fields */ 107162306a36Sopenharmony_ci memset(fd, 0, sizeof(*fd)); 107262306a36Sopenharmony_ci 107362306a36Sopenharmony_ci dpaa2_fd_set_addr(fd, addr); 107462306a36Sopenharmony_ci dpaa2_fd_set_offset(fd, (u16)(skb->data - buff_start)); 107562306a36Sopenharmony_ci dpaa2_fd_set_len(fd, skb->len); 107662306a36Sopenharmony_ci dpaa2_fd_set_format(fd, dpaa2_fd_single); 107762306a36Sopenharmony_ci 107862306a36Sopenharmony_ci return 0; 107962306a36Sopenharmony_ci} 108062306a36Sopenharmony_ci 108162306a36Sopenharmony_cistatic netdev_tx_t dpaa2_switch_port_tx(struct sk_buff *skb, 108262306a36Sopenharmony_ci struct net_device *net_dev) 108362306a36Sopenharmony_ci{ 108462306a36Sopenharmony_ci struct ethsw_port_priv *port_priv = netdev_priv(net_dev); 108562306a36Sopenharmony_ci struct ethsw_core *ethsw = port_priv->ethsw_data; 108662306a36Sopenharmony_ci int retries = DPAA2_SWITCH_SWP_BUSY_RETRIES; 108762306a36Sopenharmony_ci struct dpaa2_fd fd; 108862306a36Sopenharmony_ci int err; 108962306a36Sopenharmony_ci 109062306a36Sopenharmony_ci if (unlikely(skb_headroom(skb) < DPAA2_SWITCH_NEEDED_HEADROOM)) { 109162306a36Sopenharmony_ci struct sk_buff *ns; 109262306a36Sopenharmony_ci 109362306a36Sopenharmony_ci ns = skb_realloc_headroom(skb, DPAA2_SWITCH_NEEDED_HEADROOM); 109462306a36Sopenharmony_ci if (unlikely(!ns)) { 109562306a36Sopenharmony_ci net_err_ratelimited("%s: Error reallocating skb headroom\n", net_dev->name); 109662306a36Sopenharmony_ci goto err_free_skb; 109762306a36Sopenharmony_ci } 109862306a36Sopenharmony_ci dev_consume_skb_any(skb); 109962306a36Sopenharmony_ci skb = ns; 110062306a36Sopenharmony_ci } 110162306a36Sopenharmony_ci 110262306a36Sopenharmony_ci /* We'll be holding a back-reference to the skb until Tx confirmation */ 110362306a36Sopenharmony_ci skb = skb_unshare(skb, GFP_ATOMIC); 110462306a36Sopenharmony_ci if (unlikely(!skb)) { 110562306a36Sopenharmony_ci /* skb_unshare() has already freed the skb */ 110662306a36Sopenharmony_ci net_err_ratelimited("%s: Error copying the socket buffer\n", net_dev->name); 110762306a36Sopenharmony_ci goto err_exit; 110862306a36Sopenharmony_ci } 110962306a36Sopenharmony_ci 111062306a36Sopenharmony_ci /* At this stage, we do not support non-linear skbs so just try to 111162306a36Sopenharmony_ci * linearize the skb and if that's not working, just drop the packet. 111262306a36Sopenharmony_ci */ 111362306a36Sopenharmony_ci err = skb_linearize(skb); 111462306a36Sopenharmony_ci if (err) { 111562306a36Sopenharmony_ci net_err_ratelimited("%s: skb_linearize error (%d)!\n", net_dev->name, err); 111662306a36Sopenharmony_ci goto err_free_skb; 111762306a36Sopenharmony_ci } 111862306a36Sopenharmony_ci 111962306a36Sopenharmony_ci err = dpaa2_switch_build_single_fd(ethsw, skb, &fd); 112062306a36Sopenharmony_ci if (unlikely(err)) { 112162306a36Sopenharmony_ci net_err_ratelimited("%s: ethsw_build_*_fd() %d\n", net_dev->name, err); 112262306a36Sopenharmony_ci goto err_free_skb; 112362306a36Sopenharmony_ci } 112462306a36Sopenharmony_ci 112562306a36Sopenharmony_ci do { 112662306a36Sopenharmony_ci err = dpaa2_io_service_enqueue_qd(NULL, 112762306a36Sopenharmony_ci port_priv->tx_qdid, 112862306a36Sopenharmony_ci 8, 0, &fd); 112962306a36Sopenharmony_ci retries--; 113062306a36Sopenharmony_ci } while (err == -EBUSY && retries); 113162306a36Sopenharmony_ci 113262306a36Sopenharmony_ci if (unlikely(err < 0)) { 113362306a36Sopenharmony_ci dpaa2_switch_free_fd(ethsw, &fd); 113462306a36Sopenharmony_ci goto err_exit; 113562306a36Sopenharmony_ci } 113662306a36Sopenharmony_ci 113762306a36Sopenharmony_ci return NETDEV_TX_OK; 113862306a36Sopenharmony_ci 113962306a36Sopenharmony_cierr_free_skb: 114062306a36Sopenharmony_ci dev_kfree_skb(skb); 114162306a36Sopenharmony_cierr_exit: 114262306a36Sopenharmony_ci return NETDEV_TX_OK; 114362306a36Sopenharmony_ci} 114462306a36Sopenharmony_ci 114562306a36Sopenharmony_cistatic int 114662306a36Sopenharmony_cidpaa2_switch_setup_tc_cls_flower(struct dpaa2_switch_filter_block *filter_block, 114762306a36Sopenharmony_ci struct flow_cls_offload *f) 114862306a36Sopenharmony_ci{ 114962306a36Sopenharmony_ci switch (f->command) { 115062306a36Sopenharmony_ci case FLOW_CLS_REPLACE: 115162306a36Sopenharmony_ci return dpaa2_switch_cls_flower_replace(filter_block, f); 115262306a36Sopenharmony_ci case FLOW_CLS_DESTROY: 115362306a36Sopenharmony_ci return dpaa2_switch_cls_flower_destroy(filter_block, f); 115462306a36Sopenharmony_ci default: 115562306a36Sopenharmony_ci return -EOPNOTSUPP; 115662306a36Sopenharmony_ci } 115762306a36Sopenharmony_ci} 115862306a36Sopenharmony_ci 115962306a36Sopenharmony_cistatic int 116062306a36Sopenharmony_cidpaa2_switch_setup_tc_cls_matchall(struct dpaa2_switch_filter_block *block, 116162306a36Sopenharmony_ci struct tc_cls_matchall_offload *f) 116262306a36Sopenharmony_ci{ 116362306a36Sopenharmony_ci switch (f->command) { 116462306a36Sopenharmony_ci case TC_CLSMATCHALL_REPLACE: 116562306a36Sopenharmony_ci return dpaa2_switch_cls_matchall_replace(block, f); 116662306a36Sopenharmony_ci case TC_CLSMATCHALL_DESTROY: 116762306a36Sopenharmony_ci return dpaa2_switch_cls_matchall_destroy(block, f); 116862306a36Sopenharmony_ci default: 116962306a36Sopenharmony_ci return -EOPNOTSUPP; 117062306a36Sopenharmony_ci } 117162306a36Sopenharmony_ci} 117262306a36Sopenharmony_ci 117362306a36Sopenharmony_cistatic int dpaa2_switch_port_setup_tc_block_cb_ig(enum tc_setup_type type, 117462306a36Sopenharmony_ci void *type_data, 117562306a36Sopenharmony_ci void *cb_priv) 117662306a36Sopenharmony_ci{ 117762306a36Sopenharmony_ci switch (type) { 117862306a36Sopenharmony_ci case TC_SETUP_CLSFLOWER: 117962306a36Sopenharmony_ci return dpaa2_switch_setup_tc_cls_flower(cb_priv, type_data); 118062306a36Sopenharmony_ci case TC_SETUP_CLSMATCHALL: 118162306a36Sopenharmony_ci return dpaa2_switch_setup_tc_cls_matchall(cb_priv, type_data); 118262306a36Sopenharmony_ci default: 118362306a36Sopenharmony_ci return -EOPNOTSUPP; 118462306a36Sopenharmony_ci } 118562306a36Sopenharmony_ci} 118662306a36Sopenharmony_ci 118762306a36Sopenharmony_cistatic LIST_HEAD(dpaa2_switch_block_cb_list); 118862306a36Sopenharmony_ci 118962306a36Sopenharmony_cistatic int 119062306a36Sopenharmony_cidpaa2_switch_port_acl_tbl_bind(struct ethsw_port_priv *port_priv, 119162306a36Sopenharmony_ci struct dpaa2_switch_filter_block *block) 119262306a36Sopenharmony_ci{ 119362306a36Sopenharmony_ci struct ethsw_core *ethsw = port_priv->ethsw_data; 119462306a36Sopenharmony_ci struct net_device *netdev = port_priv->netdev; 119562306a36Sopenharmony_ci struct dpsw_acl_if_cfg acl_if_cfg; 119662306a36Sopenharmony_ci int err; 119762306a36Sopenharmony_ci 119862306a36Sopenharmony_ci if (port_priv->filter_block) 119962306a36Sopenharmony_ci return -EINVAL; 120062306a36Sopenharmony_ci 120162306a36Sopenharmony_ci acl_if_cfg.if_id[0] = port_priv->idx; 120262306a36Sopenharmony_ci acl_if_cfg.num_ifs = 1; 120362306a36Sopenharmony_ci err = dpsw_acl_add_if(ethsw->mc_io, 0, ethsw->dpsw_handle, 120462306a36Sopenharmony_ci block->acl_id, &acl_if_cfg); 120562306a36Sopenharmony_ci if (err) { 120662306a36Sopenharmony_ci netdev_err(netdev, "dpsw_acl_add_if err %d\n", err); 120762306a36Sopenharmony_ci return err; 120862306a36Sopenharmony_ci } 120962306a36Sopenharmony_ci 121062306a36Sopenharmony_ci block->ports |= BIT(port_priv->idx); 121162306a36Sopenharmony_ci port_priv->filter_block = block; 121262306a36Sopenharmony_ci 121362306a36Sopenharmony_ci return 0; 121462306a36Sopenharmony_ci} 121562306a36Sopenharmony_ci 121662306a36Sopenharmony_cistatic int 121762306a36Sopenharmony_cidpaa2_switch_port_acl_tbl_unbind(struct ethsw_port_priv *port_priv, 121862306a36Sopenharmony_ci struct dpaa2_switch_filter_block *block) 121962306a36Sopenharmony_ci{ 122062306a36Sopenharmony_ci struct ethsw_core *ethsw = port_priv->ethsw_data; 122162306a36Sopenharmony_ci struct net_device *netdev = port_priv->netdev; 122262306a36Sopenharmony_ci struct dpsw_acl_if_cfg acl_if_cfg; 122362306a36Sopenharmony_ci int err; 122462306a36Sopenharmony_ci 122562306a36Sopenharmony_ci if (port_priv->filter_block != block) 122662306a36Sopenharmony_ci return -EINVAL; 122762306a36Sopenharmony_ci 122862306a36Sopenharmony_ci acl_if_cfg.if_id[0] = port_priv->idx; 122962306a36Sopenharmony_ci acl_if_cfg.num_ifs = 1; 123062306a36Sopenharmony_ci err = dpsw_acl_remove_if(ethsw->mc_io, 0, ethsw->dpsw_handle, 123162306a36Sopenharmony_ci block->acl_id, &acl_if_cfg); 123262306a36Sopenharmony_ci if (err) { 123362306a36Sopenharmony_ci netdev_err(netdev, "dpsw_acl_add_if err %d\n", err); 123462306a36Sopenharmony_ci return err; 123562306a36Sopenharmony_ci } 123662306a36Sopenharmony_ci 123762306a36Sopenharmony_ci block->ports &= ~BIT(port_priv->idx); 123862306a36Sopenharmony_ci port_priv->filter_block = NULL; 123962306a36Sopenharmony_ci return 0; 124062306a36Sopenharmony_ci} 124162306a36Sopenharmony_ci 124262306a36Sopenharmony_cistatic int dpaa2_switch_port_block_bind(struct ethsw_port_priv *port_priv, 124362306a36Sopenharmony_ci struct dpaa2_switch_filter_block *block) 124462306a36Sopenharmony_ci{ 124562306a36Sopenharmony_ci struct dpaa2_switch_filter_block *old_block = port_priv->filter_block; 124662306a36Sopenharmony_ci int err; 124762306a36Sopenharmony_ci 124862306a36Sopenharmony_ci /* Offload all the mirror entries found in the block on this new port 124962306a36Sopenharmony_ci * joining it. 125062306a36Sopenharmony_ci */ 125162306a36Sopenharmony_ci err = dpaa2_switch_block_offload_mirror(block, port_priv); 125262306a36Sopenharmony_ci if (err) 125362306a36Sopenharmony_ci return err; 125462306a36Sopenharmony_ci 125562306a36Sopenharmony_ci /* If the port is already bound to this ACL table then do nothing. This 125662306a36Sopenharmony_ci * can happen when this port is the first one to join a tc block 125762306a36Sopenharmony_ci */ 125862306a36Sopenharmony_ci if (port_priv->filter_block == block) 125962306a36Sopenharmony_ci return 0; 126062306a36Sopenharmony_ci 126162306a36Sopenharmony_ci err = dpaa2_switch_port_acl_tbl_unbind(port_priv, old_block); 126262306a36Sopenharmony_ci if (err) 126362306a36Sopenharmony_ci return err; 126462306a36Sopenharmony_ci 126562306a36Sopenharmony_ci /* Mark the previous ACL table as being unused if this was the last 126662306a36Sopenharmony_ci * port that was using it. 126762306a36Sopenharmony_ci */ 126862306a36Sopenharmony_ci if (old_block->ports == 0) 126962306a36Sopenharmony_ci old_block->in_use = false; 127062306a36Sopenharmony_ci 127162306a36Sopenharmony_ci return dpaa2_switch_port_acl_tbl_bind(port_priv, block); 127262306a36Sopenharmony_ci} 127362306a36Sopenharmony_ci 127462306a36Sopenharmony_cistatic int 127562306a36Sopenharmony_cidpaa2_switch_port_block_unbind(struct ethsw_port_priv *port_priv, 127662306a36Sopenharmony_ci struct dpaa2_switch_filter_block *block) 127762306a36Sopenharmony_ci{ 127862306a36Sopenharmony_ci struct ethsw_core *ethsw = port_priv->ethsw_data; 127962306a36Sopenharmony_ci struct dpaa2_switch_filter_block *new_block; 128062306a36Sopenharmony_ci int err; 128162306a36Sopenharmony_ci 128262306a36Sopenharmony_ci /* Unoffload all the mirror entries found in the block from the 128362306a36Sopenharmony_ci * port leaving it. 128462306a36Sopenharmony_ci */ 128562306a36Sopenharmony_ci err = dpaa2_switch_block_unoffload_mirror(block, port_priv); 128662306a36Sopenharmony_ci if (err) 128762306a36Sopenharmony_ci return err; 128862306a36Sopenharmony_ci 128962306a36Sopenharmony_ci /* We are the last port that leaves a block (an ACL table). 129062306a36Sopenharmony_ci * We'll continue to use this table. 129162306a36Sopenharmony_ci */ 129262306a36Sopenharmony_ci if (block->ports == BIT(port_priv->idx)) 129362306a36Sopenharmony_ci return 0; 129462306a36Sopenharmony_ci 129562306a36Sopenharmony_ci err = dpaa2_switch_port_acl_tbl_unbind(port_priv, block); 129662306a36Sopenharmony_ci if (err) 129762306a36Sopenharmony_ci return err; 129862306a36Sopenharmony_ci 129962306a36Sopenharmony_ci if (block->ports == 0) 130062306a36Sopenharmony_ci block->in_use = false; 130162306a36Sopenharmony_ci 130262306a36Sopenharmony_ci new_block = dpaa2_switch_filter_block_get_unused(ethsw); 130362306a36Sopenharmony_ci new_block->in_use = true; 130462306a36Sopenharmony_ci return dpaa2_switch_port_acl_tbl_bind(port_priv, new_block); 130562306a36Sopenharmony_ci} 130662306a36Sopenharmony_ci 130762306a36Sopenharmony_cistatic int dpaa2_switch_setup_tc_block_bind(struct net_device *netdev, 130862306a36Sopenharmony_ci struct flow_block_offload *f) 130962306a36Sopenharmony_ci{ 131062306a36Sopenharmony_ci struct ethsw_port_priv *port_priv = netdev_priv(netdev); 131162306a36Sopenharmony_ci struct ethsw_core *ethsw = port_priv->ethsw_data; 131262306a36Sopenharmony_ci struct dpaa2_switch_filter_block *filter_block; 131362306a36Sopenharmony_ci struct flow_block_cb *block_cb; 131462306a36Sopenharmony_ci bool register_block = false; 131562306a36Sopenharmony_ci int err; 131662306a36Sopenharmony_ci 131762306a36Sopenharmony_ci block_cb = flow_block_cb_lookup(f->block, 131862306a36Sopenharmony_ci dpaa2_switch_port_setup_tc_block_cb_ig, 131962306a36Sopenharmony_ci ethsw); 132062306a36Sopenharmony_ci 132162306a36Sopenharmony_ci if (!block_cb) { 132262306a36Sopenharmony_ci /* If the filter block is not already known, then this port 132362306a36Sopenharmony_ci * must be the first to join it. In this case, we can just 132462306a36Sopenharmony_ci * continue to use our private table 132562306a36Sopenharmony_ci */ 132662306a36Sopenharmony_ci filter_block = port_priv->filter_block; 132762306a36Sopenharmony_ci 132862306a36Sopenharmony_ci block_cb = flow_block_cb_alloc(dpaa2_switch_port_setup_tc_block_cb_ig, 132962306a36Sopenharmony_ci ethsw, filter_block, NULL); 133062306a36Sopenharmony_ci if (IS_ERR(block_cb)) 133162306a36Sopenharmony_ci return PTR_ERR(block_cb); 133262306a36Sopenharmony_ci 133362306a36Sopenharmony_ci register_block = true; 133462306a36Sopenharmony_ci } else { 133562306a36Sopenharmony_ci filter_block = flow_block_cb_priv(block_cb); 133662306a36Sopenharmony_ci } 133762306a36Sopenharmony_ci 133862306a36Sopenharmony_ci flow_block_cb_incref(block_cb); 133962306a36Sopenharmony_ci err = dpaa2_switch_port_block_bind(port_priv, filter_block); 134062306a36Sopenharmony_ci if (err) 134162306a36Sopenharmony_ci goto err_block_bind; 134262306a36Sopenharmony_ci 134362306a36Sopenharmony_ci if (register_block) { 134462306a36Sopenharmony_ci flow_block_cb_add(block_cb, f); 134562306a36Sopenharmony_ci list_add_tail(&block_cb->driver_list, 134662306a36Sopenharmony_ci &dpaa2_switch_block_cb_list); 134762306a36Sopenharmony_ci } 134862306a36Sopenharmony_ci 134962306a36Sopenharmony_ci return 0; 135062306a36Sopenharmony_ci 135162306a36Sopenharmony_cierr_block_bind: 135262306a36Sopenharmony_ci if (!flow_block_cb_decref(block_cb)) 135362306a36Sopenharmony_ci flow_block_cb_free(block_cb); 135462306a36Sopenharmony_ci return err; 135562306a36Sopenharmony_ci} 135662306a36Sopenharmony_ci 135762306a36Sopenharmony_cistatic void dpaa2_switch_setup_tc_block_unbind(struct net_device *netdev, 135862306a36Sopenharmony_ci struct flow_block_offload *f) 135962306a36Sopenharmony_ci{ 136062306a36Sopenharmony_ci struct ethsw_port_priv *port_priv = netdev_priv(netdev); 136162306a36Sopenharmony_ci struct ethsw_core *ethsw = port_priv->ethsw_data; 136262306a36Sopenharmony_ci struct dpaa2_switch_filter_block *filter_block; 136362306a36Sopenharmony_ci struct flow_block_cb *block_cb; 136462306a36Sopenharmony_ci int err; 136562306a36Sopenharmony_ci 136662306a36Sopenharmony_ci block_cb = flow_block_cb_lookup(f->block, 136762306a36Sopenharmony_ci dpaa2_switch_port_setup_tc_block_cb_ig, 136862306a36Sopenharmony_ci ethsw); 136962306a36Sopenharmony_ci if (!block_cb) 137062306a36Sopenharmony_ci return; 137162306a36Sopenharmony_ci 137262306a36Sopenharmony_ci filter_block = flow_block_cb_priv(block_cb); 137362306a36Sopenharmony_ci err = dpaa2_switch_port_block_unbind(port_priv, filter_block); 137462306a36Sopenharmony_ci if (!err && !flow_block_cb_decref(block_cb)) { 137562306a36Sopenharmony_ci flow_block_cb_remove(block_cb, f); 137662306a36Sopenharmony_ci list_del(&block_cb->driver_list); 137762306a36Sopenharmony_ci } 137862306a36Sopenharmony_ci} 137962306a36Sopenharmony_ci 138062306a36Sopenharmony_cistatic int dpaa2_switch_setup_tc_block(struct net_device *netdev, 138162306a36Sopenharmony_ci struct flow_block_offload *f) 138262306a36Sopenharmony_ci{ 138362306a36Sopenharmony_ci if (f->binder_type != FLOW_BLOCK_BINDER_TYPE_CLSACT_INGRESS) 138462306a36Sopenharmony_ci return -EOPNOTSUPP; 138562306a36Sopenharmony_ci 138662306a36Sopenharmony_ci f->driver_block_list = &dpaa2_switch_block_cb_list; 138762306a36Sopenharmony_ci 138862306a36Sopenharmony_ci switch (f->command) { 138962306a36Sopenharmony_ci case FLOW_BLOCK_BIND: 139062306a36Sopenharmony_ci return dpaa2_switch_setup_tc_block_bind(netdev, f); 139162306a36Sopenharmony_ci case FLOW_BLOCK_UNBIND: 139262306a36Sopenharmony_ci dpaa2_switch_setup_tc_block_unbind(netdev, f); 139362306a36Sopenharmony_ci return 0; 139462306a36Sopenharmony_ci default: 139562306a36Sopenharmony_ci return -EOPNOTSUPP; 139662306a36Sopenharmony_ci } 139762306a36Sopenharmony_ci} 139862306a36Sopenharmony_ci 139962306a36Sopenharmony_cistatic int dpaa2_switch_port_setup_tc(struct net_device *netdev, 140062306a36Sopenharmony_ci enum tc_setup_type type, 140162306a36Sopenharmony_ci void *type_data) 140262306a36Sopenharmony_ci{ 140362306a36Sopenharmony_ci switch (type) { 140462306a36Sopenharmony_ci case TC_SETUP_BLOCK: { 140562306a36Sopenharmony_ci return dpaa2_switch_setup_tc_block(netdev, type_data); 140662306a36Sopenharmony_ci } 140762306a36Sopenharmony_ci default: 140862306a36Sopenharmony_ci return -EOPNOTSUPP; 140962306a36Sopenharmony_ci } 141062306a36Sopenharmony_ci 141162306a36Sopenharmony_ci return 0; 141262306a36Sopenharmony_ci} 141362306a36Sopenharmony_ci 141462306a36Sopenharmony_cistatic const struct net_device_ops dpaa2_switch_port_ops = { 141562306a36Sopenharmony_ci .ndo_open = dpaa2_switch_port_open, 141662306a36Sopenharmony_ci .ndo_stop = dpaa2_switch_port_stop, 141762306a36Sopenharmony_ci 141862306a36Sopenharmony_ci .ndo_set_mac_address = eth_mac_addr, 141962306a36Sopenharmony_ci .ndo_get_stats64 = dpaa2_switch_port_get_stats, 142062306a36Sopenharmony_ci .ndo_change_mtu = dpaa2_switch_port_change_mtu, 142162306a36Sopenharmony_ci .ndo_has_offload_stats = dpaa2_switch_port_has_offload_stats, 142262306a36Sopenharmony_ci .ndo_get_offload_stats = dpaa2_switch_port_get_offload_stats, 142362306a36Sopenharmony_ci .ndo_fdb_dump = dpaa2_switch_port_fdb_dump, 142462306a36Sopenharmony_ci .ndo_vlan_rx_add_vid = dpaa2_switch_port_vlan_add, 142562306a36Sopenharmony_ci .ndo_vlan_rx_kill_vid = dpaa2_switch_port_vlan_kill, 142662306a36Sopenharmony_ci 142762306a36Sopenharmony_ci .ndo_start_xmit = dpaa2_switch_port_tx, 142862306a36Sopenharmony_ci .ndo_get_port_parent_id = dpaa2_switch_port_parent_id, 142962306a36Sopenharmony_ci .ndo_get_phys_port_name = dpaa2_switch_port_get_phys_name, 143062306a36Sopenharmony_ci .ndo_setup_tc = dpaa2_switch_port_setup_tc, 143162306a36Sopenharmony_ci}; 143262306a36Sopenharmony_ci 143362306a36Sopenharmony_cibool dpaa2_switch_port_dev_check(const struct net_device *netdev) 143462306a36Sopenharmony_ci{ 143562306a36Sopenharmony_ci return netdev->netdev_ops == &dpaa2_switch_port_ops; 143662306a36Sopenharmony_ci} 143762306a36Sopenharmony_ci 143862306a36Sopenharmony_cistatic int dpaa2_switch_port_connect_mac(struct ethsw_port_priv *port_priv) 143962306a36Sopenharmony_ci{ 144062306a36Sopenharmony_ci struct fsl_mc_device *dpsw_port_dev, *dpmac_dev; 144162306a36Sopenharmony_ci struct dpaa2_mac *mac; 144262306a36Sopenharmony_ci int err; 144362306a36Sopenharmony_ci 144462306a36Sopenharmony_ci dpsw_port_dev = to_fsl_mc_device(port_priv->netdev->dev.parent); 144562306a36Sopenharmony_ci dpmac_dev = fsl_mc_get_endpoint(dpsw_port_dev, port_priv->idx); 144662306a36Sopenharmony_ci 144762306a36Sopenharmony_ci if (PTR_ERR(dpmac_dev) == -EPROBE_DEFER) 144862306a36Sopenharmony_ci return PTR_ERR(dpmac_dev); 144962306a36Sopenharmony_ci 145062306a36Sopenharmony_ci if (IS_ERR(dpmac_dev) || dpmac_dev->dev.type != &fsl_mc_bus_dpmac_type) 145162306a36Sopenharmony_ci return 0; 145262306a36Sopenharmony_ci 145362306a36Sopenharmony_ci mac = kzalloc(sizeof(*mac), GFP_KERNEL); 145462306a36Sopenharmony_ci if (!mac) 145562306a36Sopenharmony_ci return -ENOMEM; 145662306a36Sopenharmony_ci 145762306a36Sopenharmony_ci mac->mc_dev = dpmac_dev; 145862306a36Sopenharmony_ci mac->mc_io = port_priv->ethsw_data->mc_io; 145962306a36Sopenharmony_ci mac->net_dev = port_priv->netdev; 146062306a36Sopenharmony_ci 146162306a36Sopenharmony_ci err = dpaa2_mac_open(mac); 146262306a36Sopenharmony_ci if (err) 146362306a36Sopenharmony_ci goto err_free_mac; 146462306a36Sopenharmony_ci 146562306a36Sopenharmony_ci if (dpaa2_mac_is_type_phy(mac)) { 146662306a36Sopenharmony_ci err = dpaa2_mac_connect(mac); 146762306a36Sopenharmony_ci if (err) { 146862306a36Sopenharmony_ci netdev_err(port_priv->netdev, 146962306a36Sopenharmony_ci "Error connecting to the MAC endpoint %pe\n", 147062306a36Sopenharmony_ci ERR_PTR(err)); 147162306a36Sopenharmony_ci goto err_close_mac; 147262306a36Sopenharmony_ci } 147362306a36Sopenharmony_ci } 147462306a36Sopenharmony_ci 147562306a36Sopenharmony_ci mutex_lock(&port_priv->mac_lock); 147662306a36Sopenharmony_ci port_priv->mac = mac; 147762306a36Sopenharmony_ci mutex_unlock(&port_priv->mac_lock); 147862306a36Sopenharmony_ci 147962306a36Sopenharmony_ci return 0; 148062306a36Sopenharmony_ci 148162306a36Sopenharmony_cierr_close_mac: 148262306a36Sopenharmony_ci dpaa2_mac_close(mac); 148362306a36Sopenharmony_cierr_free_mac: 148462306a36Sopenharmony_ci kfree(mac); 148562306a36Sopenharmony_ci return err; 148662306a36Sopenharmony_ci} 148762306a36Sopenharmony_ci 148862306a36Sopenharmony_cistatic void dpaa2_switch_port_disconnect_mac(struct ethsw_port_priv *port_priv) 148962306a36Sopenharmony_ci{ 149062306a36Sopenharmony_ci struct dpaa2_mac *mac; 149162306a36Sopenharmony_ci 149262306a36Sopenharmony_ci mutex_lock(&port_priv->mac_lock); 149362306a36Sopenharmony_ci mac = port_priv->mac; 149462306a36Sopenharmony_ci port_priv->mac = NULL; 149562306a36Sopenharmony_ci mutex_unlock(&port_priv->mac_lock); 149662306a36Sopenharmony_ci 149762306a36Sopenharmony_ci if (!mac) 149862306a36Sopenharmony_ci return; 149962306a36Sopenharmony_ci 150062306a36Sopenharmony_ci if (dpaa2_mac_is_type_phy(mac)) 150162306a36Sopenharmony_ci dpaa2_mac_disconnect(mac); 150262306a36Sopenharmony_ci 150362306a36Sopenharmony_ci dpaa2_mac_close(mac); 150462306a36Sopenharmony_ci kfree(mac); 150562306a36Sopenharmony_ci} 150662306a36Sopenharmony_ci 150762306a36Sopenharmony_cistatic irqreturn_t dpaa2_switch_irq0_handler_thread(int irq_num, void *arg) 150862306a36Sopenharmony_ci{ 150962306a36Sopenharmony_ci struct device *dev = (struct device *)arg; 151062306a36Sopenharmony_ci struct ethsw_core *ethsw = dev_get_drvdata(dev); 151162306a36Sopenharmony_ci struct ethsw_port_priv *port_priv; 151262306a36Sopenharmony_ci u32 status = ~0; 151362306a36Sopenharmony_ci int err, if_id; 151462306a36Sopenharmony_ci bool had_mac; 151562306a36Sopenharmony_ci 151662306a36Sopenharmony_ci err = dpsw_get_irq_status(ethsw->mc_io, 0, ethsw->dpsw_handle, 151762306a36Sopenharmony_ci DPSW_IRQ_INDEX_IF, &status); 151862306a36Sopenharmony_ci if (err) { 151962306a36Sopenharmony_ci dev_err(dev, "Can't get irq status (err %d)\n", err); 152062306a36Sopenharmony_ci goto out; 152162306a36Sopenharmony_ci } 152262306a36Sopenharmony_ci 152362306a36Sopenharmony_ci if_id = (status & 0xFFFF0000) >> 16; 152462306a36Sopenharmony_ci port_priv = ethsw->ports[if_id]; 152562306a36Sopenharmony_ci 152662306a36Sopenharmony_ci if (status & DPSW_IRQ_EVENT_LINK_CHANGED) { 152762306a36Sopenharmony_ci dpaa2_switch_port_link_state_update(port_priv->netdev); 152862306a36Sopenharmony_ci dpaa2_switch_port_set_mac_addr(port_priv); 152962306a36Sopenharmony_ci } 153062306a36Sopenharmony_ci 153162306a36Sopenharmony_ci if (status & DPSW_IRQ_EVENT_ENDPOINT_CHANGED) { 153262306a36Sopenharmony_ci /* We can avoid locking because the "endpoint changed" IRQ 153362306a36Sopenharmony_ci * handler is the only one who changes priv->mac at runtime, 153462306a36Sopenharmony_ci * so we are not racing with anyone. 153562306a36Sopenharmony_ci */ 153662306a36Sopenharmony_ci had_mac = !!port_priv->mac; 153762306a36Sopenharmony_ci if (had_mac) 153862306a36Sopenharmony_ci dpaa2_switch_port_disconnect_mac(port_priv); 153962306a36Sopenharmony_ci else 154062306a36Sopenharmony_ci dpaa2_switch_port_connect_mac(port_priv); 154162306a36Sopenharmony_ci } 154262306a36Sopenharmony_ci 154362306a36Sopenharmony_ciout: 154462306a36Sopenharmony_ci err = dpsw_clear_irq_status(ethsw->mc_io, 0, ethsw->dpsw_handle, 154562306a36Sopenharmony_ci DPSW_IRQ_INDEX_IF, status); 154662306a36Sopenharmony_ci if (err) 154762306a36Sopenharmony_ci dev_err(dev, "Can't clear irq status (err %d)\n", err); 154862306a36Sopenharmony_ci 154962306a36Sopenharmony_ci return IRQ_HANDLED; 155062306a36Sopenharmony_ci} 155162306a36Sopenharmony_ci 155262306a36Sopenharmony_cistatic int dpaa2_switch_setup_irqs(struct fsl_mc_device *sw_dev) 155362306a36Sopenharmony_ci{ 155462306a36Sopenharmony_ci struct device *dev = &sw_dev->dev; 155562306a36Sopenharmony_ci struct ethsw_core *ethsw = dev_get_drvdata(dev); 155662306a36Sopenharmony_ci u32 mask = DPSW_IRQ_EVENT_LINK_CHANGED; 155762306a36Sopenharmony_ci struct fsl_mc_device_irq *irq; 155862306a36Sopenharmony_ci int err; 155962306a36Sopenharmony_ci 156062306a36Sopenharmony_ci err = fsl_mc_allocate_irqs(sw_dev); 156162306a36Sopenharmony_ci if (err) { 156262306a36Sopenharmony_ci dev_err(dev, "MC irqs allocation failed\n"); 156362306a36Sopenharmony_ci return err; 156462306a36Sopenharmony_ci } 156562306a36Sopenharmony_ci 156662306a36Sopenharmony_ci if (WARN_ON(sw_dev->obj_desc.irq_count != DPSW_IRQ_NUM)) { 156762306a36Sopenharmony_ci err = -EINVAL; 156862306a36Sopenharmony_ci goto free_irq; 156962306a36Sopenharmony_ci } 157062306a36Sopenharmony_ci 157162306a36Sopenharmony_ci err = dpsw_set_irq_enable(ethsw->mc_io, 0, ethsw->dpsw_handle, 157262306a36Sopenharmony_ci DPSW_IRQ_INDEX_IF, 0); 157362306a36Sopenharmony_ci if (err) { 157462306a36Sopenharmony_ci dev_err(dev, "dpsw_set_irq_enable err %d\n", err); 157562306a36Sopenharmony_ci goto free_irq; 157662306a36Sopenharmony_ci } 157762306a36Sopenharmony_ci 157862306a36Sopenharmony_ci irq = sw_dev->irqs[DPSW_IRQ_INDEX_IF]; 157962306a36Sopenharmony_ci 158062306a36Sopenharmony_ci err = devm_request_threaded_irq(dev, irq->virq, NULL, 158162306a36Sopenharmony_ci dpaa2_switch_irq0_handler_thread, 158262306a36Sopenharmony_ci IRQF_NO_SUSPEND | IRQF_ONESHOT, 158362306a36Sopenharmony_ci dev_name(dev), dev); 158462306a36Sopenharmony_ci if (err) { 158562306a36Sopenharmony_ci dev_err(dev, "devm_request_threaded_irq(): %d\n", err); 158662306a36Sopenharmony_ci goto free_irq; 158762306a36Sopenharmony_ci } 158862306a36Sopenharmony_ci 158962306a36Sopenharmony_ci err = dpsw_set_irq_mask(ethsw->mc_io, 0, ethsw->dpsw_handle, 159062306a36Sopenharmony_ci DPSW_IRQ_INDEX_IF, mask); 159162306a36Sopenharmony_ci if (err) { 159262306a36Sopenharmony_ci dev_err(dev, "dpsw_set_irq_mask(): %d\n", err); 159362306a36Sopenharmony_ci goto free_devm_irq; 159462306a36Sopenharmony_ci } 159562306a36Sopenharmony_ci 159662306a36Sopenharmony_ci err = dpsw_set_irq_enable(ethsw->mc_io, 0, ethsw->dpsw_handle, 159762306a36Sopenharmony_ci DPSW_IRQ_INDEX_IF, 1); 159862306a36Sopenharmony_ci if (err) { 159962306a36Sopenharmony_ci dev_err(dev, "dpsw_set_irq_enable(): %d\n", err); 160062306a36Sopenharmony_ci goto free_devm_irq; 160162306a36Sopenharmony_ci } 160262306a36Sopenharmony_ci 160362306a36Sopenharmony_ci return 0; 160462306a36Sopenharmony_ci 160562306a36Sopenharmony_cifree_devm_irq: 160662306a36Sopenharmony_ci devm_free_irq(dev, irq->virq, dev); 160762306a36Sopenharmony_cifree_irq: 160862306a36Sopenharmony_ci fsl_mc_free_irqs(sw_dev); 160962306a36Sopenharmony_ci return err; 161062306a36Sopenharmony_ci} 161162306a36Sopenharmony_ci 161262306a36Sopenharmony_cistatic void dpaa2_switch_teardown_irqs(struct fsl_mc_device *sw_dev) 161362306a36Sopenharmony_ci{ 161462306a36Sopenharmony_ci struct device *dev = &sw_dev->dev; 161562306a36Sopenharmony_ci struct ethsw_core *ethsw = dev_get_drvdata(dev); 161662306a36Sopenharmony_ci int err; 161762306a36Sopenharmony_ci 161862306a36Sopenharmony_ci err = dpsw_set_irq_enable(ethsw->mc_io, 0, ethsw->dpsw_handle, 161962306a36Sopenharmony_ci DPSW_IRQ_INDEX_IF, 0); 162062306a36Sopenharmony_ci if (err) 162162306a36Sopenharmony_ci dev_err(dev, "dpsw_set_irq_enable err %d\n", err); 162262306a36Sopenharmony_ci 162362306a36Sopenharmony_ci fsl_mc_free_irqs(sw_dev); 162462306a36Sopenharmony_ci} 162562306a36Sopenharmony_ci 162662306a36Sopenharmony_cistatic int dpaa2_switch_port_set_learning(struct ethsw_port_priv *port_priv, bool enable) 162762306a36Sopenharmony_ci{ 162862306a36Sopenharmony_ci struct ethsw_core *ethsw = port_priv->ethsw_data; 162962306a36Sopenharmony_ci enum dpsw_learning_mode learn_mode; 163062306a36Sopenharmony_ci int err; 163162306a36Sopenharmony_ci 163262306a36Sopenharmony_ci if (enable) 163362306a36Sopenharmony_ci learn_mode = DPSW_LEARNING_MODE_HW; 163462306a36Sopenharmony_ci else 163562306a36Sopenharmony_ci learn_mode = DPSW_LEARNING_MODE_DIS; 163662306a36Sopenharmony_ci 163762306a36Sopenharmony_ci err = dpsw_if_set_learning_mode(ethsw->mc_io, 0, ethsw->dpsw_handle, 163862306a36Sopenharmony_ci port_priv->idx, learn_mode); 163962306a36Sopenharmony_ci if (err) 164062306a36Sopenharmony_ci netdev_err(port_priv->netdev, "dpsw_if_set_learning_mode err %d\n", err); 164162306a36Sopenharmony_ci 164262306a36Sopenharmony_ci if (!enable) 164362306a36Sopenharmony_ci dpaa2_switch_port_fast_age(port_priv); 164462306a36Sopenharmony_ci 164562306a36Sopenharmony_ci return err; 164662306a36Sopenharmony_ci} 164762306a36Sopenharmony_ci 164862306a36Sopenharmony_cistatic int dpaa2_switch_port_attr_stp_state_set(struct net_device *netdev, 164962306a36Sopenharmony_ci u8 state) 165062306a36Sopenharmony_ci{ 165162306a36Sopenharmony_ci struct ethsw_port_priv *port_priv = netdev_priv(netdev); 165262306a36Sopenharmony_ci int err; 165362306a36Sopenharmony_ci 165462306a36Sopenharmony_ci err = dpaa2_switch_port_set_stp_state(port_priv, state); 165562306a36Sopenharmony_ci if (err) 165662306a36Sopenharmony_ci return err; 165762306a36Sopenharmony_ci 165862306a36Sopenharmony_ci switch (state) { 165962306a36Sopenharmony_ci case BR_STATE_DISABLED: 166062306a36Sopenharmony_ci case BR_STATE_BLOCKING: 166162306a36Sopenharmony_ci case BR_STATE_LISTENING: 166262306a36Sopenharmony_ci err = dpaa2_switch_port_set_learning(port_priv, false); 166362306a36Sopenharmony_ci break; 166462306a36Sopenharmony_ci case BR_STATE_LEARNING: 166562306a36Sopenharmony_ci case BR_STATE_FORWARDING: 166662306a36Sopenharmony_ci err = dpaa2_switch_port_set_learning(port_priv, 166762306a36Sopenharmony_ci port_priv->learn_ena); 166862306a36Sopenharmony_ci break; 166962306a36Sopenharmony_ci } 167062306a36Sopenharmony_ci 167162306a36Sopenharmony_ci return err; 167262306a36Sopenharmony_ci} 167362306a36Sopenharmony_ci 167462306a36Sopenharmony_cistatic int dpaa2_switch_port_flood(struct ethsw_port_priv *port_priv, 167562306a36Sopenharmony_ci struct switchdev_brport_flags flags) 167662306a36Sopenharmony_ci{ 167762306a36Sopenharmony_ci struct ethsw_core *ethsw = port_priv->ethsw_data; 167862306a36Sopenharmony_ci 167962306a36Sopenharmony_ci if (flags.mask & BR_BCAST_FLOOD) 168062306a36Sopenharmony_ci port_priv->bcast_flood = !!(flags.val & BR_BCAST_FLOOD); 168162306a36Sopenharmony_ci 168262306a36Sopenharmony_ci if (flags.mask & BR_FLOOD) 168362306a36Sopenharmony_ci port_priv->ucast_flood = !!(flags.val & BR_FLOOD); 168462306a36Sopenharmony_ci 168562306a36Sopenharmony_ci return dpaa2_switch_fdb_set_egress_flood(ethsw, port_priv->fdb->fdb_id); 168662306a36Sopenharmony_ci} 168762306a36Sopenharmony_ci 168862306a36Sopenharmony_cistatic int dpaa2_switch_port_pre_bridge_flags(struct net_device *netdev, 168962306a36Sopenharmony_ci struct switchdev_brport_flags flags, 169062306a36Sopenharmony_ci struct netlink_ext_ack *extack) 169162306a36Sopenharmony_ci{ 169262306a36Sopenharmony_ci if (flags.mask & ~(BR_LEARNING | BR_BCAST_FLOOD | BR_FLOOD | 169362306a36Sopenharmony_ci BR_MCAST_FLOOD)) 169462306a36Sopenharmony_ci return -EINVAL; 169562306a36Sopenharmony_ci 169662306a36Sopenharmony_ci if (flags.mask & (BR_FLOOD | BR_MCAST_FLOOD)) { 169762306a36Sopenharmony_ci bool multicast = !!(flags.val & BR_MCAST_FLOOD); 169862306a36Sopenharmony_ci bool unicast = !!(flags.val & BR_FLOOD); 169962306a36Sopenharmony_ci 170062306a36Sopenharmony_ci if (unicast != multicast) { 170162306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, 170262306a36Sopenharmony_ci "Cannot configure multicast flooding independently of unicast"); 170362306a36Sopenharmony_ci return -EINVAL; 170462306a36Sopenharmony_ci } 170562306a36Sopenharmony_ci } 170662306a36Sopenharmony_ci 170762306a36Sopenharmony_ci return 0; 170862306a36Sopenharmony_ci} 170962306a36Sopenharmony_ci 171062306a36Sopenharmony_cistatic int dpaa2_switch_port_bridge_flags(struct net_device *netdev, 171162306a36Sopenharmony_ci struct switchdev_brport_flags flags, 171262306a36Sopenharmony_ci struct netlink_ext_ack *extack) 171362306a36Sopenharmony_ci{ 171462306a36Sopenharmony_ci struct ethsw_port_priv *port_priv = netdev_priv(netdev); 171562306a36Sopenharmony_ci int err; 171662306a36Sopenharmony_ci 171762306a36Sopenharmony_ci if (flags.mask & BR_LEARNING) { 171862306a36Sopenharmony_ci bool learn_ena = !!(flags.val & BR_LEARNING); 171962306a36Sopenharmony_ci 172062306a36Sopenharmony_ci err = dpaa2_switch_port_set_learning(port_priv, learn_ena); 172162306a36Sopenharmony_ci if (err) 172262306a36Sopenharmony_ci return err; 172362306a36Sopenharmony_ci port_priv->learn_ena = learn_ena; 172462306a36Sopenharmony_ci } 172562306a36Sopenharmony_ci 172662306a36Sopenharmony_ci if (flags.mask & (BR_BCAST_FLOOD | BR_FLOOD | BR_MCAST_FLOOD)) { 172762306a36Sopenharmony_ci err = dpaa2_switch_port_flood(port_priv, flags); 172862306a36Sopenharmony_ci if (err) 172962306a36Sopenharmony_ci return err; 173062306a36Sopenharmony_ci } 173162306a36Sopenharmony_ci 173262306a36Sopenharmony_ci return 0; 173362306a36Sopenharmony_ci} 173462306a36Sopenharmony_ci 173562306a36Sopenharmony_cistatic int dpaa2_switch_port_attr_set(struct net_device *netdev, const void *ctx, 173662306a36Sopenharmony_ci const struct switchdev_attr *attr, 173762306a36Sopenharmony_ci struct netlink_ext_ack *extack) 173862306a36Sopenharmony_ci{ 173962306a36Sopenharmony_ci int err = 0; 174062306a36Sopenharmony_ci 174162306a36Sopenharmony_ci switch (attr->id) { 174262306a36Sopenharmony_ci case SWITCHDEV_ATTR_ID_PORT_STP_STATE: 174362306a36Sopenharmony_ci err = dpaa2_switch_port_attr_stp_state_set(netdev, 174462306a36Sopenharmony_ci attr->u.stp_state); 174562306a36Sopenharmony_ci break; 174662306a36Sopenharmony_ci case SWITCHDEV_ATTR_ID_BRIDGE_VLAN_FILTERING: 174762306a36Sopenharmony_ci if (!attr->u.vlan_filtering) { 174862306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, 174962306a36Sopenharmony_ci "The DPAA2 switch does not support VLAN-unaware operation"); 175062306a36Sopenharmony_ci return -EOPNOTSUPP; 175162306a36Sopenharmony_ci } 175262306a36Sopenharmony_ci break; 175362306a36Sopenharmony_ci case SWITCHDEV_ATTR_ID_PORT_PRE_BRIDGE_FLAGS: 175462306a36Sopenharmony_ci err = dpaa2_switch_port_pre_bridge_flags(netdev, attr->u.brport_flags, extack); 175562306a36Sopenharmony_ci break; 175662306a36Sopenharmony_ci case SWITCHDEV_ATTR_ID_PORT_BRIDGE_FLAGS: 175762306a36Sopenharmony_ci err = dpaa2_switch_port_bridge_flags(netdev, attr->u.brport_flags, extack); 175862306a36Sopenharmony_ci break; 175962306a36Sopenharmony_ci default: 176062306a36Sopenharmony_ci err = -EOPNOTSUPP; 176162306a36Sopenharmony_ci break; 176262306a36Sopenharmony_ci } 176362306a36Sopenharmony_ci 176462306a36Sopenharmony_ci return err; 176562306a36Sopenharmony_ci} 176662306a36Sopenharmony_ci 176762306a36Sopenharmony_ciint dpaa2_switch_port_vlans_add(struct net_device *netdev, 176862306a36Sopenharmony_ci const struct switchdev_obj_port_vlan *vlan) 176962306a36Sopenharmony_ci{ 177062306a36Sopenharmony_ci struct ethsw_port_priv *port_priv = netdev_priv(netdev); 177162306a36Sopenharmony_ci struct ethsw_core *ethsw = port_priv->ethsw_data; 177262306a36Sopenharmony_ci struct dpsw_attr *attr = ðsw->sw_attr; 177362306a36Sopenharmony_ci int err = 0; 177462306a36Sopenharmony_ci 177562306a36Sopenharmony_ci /* Make sure that the VLAN is not already configured 177662306a36Sopenharmony_ci * on the switch port 177762306a36Sopenharmony_ci */ 177862306a36Sopenharmony_ci if (port_priv->vlans[vlan->vid] & ETHSW_VLAN_MEMBER) 177962306a36Sopenharmony_ci return -EEXIST; 178062306a36Sopenharmony_ci 178162306a36Sopenharmony_ci /* Check if there is space for a new VLAN */ 178262306a36Sopenharmony_ci err = dpsw_get_attributes(ethsw->mc_io, 0, ethsw->dpsw_handle, 178362306a36Sopenharmony_ci ðsw->sw_attr); 178462306a36Sopenharmony_ci if (err) { 178562306a36Sopenharmony_ci netdev_err(netdev, "dpsw_get_attributes err %d\n", err); 178662306a36Sopenharmony_ci return err; 178762306a36Sopenharmony_ci } 178862306a36Sopenharmony_ci if (attr->max_vlans - attr->num_vlans < 1) 178962306a36Sopenharmony_ci return -ENOSPC; 179062306a36Sopenharmony_ci 179162306a36Sopenharmony_ci /* Check if there is space for a new VLAN */ 179262306a36Sopenharmony_ci err = dpsw_get_attributes(ethsw->mc_io, 0, ethsw->dpsw_handle, 179362306a36Sopenharmony_ci ðsw->sw_attr); 179462306a36Sopenharmony_ci if (err) { 179562306a36Sopenharmony_ci netdev_err(netdev, "dpsw_get_attributes err %d\n", err); 179662306a36Sopenharmony_ci return err; 179762306a36Sopenharmony_ci } 179862306a36Sopenharmony_ci if (attr->max_vlans - attr->num_vlans < 1) 179962306a36Sopenharmony_ci return -ENOSPC; 180062306a36Sopenharmony_ci 180162306a36Sopenharmony_ci if (!port_priv->ethsw_data->vlans[vlan->vid]) { 180262306a36Sopenharmony_ci /* this is a new VLAN */ 180362306a36Sopenharmony_ci err = dpaa2_switch_add_vlan(port_priv, vlan->vid); 180462306a36Sopenharmony_ci if (err) 180562306a36Sopenharmony_ci return err; 180662306a36Sopenharmony_ci 180762306a36Sopenharmony_ci port_priv->ethsw_data->vlans[vlan->vid] |= ETHSW_VLAN_GLOBAL; 180862306a36Sopenharmony_ci } 180962306a36Sopenharmony_ci 181062306a36Sopenharmony_ci return dpaa2_switch_port_add_vlan(port_priv, vlan->vid, vlan->flags); 181162306a36Sopenharmony_ci} 181262306a36Sopenharmony_ci 181362306a36Sopenharmony_cistatic int dpaa2_switch_port_lookup_address(struct net_device *netdev, int is_uc, 181462306a36Sopenharmony_ci const unsigned char *addr) 181562306a36Sopenharmony_ci{ 181662306a36Sopenharmony_ci struct netdev_hw_addr_list *list = (is_uc) ? &netdev->uc : &netdev->mc; 181762306a36Sopenharmony_ci struct netdev_hw_addr *ha; 181862306a36Sopenharmony_ci 181962306a36Sopenharmony_ci netif_addr_lock_bh(netdev); 182062306a36Sopenharmony_ci list_for_each_entry(ha, &list->list, list) { 182162306a36Sopenharmony_ci if (ether_addr_equal(ha->addr, addr)) { 182262306a36Sopenharmony_ci netif_addr_unlock_bh(netdev); 182362306a36Sopenharmony_ci return 1; 182462306a36Sopenharmony_ci } 182562306a36Sopenharmony_ci } 182662306a36Sopenharmony_ci netif_addr_unlock_bh(netdev); 182762306a36Sopenharmony_ci return 0; 182862306a36Sopenharmony_ci} 182962306a36Sopenharmony_ci 183062306a36Sopenharmony_cistatic int dpaa2_switch_port_mdb_add(struct net_device *netdev, 183162306a36Sopenharmony_ci const struct switchdev_obj_port_mdb *mdb) 183262306a36Sopenharmony_ci{ 183362306a36Sopenharmony_ci struct ethsw_port_priv *port_priv = netdev_priv(netdev); 183462306a36Sopenharmony_ci int err; 183562306a36Sopenharmony_ci 183662306a36Sopenharmony_ci /* Check if address is already set on this port */ 183762306a36Sopenharmony_ci if (dpaa2_switch_port_lookup_address(netdev, 0, mdb->addr)) 183862306a36Sopenharmony_ci return -EEXIST; 183962306a36Sopenharmony_ci 184062306a36Sopenharmony_ci err = dpaa2_switch_port_fdb_add_mc(port_priv, mdb->addr); 184162306a36Sopenharmony_ci if (err) 184262306a36Sopenharmony_ci return err; 184362306a36Sopenharmony_ci 184462306a36Sopenharmony_ci err = dev_mc_add(netdev, mdb->addr); 184562306a36Sopenharmony_ci if (err) { 184662306a36Sopenharmony_ci netdev_err(netdev, "dev_mc_add err %d\n", err); 184762306a36Sopenharmony_ci dpaa2_switch_port_fdb_del_mc(port_priv, mdb->addr); 184862306a36Sopenharmony_ci } 184962306a36Sopenharmony_ci 185062306a36Sopenharmony_ci return err; 185162306a36Sopenharmony_ci} 185262306a36Sopenharmony_ci 185362306a36Sopenharmony_cistatic int dpaa2_switch_port_obj_add(struct net_device *netdev, 185462306a36Sopenharmony_ci const struct switchdev_obj *obj) 185562306a36Sopenharmony_ci{ 185662306a36Sopenharmony_ci int err; 185762306a36Sopenharmony_ci 185862306a36Sopenharmony_ci switch (obj->id) { 185962306a36Sopenharmony_ci case SWITCHDEV_OBJ_ID_PORT_VLAN: 186062306a36Sopenharmony_ci err = dpaa2_switch_port_vlans_add(netdev, 186162306a36Sopenharmony_ci SWITCHDEV_OBJ_PORT_VLAN(obj)); 186262306a36Sopenharmony_ci break; 186362306a36Sopenharmony_ci case SWITCHDEV_OBJ_ID_PORT_MDB: 186462306a36Sopenharmony_ci err = dpaa2_switch_port_mdb_add(netdev, 186562306a36Sopenharmony_ci SWITCHDEV_OBJ_PORT_MDB(obj)); 186662306a36Sopenharmony_ci break; 186762306a36Sopenharmony_ci default: 186862306a36Sopenharmony_ci err = -EOPNOTSUPP; 186962306a36Sopenharmony_ci break; 187062306a36Sopenharmony_ci } 187162306a36Sopenharmony_ci 187262306a36Sopenharmony_ci return err; 187362306a36Sopenharmony_ci} 187462306a36Sopenharmony_ci 187562306a36Sopenharmony_cistatic int dpaa2_switch_port_del_vlan(struct ethsw_port_priv *port_priv, u16 vid) 187662306a36Sopenharmony_ci{ 187762306a36Sopenharmony_ci struct ethsw_core *ethsw = port_priv->ethsw_data; 187862306a36Sopenharmony_ci struct net_device *netdev = port_priv->netdev; 187962306a36Sopenharmony_ci struct dpsw_vlan_if_cfg vcfg; 188062306a36Sopenharmony_ci int i, err; 188162306a36Sopenharmony_ci 188262306a36Sopenharmony_ci if (!port_priv->vlans[vid]) 188362306a36Sopenharmony_ci return -ENOENT; 188462306a36Sopenharmony_ci 188562306a36Sopenharmony_ci if (port_priv->vlans[vid] & ETHSW_VLAN_PVID) { 188662306a36Sopenharmony_ci /* If we are deleting the PVID of a port, use VLAN 4095 instead 188762306a36Sopenharmony_ci * as we are sure that neither the bridge nor the 8021q module 188862306a36Sopenharmony_ci * will use it 188962306a36Sopenharmony_ci */ 189062306a36Sopenharmony_ci err = dpaa2_switch_port_set_pvid(port_priv, 4095); 189162306a36Sopenharmony_ci if (err) 189262306a36Sopenharmony_ci return err; 189362306a36Sopenharmony_ci } 189462306a36Sopenharmony_ci 189562306a36Sopenharmony_ci vcfg.num_ifs = 1; 189662306a36Sopenharmony_ci vcfg.if_id[0] = port_priv->idx; 189762306a36Sopenharmony_ci if (port_priv->vlans[vid] & ETHSW_VLAN_UNTAGGED) { 189862306a36Sopenharmony_ci err = dpsw_vlan_remove_if_untagged(ethsw->mc_io, 0, 189962306a36Sopenharmony_ci ethsw->dpsw_handle, 190062306a36Sopenharmony_ci vid, &vcfg); 190162306a36Sopenharmony_ci if (err) { 190262306a36Sopenharmony_ci netdev_err(netdev, 190362306a36Sopenharmony_ci "dpsw_vlan_remove_if_untagged err %d\n", 190462306a36Sopenharmony_ci err); 190562306a36Sopenharmony_ci } 190662306a36Sopenharmony_ci port_priv->vlans[vid] &= ~ETHSW_VLAN_UNTAGGED; 190762306a36Sopenharmony_ci } 190862306a36Sopenharmony_ci 190962306a36Sopenharmony_ci if (port_priv->vlans[vid] & ETHSW_VLAN_MEMBER) { 191062306a36Sopenharmony_ci err = dpsw_vlan_remove_if(ethsw->mc_io, 0, ethsw->dpsw_handle, 191162306a36Sopenharmony_ci vid, &vcfg); 191262306a36Sopenharmony_ci if (err) { 191362306a36Sopenharmony_ci netdev_err(netdev, 191462306a36Sopenharmony_ci "dpsw_vlan_remove_if err %d\n", err); 191562306a36Sopenharmony_ci return err; 191662306a36Sopenharmony_ci } 191762306a36Sopenharmony_ci port_priv->vlans[vid] &= ~ETHSW_VLAN_MEMBER; 191862306a36Sopenharmony_ci 191962306a36Sopenharmony_ci /* Delete VLAN from switch if it is no longer configured on 192062306a36Sopenharmony_ci * any port 192162306a36Sopenharmony_ci */ 192262306a36Sopenharmony_ci for (i = 0; i < ethsw->sw_attr.num_ifs; i++) { 192362306a36Sopenharmony_ci if (ethsw->ports[i] && 192462306a36Sopenharmony_ci ethsw->ports[i]->vlans[vid] & ETHSW_VLAN_MEMBER) 192562306a36Sopenharmony_ci return 0; /* Found a port member in VID */ 192662306a36Sopenharmony_ci } 192762306a36Sopenharmony_ci 192862306a36Sopenharmony_ci ethsw->vlans[vid] &= ~ETHSW_VLAN_GLOBAL; 192962306a36Sopenharmony_ci 193062306a36Sopenharmony_ci err = dpaa2_switch_dellink(ethsw, vid); 193162306a36Sopenharmony_ci if (err) 193262306a36Sopenharmony_ci return err; 193362306a36Sopenharmony_ci } 193462306a36Sopenharmony_ci 193562306a36Sopenharmony_ci return 0; 193662306a36Sopenharmony_ci} 193762306a36Sopenharmony_ci 193862306a36Sopenharmony_ciint dpaa2_switch_port_vlans_del(struct net_device *netdev, 193962306a36Sopenharmony_ci const struct switchdev_obj_port_vlan *vlan) 194062306a36Sopenharmony_ci{ 194162306a36Sopenharmony_ci struct ethsw_port_priv *port_priv = netdev_priv(netdev); 194262306a36Sopenharmony_ci 194362306a36Sopenharmony_ci if (netif_is_bridge_master(vlan->obj.orig_dev)) 194462306a36Sopenharmony_ci return -EOPNOTSUPP; 194562306a36Sopenharmony_ci 194662306a36Sopenharmony_ci return dpaa2_switch_port_del_vlan(port_priv, vlan->vid); 194762306a36Sopenharmony_ci} 194862306a36Sopenharmony_ci 194962306a36Sopenharmony_cistatic int dpaa2_switch_port_mdb_del(struct net_device *netdev, 195062306a36Sopenharmony_ci const struct switchdev_obj_port_mdb *mdb) 195162306a36Sopenharmony_ci{ 195262306a36Sopenharmony_ci struct ethsw_port_priv *port_priv = netdev_priv(netdev); 195362306a36Sopenharmony_ci int err; 195462306a36Sopenharmony_ci 195562306a36Sopenharmony_ci if (!dpaa2_switch_port_lookup_address(netdev, 0, mdb->addr)) 195662306a36Sopenharmony_ci return -ENOENT; 195762306a36Sopenharmony_ci 195862306a36Sopenharmony_ci err = dpaa2_switch_port_fdb_del_mc(port_priv, mdb->addr); 195962306a36Sopenharmony_ci if (err) 196062306a36Sopenharmony_ci return err; 196162306a36Sopenharmony_ci 196262306a36Sopenharmony_ci err = dev_mc_del(netdev, mdb->addr); 196362306a36Sopenharmony_ci if (err) { 196462306a36Sopenharmony_ci netdev_err(netdev, "dev_mc_del err %d\n", err); 196562306a36Sopenharmony_ci return err; 196662306a36Sopenharmony_ci } 196762306a36Sopenharmony_ci 196862306a36Sopenharmony_ci return err; 196962306a36Sopenharmony_ci} 197062306a36Sopenharmony_ci 197162306a36Sopenharmony_cistatic int dpaa2_switch_port_obj_del(struct net_device *netdev, 197262306a36Sopenharmony_ci const struct switchdev_obj *obj) 197362306a36Sopenharmony_ci{ 197462306a36Sopenharmony_ci int err; 197562306a36Sopenharmony_ci 197662306a36Sopenharmony_ci switch (obj->id) { 197762306a36Sopenharmony_ci case SWITCHDEV_OBJ_ID_PORT_VLAN: 197862306a36Sopenharmony_ci err = dpaa2_switch_port_vlans_del(netdev, SWITCHDEV_OBJ_PORT_VLAN(obj)); 197962306a36Sopenharmony_ci break; 198062306a36Sopenharmony_ci case SWITCHDEV_OBJ_ID_PORT_MDB: 198162306a36Sopenharmony_ci err = dpaa2_switch_port_mdb_del(netdev, SWITCHDEV_OBJ_PORT_MDB(obj)); 198262306a36Sopenharmony_ci break; 198362306a36Sopenharmony_ci default: 198462306a36Sopenharmony_ci err = -EOPNOTSUPP; 198562306a36Sopenharmony_ci break; 198662306a36Sopenharmony_ci } 198762306a36Sopenharmony_ci return err; 198862306a36Sopenharmony_ci} 198962306a36Sopenharmony_ci 199062306a36Sopenharmony_cistatic int dpaa2_switch_port_attr_set_event(struct net_device *netdev, 199162306a36Sopenharmony_ci struct switchdev_notifier_port_attr_info *ptr) 199262306a36Sopenharmony_ci{ 199362306a36Sopenharmony_ci int err; 199462306a36Sopenharmony_ci 199562306a36Sopenharmony_ci err = switchdev_handle_port_attr_set(netdev, ptr, 199662306a36Sopenharmony_ci dpaa2_switch_port_dev_check, 199762306a36Sopenharmony_ci dpaa2_switch_port_attr_set); 199862306a36Sopenharmony_ci return notifier_from_errno(err); 199962306a36Sopenharmony_ci} 200062306a36Sopenharmony_ci 200162306a36Sopenharmony_cistatic int dpaa2_switch_port_bridge_join(struct net_device *netdev, 200262306a36Sopenharmony_ci struct net_device *upper_dev, 200362306a36Sopenharmony_ci struct netlink_ext_ack *extack) 200462306a36Sopenharmony_ci{ 200562306a36Sopenharmony_ci struct ethsw_port_priv *port_priv = netdev_priv(netdev); 200662306a36Sopenharmony_ci struct ethsw_core *ethsw = port_priv->ethsw_data; 200762306a36Sopenharmony_ci struct ethsw_port_priv *other_port_priv; 200862306a36Sopenharmony_ci struct net_device *other_dev; 200962306a36Sopenharmony_ci struct list_head *iter; 201062306a36Sopenharmony_ci bool learn_ena; 201162306a36Sopenharmony_ci int err; 201262306a36Sopenharmony_ci 201362306a36Sopenharmony_ci netdev_for_each_lower_dev(upper_dev, other_dev, iter) { 201462306a36Sopenharmony_ci if (!dpaa2_switch_port_dev_check(other_dev)) 201562306a36Sopenharmony_ci continue; 201662306a36Sopenharmony_ci 201762306a36Sopenharmony_ci other_port_priv = netdev_priv(other_dev); 201862306a36Sopenharmony_ci if (other_port_priv->ethsw_data != port_priv->ethsw_data) { 201962306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, 202062306a36Sopenharmony_ci "Interface from a different DPSW is in the bridge already"); 202162306a36Sopenharmony_ci return -EINVAL; 202262306a36Sopenharmony_ci } 202362306a36Sopenharmony_ci } 202462306a36Sopenharmony_ci 202562306a36Sopenharmony_ci /* Delete the previously manually installed VLAN 1 */ 202662306a36Sopenharmony_ci err = dpaa2_switch_port_del_vlan(port_priv, 1); 202762306a36Sopenharmony_ci if (err) 202862306a36Sopenharmony_ci return err; 202962306a36Sopenharmony_ci 203062306a36Sopenharmony_ci dpaa2_switch_port_set_fdb(port_priv, upper_dev); 203162306a36Sopenharmony_ci 203262306a36Sopenharmony_ci /* Inherit the initial bridge port learning state */ 203362306a36Sopenharmony_ci learn_ena = br_port_flag_is_set(netdev, BR_LEARNING); 203462306a36Sopenharmony_ci err = dpaa2_switch_port_set_learning(port_priv, learn_ena); 203562306a36Sopenharmony_ci port_priv->learn_ena = learn_ena; 203662306a36Sopenharmony_ci 203762306a36Sopenharmony_ci /* Setup the egress flood policy (broadcast, unknown unicast) */ 203862306a36Sopenharmony_ci err = dpaa2_switch_fdb_set_egress_flood(ethsw, port_priv->fdb->fdb_id); 203962306a36Sopenharmony_ci if (err) 204062306a36Sopenharmony_ci goto err_egress_flood; 204162306a36Sopenharmony_ci 204262306a36Sopenharmony_ci err = switchdev_bridge_port_offload(netdev, netdev, NULL, 204362306a36Sopenharmony_ci NULL, NULL, false, extack); 204462306a36Sopenharmony_ci if (err) 204562306a36Sopenharmony_ci goto err_switchdev_offload; 204662306a36Sopenharmony_ci 204762306a36Sopenharmony_ci return 0; 204862306a36Sopenharmony_ci 204962306a36Sopenharmony_cierr_switchdev_offload: 205062306a36Sopenharmony_cierr_egress_flood: 205162306a36Sopenharmony_ci dpaa2_switch_port_set_fdb(port_priv, NULL); 205262306a36Sopenharmony_ci return err; 205362306a36Sopenharmony_ci} 205462306a36Sopenharmony_ci 205562306a36Sopenharmony_cistatic int dpaa2_switch_port_clear_rxvlan(struct net_device *vdev, int vid, void *arg) 205662306a36Sopenharmony_ci{ 205762306a36Sopenharmony_ci __be16 vlan_proto = htons(ETH_P_8021Q); 205862306a36Sopenharmony_ci 205962306a36Sopenharmony_ci if (vdev) 206062306a36Sopenharmony_ci vlan_proto = vlan_dev_vlan_proto(vdev); 206162306a36Sopenharmony_ci 206262306a36Sopenharmony_ci return dpaa2_switch_port_vlan_kill(arg, vlan_proto, vid); 206362306a36Sopenharmony_ci} 206462306a36Sopenharmony_ci 206562306a36Sopenharmony_cistatic int dpaa2_switch_port_restore_rxvlan(struct net_device *vdev, int vid, void *arg) 206662306a36Sopenharmony_ci{ 206762306a36Sopenharmony_ci __be16 vlan_proto = htons(ETH_P_8021Q); 206862306a36Sopenharmony_ci 206962306a36Sopenharmony_ci if (vdev) 207062306a36Sopenharmony_ci vlan_proto = vlan_dev_vlan_proto(vdev); 207162306a36Sopenharmony_ci 207262306a36Sopenharmony_ci return dpaa2_switch_port_vlan_add(arg, vlan_proto, vid); 207362306a36Sopenharmony_ci} 207462306a36Sopenharmony_ci 207562306a36Sopenharmony_cistatic void dpaa2_switch_port_pre_bridge_leave(struct net_device *netdev) 207662306a36Sopenharmony_ci{ 207762306a36Sopenharmony_ci switchdev_bridge_port_unoffload(netdev, NULL, NULL, NULL); 207862306a36Sopenharmony_ci} 207962306a36Sopenharmony_ci 208062306a36Sopenharmony_cistatic int dpaa2_switch_port_bridge_leave(struct net_device *netdev) 208162306a36Sopenharmony_ci{ 208262306a36Sopenharmony_ci struct ethsw_port_priv *port_priv = netdev_priv(netdev); 208362306a36Sopenharmony_ci struct dpaa2_switch_fdb *old_fdb = port_priv->fdb; 208462306a36Sopenharmony_ci struct ethsw_core *ethsw = port_priv->ethsw_data; 208562306a36Sopenharmony_ci int err; 208662306a36Sopenharmony_ci 208762306a36Sopenharmony_ci /* First of all, fast age any learn FDB addresses on this switch port */ 208862306a36Sopenharmony_ci dpaa2_switch_port_fast_age(port_priv); 208962306a36Sopenharmony_ci 209062306a36Sopenharmony_ci /* Clear all RX VLANs installed through vlan_vid_add() either as VLAN 209162306a36Sopenharmony_ci * upper devices or otherwise from the FDB table that we are about to 209262306a36Sopenharmony_ci * leave 209362306a36Sopenharmony_ci */ 209462306a36Sopenharmony_ci err = vlan_for_each(netdev, dpaa2_switch_port_clear_rxvlan, netdev); 209562306a36Sopenharmony_ci if (err) 209662306a36Sopenharmony_ci netdev_err(netdev, "Unable to clear RX VLANs from old FDB table, err (%d)\n", err); 209762306a36Sopenharmony_ci 209862306a36Sopenharmony_ci dpaa2_switch_port_set_fdb(port_priv, NULL); 209962306a36Sopenharmony_ci 210062306a36Sopenharmony_ci /* Restore all RX VLANs into the new FDB table that we just joined */ 210162306a36Sopenharmony_ci err = vlan_for_each(netdev, dpaa2_switch_port_restore_rxvlan, netdev); 210262306a36Sopenharmony_ci if (err) 210362306a36Sopenharmony_ci netdev_err(netdev, "Unable to restore RX VLANs to the new FDB, err (%d)\n", err); 210462306a36Sopenharmony_ci 210562306a36Sopenharmony_ci /* Reset the flooding state to denote that this port can send any 210662306a36Sopenharmony_ci * packet in standalone mode. With this, we are also ensuring that any 210762306a36Sopenharmony_ci * later bridge join will have the flooding flag on. 210862306a36Sopenharmony_ci */ 210962306a36Sopenharmony_ci port_priv->bcast_flood = true; 211062306a36Sopenharmony_ci port_priv->ucast_flood = true; 211162306a36Sopenharmony_ci 211262306a36Sopenharmony_ci /* Setup the egress flood policy (broadcast, unknown unicast). 211362306a36Sopenharmony_ci * When the port is not under a bridge, only the CTRL interface is part 211462306a36Sopenharmony_ci * of the flooding domain besides the actual port 211562306a36Sopenharmony_ci */ 211662306a36Sopenharmony_ci err = dpaa2_switch_fdb_set_egress_flood(ethsw, port_priv->fdb->fdb_id); 211762306a36Sopenharmony_ci if (err) 211862306a36Sopenharmony_ci return err; 211962306a36Sopenharmony_ci 212062306a36Sopenharmony_ci /* Recreate the egress flood domain of the FDB that we just left */ 212162306a36Sopenharmony_ci err = dpaa2_switch_fdb_set_egress_flood(ethsw, old_fdb->fdb_id); 212262306a36Sopenharmony_ci if (err) 212362306a36Sopenharmony_ci return err; 212462306a36Sopenharmony_ci 212562306a36Sopenharmony_ci /* No HW learning when not under a bridge */ 212662306a36Sopenharmony_ci err = dpaa2_switch_port_set_learning(port_priv, false); 212762306a36Sopenharmony_ci if (err) 212862306a36Sopenharmony_ci return err; 212962306a36Sopenharmony_ci port_priv->learn_ena = false; 213062306a36Sopenharmony_ci 213162306a36Sopenharmony_ci /* Add the VLAN 1 as PVID when not under a bridge. We need this since 213262306a36Sopenharmony_ci * the dpaa2 switch interfaces are not capable to be VLAN unaware 213362306a36Sopenharmony_ci */ 213462306a36Sopenharmony_ci return dpaa2_switch_port_add_vlan(port_priv, DEFAULT_VLAN_ID, 213562306a36Sopenharmony_ci BRIDGE_VLAN_INFO_UNTAGGED | BRIDGE_VLAN_INFO_PVID); 213662306a36Sopenharmony_ci} 213762306a36Sopenharmony_ci 213862306a36Sopenharmony_cistatic int dpaa2_switch_prevent_bridging_with_8021q_upper(struct net_device *netdev) 213962306a36Sopenharmony_ci{ 214062306a36Sopenharmony_ci struct net_device *upper_dev; 214162306a36Sopenharmony_ci struct list_head *iter; 214262306a36Sopenharmony_ci 214362306a36Sopenharmony_ci /* RCU read lock not necessary because we have write-side protection 214462306a36Sopenharmony_ci * (rtnl_mutex), however a non-rcu iterator does not exist. 214562306a36Sopenharmony_ci */ 214662306a36Sopenharmony_ci netdev_for_each_upper_dev_rcu(netdev, upper_dev, iter) 214762306a36Sopenharmony_ci if (is_vlan_dev(upper_dev)) 214862306a36Sopenharmony_ci return -EOPNOTSUPP; 214962306a36Sopenharmony_ci 215062306a36Sopenharmony_ci return 0; 215162306a36Sopenharmony_ci} 215262306a36Sopenharmony_ci 215362306a36Sopenharmony_cistatic int 215462306a36Sopenharmony_cidpaa2_switch_prechangeupper_sanity_checks(struct net_device *netdev, 215562306a36Sopenharmony_ci struct net_device *upper_dev, 215662306a36Sopenharmony_ci struct netlink_ext_ack *extack) 215762306a36Sopenharmony_ci{ 215862306a36Sopenharmony_ci int err; 215962306a36Sopenharmony_ci 216062306a36Sopenharmony_ci if (!br_vlan_enabled(upper_dev)) { 216162306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "Cannot join a VLAN-unaware bridge"); 216262306a36Sopenharmony_ci return -EOPNOTSUPP; 216362306a36Sopenharmony_ci } 216462306a36Sopenharmony_ci 216562306a36Sopenharmony_ci err = dpaa2_switch_prevent_bridging_with_8021q_upper(netdev); 216662306a36Sopenharmony_ci if (err) { 216762306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, 216862306a36Sopenharmony_ci "Cannot join a bridge while VLAN uppers are present"); 216962306a36Sopenharmony_ci return 0; 217062306a36Sopenharmony_ci } 217162306a36Sopenharmony_ci 217262306a36Sopenharmony_ci return 0; 217362306a36Sopenharmony_ci} 217462306a36Sopenharmony_ci 217562306a36Sopenharmony_cistatic int dpaa2_switch_port_netdevice_event(struct notifier_block *nb, 217662306a36Sopenharmony_ci unsigned long event, void *ptr) 217762306a36Sopenharmony_ci{ 217862306a36Sopenharmony_ci struct net_device *netdev = netdev_notifier_info_to_dev(ptr); 217962306a36Sopenharmony_ci struct netdev_notifier_changeupper_info *info = ptr; 218062306a36Sopenharmony_ci struct netlink_ext_ack *extack; 218162306a36Sopenharmony_ci struct net_device *upper_dev; 218262306a36Sopenharmony_ci int err = 0; 218362306a36Sopenharmony_ci 218462306a36Sopenharmony_ci if (!dpaa2_switch_port_dev_check(netdev)) 218562306a36Sopenharmony_ci return NOTIFY_DONE; 218662306a36Sopenharmony_ci 218762306a36Sopenharmony_ci extack = netdev_notifier_info_to_extack(&info->info); 218862306a36Sopenharmony_ci 218962306a36Sopenharmony_ci switch (event) { 219062306a36Sopenharmony_ci case NETDEV_PRECHANGEUPPER: 219162306a36Sopenharmony_ci upper_dev = info->upper_dev; 219262306a36Sopenharmony_ci if (!netif_is_bridge_master(upper_dev)) 219362306a36Sopenharmony_ci break; 219462306a36Sopenharmony_ci 219562306a36Sopenharmony_ci err = dpaa2_switch_prechangeupper_sanity_checks(netdev, 219662306a36Sopenharmony_ci upper_dev, 219762306a36Sopenharmony_ci extack); 219862306a36Sopenharmony_ci if (err) 219962306a36Sopenharmony_ci goto out; 220062306a36Sopenharmony_ci 220162306a36Sopenharmony_ci if (!info->linking) 220262306a36Sopenharmony_ci dpaa2_switch_port_pre_bridge_leave(netdev); 220362306a36Sopenharmony_ci 220462306a36Sopenharmony_ci break; 220562306a36Sopenharmony_ci case NETDEV_CHANGEUPPER: 220662306a36Sopenharmony_ci upper_dev = info->upper_dev; 220762306a36Sopenharmony_ci if (netif_is_bridge_master(upper_dev)) { 220862306a36Sopenharmony_ci if (info->linking) 220962306a36Sopenharmony_ci err = dpaa2_switch_port_bridge_join(netdev, 221062306a36Sopenharmony_ci upper_dev, 221162306a36Sopenharmony_ci extack); 221262306a36Sopenharmony_ci else 221362306a36Sopenharmony_ci err = dpaa2_switch_port_bridge_leave(netdev); 221462306a36Sopenharmony_ci } 221562306a36Sopenharmony_ci break; 221662306a36Sopenharmony_ci } 221762306a36Sopenharmony_ci 221862306a36Sopenharmony_ciout: 221962306a36Sopenharmony_ci return notifier_from_errno(err); 222062306a36Sopenharmony_ci} 222162306a36Sopenharmony_ci 222262306a36Sopenharmony_cistruct ethsw_switchdev_event_work { 222362306a36Sopenharmony_ci struct work_struct work; 222462306a36Sopenharmony_ci struct switchdev_notifier_fdb_info fdb_info; 222562306a36Sopenharmony_ci struct net_device *dev; 222662306a36Sopenharmony_ci unsigned long event; 222762306a36Sopenharmony_ci}; 222862306a36Sopenharmony_ci 222962306a36Sopenharmony_cistatic void dpaa2_switch_event_work(struct work_struct *work) 223062306a36Sopenharmony_ci{ 223162306a36Sopenharmony_ci struct ethsw_switchdev_event_work *switchdev_work = 223262306a36Sopenharmony_ci container_of(work, struct ethsw_switchdev_event_work, work); 223362306a36Sopenharmony_ci struct net_device *dev = switchdev_work->dev; 223462306a36Sopenharmony_ci struct switchdev_notifier_fdb_info *fdb_info; 223562306a36Sopenharmony_ci int err; 223662306a36Sopenharmony_ci 223762306a36Sopenharmony_ci rtnl_lock(); 223862306a36Sopenharmony_ci fdb_info = &switchdev_work->fdb_info; 223962306a36Sopenharmony_ci 224062306a36Sopenharmony_ci switch (switchdev_work->event) { 224162306a36Sopenharmony_ci case SWITCHDEV_FDB_ADD_TO_DEVICE: 224262306a36Sopenharmony_ci if (!fdb_info->added_by_user || fdb_info->is_local) 224362306a36Sopenharmony_ci break; 224462306a36Sopenharmony_ci if (is_unicast_ether_addr(fdb_info->addr)) 224562306a36Sopenharmony_ci err = dpaa2_switch_port_fdb_add_uc(netdev_priv(dev), 224662306a36Sopenharmony_ci fdb_info->addr); 224762306a36Sopenharmony_ci else 224862306a36Sopenharmony_ci err = dpaa2_switch_port_fdb_add_mc(netdev_priv(dev), 224962306a36Sopenharmony_ci fdb_info->addr); 225062306a36Sopenharmony_ci if (err) 225162306a36Sopenharmony_ci break; 225262306a36Sopenharmony_ci fdb_info->offloaded = true; 225362306a36Sopenharmony_ci call_switchdev_notifiers(SWITCHDEV_FDB_OFFLOADED, dev, 225462306a36Sopenharmony_ci &fdb_info->info, NULL); 225562306a36Sopenharmony_ci break; 225662306a36Sopenharmony_ci case SWITCHDEV_FDB_DEL_TO_DEVICE: 225762306a36Sopenharmony_ci if (!fdb_info->added_by_user || fdb_info->is_local) 225862306a36Sopenharmony_ci break; 225962306a36Sopenharmony_ci if (is_unicast_ether_addr(fdb_info->addr)) 226062306a36Sopenharmony_ci dpaa2_switch_port_fdb_del_uc(netdev_priv(dev), fdb_info->addr); 226162306a36Sopenharmony_ci else 226262306a36Sopenharmony_ci dpaa2_switch_port_fdb_del_mc(netdev_priv(dev), fdb_info->addr); 226362306a36Sopenharmony_ci break; 226462306a36Sopenharmony_ci } 226562306a36Sopenharmony_ci 226662306a36Sopenharmony_ci rtnl_unlock(); 226762306a36Sopenharmony_ci kfree(switchdev_work->fdb_info.addr); 226862306a36Sopenharmony_ci kfree(switchdev_work); 226962306a36Sopenharmony_ci dev_put(dev); 227062306a36Sopenharmony_ci} 227162306a36Sopenharmony_ci 227262306a36Sopenharmony_ci/* Called under rcu_read_lock() */ 227362306a36Sopenharmony_cistatic int dpaa2_switch_port_event(struct notifier_block *nb, 227462306a36Sopenharmony_ci unsigned long event, void *ptr) 227562306a36Sopenharmony_ci{ 227662306a36Sopenharmony_ci struct net_device *dev = switchdev_notifier_info_to_dev(ptr); 227762306a36Sopenharmony_ci struct ethsw_port_priv *port_priv = netdev_priv(dev); 227862306a36Sopenharmony_ci struct ethsw_switchdev_event_work *switchdev_work; 227962306a36Sopenharmony_ci struct switchdev_notifier_fdb_info *fdb_info = ptr; 228062306a36Sopenharmony_ci struct ethsw_core *ethsw = port_priv->ethsw_data; 228162306a36Sopenharmony_ci 228262306a36Sopenharmony_ci if (event == SWITCHDEV_PORT_ATTR_SET) 228362306a36Sopenharmony_ci return dpaa2_switch_port_attr_set_event(dev, ptr); 228462306a36Sopenharmony_ci 228562306a36Sopenharmony_ci if (!dpaa2_switch_port_dev_check(dev)) 228662306a36Sopenharmony_ci return NOTIFY_DONE; 228762306a36Sopenharmony_ci 228862306a36Sopenharmony_ci switchdev_work = kzalloc(sizeof(*switchdev_work), GFP_ATOMIC); 228962306a36Sopenharmony_ci if (!switchdev_work) 229062306a36Sopenharmony_ci return NOTIFY_BAD; 229162306a36Sopenharmony_ci 229262306a36Sopenharmony_ci INIT_WORK(&switchdev_work->work, dpaa2_switch_event_work); 229362306a36Sopenharmony_ci switchdev_work->dev = dev; 229462306a36Sopenharmony_ci switchdev_work->event = event; 229562306a36Sopenharmony_ci 229662306a36Sopenharmony_ci switch (event) { 229762306a36Sopenharmony_ci case SWITCHDEV_FDB_ADD_TO_DEVICE: 229862306a36Sopenharmony_ci case SWITCHDEV_FDB_DEL_TO_DEVICE: 229962306a36Sopenharmony_ci memcpy(&switchdev_work->fdb_info, ptr, 230062306a36Sopenharmony_ci sizeof(switchdev_work->fdb_info)); 230162306a36Sopenharmony_ci switchdev_work->fdb_info.addr = kzalloc(ETH_ALEN, GFP_ATOMIC); 230262306a36Sopenharmony_ci if (!switchdev_work->fdb_info.addr) 230362306a36Sopenharmony_ci goto err_addr_alloc; 230462306a36Sopenharmony_ci 230562306a36Sopenharmony_ci ether_addr_copy((u8 *)switchdev_work->fdb_info.addr, 230662306a36Sopenharmony_ci fdb_info->addr); 230762306a36Sopenharmony_ci 230862306a36Sopenharmony_ci /* Take a reference on the device to avoid being freed. */ 230962306a36Sopenharmony_ci dev_hold(dev); 231062306a36Sopenharmony_ci break; 231162306a36Sopenharmony_ci default: 231262306a36Sopenharmony_ci kfree(switchdev_work); 231362306a36Sopenharmony_ci return NOTIFY_DONE; 231462306a36Sopenharmony_ci } 231562306a36Sopenharmony_ci 231662306a36Sopenharmony_ci queue_work(ethsw->workqueue, &switchdev_work->work); 231762306a36Sopenharmony_ci 231862306a36Sopenharmony_ci return NOTIFY_DONE; 231962306a36Sopenharmony_ci 232062306a36Sopenharmony_cierr_addr_alloc: 232162306a36Sopenharmony_ci kfree(switchdev_work); 232262306a36Sopenharmony_ci return NOTIFY_BAD; 232362306a36Sopenharmony_ci} 232462306a36Sopenharmony_ci 232562306a36Sopenharmony_cistatic int dpaa2_switch_port_obj_event(unsigned long event, 232662306a36Sopenharmony_ci struct net_device *netdev, 232762306a36Sopenharmony_ci struct switchdev_notifier_port_obj_info *port_obj_info) 232862306a36Sopenharmony_ci{ 232962306a36Sopenharmony_ci int err = -EOPNOTSUPP; 233062306a36Sopenharmony_ci 233162306a36Sopenharmony_ci if (!dpaa2_switch_port_dev_check(netdev)) 233262306a36Sopenharmony_ci return NOTIFY_DONE; 233362306a36Sopenharmony_ci 233462306a36Sopenharmony_ci switch (event) { 233562306a36Sopenharmony_ci case SWITCHDEV_PORT_OBJ_ADD: 233662306a36Sopenharmony_ci err = dpaa2_switch_port_obj_add(netdev, port_obj_info->obj); 233762306a36Sopenharmony_ci break; 233862306a36Sopenharmony_ci case SWITCHDEV_PORT_OBJ_DEL: 233962306a36Sopenharmony_ci err = dpaa2_switch_port_obj_del(netdev, port_obj_info->obj); 234062306a36Sopenharmony_ci break; 234162306a36Sopenharmony_ci } 234262306a36Sopenharmony_ci 234362306a36Sopenharmony_ci port_obj_info->handled = true; 234462306a36Sopenharmony_ci return notifier_from_errno(err); 234562306a36Sopenharmony_ci} 234662306a36Sopenharmony_ci 234762306a36Sopenharmony_cistatic int dpaa2_switch_port_blocking_event(struct notifier_block *nb, 234862306a36Sopenharmony_ci unsigned long event, void *ptr) 234962306a36Sopenharmony_ci{ 235062306a36Sopenharmony_ci struct net_device *dev = switchdev_notifier_info_to_dev(ptr); 235162306a36Sopenharmony_ci 235262306a36Sopenharmony_ci switch (event) { 235362306a36Sopenharmony_ci case SWITCHDEV_PORT_OBJ_ADD: 235462306a36Sopenharmony_ci case SWITCHDEV_PORT_OBJ_DEL: 235562306a36Sopenharmony_ci return dpaa2_switch_port_obj_event(event, dev, ptr); 235662306a36Sopenharmony_ci case SWITCHDEV_PORT_ATTR_SET: 235762306a36Sopenharmony_ci return dpaa2_switch_port_attr_set_event(dev, ptr); 235862306a36Sopenharmony_ci } 235962306a36Sopenharmony_ci 236062306a36Sopenharmony_ci return NOTIFY_DONE; 236162306a36Sopenharmony_ci} 236262306a36Sopenharmony_ci 236362306a36Sopenharmony_ci/* Build a linear skb based on a single-buffer frame descriptor */ 236462306a36Sopenharmony_cistatic struct sk_buff *dpaa2_switch_build_linear_skb(struct ethsw_core *ethsw, 236562306a36Sopenharmony_ci const struct dpaa2_fd *fd) 236662306a36Sopenharmony_ci{ 236762306a36Sopenharmony_ci u16 fd_offset = dpaa2_fd_get_offset(fd); 236862306a36Sopenharmony_ci dma_addr_t addr = dpaa2_fd_get_addr(fd); 236962306a36Sopenharmony_ci u32 fd_length = dpaa2_fd_get_len(fd); 237062306a36Sopenharmony_ci struct device *dev = ethsw->dev; 237162306a36Sopenharmony_ci struct sk_buff *skb = NULL; 237262306a36Sopenharmony_ci void *fd_vaddr; 237362306a36Sopenharmony_ci 237462306a36Sopenharmony_ci fd_vaddr = dpaa2_iova_to_virt(ethsw->iommu_domain, addr); 237562306a36Sopenharmony_ci dma_unmap_page(dev, addr, DPAA2_SWITCH_RX_BUF_SIZE, 237662306a36Sopenharmony_ci DMA_FROM_DEVICE); 237762306a36Sopenharmony_ci 237862306a36Sopenharmony_ci skb = build_skb(fd_vaddr, DPAA2_SWITCH_RX_BUF_SIZE + 237962306a36Sopenharmony_ci SKB_DATA_ALIGN(sizeof(struct skb_shared_info))); 238062306a36Sopenharmony_ci if (unlikely(!skb)) { 238162306a36Sopenharmony_ci dev_err(dev, "build_skb() failed\n"); 238262306a36Sopenharmony_ci return NULL; 238362306a36Sopenharmony_ci } 238462306a36Sopenharmony_ci 238562306a36Sopenharmony_ci skb_reserve(skb, fd_offset); 238662306a36Sopenharmony_ci skb_put(skb, fd_length); 238762306a36Sopenharmony_ci 238862306a36Sopenharmony_ci ethsw->buf_count--; 238962306a36Sopenharmony_ci 239062306a36Sopenharmony_ci return skb; 239162306a36Sopenharmony_ci} 239262306a36Sopenharmony_ci 239362306a36Sopenharmony_cistatic void dpaa2_switch_tx_conf(struct dpaa2_switch_fq *fq, 239462306a36Sopenharmony_ci const struct dpaa2_fd *fd) 239562306a36Sopenharmony_ci{ 239662306a36Sopenharmony_ci dpaa2_switch_free_fd(fq->ethsw, fd); 239762306a36Sopenharmony_ci} 239862306a36Sopenharmony_ci 239962306a36Sopenharmony_cistatic void dpaa2_switch_rx(struct dpaa2_switch_fq *fq, 240062306a36Sopenharmony_ci const struct dpaa2_fd *fd) 240162306a36Sopenharmony_ci{ 240262306a36Sopenharmony_ci struct ethsw_core *ethsw = fq->ethsw; 240362306a36Sopenharmony_ci struct ethsw_port_priv *port_priv; 240462306a36Sopenharmony_ci struct net_device *netdev; 240562306a36Sopenharmony_ci struct vlan_ethhdr *hdr; 240662306a36Sopenharmony_ci struct sk_buff *skb; 240762306a36Sopenharmony_ci u16 vlan_tci, vid; 240862306a36Sopenharmony_ci int if_id, err; 240962306a36Sopenharmony_ci 241062306a36Sopenharmony_ci /* get switch ingress interface ID */ 241162306a36Sopenharmony_ci if_id = upper_32_bits(dpaa2_fd_get_flc(fd)) & 0x0000FFFF; 241262306a36Sopenharmony_ci 241362306a36Sopenharmony_ci if (if_id >= ethsw->sw_attr.num_ifs) { 241462306a36Sopenharmony_ci dev_err(ethsw->dev, "Frame received from unknown interface!\n"); 241562306a36Sopenharmony_ci goto err_free_fd; 241662306a36Sopenharmony_ci } 241762306a36Sopenharmony_ci port_priv = ethsw->ports[if_id]; 241862306a36Sopenharmony_ci netdev = port_priv->netdev; 241962306a36Sopenharmony_ci 242062306a36Sopenharmony_ci /* build the SKB based on the FD received */ 242162306a36Sopenharmony_ci if (dpaa2_fd_get_format(fd) != dpaa2_fd_single) { 242262306a36Sopenharmony_ci if (net_ratelimit()) { 242362306a36Sopenharmony_ci netdev_err(netdev, "Received invalid frame format\n"); 242462306a36Sopenharmony_ci goto err_free_fd; 242562306a36Sopenharmony_ci } 242662306a36Sopenharmony_ci } 242762306a36Sopenharmony_ci 242862306a36Sopenharmony_ci skb = dpaa2_switch_build_linear_skb(ethsw, fd); 242962306a36Sopenharmony_ci if (unlikely(!skb)) 243062306a36Sopenharmony_ci goto err_free_fd; 243162306a36Sopenharmony_ci 243262306a36Sopenharmony_ci skb_reset_mac_header(skb); 243362306a36Sopenharmony_ci 243462306a36Sopenharmony_ci /* Remove the VLAN header if the packet that we just received has a vid 243562306a36Sopenharmony_ci * equal to the port PVIDs. Since the dpaa2-switch can operate only in 243662306a36Sopenharmony_ci * VLAN-aware mode and no alterations are made on the packet when it's 243762306a36Sopenharmony_ci * redirected/mirrored to the control interface, we are sure that there 243862306a36Sopenharmony_ci * will always be a VLAN header present. 243962306a36Sopenharmony_ci */ 244062306a36Sopenharmony_ci hdr = vlan_eth_hdr(skb); 244162306a36Sopenharmony_ci vid = ntohs(hdr->h_vlan_TCI) & VLAN_VID_MASK; 244262306a36Sopenharmony_ci if (vid == port_priv->pvid) { 244362306a36Sopenharmony_ci err = __skb_vlan_pop(skb, &vlan_tci); 244462306a36Sopenharmony_ci if (err) { 244562306a36Sopenharmony_ci dev_info(ethsw->dev, "__skb_vlan_pop() returned %d", err); 244662306a36Sopenharmony_ci goto err_free_fd; 244762306a36Sopenharmony_ci } 244862306a36Sopenharmony_ci } 244962306a36Sopenharmony_ci 245062306a36Sopenharmony_ci skb->dev = netdev; 245162306a36Sopenharmony_ci skb->protocol = eth_type_trans(skb, skb->dev); 245262306a36Sopenharmony_ci 245362306a36Sopenharmony_ci /* Setup the offload_fwd_mark only if the port is under a bridge */ 245462306a36Sopenharmony_ci skb->offload_fwd_mark = !!(port_priv->fdb->bridge_dev); 245562306a36Sopenharmony_ci 245662306a36Sopenharmony_ci netif_receive_skb(skb); 245762306a36Sopenharmony_ci 245862306a36Sopenharmony_ci return; 245962306a36Sopenharmony_ci 246062306a36Sopenharmony_cierr_free_fd: 246162306a36Sopenharmony_ci dpaa2_switch_free_fd(ethsw, fd); 246262306a36Sopenharmony_ci} 246362306a36Sopenharmony_ci 246462306a36Sopenharmony_cistatic void dpaa2_switch_detect_features(struct ethsw_core *ethsw) 246562306a36Sopenharmony_ci{ 246662306a36Sopenharmony_ci ethsw->features = 0; 246762306a36Sopenharmony_ci 246862306a36Sopenharmony_ci if (ethsw->major > 8 || (ethsw->major == 8 && ethsw->minor >= 6)) 246962306a36Sopenharmony_ci ethsw->features |= ETHSW_FEATURE_MAC_ADDR; 247062306a36Sopenharmony_ci} 247162306a36Sopenharmony_ci 247262306a36Sopenharmony_cistatic int dpaa2_switch_setup_fqs(struct ethsw_core *ethsw) 247362306a36Sopenharmony_ci{ 247462306a36Sopenharmony_ci struct dpsw_ctrl_if_attr ctrl_if_attr; 247562306a36Sopenharmony_ci struct device *dev = ethsw->dev; 247662306a36Sopenharmony_ci int i = 0; 247762306a36Sopenharmony_ci int err; 247862306a36Sopenharmony_ci 247962306a36Sopenharmony_ci err = dpsw_ctrl_if_get_attributes(ethsw->mc_io, 0, ethsw->dpsw_handle, 248062306a36Sopenharmony_ci &ctrl_if_attr); 248162306a36Sopenharmony_ci if (err) { 248262306a36Sopenharmony_ci dev_err(dev, "dpsw_ctrl_if_get_attributes() = %d\n", err); 248362306a36Sopenharmony_ci return err; 248462306a36Sopenharmony_ci } 248562306a36Sopenharmony_ci 248662306a36Sopenharmony_ci ethsw->fq[i].fqid = ctrl_if_attr.rx_fqid; 248762306a36Sopenharmony_ci ethsw->fq[i].ethsw = ethsw; 248862306a36Sopenharmony_ci ethsw->fq[i++].type = DPSW_QUEUE_RX; 248962306a36Sopenharmony_ci 249062306a36Sopenharmony_ci ethsw->fq[i].fqid = ctrl_if_attr.tx_err_conf_fqid; 249162306a36Sopenharmony_ci ethsw->fq[i].ethsw = ethsw; 249262306a36Sopenharmony_ci ethsw->fq[i++].type = DPSW_QUEUE_TX_ERR_CONF; 249362306a36Sopenharmony_ci 249462306a36Sopenharmony_ci return 0; 249562306a36Sopenharmony_ci} 249662306a36Sopenharmony_ci 249762306a36Sopenharmony_ci/* Free buffers acquired from the buffer pool or which were meant to 249862306a36Sopenharmony_ci * be released in the pool 249962306a36Sopenharmony_ci */ 250062306a36Sopenharmony_cistatic void dpaa2_switch_free_bufs(struct ethsw_core *ethsw, u64 *buf_array, int count) 250162306a36Sopenharmony_ci{ 250262306a36Sopenharmony_ci struct device *dev = ethsw->dev; 250362306a36Sopenharmony_ci void *vaddr; 250462306a36Sopenharmony_ci int i; 250562306a36Sopenharmony_ci 250662306a36Sopenharmony_ci for (i = 0; i < count; i++) { 250762306a36Sopenharmony_ci vaddr = dpaa2_iova_to_virt(ethsw->iommu_domain, buf_array[i]); 250862306a36Sopenharmony_ci dma_unmap_page(dev, buf_array[i], DPAA2_SWITCH_RX_BUF_SIZE, 250962306a36Sopenharmony_ci DMA_FROM_DEVICE); 251062306a36Sopenharmony_ci free_pages((unsigned long)vaddr, 0); 251162306a36Sopenharmony_ci } 251262306a36Sopenharmony_ci} 251362306a36Sopenharmony_ci 251462306a36Sopenharmony_ci/* Perform a single release command to add buffers 251562306a36Sopenharmony_ci * to the specified buffer pool 251662306a36Sopenharmony_ci */ 251762306a36Sopenharmony_cistatic int dpaa2_switch_add_bufs(struct ethsw_core *ethsw, u16 bpid) 251862306a36Sopenharmony_ci{ 251962306a36Sopenharmony_ci struct device *dev = ethsw->dev; 252062306a36Sopenharmony_ci u64 buf_array[BUFS_PER_CMD]; 252162306a36Sopenharmony_ci struct page *page; 252262306a36Sopenharmony_ci int retries = 0; 252362306a36Sopenharmony_ci dma_addr_t addr; 252462306a36Sopenharmony_ci int err; 252562306a36Sopenharmony_ci int i; 252662306a36Sopenharmony_ci 252762306a36Sopenharmony_ci for (i = 0; i < BUFS_PER_CMD; i++) { 252862306a36Sopenharmony_ci /* Allocate one page for each Rx buffer. WRIOP sees 252962306a36Sopenharmony_ci * the entire page except for a tailroom reserved for 253062306a36Sopenharmony_ci * skb shared info 253162306a36Sopenharmony_ci */ 253262306a36Sopenharmony_ci page = dev_alloc_pages(0); 253362306a36Sopenharmony_ci if (!page) { 253462306a36Sopenharmony_ci dev_err(dev, "buffer allocation failed\n"); 253562306a36Sopenharmony_ci goto err_alloc; 253662306a36Sopenharmony_ci } 253762306a36Sopenharmony_ci 253862306a36Sopenharmony_ci addr = dma_map_page(dev, page, 0, DPAA2_SWITCH_RX_BUF_SIZE, 253962306a36Sopenharmony_ci DMA_FROM_DEVICE); 254062306a36Sopenharmony_ci if (dma_mapping_error(dev, addr)) { 254162306a36Sopenharmony_ci dev_err(dev, "dma_map_single() failed\n"); 254262306a36Sopenharmony_ci goto err_map; 254362306a36Sopenharmony_ci } 254462306a36Sopenharmony_ci buf_array[i] = addr; 254562306a36Sopenharmony_ci } 254662306a36Sopenharmony_ci 254762306a36Sopenharmony_cirelease_bufs: 254862306a36Sopenharmony_ci /* In case the portal is busy, retry until successful or 254962306a36Sopenharmony_ci * max retries hit. 255062306a36Sopenharmony_ci */ 255162306a36Sopenharmony_ci while ((err = dpaa2_io_service_release(NULL, bpid, 255262306a36Sopenharmony_ci buf_array, i)) == -EBUSY) { 255362306a36Sopenharmony_ci if (retries++ >= DPAA2_SWITCH_SWP_BUSY_RETRIES) 255462306a36Sopenharmony_ci break; 255562306a36Sopenharmony_ci 255662306a36Sopenharmony_ci cpu_relax(); 255762306a36Sopenharmony_ci } 255862306a36Sopenharmony_ci 255962306a36Sopenharmony_ci /* If release command failed, clean up and bail out. */ 256062306a36Sopenharmony_ci if (err) { 256162306a36Sopenharmony_ci dpaa2_switch_free_bufs(ethsw, buf_array, i); 256262306a36Sopenharmony_ci return 0; 256362306a36Sopenharmony_ci } 256462306a36Sopenharmony_ci 256562306a36Sopenharmony_ci return i; 256662306a36Sopenharmony_ci 256762306a36Sopenharmony_cierr_map: 256862306a36Sopenharmony_ci __free_pages(page, 0); 256962306a36Sopenharmony_cierr_alloc: 257062306a36Sopenharmony_ci /* If we managed to allocate at least some buffers, 257162306a36Sopenharmony_ci * release them to hardware 257262306a36Sopenharmony_ci */ 257362306a36Sopenharmony_ci if (i) 257462306a36Sopenharmony_ci goto release_bufs; 257562306a36Sopenharmony_ci 257662306a36Sopenharmony_ci return 0; 257762306a36Sopenharmony_ci} 257862306a36Sopenharmony_ci 257962306a36Sopenharmony_cistatic int dpaa2_switch_refill_bp(struct ethsw_core *ethsw) 258062306a36Sopenharmony_ci{ 258162306a36Sopenharmony_ci int *count = ðsw->buf_count; 258262306a36Sopenharmony_ci int new_count; 258362306a36Sopenharmony_ci int err = 0; 258462306a36Sopenharmony_ci 258562306a36Sopenharmony_ci if (unlikely(*count < DPAA2_ETHSW_REFILL_THRESH)) { 258662306a36Sopenharmony_ci do { 258762306a36Sopenharmony_ci new_count = dpaa2_switch_add_bufs(ethsw, ethsw->bpid); 258862306a36Sopenharmony_ci if (unlikely(!new_count)) { 258962306a36Sopenharmony_ci /* Out of memory; abort for now, we'll 259062306a36Sopenharmony_ci * try later on 259162306a36Sopenharmony_ci */ 259262306a36Sopenharmony_ci break; 259362306a36Sopenharmony_ci } 259462306a36Sopenharmony_ci *count += new_count; 259562306a36Sopenharmony_ci } while (*count < DPAA2_ETHSW_NUM_BUFS); 259662306a36Sopenharmony_ci 259762306a36Sopenharmony_ci if (unlikely(*count < DPAA2_ETHSW_NUM_BUFS)) 259862306a36Sopenharmony_ci err = -ENOMEM; 259962306a36Sopenharmony_ci } 260062306a36Sopenharmony_ci 260162306a36Sopenharmony_ci return err; 260262306a36Sopenharmony_ci} 260362306a36Sopenharmony_ci 260462306a36Sopenharmony_cistatic int dpaa2_switch_seed_bp(struct ethsw_core *ethsw) 260562306a36Sopenharmony_ci{ 260662306a36Sopenharmony_ci int *count, i; 260762306a36Sopenharmony_ci 260862306a36Sopenharmony_ci for (i = 0; i < DPAA2_ETHSW_NUM_BUFS; i += BUFS_PER_CMD) { 260962306a36Sopenharmony_ci count = ðsw->buf_count; 261062306a36Sopenharmony_ci *count += dpaa2_switch_add_bufs(ethsw, ethsw->bpid); 261162306a36Sopenharmony_ci 261262306a36Sopenharmony_ci if (unlikely(*count < BUFS_PER_CMD)) 261362306a36Sopenharmony_ci return -ENOMEM; 261462306a36Sopenharmony_ci } 261562306a36Sopenharmony_ci 261662306a36Sopenharmony_ci return 0; 261762306a36Sopenharmony_ci} 261862306a36Sopenharmony_ci 261962306a36Sopenharmony_cistatic void dpaa2_switch_drain_bp(struct ethsw_core *ethsw) 262062306a36Sopenharmony_ci{ 262162306a36Sopenharmony_ci u64 buf_array[BUFS_PER_CMD]; 262262306a36Sopenharmony_ci int ret; 262362306a36Sopenharmony_ci 262462306a36Sopenharmony_ci do { 262562306a36Sopenharmony_ci ret = dpaa2_io_service_acquire(NULL, ethsw->bpid, 262662306a36Sopenharmony_ci buf_array, BUFS_PER_CMD); 262762306a36Sopenharmony_ci if (ret < 0) { 262862306a36Sopenharmony_ci dev_err(ethsw->dev, 262962306a36Sopenharmony_ci "dpaa2_io_service_acquire() = %d\n", ret); 263062306a36Sopenharmony_ci return; 263162306a36Sopenharmony_ci } 263262306a36Sopenharmony_ci dpaa2_switch_free_bufs(ethsw, buf_array, ret); 263362306a36Sopenharmony_ci 263462306a36Sopenharmony_ci } while (ret); 263562306a36Sopenharmony_ci} 263662306a36Sopenharmony_ci 263762306a36Sopenharmony_cistatic int dpaa2_switch_setup_dpbp(struct ethsw_core *ethsw) 263862306a36Sopenharmony_ci{ 263962306a36Sopenharmony_ci struct dpsw_ctrl_if_pools_cfg dpsw_ctrl_if_pools_cfg = { 0 }; 264062306a36Sopenharmony_ci struct device *dev = ethsw->dev; 264162306a36Sopenharmony_ci struct fsl_mc_device *dpbp_dev; 264262306a36Sopenharmony_ci struct dpbp_attr dpbp_attrs; 264362306a36Sopenharmony_ci int err; 264462306a36Sopenharmony_ci 264562306a36Sopenharmony_ci err = fsl_mc_object_allocate(to_fsl_mc_device(dev), FSL_MC_POOL_DPBP, 264662306a36Sopenharmony_ci &dpbp_dev); 264762306a36Sopenharmony_ci if (err) { 264862306a36Sopenharmony_ci if (err == -ENXIO) 264962306a36Sopenharmony_ci err = -EPROBE_DEFER; 265062306a36Sopenharmony_ci else 265162306a36Sopenharmony_ci dev_err(dev, "DPBP device allocation failed\n"); 265262306a36Sopenharmony_ci return err; 265362306a36Sopenharmony_ci } 265462306a36Sopenharmony_ci ethsw->dpbp_dev = dpbp_dev; 265562306a36Sopenharmony_ci 265662306a36Sopenharmony_ci err = dpbp_open(ethsw->mc_io, 0, dpbp_dev->obj_desc.id, 265762306a36Sopenharmony_ci &dpbp_dev->mc_handle); 265862306a36Sopenharmony_ci if (err) { 265962306a36Sopenharmony_ci dev_err(dev, "dpbp_open() failed\n"); 266062306a36Sopenharmony_ci goto err_open; 266162306a36Sopenharmony_ci } 266262306a36Sopenharmony_ci 266362306a36Sopenharmony_ci err = dpbp_reset(ethsw->mc_io, 0, dpbp_dev->mc_handle); 266462306a36Sopenharmony_ci if (err) { 266562306a36Sopenharmony_ci dev_err(dev, "dpbp_reset() failed\n"); 266662306a36Sopenharmony_ci goto err_reset; 266762306a36Sopenharmony_ci } 266862306a36Sopenharmony_ci 266962306a36Sopenharmony_ci err = dpbp_enable(ethsw->mc_io, 0, dpbp_dev->mc_handle); 267062306a36Sopenharmony_ci if (err) { 267162306a36Sopenharmony_ci dev_err(dev, "dpbp_enable() failed\n"); 267262306a36Sopenharmony_ci goto err_enable; 267362306a36Sopenharmony_ci } 267462306a36Sopenharmony_ci 267562306a36Sopenharmony_ci err = dpbp_get_attributes(ethsw->mc_io, 0, dpbp_dev->mc_handle, 267662306a36Sopenharmony_ci &dpbp_attrs); 267762306a36Sopenharmony_ci if (err) { 267862306a36Sopenharmony_ci dev_err(dev, "dpbp_get_attributes() failed\n"); 267962306a36Sopenharmony_ci goto err_get_attr; 268062306a36Sopenharmony_ci } 268162306a36Sopenharmony_ci 268262306a36Sopenharmony_ci dpsw_ctrl_if_pools_cfg.num_dpbp = 1; 268362306a36Sopenharmony_ci dpsw_ctrl_if_pools_cfg.pools[0].dpbp_id = dpbp_attrs.id; 268462306a36Sopenharmony_ci dpsw_ctrl_if_pools_cfg.pools[0].buffer_size = DPAA2_SWITCH_RX_BUF_SIZE; 268562306a36Sopenharmony_ci dpsw_ctrl_if_pools_cfg.pools[0].backup_pool = 0; 268662306a36Sopenharmony_ci 268762306a36Sopenharmony_ci err = dpsw_ctrl_if_set_pools(ethsw->mc_io, 0, ethsw->dpsw_handle, 268862306a36Sopenharmony_ci &dpsw_ctrl_if_pools_cfg); 268962306a36Sopenharmony_ci if (err) { 269062306a36Sopenharmony_ci dev_err(dev, "dpsw_ctrl_if_set_pools() failed\n"); 269162306a36Sopenharmony_ci goto err_get_attr; 269262306a36Sopenharmony_ci } 269362306a36Sopenharmony_ci ethsw->bpid = dpbp_attrs.id; 269462306a36Sopenharmony_ci 269562306a36Sopenharmony_ci return 0; 269662306a36Sopenharmony_ci 269762306a36Sopenharmony_cierr_get_attr: 269862306a36Sopenharmony_ci dpbp_disable(ethsw->mc_io, 0, dpbp_dev->mc_handle); 269962306a36Sopenharmony_cierr_enable: 270062306a36Sopenharmony_cierr_reset: 270162306a36Sopenharmony_ci dpbp_close(ethsw->mc_io, 0, dpbp_dev->mc_handle); 270262306a36Sopenharmony_cierr_open: 270362306a36Sopenharmony_ci fsl_mc_object_free(dpbp_dev); 270462306a36Sopenharmony_ci return err; 270562306a36Sopenharmony_ci} 270662306a36Sopenharmony_ci 270762306a36Sopenharmony_cistatic void dpaa2_switch_free_dpbp(struct ethsw_core *ethsw) 270862306a36Sopenharmony_ci{ 270962306a36Sopenharmony_ci dpbp_disable(ethsw->mc_io, 0, ethsw->dpbp_dev->mc_handle); 271062306a36Sopenharmony_ci dpbp_close(ethsw->mc_io, 0, ethsw->dpbp_dev->mc_handle); 271162306a36Sopenharmony_ci fsl_mc_object_free(ethsw->dpbp_dev); 271262306a36Sopenharmony_ci} 271362306a36Sopenharmony_ci 271462306a36Sopenharmony_cistatic int dpaa2_switch_alloc_rings(struct ethsw_core *ethsw) 271562306a36Sopenharmony_ci{ 271662306a36Sopenharmony_ci int i; 271762306a36Sopenharmony_ci 271862306a36Sopenharmony_ci for (i = 0; i < DPAA2_SWITCH_RX_NUM_FQS; i++) { 271962306a36Sopenharmony_ci ethsw->fq[i].store = 272062306a36Sopenharmony_ci dpaa2_io_store_create(DPAA2_SWITCH_STORE_SIZE, 272162306a36Sopenharmony_ci ethsw->dev); 272262306a36Sopenharmony_ci if (!ethsw->fq[i].store) { 272362306a36Sopenharmony_ci dev_err(ethsw->dev, "dpaa2_io_store_create failed\n"); 272462306a36Sopenharmony_ci while (--i >= 0) 272562306a36Sopenharmony_ci dpaa2_io_store_destroy(ethsw->fq[i].store); 272662306a36Sopenharmony_ci return -ENOMEM; 272762306a36Sopenharmony_ci } 272862306a36Sopenharmony_ci } 272962306a36Sopenharmony_ci 273062306a36Sopenharmony_ci return 0; 273162306a36Sopenharmony_ci} 273262306a36Sopenharmony_ci 273362306a36Sopenharmony_cistatic void dpaa2_switch_destroy_rings(struct ethsw_core *ethsw) 273462306a36Sopenharmony_ci{ 273562306a36Sopenharmony_ci int i; 273662306a36Sopenharmony_ci 273762306a36Sopenharmony_ci for (i = 0; i < DPAA2_SWITCH_RX_NUM_FQS; i++) 273862306a36Sopenharmony_ci dpaa2_io_store_destroy(ethsw->fq[i].store); 273962306a36Sopenharmony_ci} 274062306a36Sopenharmony_ci 274162306a36Sopenharmony_cistatic int dpaa2_switch_pull_fq(struct dpaa2_switch_fq *fq) 274262306a36Sopenharmony_ci{ 274362306a36Sopenharmony_ci int err, retries = 0; 274462306a36Sopenharmony_ci 274562306a36Sopenharmony_ci /* Try to pull from the FQ while the portal is busy and we didn't hit 274662306a36Sopenharmony_ci * the maximum number fo retries 274762306a36Sopenharmony_ci */ 274862306a36Sopenharmony_ci do { 274962306a36Sopenharmony_ci err = dpaa2_io_service_pull_fq(NULL, fq->fqid, fq->store); 275062306a36Sopenharmony_ci cpu_relax(); 275162306a36Sopenharmony_ci } while (err == -EBUSY && retries++ < DPAA2_SWITCH_SWP_BUSY_RETRIES); 275262306a36Sopenharmony_ci 275362306a36Sopenharmony_ci if (unlikely(err)) 275462306a36Sopenharmony_ci dev_err(fq->ethsw->dev, "dpaa2_io_service_pull err %d", err); 275562306a36Sopenharmony_ci 275662306a36Sopenharmony_ci return err; 275762306a36Sopenharmony_ci} 275862306a36Sopenharmony_ci 275962306a36Sopenharmony_ci/* Consume all frames pull-dequeued into the store */ 276062306a36Sopenharmony_cistatic int dpaa2_switch_store_consume(struct dpaa2_switch_fq *fq) 276162306a36Sopenharmony_ci{ 276262306a36Sopenharmony_ci struct ethsw_core *ethsw = fq->ethsw; 276362306a36Sopenharmony_ci int cleaned = 0, is_last; 276462306a36Sopenharmony_ci struct dpaa2_dq *dq; 276562306a36Sopenharmony_ci int retries = 0; 276662306a36Sopenharmony_ci 276762306a36Sopenharmony_ci do { 276862306a36Sopenharmony_ci /* Get the next available FD from the store */ 276962306a36Sopenharmony_ci dq = dpaa2_io_store_next(fq->store, &is_last); 277062306a36Sopenharmony_ci if (unlikely(!dq)) { 277162306a36Sopenharmony_ci if (retries++ >= DPAA2_SWITCH_SWP_BUSY_RETRIES) { 277262306a36Sopenharmony_ci dev_err_once(ethsw->dev, 277362306a36Sopenharmony_ci "No valid dequeue response\n"); 277462306a36Sopenharmony_ci return -ETIMEDOUT; 277562306a36Sopenharmony_ci } 277662306a36Sopenharmony_ci continue; 277762306a36Sopenharmony_ci } 277862306a36Sopenharmony_ci 277962306a36Sopenharmony_ci if (fq->type == DPSW_QUEUE_RX) 278062306a36Sopenharmony_ci dpaa2_switch_rx(fq, dpaa2_dq_fd(dq)); 278162306a36Sopenharmony_ci else 278262306a36Sopenharmony_ci dpaa2_switch_tx_conf(fq, dpaa2_dq_fd(dq)); 278362306a36Sopenharmony_ci cleaned++; 278462306a36Sopenharmony_ci 278562306a36Sopenharmony_ci } while (!is_last); 278662306a36Sopenharmony_ci 278762306a36Sopenharmony_ci return cleaned; 278862306a36Sopenharmony_ci} 278962306a36Sopenharmony_ci 279062306a36Sopenharmony_ci/* NAPI poll routine */ 279162306a36Sopenharmony_cistatic int dpaa2_switch_poll(struct napi_struct *napi, int budget) 279262306a36Sopenharmony_ci{ 279362306a36Sopenharmony_ci int err, cleaned = 0, store_cleaned, work_done; 279462306a36Sopenharmony_ci struct dpaa2_switch_fq *fq; 279562306a36Sopenharmony_ci int retries = 0; 279662306a36Sopenharmony_ci 279762306a36Sopenharmony_ci fq = container_of(napi, struct dpaa2_switch_fq, napi); 279862306a36Sopenharmony_ci 279962306a36Sopenharmony_ci do { 280062306a36Sopenharmony_ci err = dpaa2_switch_pull_fq(fq); 280162306a36Sopenharmony_ci if (unlikely(err)) 280262306a36Sopenharmony_ci break; 280362306a36Sopenharmony_ci 280462306a36Sopenharmony_ci /* Refill pool if appropriate */ 280562306a36Sopenharmony_ci dpaa2_switch_refill_bp(fq->ethsw); 280662306a36Sopenharmony_ci 280762306a36Sopenharmony_ci store_cleaned = dpaa2_switch_store_consume(fq); 280862306a36Sopenharmony_ci cleaned += store_cleaned; 280962306a36Sopenharmony_ci 281062306a36Sopenharmony_ci if (cleaned >= budget) { 281162306a36Sopenharmony_ci work_done = budget; 281262306a36Sopenharmony_ci goto out; 281362306a36Sopenharmony_ci } 281462306a36Sopenharmony_ci 281562306a36Sopenharmony_ci } while (store_cleaned); 281662306a36Sopenharmony_ci 281762306a36Sopenharmony_ci /* We didn't consume the entire budget, so finish napi and re-enable 281862306a36Sopenharmony_ci * data availability notifications 281962306a36Sopenharmony_ci */ 282062306a36Sopenharmony_ci napi_complete_done(napi, cleaned); 282162306a36Sopenharmony_ci do { 282262306a36Sopenharmony_ci err = dpaa2_io_service_rearm(NULL, &fq->nctx); 282362306a36Sopenharmony_ci cpu_relax(); 282462306a36Sopenharmony_ci } while (err == -EBUSY && retries++ < DPAA2_SWITCH_SWP_BUSY_RETRIES); 282562306a36Sopenharmony_ci 282662306a36Sopenharmony_ci work_done = max(cleaned, 1); 282762306a36Sopenharmony_ciout: 282862306a36Sopenharmony_ci 282962306a36Sopenharmony_ci return work_done; 283062306a36Sopenharmony_ci} 283162306a36Sopenharmony_ci 283262306a36Sopenharmony_cistatic void dpaa2_switch_fqdan_cb(struct dpaa2_io_notification_ctx *nctx) 283362306a36Sopenharmony_ci{ 283462306a36Sopenharmony_ci struct dpaa2_switch_fq *fq; 283562306a36Sopenharmony_ci 283662306a36Sopenharmony_ci fq = container_of(nctx, struct dpaa2_switch_fq, nctx); 283762306a36Sopenharmony_ci 283862306a36Sopenharmony_ci napi_schedule(&fq->napi); 283962306a36Sopenharmony_ci} 284062306a36Sopenharmony_ci 284162306a36Sopenharmony_cistatic int dpaa2_switch_setup_dpio(struct ethsw_core *ethsw) 284262306a36Sopenharmony_ci{ 284362306a36Sopenharmony_ci struct dpsw_ctrl_if_queue_cfg queue_cfg; 284462306a36Sopenharmony_ci struct dpaa2_io_notification_ctx *nctx; 284562306a36Sopenharmony_ci int err, i, j; 284662306a36Sopenharmony_ci 284762306a36Sopenharmony_ci for (i = 0; i < DPAA2_SWITCH_RX_NUM_FQS; i++) { 284862306a36Sopenharmony_ci nctx = ðsw->fq[i].nctx; 284962306a36Sopenharmony_ci 285062306a36Sopenharmony_ci /* Register a new software context for the FQID. 285162306a36Sopenharmony_ci * By using NULL as the first parameter, we specify that we do 285262306a36Sopenharmony_ci * not care on which cpu are interrupts received for this queue 285362306a36Sopenharmony_ci */ 285462306a36Sopenharmony_ci nctx->is_cdan = 0; 285562306a36Sopenharmony_ci nctx->id = ethsw->fq[i].fqid; 285662306a36Sopenharmony_ci nctx->desired_cpu = DPAA2_IO_ANY_CPU; 285762306a36Sopenharmony_ci nctx->cb = dpaa2_switch_fqdan_cb; 285862306a36Sopenharmony_ci err = dpaa2_io_service_register(NULL, nctx, ethsw->dev); 285962306a36Sopenharmony_ci if (err) { 286062306a36Sopenharmony_ci err = -EPROBE_DEFER; 286162306a36Sopenharmony_ci goto err_register; 286262306a36Sopenharmony_ci } 286362306a36Sopenharmony_ci 286462306a36Sopenharmony_ci queue_cfg.options = DPSW_CTRL_IF_QUEUE_OPT_DEST | 286562306a36Sopenharmony_ci DPSW_CTRL_IF_QUEUE_OPT_USER_CTX; 286662306a36Sopenharmony_ci queue_cfg.dest_cfg.dest_type = DPSW_CTRL_IF_DEST_DPIO; 286762306a36Sopenharmony_ci queue_cfg.dest_cfg.dest_id = nctx->dpio_id; 286862306a36Sopenharmony_ci queue_cfg.dest_cfg.priority = 0; 286962306a36Sopenharmony_ci queue_cfg.user_ctx = nctx->qman64; 287062306a36Sopenharmony_ci 287162306a36Sopenharmony_ci err = dpsw_ctrl_if_set_queue(ethsw->mc_io, 0, 287262306a36Sopenharmony_ci ethsw->dpsw_handle, 287362306a36Sopenharmony_ci ethsw->fq[i].type, 287462306a36Sopenharmony_ci &queue_cfg); 287562306a36Sopenharmony_ci if (err) 287662306a36Sopenharmony_ci goto err_set_queue; 287762306a36Sopenharmony_ci } 287862306a36Sopenharmony_ci 287962306a36Sopenharmony_ci return 0; 288062306a36Sopenharmony_ci 288162306a36Sopenharmony_cierr_set_queue: 288262306a36Sopenharmony_ci dpaa2_io_service_deregister(NULL, nctx, ethsw->dev); 288362306a36Sopenharmony_cierr_register: 288462306a36Sopenharmony_ci for (j = 0; j < i; j++) 288562306a36Sopenharmony_ci dpaa2_io_service_deregister(NULL, ðsw->fq[j].nctx, 288662306a36Sopenharmony_ci ethsw->dev); 288762306a36Sopenharmony_ci 288862306a36Sopenharmony_ci return err; 288962306a36Sopenharmony_ci} 289062306a36Sopenharmony_ci 289162306a36Sopenharmony_cistatic void dpaa2_switch_free_dpio(struct ethsw_core *ethsw) 289262306a36Sopenharmony_ci{ 289362306a36Sopenharmony_ci int i; 289462306a36Sopenharmony_ci 289562306a36Sopenharmony_ci for (i = 0; i < DPAA2_SWITCH_RX_NUM_FQS; i++) 289662306a36Sopenharmony_ci dpaa2_io_service_deregister(NULL, ðsw->fq[i].nctx, 289762306a36Sopenharmony_ci ethsw->dev); 289862306a36Sopenharmony_ci} 289962306a36Sopenharmony_ci 290062306a36Sopenharmony_cistatic int dpaa2_switch_ctrl_if_setup(struct ethsw_core *ethsw) 290162306a36Sopenharmony_ci{ 290262306a36Sopenharmony_ci int err; 290362306a36Sopenharmony_ci 290462306a36Sopenharmony_ci /* setup FQs for Rx and Tx Conf */ 290562306a36Sopenharmony_ci err = dpaa2_switch_setup_fqs(ethsw); 290662306a36Sopenharmony_ci if (err) 290762306a36Sopenharmony_ci return err; 290862306a36Sopenharmony_ci 290962306a36Sopenharmony_ci /* setup the buffer pool needed on the Rx path */ 291062306a36Sopenharmony_ci err = dpaa2_switch_setup_dpbp(ethsw); 291162306a36Sopenharmony_ci if (err) 291262306a36Sopenharmony_ci return err; 291362306a36Sopenharmony_ci 291462306a36Sopenharmony_ci err = dpaa2_switch_alloc_rings(ethsw); 291562306a36Sopenharmony_ci if (err) 291662306a36Sopenharmony_ci goto err_free_dpbp; 291762306a36Sopenharmony_ci 291862306a36Sopenharmony_ci err = dpaa2_switch_setup_dpio(ethsw); 291962306a36Sopenharmony_ci if (err) 292062306a36Sopenharmony_ci goto err_destroy_rings; 292162306a36Sopenharmony_ci 292262306a36Sopenharmony_ci err = dpaa2_switch_seed_bp(ethsw); 292362306a36Sopenharmony_ci if (err) 292462306a36Sopenharmony_ci goto err_deregister_dpio; 292562306a36Sopenharmony_ci 292662306a36Sopenharmony_ci err = dpsw_ctrl_if_enable(ethsw->mc_io, 0, ethsw->dpsw_handle); 292762306a36Sopenharmony_ci if (err) { 292862306a36Sopenharmony_ci dev_err(ethsw->dev, "dpsw_ctrl_if_enable err %d\n", err); 292962306a36Sopenharmony_ci goto err_drain_dpbp; 293062306a36Sopenharmony_ci } 293162306a36Sopenharmony_ci 293262306a36Sopenharmony_ci return 0; 293362306a36Sopenharmony_ci 293462306a36Sopenharmony_cierr_drain_dpbp: 293562306a36Sopenharmony_ci dpaa2_switch_drain_bp(ethsw); 293662306a36Sopenharmony_cierr_deregister_dpio: 293762306a36Sopenharmony_ci dpaa2_switch_free_dpio(ethsw); 293862306a36Sopenharmony_cierr_destroy_rings: 293962306a36Sopenharmony_ci dpaa2_switch_destroy_rings(ethsw); 294062306a36Sopenharmony_cierr_free_dpbp: 294162306a36Sopenharmony_ci dpaa2_switch_free_dpbp(ethsw); 294262306a36Sopenharmony_ci 294362306a36Sopenharmony_ci return err; 294462306a36Sopenharmony_ci} 294562306a36Sopenharmony_ci 294662306a36Sopenharmony_cistatic void dpaa2_switch_remove_port(struct ethsw_core *ethsw, 294762306a36Sopenharmony_ci u16 port_idx) 294862306a36Sopenharmony_ci{ 294962306a36Sopenharmony_ci struct ethsw_port_priv *port_priv = ethsw->ports[port_idx]; 295062306a36Sopenharmony_ci 295162306a36Sopenharmony_ci dpaa2_switch_port_disconnect_mac(port_priv); 295262306a36Sopenharmony_ci free_netdev(port_priv->netdev); 295362306a36Sopenharmony_ci ethsw->ports[port_idx] = NULL; 295462306a36Sopenharmony_ci} 295562306a36Sopenharmony_ci 295662306a36Sopenharmony_cistatic int dpaa2_switch_init(struct fsl_mc_device *sw_dev) 295762306a36Sopenharmony_ci{ 295862306a36Sopenharmony_ci struct device *dev = &sw_dev->dev; 295962306a36Sopenharmony_ci struct ethsw_core *ethsw = dev_get_drvdata(dev); 296062306a36Sopenharmony_ci struct dpsw_vlan_if_cfg vcfg = {0}; 296162306a36Sopenharmony_ci struct dpsw_tci_cfg tci_cfg = {0}; 296262306a36Sopenharmony_ci struct dpsw_stp_cfg stp_cfg; 296362306a36Sopenharmony_ci int err; 296462306a36Sopenharmony_ci u16 i; 296562306a36Sopenharmony_ci 296662306a36Sopenharmony_ci ethsw->dev_id = sw_dev->obj_desc.id; 296762306a36Sopenharmony_ci 296862306a36Sopenharmony_ci err = dpsw_open(ethsw->mc_io, 0, ethsw->dev_id, ðsw->dpsw_handle); 296962306a36Sopenharmony_ci if (err) { 297062306a36Sopenharmony_ci dev_err(dev, "dpsw_open err %d\n", err); 297162306a36Sopenharmony_ci return err; 297262306a36Sopenharmony_ci } 297362306a36Sopenharmony_ci 297462306a36Sopenharmony_ci err = dpsw_get_attributes(ethsw->mc_io, 0, ethsw->dpsw_handle, 297562306a36Sopenharmony_ci ðsw->sw_attr); 297662306a36Sopenharmony_ci if (err) { 297762306a36Sopenharmony_ci dev_err(dev, "dpsw_get_attributes err %d\n", err); 297862306a36Sopenharmony_ci goto err_close; 297962306a36Sopenharmony_ci } 298062306a36Sopenharmony_ci 298162306a36Sopenharmony_ci err = dpsw_get_api_version(ethsw->mc_io, 0, 298262306a36Sopenharmony_ci ðsw->major, 298362306a36Sopenharmony_ci ðsw->minor); 298462306a36Sopenharmony_ci if (err) { 298562306a36Sopenharmony_ci dev_err(dev, "dpsw_get_api_version err %d\n", err); 298662306a36Sopenharmony_ci goto err_close; 298762306a36Sopenharmony_ci } 298862306a36Sopenharmony_ci 298962306a36Sopenharmony_ci /* Minimum supported DPSW version check */ 299062306a36Sopenharmony_ci if (ethsw->major < DPSW_MIN_VER_MAJOR || 299162306a36Sopenharmony_ci (ethsw->major == DPSW_MIN_VER_MAJOR && 299262306a36Sopenharmony_ci ethsw->minor < DPSW_MIN_VER_MINOR)) { 299362306a36Sopenharmony_ci dev_err(dev, "DPSW version %d:%d not supported. Use firmware 10.28.0 or greater.\n", 299462306a36Sopenharmony_ci ethsw->major, ethsw->minor); 299562306a36Sopenharmony_ci err = -EOPNOTSUPP; 299662306a36Sopenharmony_ci goto err_close; 299762306a36Sopenharmony_ci } 299862306a36Sopenharmony_ci 299962306a36Sopenharmony_ci if (!dpaa2_switch_supports_cpu_traffic(ethsw)) { 300062306a36Sopenharmony_ci err = -EOPNOTSUPP; 300162306a36Sopenharmony_ci goto err_close; 300262306a36Sopenharmony_ci } 300362306a36Sopenharmony_ci 300462306a36Sopenharmony_ci dpaa2_switch_detect_features(ethsw); 300562306a36Sopenharmony_ci 300662306a36Sopenharmony_ci err = dpsw_reset(ethsw->mc_io, 0, ethsw->dpsw_handle); 300762306a36Sopenharmony_ci if (err) { 300862306a36Sopenharmony_ci dev_err(dev, "dpsw_reset err %d\n", err); 300962306a36Sopenharmony_ci goto err_close; 301062306a36Sopenharmony_ci } 301162306a36Sopenharmony_ci 301262306a36Sopenharmony_ci stp_cfg.vlan_id = DEFAULT_VLAN_ID; 301362306a36Sopenharmony_ci stp_cfg.state = DPSW_STP_STATE_FORWARDING; 301462306a36Sopenharmony_ci 301562306a36Sopenharmony_ci for (i = 0; i < ethsw->sw_attr.num_ifs; i++) { 301662306a36Sopenharmony_ci err = dpsw_if_disable(ethsw->mc_io, 0, ethsw->dpsw_handle, i); 301762306a36Sopenharmony_ci if (err) { 301862306a36Sopenharmony_ci dev_err(dev, "dpsw_if_disable err %d\n", err); 301962306a36Sopenharmony_ci goto err_close; 302062306a36Sopenharmony_ci } 302162306a36Sopenharmony_ci 302262306a36Sopenharmony_ci err = dpsw_if_set_stp(ethsw->mc_io, 0, ethsw->dpsw_handle, i, 302362306a36Sopenharmony_ci &stp_cfg); 302462306a36Sopenharmony_ci if (err) { 302562306a36Sopenharmony_ci dev_err(dev, "dpsw_if_set_stp err %d for port %d\n", 302662306a36Sopenharmony_ci err, i); 302762306a36Sopenharmony_ci goto err_close; 302862306a36Sopenharmony_ci } 302962306a36Sopenharmony_ci 303062306a36Sopenharmony_ci /* Switch starts with all ports configured to VLAN 1. Need to 303162306a36Sopenharmony_ci * remove this setting to allow configuration at bridge join 303262306a36Sopenharmony_ci */ 303362306a36Sopenharmony_ci vcfg.num_ifs = 1; 303462306a36Sopenharmony_ci vcfg.if_id[0] = i; 303562306a36Sopenharmony_ci err = dpsw_vlan_remove_if_untagged(ethsw->mc_io, 0, ethsw->dpsw_handle, 303662306a36Sopenharmony_ci DEFAULT_VLAN_ID, &vcfg); 303762306a36Sopenharmony_ci if (err) { 303862306a36Sopenharmony_ci dev_err(dev, "dpsw_vlan_remove_if_untagged err %d\n", 303962306a36Sopenharmony_ci err); 304062306a36Sopenharmony_ci goto err_close; 304162306a36Sopenharmony_ci } 304262306a36Sopenharmony_ci 304362306a36Sopenharmony_ci tci_cfg.vlan_id = 4095; 304462306a36Sopenharmony_ci err = dpsw_if_set_tci(ethsw->mc_io, 0, ethsw->dpsw_handle, i, &tci_cfg); 304562306a36Sopenharmony_ci if (err) { 304662306a36Sopenharmony_ci dev_err(dev, "dpsw_if_set_tci err %d\n", err); 304762306a36Sopenharmony_ci goto err_close; 304862306a36Sopenharmony_ci } 304962306a36Sopenharmony_ci 305062306a36Sopenharmony_ci err = dpsw_vlan_remove_if(ethsw->mc_io, 0, ethsw->dpsw_handle, 305162306a36Sopenharmony_ci DEFAULT_VLAN_ID, &vcfg); 305262306a36Sopenharmony_ci if (err) { 305362306a36Sopenharmony_ci dev_err(dev, "dpsw_vlan_remove_if err %d\n", err); 305462306a36Sopenharmony_ci goto err_close; 305562306a36Sopenharmony_ci } 305662306a36Sopenharmony_ci } 305762306a36Sopenharmony_ci 305862306a36Sopenharmony_ci err = dpsw_vlan_remove(ethsw->mc_io, 0, ethsw->dpsw_handle, DEFAULT_VLAN_ID); 305962306a36Sopenharmony_ci if (err) { 306062306a36Sopenharmony_ci dev_err(dev, "dpsw_vlan_remove err %d\n", err); 306162306a36Sopenharmony_ci goto err_close; 306262306a36Sopenharmony_ci } 306362306a36Sopenharmony_ci 306462306a36Sopenharmony_ci ethsw->workqueue = alloc_ordered_workqueue("%s_%d_ordered", 306562306a36Sopenharmony_ci WQ_MEM_RECLAIM, "ethsw", 306662306a36Sopenharmony_ci ethsw->sw_attr.id); 306762306a36Sopenharmony_ci if (!ethsw->workqueue) { 306862306a36Sopenharmony_ci err = -ENOMEM; 306962306a36Sopenharmony_ci goto err_close; 307062306a36Sopenharmony_ci } 307162306a36Sopenharmony_ci 307262306a36Sopenharmony_ci err = dpsw_fdb_remove(ethsw->mc_io, 0, ethsw->dpsw_handle, 0); 307362306a36Sopenharmony_ci if (err) 307462306a36Sopenharmony_ci goto err_destroy_ordered_workqueue; 307562306a36Sopenharmony_ci 307662306a36Sopenharmony_ci err = dpaa2_switch_ctrl_if_setup(ethsw); 307762306a36Sopenharmony_ci if (err) 307862306a36Sopenharmony_ci goto err_destroy_ordered_workqueue; 307962306a36Sopenharmony_ci 308062306a36Sopenharmony_ci return 0; 308162306a36Sopenharmony_ci 308262306a36Sopenharmony_cierr_destroy_ordered_workqueue: 308362306a36Sopenharmony_ci destroy_workqueue(ethsw->workqueue); 308462306a36Sopenharmony_ci 308562306a36Sopenharmony_cierr_close: 308662306a36Sopenharmony_ci dpsw_close(ethsw->mc_io, 0, ethsw->dpsw_handle); 308762306a36Sopenharmony_ci return err; 308862306a36Sopenharmony_ci} 308962306a36Sopenharmony_ci 309062306a36Sopenharmony_ci/* Add an ACL to redirect frames with specific destination MAC address to 309162306a36Sopenharmony_ci * control interface 309262306a36Sopenharmony_ci */ 309362306a36Sopenharmony_cistatic int dpaa2_switch_port_trap_mac_addr(struct ethsw_port_priv *port_priv, 309462306a36Sopenharmony_ci const char *mac) 309562306a36Sopenharmony_ci{ 309662306a36Sopenharmony_ci struct dpaa2_switch_acl_entry acl_entry = {0}; 309762306a36Sopenharmony_ci 309862306a36Sopenharmony_ci /* Match on the destination MAC address */ 309962306a36Sopenharmony_ci ether_addr_copy(acl_entry.key.match.l2_dest_mac, mac); 310062306a36Sopenharmony_ci eth_broadcast_addr(acl_entry.key.mask.l2_dest_mac); 310162306a36Sopenharmony_ci 310262306a36Sopenharmony_ci /* Trap to CPU */ 310362306a36Sopenharmony_ci acl_entry.cfg.precedence = 0; 310462306a36Sopenharmony_ci acl_entry.cfg.result.action = DPSW_ACL_ACTION_REDIRECT_TO_CTRL_IF; 310562306a36Sopenharmony_ci 310662306a36Sopenharmony_ci return dpaa2_switch_acl_entry_add(port_priv->filter_block, &acl_entry); 310762306a36Sopenharmony_ci} 310862306a36Sopenharmony_ci 310962306a36Sopenharmony_cistatic int dpaa2_switch_port_init(struct ethsw_port_priv *port_priv, u16 port) 311062306a36Sopenharmony_ci{ 311162306a36Sopenharmony_ci const char stpa[ETH_ALEN] = {0x01, 0x80, 0xc2, 0x00, 0x00, 0x00}; 311262306a36Sopenharmony_ci struct switchdev_obj_port_vlan vlan = { 311362306a36Sopenharmony_ci .obj.id = SWITCHDEV_OBJ_ID_PORT_VLAN, 311462306a36Sopenharmony_ci .vid = DEFAULT_VLAN_ID, 311562306a36Sopenharmony_ci .flags = BRIDGE_VLAN_INFO_UNTAGGED | BRIDGE_VLAN_INFO_PVID, 311662306a36Sopenharmony_ci }; 311762306a36Sopenharmony_ci struct net_device *netdev = port_priv->netdev; 311862306a36Sopenharmony_ci struct ethsw_core *ethsw = port_priv->ethsw_data; 311962306a36Sopenharmony_ci struct dpaa2_switch_filter_block *filter_block; 312062306a36Sopenharmony_ci struct dpsw_fdb_cfg fdb_cfg = {0}; 312162306a36Sopenharmony_ci struct dpsw_if_attr dpsw_if_attr; 312262306a36Sopenharmony_ci struct dpaa2_switch_fdb *fdb; 312362306a36Sopenharmony_ci struct dpsw_acl_cfg acl_cfg; 312462306a36Sopenharmony_ci u16 fdb_id, acl_tbl_id; 312562306a36Sopenharmony_ci int err; 312662306a36Sopenharmony_ci 312762306a36Sopenharmony_ci /* Get the Tx queue for this specific port */ 312862306a36Sopenharmony_ci err = dpsw_if_get_attributes(ethsw->mc_io, 0, ethsw->dpsw_handle, 312962306a36Sopenharmony_ci port_priv->idx, &dpsw_if_attr); 313062306a36Sopenharmony_ci if (err) { 313162306a36Sopenharmony_ci netdev_err(netdev, "dpsw_if_get_attributes err %d\n", err); 313262306a36Sopenharmony_ci return err; 313362306a36Sopenharmony_ci } 313462306a36Sopenharmony_ci port_priv->tx_qdid = dpsw_if_attr.qdid; 313562306a36Sopenharmony_ci 313662306a36Sopenharmony_ci /* Create a FDB table for this particular switch port */ 313762306a36Sopenharmony_ci fdb_cfg.num_fdb_entries = ethsw->sw_attr.max_fdb_entries / ethsw->sw_attr.num_ifs; 313862306a36Sopenharmony_ci err = dpsw_fdb_add(ethsw->mc_io, 0, ethsw->dpsw_handle, 313962306a36Sopenharmony_ci &fdb_id, &fdb_cfg); 314062306a36Sopenharmony_ci if (err) { 314162306a36Sopenharmony_ci netdev_err(netdev, "dpsw_fdb_add err %d\n", err); 314262306a36Sopenharmony_ci return err; 314362306a36Sopenharmony_ci } 314462306a36Sopenharmony_ci 314562306a36Sopenharmony_ci /* Find an unused dpaa2_switch_fdb structure and use it */ 314662306a36Sopenharmony_ci fdb = dpaa2_switch_fdb_get_unused(ethsw); 314762306a36Sopenharmony_ci fdb->fdb_id = fdb_id; 314862306a36Sopenharmony_ci fdb->in_use = true; 314962306a36Sopenharmony_ci fdb->bridge_dev = NULL; 315062306a36Sopenharmony_ci port_priv->fdb = fdb; 315162306a36Sopenharmony_ci 315262306a36Sopenharmony_ci /* We need to add VLAN 1 as the PVID on this port until it is under a 315362306a36Sopenharmony_ci * bridge since the DPAA2 switch is not able to handle the traffic in a 315462306a36Sopenharmony_ci * VLAN unaware fashion 315562306a36Sopenharmony_ci */ 315662306a36Sopenharmony_ci err = dpaa2_switch_port_vlans_add(netdev, &vlan); 315762306a36Sopenharmony_ci if (err) 315862306a36Sopenharmony_ci return err; 315962306a36Sopenharmony_ci 316062306a36Sopenharmony_ci /* Setup the egress flooding domains (broadcast, unknown unicast */ 316162306a36Sopenharmony_ci err = dpaa2_switch_fdb_set_egress_flood(ethsw, port_priv->fdb->fdb_id); 316262306a36Sopenharmony_ci if (err) 316362306a36Sopenharmony_ci return err; 316462306a36Sopenharmony_ci 316562306a36Sopenharmony_ci /* Create an ACL table to be used by this switch port */ 316662306a36Sopenharmony_ci acl_cfg.max_entries = DPAA2_ETHSW_PORT_MAX_ACL_ENTRIES; 316762306a36Sopenharmony_ci err = dpsw_acl_add(ethsw->mc_io, 0, ethsw->dpsw_handle, 316862306a36Sopenharmony_ci &acl_tbl_id, &acl_cfg); 316962306a36Sopenharmony_ci if (err) { 317062306a36Sopenharmony_ci netdev_err(netdev, "dpsw_acl_add err %d\n", err); 317162306a36Sopenharmony_ci return err; 317262306a36Sopenharmony_ci } 317362306a36Sopenharmony_ci 317462306a36Sopenharmony_ci filter_block = dpaa2_switch_filter_block_get_unused(ethsw); 317562306a36Sopenharmony_ci filter_block->ethsw = ethsw; 317662306a36Sopenharmony_ci filter_block->acl_id = acl_tbl_id; 317762306a36Sopenharmony_ci filter_block->in_use = true; 317862306a36Sopenharmony_ci filter_block->num_acl_rules = 0; 317962306a36Sopenharmony_ci INIT_LIST_HEAD(&filter_block->acl_entries); 318062306a36Sopenharmony_ci INIT_LIST_HEAD(&filter_block->mirror_entries); 318162306a36Sopenharmony_ci 318262306a36Sopenharmony_ci err = dpaa2_switch_port_acl_tbl_bind(port_priv, filter_block); 318362306a36Sopenharmony_ci if (err) 318462306a36Sopenharmony_ci return err; 318562306a36Sopenharmony_ci 318662306a36Sopenharmony_ci err = dpaa2_switch_port_trap_mac_addr(port_priv, stpa); 318762306a36Sopenharmony_ci if (err) 318862306a36Sopenharmony_ci return err; 318962306a36Sopenharmony_ci 319062306a36Sopenharmony_ci return err; 319162306a36Sopenharmony_ci} 319262306a36Sopenharmony_ci 319362306a36Sopenharmony_cistatic void dpaa2_switch_ctrl_if_teardown(struct ethsw_core *ethsw) 319462306a36Sopenharmony_ci{ 319562306a36Sopenharmony_ci dpsw_ctrl_if_disable(ethsw->mc_io, 0, ethsw->dpsw_handle); 319662306a36Sopenharmony_ci dpaa2_switch_free_dpio(ethsw); 319762306a36Sopenharmony_ci dpaa2_switch_destroy_rings(ethsw); 319862306a36Sopenharmony_ci dpaa2_switch_drain_bp(ethsw); 319962306a36Sopenharmony_ci dpaa2_switch_free_dpbp(ethsw); 320062306a36Sopenharmony_ci} 320162306a36Sopenharmony_ci 320262306a36Sopenharmony_cistatic void dpaa2_switch_teardown(struct fsl_mc_device *sw_dev) 320362306a36Sopenharmony_ci{ 320462306a36Sopenharmony_ci struct device *dev = &sw_dev->dev; 320562306a36Sopenharmony_ci struct ethsw_core *ethsw = dev_get_drvdata(dev); 320662306a36Sopenharmony_ci int err; 320762306a36Sopenharmony_ci 320862306a36Sopenharmony_ci dpaa2_switch_ctrl_if_teardown(ethsw); 320962306a36Sopenharmony_ci 321062306a36Sopenharmony_ci destroy_workqueue(ethsw->workqueue); 321162306a36Sopenharmony_ci 321262306a36Sopenharmony_ci err = dpsw_close(ethsw->mc_io, 0, ethsw->dpsw_handle); 321362306a36Sopenharmony_ci if (err) 321462306a36Sopenharmony_ci dev_warn(dev, "dpsw_close err %d\n", err); 321562306a36Sopenharmony_ci} 321662306a36Sopenharmony_ci 321762306a36Sopenharmony_cistatic void dpaa2_switch_remove(struct fsl_mc_device *sw_dev) 321862306a36Sopenharmony_ci{ 321962306a36Sopenharmony_ci struct ethsw_port_priv *port_priv; 322062306a36Sopenharmony_ci struct ethsw_core *ethsw; 322162306a36Sopenharmony_ci struct device *dev; 322262306a36Sopenharmony_ci int i; 322362306a36Sopenharmony_ci 322462306a36Sopenharmony_ci dev = &sw_dev->dev; 322562306a36Sopenharmony_ci ethsw = dev_get_drvdata(dev); 322662306a36Sopenharmony_ci 322762306a36Sopenharmony_ci dpaa2_switch_teardown_irqs(sw_dev); 322862306a36Sopenharmony_ci 322962306a36Sopenharmony_ci dpsw_disable(ethsw->mc_io, 0, ethsw->dpsw_handle); 323062306a36Sopenharmony_ci 323162306a36Sopenharmony_ci for (i = 0; i < ethsw->sw_attr.num_ifs; i++) { 323262306a36Sopenharmony_ci port_priv = ethsw->ports[i]; 323362306a36Sopenharmony_ci unregister_netdev(port_priv->netdev); 323462306a36Sopenharmony_ci dpaa2_switch_remove_port(ethsw, i); 323562306a36Sopenharmony_ci } 323662306a36Sopenharmony_ci 323762306a36Sopenharmony_ci kfree(ethsw->fdbs); 323862306a36Sopenharmony_ci kfree(ethsw->filter_blocks); 323962306a36Sopenharmony_ci kfree(ethsw->ports); 324062306a36Sopenharmony_ci 324162306a36Sopenharmony_ci dpaa2_switch_teardown(sw_dev); 324262306a36Sopenharmony_ci 324362306a36Sopenharmony_ci fsl_mc_portal_free(ethsw->mc_io); 324462306a36Sopenharmony_ci 324562306a36Sopenharmony_ci kfree(ethsw); 324662306a36Sopenharmony_ci 324762306a36Sopenharmony_ci dev_set_drvdata(dev, NULL); 324862306a36Sopenharmony_ci} 324962306a36Sopenharmony_ci 325062306a36Sopenharmony_cistatic int dpaa2_switch_probe_port(struct ethsw_core *ethsw, 325162306a36Sopenharmony_ci u16 port_idx) 325262306a36Sopenharmony_ci{ 325362306a36Sopenharmony_ci struct ethsw_port_priv *port_priv; 325462306a36Sopenharmony_ci struct device *dev = ethsw->dev; 325562306a36Sopenharmony_ci struct net_device *port_netdev; 325662306a36Sopenharmony_ci int err; 325762306a36Sopenharmony_ci 325862306a36Sopenharmony_ci port_netdev = alloc_etherdev(sizeof(struct ethsw_port_priv)); 325962306a36Sopenharmony_ci if (!port_netdev) { 326062306a36Sopenharmony_ci dev_err(dev, "alloc_etherdev error\n"); 326162306a36Sopenharmony_ci return -ENOMEM; 326262306a36Sopenharmony_ci } 326362306a36Sopenharmony_ci 326462306a36Sopenharmony_ci port_priv = netdev_priv(port_netdev); 326562306a36Sopenharmony_ci port_priv->netdev = port_netdev; 326662306a36Sopenharmony_ci port_priv->ethsw_data = ethsw; 326762306a36Sopenharmony_ci 326862306a36Sopenharmony_ci mutex_init(&port_priv->mac_lock); 326962306a36Sopenharmony_ci 327062306a36Sopenharmony_ci port_priv->idx = port_idx; 327162306a36Sopenharmony_ci port_priv->stp_state = BR_STATE_FORWARDING; 327262306a36Sopenharmony_ci 327362306a36Sopenharmony_ci SET_NETDEV_DEV(port_netdev, dev); 327462306a36Sopenharmony_ci port_netdev->netdev_ops = &dpaa2_switch_port_ops; 327562306a36Sopenharmony_ci port_netdev->ethtool_ops = &dpaa2_switch_port_ethtool_ops; 327662306a36Sopenharmony_ci 327762306a36Sopenharmony_ci port_netdev->needed_headroom = DPAA2_SWITCH_NEEDED_HEADROOM; 327862306a36Sopenharmony_ci 327962306a36Sopenharmony_ci port_priv->bcast_flood = true; 328062306a36Sopenharmony_ci port_priv->ucast_flood = true; 328162306a36Sopenharmony_ci 328262306a36Sopenharmony_ci /* Set MTU limits */ 328362306a36Sopenharmony_ci port_netdev->min_mtu = ETH_MIN_MTU; 328462306a36Sopenharmony_ci port_netdev->max_mtu = ETHSW_MAX_FRAME_LENGTH; 328562306a36Sopenharmony_ci 328662306a36Sopenharmony_ci /* Populate the private port structure so that later calls to 328762306a36Sopenharmony_ci * dpaa2_switch_port_init() can use it. 328862306a36Sopenharmony_ci */ 328962306a36Sopenharmony_ci ethsw->ports[port_idx] = port_priv; 329062306a36Sopenharmony_ci 329162306a36Sopenharmony_ci /* The DPAA2 switch's ingress path depends on the VLAN table, 329262306a36Sopenharmony_ci * thus we are not able to disable VLAN filtering. 329362306a36Sopenharmony_ci */ 329462306a36Sopenharmony_ci port_netdev->features = NETIF_F_HW_VLAN_CTAG_FILTER | 329562306a36Sopenharmony_ci NETIF_F_HW_VLAN_STAG_FILTER | 329662306a36Sopenharmony_ci NETIF_F_HW_TC; 329762306a36Sopenharmony_ci 329862306a36Sopenharmony_ci err = dpaa2_switch_port_init(port_priv, port_idx); 329962306a36Sopenharmony_ci if (err) 330062306a36Sopenharmony_ci goto err_port_probe; 330162306a36Sopenharmony_ci 330262306a36Sopenharmony_ci err = dpaa2_switch_port_set_mac_addr(port_priv); 330362306a36Sopenharmony_ci if (err) 330462306a36Sopenharmony_ci goto err_port_probe; 330562306a36Sopenharmony_ci 330662306a36Sopenharmony_ci err = dpaa2_switch_port_set_learning(port_priv, false); 330762306a36Sopenharmony_ci if (err) 330862306a36Sopenharmony_ci goto err_port_probe; 330962306a36Sopenharmony_ci port_priv->learn_ena = false; 331062306a36Sopenharmony_ci 331162306a36Sopenharmony_ci err = dpaa2_switch_port_connect_mac(port_priv); 331262306a36Sopenharmony_ci if (err) 331362306a36Sopenharmony_ci goto err_port_probe; 331462306a36Sopenharmony_ci 331562306a36Sopenharmony_ci return 0; 331662306a36Sopenharmony_ci 331762306a36Sopenharmony_cierr_port_probe: 331862306a36Sopenharmony_ci free_netdev(port_netdev); 331962306a36Sopenharmony_ci ethsw->ports[port_idx] = NULL; 332062306a36Sopenharmony_ci 332162306a36Sopenharmony_ci return err; 332262306a36Sopenharmony_ci} 332362306a36Sopenharmony_ci 332462306a36Sopenharmony_cistatic int dpaa2_switch_probe(struct fsl_mc_device *sw_dev) 332562306a36Sopenharmony_ci{ 332662306a36Sopenharmony_ci struct device *dev = &sw_dev->dev; 332762306a36Sopenharmony_ci struct ethsw_core *ethsw; 332862306a36Sopenharmony_ci int i, err; 332962306a36Sopenharmony_ci 333062306a36Sopenharmony_ci /* Allocate switch core*/ 333162306a36Sopenharmony_ci ethsw = kzalloc(sizeof(*ethsw), GFP_KERNEL); 333262306a36Sopenharmony_ci 333362306a36Sopenharmony_ci if (!ethsw) 333462306a36Sopenharmony_ci return -ENOMEM; 333562306a36Sopenharmony_ci 333662306a36Sopenharmony_ci ethsw->dev = dev; 333762306a36Sopenharmony_ci ethsw->iommu_domain = iommu_get_domain_for_dev(dev); 333862306a36Sopenharmony_ci dev_set_drvdata(dev, ethsw); 333962306a36Sopenharmony_ci 334062306a36Sopenharmony_ci err = fsl_mc_portal_allocate(sw_dev, FSL_MC_IO_ATOMIC_CONTEXT_PORTAL, 334162306a36Sopenharmony_ci ðsw->mc_io); 334262306a36Sopenharmony_ci if (err) { 334362306a36Sopenharmony_ci if (err == -ENXIO) 334462306a36Sopenharmony_ci err = -EPROBE_DEFER; 334562306a36Sopenharmony_ci else 334662306a36Sopenharmony_ci dev_err(dev, "fsl_mc_portal_allocate err %d\n", err); 334762306a36Sopenharmony_ci goto err_free_drvdata; 334862306a36Sopenharmony_ci } 334962306a36Sopenharmony_ci 335062306a36Sopenharmony_ci err = dpaa2_switch_init(sw_dev); 335162306a36Sopenharmony_ci if (err) 335262306a36Sopenharmony_ci goto err_free_cmdport; 335362306a36Sopenharmony_ci 335462306a36Sopenharmony_ci ethsw->ports = kcalloc(ethsw->sw_attr.num_ifs, sizeof(*ethsw->ports), 335562306a36Sopenharmony_ci GFP_KERNEL); 335662306a36Sopenharmony_ci if (!(ethsw->ports)) { 335762306a36Sopenharmony_ci err = -ENOMEM; 335862306a36Sopenharmony_ci goto err_teardown; 335962306a36Sopenharmony_ci } 336062306a36Sopenharmony_ci 336162306a36Sopenharmony_ci ethsw->fdbs = kcalloc(ethsw->sw_attr.num_ifs, sizeof(*ethsw->fdbs), 336262306a36Sopenharmony_ci GFP_KERNEL); 336362306a36Sopenharmony_ci if (!ethsw->fdbs) { 336462306a36Sopenharmony_ci err = -ENOMEM; 336562306a36Sopenharmony_ci goto err_free_ports; 336662306a36Sopenharmony_ci } 336762306a36Sopenharmony_ci 336862306a36Sopenharmony_ci ethsw->filter_blocks = kcalloc(ethsw->sw_attr.num_ifs, 336962306a36Sopenharmony_ci sizeof(*ethsw->filter_blocks), 337062306a36Sopenharmony_ci GFP_KERNEL); 337162306a36Sopenharmony_ci if (!ethsw->filter_blocks) { 337262306a36Sopenharmony_ci err = -ENOMEM; 337362306a36Sopenharmony_ci goto err_free_fdbs; 337462306a36Sopenharmony_ci } 337562306a36Sopenharmony_ci 337662306a36Sopenharmony_ci for (i = 0; i < ethsw->sw_attr.num_ifs; i++) { 337762306a36Sopenharmony_ci err = dpaa2_switch_probe_port(ethsw, i); 337862306a36Sopenharmony_ci if (err) 337962306a36Sopenharmony_ci goto err_free_netdev; 338062306a36Sopenharmony_ci } 338162306a36Sopenharmony_ci 338262306a36Sopenharmony_ci /* Add a NAPI instance for each of the Rx queues. The first port's 338362306a36Sopenharmony_ci * net_device will be associated with the instances since we do not have 338462306a36Sopenharmony_ci * different queues for each switch ports. 338562306a36Sopenharmony_ci */ 338662306a36Sopenharmony_ci for (i = 0; i < DPAA2_SWITCH_RX_NUM_FQS; i++) 338762306a36Sopenharmony_ci netif_napi_add(ethsw->ports[0]->netdev, ðsw->fq[i].napi, 338862306a36Sopenharmony_ci dpaa2_switch_poll); 338962306a36Sopenharmony_ci 339062306a36Sopenharmony_ci /* Setup IRQs */ 339162306a36Sopenharmony_ci err = dpaa2_switch_setup_irqs(sw_dev); 339262306a36Sopenharmony_ci if (err) 339362306a36Sopenharmony_ci goto err_stop; 339462306a36Sopenharmony_ci 339562306a36Sopenharmony_ci /* By convention, if the mirror port is equal to the number of switch 339662306a36Sopenharmony_ci * interfaces, then mirroring of any kind is disabled. 339762306a36Sopenharmony_ci */ 339862306a36Sopenharmony_ci ethsw->mirror_port = ethsw->sw_attr.num_ifs; 339962306a36Sopenharmony_ci 340062306a36Sopenharmony_ci /* Register the netdev only when the entire setup is done and the 340162306a36Sopenharmony_ci * switch port interfaces are ready to receive traffic 340262306a36Sopenharmony_ci */ 340362306a36Sopenharmony_ci for (i = 0; i < ethsw->sw_attr.num_ifs; i++) { 340462306a36Sopenharmony_ci err = register_netdev(ethsw->ports[i]->netdev); 340562306a36Sopenharmony_ci if (err < 0) { 340662306a36Sopenharmony_ci dev_err(dev, "register_netdev error %d\n", err); 340762306a36Sopenharmony_ci goto err_unregister_ports; 340862306a36Sopenharmony_ci } 340962306a36Sopenharmony_ci } 341062306a36Sopenharmony_ci 341162306a36Sopenharmony_ci return 0; 341262306a36Sopenharmony_ci 341362306a36Sopenharmony_cierr_unregister_ports: 341462306a36Sopenharmony_ci for (i--; i >= 0; i--) 341562306a36Sopenharmony_ci unregister_netdev(ethsw->ports[i]->netdev); 341662306a36Sopenharmony_ci dpaa2_switch_teardown_irqs(sw_dev); 341762306a36Sopenharmony_cierr_stop: 341862306a36Sopenharmony_ci dpsw_disable(ethsw->mc_io, 0, ethsw->dpsw_handle); 341962306a36Sopenharmony_cierr_free_netdev: 342062306a36Sopenharmony_ci for (i--; i >= 0; i--) 342162306a36Sopenharmony_ci dpaa2_switch_remove_port(ethsw, i); 342262306a36Sopenharmony_ci kfree(ethsw->filter_blocks); 342362306a36Sopenharmony_cierr_free_fdbs: 342462306a36Sopenharmony_ci kfree(ethsw->fdbs); 342562306a36Sopenharmony_cierr_free_ports: 342662306a36Sopenharmony_ci kfree(ethsw->ports); 342762306a36Sopenharmony_ci 342862306a36Sopenharmony_cierr_teardown: 342962306a36Sopenharmony_ci dpaa2_switch_teardown(sw_dev); 343062306a36Sopenharmony_ci 343162306a36Sopenharmony_cierr_free_cmdport: 343262306a36Sopenharmony_ci fsl_mc_portal_free(ethsw->mc_io); 343362306a36Sopenharmony_ci 343462306a36Sopenharmony_cierr_free_drvdata: 343562306a36Sopenharmony_ci kfree(ethsw); 343662306a36Sopenharmony_ci dev_set_drvdata(dev, NULL); 343762306a36Sopenharmony_ci 343862306a36Sopenharmony_ci return err; 343962306a36Sopenharmony_ci} 344062306a36Sopenharmony_ci 344162306a36Sopenharmony_cistatic const struct fsl_mc_device_id dpaa2_switch_match_id_table[] = { 344262306a36Sopenharmony_ci { 344362306a36Sopenharmony_ci .vendor = FSL_MC_VENDOR_FREESCALE, 344462306a36Sopenharmony_ci .obj_type = "dpsw", 344562306a36Sopenharmony_ci }, 344662306a36Sopenharmony_ci { .vendor = 0x0 } 344762306a36Sopenharmony_ci}; 344862306a36Sopenharmony_ciMODULE_DEVICE_TABLE(fslmc, dpaa2_switch_match_id_table); 344962306a36Sopenharmony_ci 345062306a36Sopenharmony_cistatic struct fsl_mc_driver dpaa2_switch_drv = { 345162306a36Sopenharmony_ci .driver = { 345262306a36Sopenharmony_ci .name = KBUILD_MODNAME, 345362306a36Sopenharmony_ci }, 345462306a36Sopenharmony_ci .probe = dpaa2_switch_probe, 345562306a36Sopenharmony_ci .remove = dpaa2_switch_remove, 345662306a36Sopenharmony_ci .match_id_table = dpaa2_switch_match_id_table 345762306a36Sopenharmony_ci}; 345862306a36Sopenharmony_ci 345962306a36Sopenharmony_cistatic struct notifier_block dpaa2_switch_port_nb __read_mostly = { 346062306a36Sopenharmony_ci .notifier_call = dpaa2_switch_port_netdevice_event, 346162306a36Sopenharmony_ci}; 346262306a36Sopenharmony_ci 346362306a36Sopenharmony_cistatic struct notifier_block dpaa2_switch_port_switchdev_nb = { 346462306a36Sopenharmony_ci .notifier_call = dpaa2_switch_port_event, 346562306a36Sopenharmony_ci}; 346662306a36Sopenharmony_ci 346762306a36Sopenharmony_cistatic struct notifier_block dpaa2_switch_port_switchdev_blocking_nb = { 346862306a36Sopenharmony_ci .notifier_call = dpaa2_switch_port_blocking_event, 346962306a36Sopenharmony_ci}; 347062306a36Sopenharmony_ci 347162306a36Sopenharmony_cistatic int dpaa2_switch_register_notifiers(void) 347262306a36Sopenharmony_ci{ 347362306a36Sopenharmony_ci int err; 347462306a36Sopenharmony_ci 347562306a36Sopenharmony_ci err = register_netdevice_notifier(&dpaa2_switch_port_nb); 347662306a36Sopenharmony_ci if (err) { 347762306a36Sopenharmony_ci pr_err("dpaa2-switch: failed to register net_device notifier (%d)\n", err); 347862306a36Sopenharmony_ci return err; 347962306a36Sopenharmony_ci } 348062306a36Sopenharmony_ci 348162306a36Sopenharmony_ci err = register_switchdev_notifier(&dpaa2_switch_port_switchdev_nb); 348262306a36Sopenharmony_ci if (err) { 348362306a36Sopenharmony_ci pr_err("dpaa2-switch: failed to register switchdev notifier (%d)\n", err); 348462306a36Sopenharmony_ci goto err_switchdev_nb; 348562306a36Sopenharmony_ci } 348662306a36Sopenharmony_ci 348762306a36Sopenharmony_ci err = register_switchdev_blocking_notifier(&dpaa2_switch_port_switchdev_blocking_nb); 348862306a36Sopenharmony_ci if (err) { 348962306a36Sopenharmony_ci pr_err("dpaa2-switch: failed to register switchdev blocking notifier (%d)\n", err); 349062306a36Sopenharmony_ci goto err_switchdev_blocking_nb; 349162306a36Sopenharmony_ci } 349262306a36Sopenharmony_ci 349362306a36Sopenharmony_ci return 0; 349462306a36Sopenharmony_ci 349562306a36Sopenharmony_cierr_switchdev_blocking_nb: 349662306a36Sopenharmony_ci unregister_switchdev_notifier(&dpaa2_switch_port_switchdev_nb); 349762306a36Sopenharmony_cierr_switchdev_nb: 349862306a36Sopenharmony_ci unregister_netdevice_notifier(&dpaa2_switch_port_nb); 349962306a36Sopenharmony_ci 350062306a36Sopenharmony_ci return err; 350162306a36Sopenharmony_ci} 350262306a36Sopenharmony_ci 350362306a36Sopenharmony_cistatic void dpaa2_switch_unregister_notifiers(void) 350462306a36Sopenharmony_ci{ 350562306a36Sopenharmony_ci int err; 350662306a36Sopenharmony_ci 350762306a36Sopenharmony_ci err = unregister_switchdev_blocking_notifier(&dpaa2_switch_port_switchdev_blocking_nb); 350862306a36Sopenharmony_ci if (err) 350962306a36Sopenharmony_ci pr_err("dpaa2-switch: failed to unregister switchdev blocking notifier (%d)\n", 351062306a36Sopenharmony_ci err); 351162306a36Sopenharmony_ci 351262306a36Sopenharmony_ci err = unregister_switchdev_notifier(&dpaa2_switch_port_switchdev_nb); 351362306a36Sopenharmony_ci if (err) 351462306a36Sopenharmony_ci pr_err("dpaa2-switch: failed to unregister switchdev notifier (%d)\n", err); 351562306a36Sopenharmony_ci 351662306a36Sopenharmony_ci err = unregister_netdevice_notifier(&dpaa2_switch_port_nb); 351762306a36Sopenharmony_ci if (err) 351862306a36Sopenharmony_ci pr_err("dpaa2-switch: failed to unregister net_device notifier (%d)\n", err); 351962306a36Sopenharmony_ci} 352062306a36Sopenharmony_ci 352162306a36Sopenharmony_cistatic int __init dpaa2_switch_driver_init(void) 352262306a36Sopenharmony_ci{ 352362306a36Sopenharmony_ci int err; 352462306a36Sopenharmony_ci 352562306a36Sopenharmony_ci err = fsl_mc_driver_register(&dpaa2_switch_drv); 352662306a36Sopenharmony_ci if (err) 352762306a36Sopenharmony_ci return err; 352862306a36Sopenharmony_ci 352962306a36Sopenharmony_ci err = dpaa2_switch_register_notifiers(); 353062306a36Sopenharmony_ci if (err) { 353162306a36Sopenharmony_ci fsl_mc_driver_unregister(&dpaa2_switch_drv); 353262306a36Sopenharmony_ci return err; 353362306a36Sopenharmony_ci } 353462306a36Sopenharmony_ci 353562306a36Sopenharmony_ci return 0; 353662306a36Sopenharmony_ci} 353762306a36Sopenharmony_ci 353862306a36Sopenharmony_cistatic void __exit dpaa2_switch_driver_exit(void) 353962306a36Sopenharmony_ci{ 354062306a36Sopenharmony_ci dpaa2_switch_unregister_notifiers(); 354162306a36Sopenharmony_ci fsl_mc_driver_unregister(&dpaa2_switch_drv); 354262306a36Sopenharmony_ci} 354362306a36Sopenharmony_ci 354462306a36Sopenharmony_cimodule_init(dpaa2_switch_driver_init); 354562306a36Sopenharmony_cimodule_exit(dpaa2_switch_driver_exit); 354662306a36Sopenharmony_ci 354762306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 354862306a36Sopenharmony_ciMODULE_DESCRIPTION("DPAA2 Ethernet Switch Driver"); 3549