162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* Copyright (c) 2018, Sensor-Technik Wiedemann GmbH 362306a36Sopenharmony_ci * Copyright (c) 2018-2019, Vladimir Oltean <olteanv@gmail.com> 462306a36Sopenharmony_ci */ 562306a36Sopenharmony_ci 662306a36Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include <linux/delay.h> 962306a36Sopenharmony_ci#include <linux/module.h> 1062306a36Sopenharmony_ci#include <linux/printk.h> 1162306a36Sopenharmony_ci#include <linux/spi/spi.h> 1262306a36Sopenharmony_ci#include <linux/errno.h> 1362306a36Sopenharmony_ci#include <linux/gpio/consumer.h> 1462306a36Sopenharmony_ci#include <linux/phylink.h> 1562306a36Sopenharmony_ci#include <linux/of.h> 1662306a36Sopenharmony_ci#include <linux/of_net.h> 1762306a36Sopenharmony_ci#include <linux/of_mdio.h> 1862306a36Sopenharmony_ci#include <linux/pcs/pcs-xpcs.h> 1962306a36Sopenharmony_ci#include <linux/netdev_features.h> 2062306a36Sopenharmony_ci#include <linux/netdevice.h> 2162306a36Sopenharmony_ci#include <linux/if_bridge.h> 2262306a36Sopenharmony_ci#include <linux/if_ether.h> 2362306a36Sopenharmony_ci#include <linux/dsa/8021q.h> 2462306a36Sopenharmony_ci#include "sja1105.h" 2562306a36Sopenharmony_ci#include "sja1105_tas.h" 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci#define SJA1105_UNKNOWN_MULTICAST 0x010000000000ull 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci/* Configure the optional reset pin and bring up switch */ 3062306a36Sopenharmony_cistatic int sja1105_hw_reset(struct device *dev, unsigned int pulse_len, 3162306a36Sopenharmony_ci unsigned int startup_delay) 3262306a36Sopenharmony_ci{ 3362306a36Sopenharmony_ci struct gpio_desc *gpio; 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci gpio = gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH); 3662306a36Sopenharmony_ci if (IS_ERR(gpio)) 3762306a36Sopenharmony_ci return PTR_ERR(gpio); 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_ci if (!gpio) 4062306a36Sopenharmony_ci return 0; 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ci gpiod_set_value_cansleep(gpio, 1); 4362306a36Sopenharmony_ci /* Wait for minimum reset pulse length */ 4462306a36Sopenharmony_ci msleep(pulse_len); 4562306a36Sopenharmony_ci gpiod_set_value_cansleep(gpio, 0); 4662306a36Sopenharmony_ci /* Wait until chip is ready after reset */ 4762306a36Sopenharmony_ci msleep(startup_delay); 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ci gpiod_put(gpio); 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_ci return 0; 5262306a36Sopenharmony_ci} 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_cistatic void 5562306a36Sopenharmony_cisja1105_port_allow_traffic(struct sja1105_l2_forwarding_entry *l2_fwd, 5662306a36Sopenharmony_ci int from, int to, bool allow) 5762306a36Sopenharmony_ci{ 5862306a36Sopenharmony_ci if (allow) 5962306a36Sopenharmony_ci l2_fwd[from].reach_port |= BIT(to); 6062306a36Sopenharmony_ci else 6162306a36Sopenharmony_ci l2_fwd[from].reach_port &= ~BIT(to); 6262306a36Sopenharmony_ci} 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_cistatic bool sja1105_can_forward(struct sja1105_l2_forwarding_entry *l2_fwd, 6562306a36Sopenharmony_ci int from, int to) 6662306a36Sopenharmony_ci{ 6762306a36Sopenharmony_ci return !!(l2_fwd[from].reach_port & BIT(to)); 6862306a36Sopenharmony_ci} 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_cistatic int sja1105_is_vlan_configured(struct sja1105_private *priv, u16 vid) 7162306a36Sopenharmony_ci{ 7262306a36Sopenharmony_ci struct sja1105_vlan_lookup_entry *vlan; 7362306a36Sopenharmony_ci int count, i; 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci vlan = priv->static_config.tables[BLK_IDX_VLAN_LOOKUP].entries; 7662306a36Sopenharmony_ci count = priv->static_config.tables[BLK_IDX_VLAN_LOOKUP].entry_count; 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci for (i = 0; i < count; i++) 7962306a36Sopenharmony_ci if (vlan[i].vlanid == vid) 8062306a36Sopenharmony_ci return i; 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci /* Return an invalid entry index if not found */ 8362306a36Sopenharmony_ci return -1; 8462306a36Sopenharmony_ci} 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_cistatic int sja1105_drop_untagged(struct dsa_switch *ds, int port, bool drop) 8762306a36Sopenharmony_ci{ 8862306a36Sopenharmony_ci struct sja1105_private *priv = ds->priv; 8962306a36Sopenharmony_ci struct sja1105_mac_config_entry *mac; 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci mac = priv->static_config.tables[BLK_IDX_MAC_CONFIG].entries; 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci if (mac[port].drpuntag == drop) 9462306a36Sopenharmony_ci return 0; 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci mac[port].drpuntag = drop; 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci return sja1105_dynamic_config_write(priv, BLK_IDX_MAC_CONFIG, port, 9962306a36Sopenharmony_ci &mac[port], true); 10062306a36Sopenharmony_ci} 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_cistatic int sja1105_pvid_apply(struct sja1105_private *priv, int port, u16 pvid) 10362306a36Sopenharmony_ci{ 10462306a36Sopenharmony_ci struct sja1105_mac_config_entry *mac; 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci mac = priv->static_config.tables[BLK_IDX_MAC_CONFIG].entries; 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci if (mac[port].vlanid == pvid) 10962306a36Sopenharmony_ci return 0; 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci mac[port].vlanid = pvid; 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci return sja1105_dynamic_config_write(priv, BLK_IDX_MAC_CONFIG, port, 11462306a36Sopenharmony_ci &mac[port], true); 11562306a36Sopenharmony_ci} 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_cistatic int sja1105_commit_pvid(struct dsa_switch *ds, int port) 11862306a36Sopenharmony_ci{ 11962306a36Sopenharmony_ci struct dsa_port *dp = dsa_to_port(ds, port); 12062306a36Sopenharmony_ci struct net_device *br = dsa_port_bridge_dev_get(dp); 12162306a36Sopenharmony_ci struct sja1105_private *priv = ds->priv; 12262306a36Sopenharmony_ci struct sja1105_vlan_lookup_entry *vlan; 12362306a36Sopenharmony_ci bool drop_untagged = false; 12462306a36Sopenharmony_ci int match, rc; 12562306a36Sopenharmony_ci u16 pvid; 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci if (br && br_vlan_enabled(br)) 12862306a36Sopenharmony_ci pvid = priv->bridge_pvid[port]; 12962306a36Sopenharmony_ci else 13062306a36Sopenharmony_ci pvid = priv->tag_8021q_pvid[port]; 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci rc = sja1105_pvid_apply(priv, port, pvid); 13362306a36Sopenharmony_ci if (rc) 13462306a36Sopenharmony_ci return rc; 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci /* Only force dropping of untagged packets when the port is under a 13762306a36Sopenharmony_ci * VLAN-aware bridge. When the tag_8021q pvid is used, we are 13862306a36Sopenharmony_ci * deliberately removing the RX VLAN from the port's VMEMB_PORT list, 13962306a36Sopenharmony_ci * to prevent DSA tag spoofing from the link partner. Untagged packets 14062306a36Sopenharmony_ci * are the only ones that should be received with tag_8021q, so 14162306a36Sopenharmony_ci * definitely don't drop them. 14262306a36Sopenharmony_ci */ 14362306a36Sopenharmony_ci if (pvid == priv->bridge_pvid[port]) { 14462306a36Sopenharmony_ci vlan = priv->static_config.tables[BLK_IDX_VLAN_LOOKUP].entries; 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci match = sja1105_is_vlan_configured(priv, pvid); 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci if (match < 0 || !(vlan[match].vmemb_port & BIT(port))) 14962306a36Sopenharmony_ci drop_untagged = true; 15062306a36Sopenharmony_ci } 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci if (dsa_is_cpu_port(ds, port) || dsa_is_dsa_port(ds, port)) 15362306a36Sopenharmony_ci drop_untagged = true; 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci return sja1105_drop_untagged(ds, port, drop_untagged); 15662306a36Sopenharmony_ci} 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_cistatic int sja1105_init_mac_settings(struct sja1105_private *priv) 15962306a36Sopenharmony_ci{ 16062306a36Sopenharmony_ci struct sja1105_mac_config_entry default_mac = { 16162306a36Sopenharmony_ci /* Enable all 8 priority queues on egress. 16262306a36Sopenharmony_ci * Every queue i holds top[i] - base[i] frames. 16362306a36Sopenharmony_ci * Sum of top[i] - base[i] is 511 (max hardware limit). 16462306a36Sopenharmony_ci */ 16562306a36Sopenharmony_ci .top = {0x3F, 0x7F, 0xBF, 0xFF, 0x13F, 0x17F, 0x1BF, 0x1FF}, 16662306a36Sopenharmony_ci .base = {0x0, 0x40, 0x80, 0xC0, 0x100, 0x140, 0x180, 0x1C0}, 16762306a36Sopenharmony_ci .enabled = {true, true, true, true, true, true, true, true}, 16862306a36Sopenharmony_ci /* Keep standard IFG of 12 bytes on egress. */ 16962306a36Sopenharmony_ci .ifg = 0, 17062306a36Sopenharmony_ci /* Always put the MAC speed in automatic mode, where it can be 17162306a36Sopenharmony_ci * adjusted at runtime by PHYLINK. 17262306a36Sopenharmony_ci */ 17362306a36Sopenharmony_ci .speed = priv->info->port_speed[SJA1105_SPEED_AUTO], 17462306a36Sopenharmony_ci /* No static correction for 1-step 1588 events */ 17562306a36Sopenharmony_ci .tp_delin = 0, 17662306a36Sopenharmony_ci .tp_delout = 0, 17762306a36Sopenharmony_ci /* Disable aging for critical TTEthernet traffic */ 17862306a36Sopenharmony_ci .maxage = 0xFF, 17962306a36Sopenharmony_ci /* Internal VLAN (pvid) to apply to untagged ingress */ 18062306a36Sopenharmony_ci .vlanprio = 0, 18162306a36Sopenharmony_ci .vlanid = 1, 18262306a36Sopenharmony_ci .ing_mirr = false, 18362306a36Sopenharmony_ci .egr_mirr = false, 18462306a36Sopenharmony_ci /* Don't drop traffic with other EtherType than ETH_P_IP */ 18562306a36Sopenharmony_ci .drpnona664 = false, 18662306a36Sopenharmony_ci /* Don't drop double-tagged traffic */ 18762306a36Sopenharmony_ci .drpdtag = false, 18862306a36Sopenharmony_ci /* Don't drop untagged traffic */ 18962306a36Sopenharmony_ci .drpuntag = false, 19062306a36Sopenharmony_ci /* Don't retag 802.1p (VID 0) traffic with the pvid */ 19162306a36Sopenharmony_ci .retag = false, 19262306a36Sopenharmony_ci /* Disable learning and I/O on user ports by default - 19362306a36Sopenharmony_ci * STP will enable it. 19462306a36Sopenharmony_ci */ 19562306a36Sopenharmony_ci .dyn_learn = false, 19662306a36Sopenharmony_ci .egress = false, 19762306a36Sopenharmony_ci .ingress = false, 19862306a36Sopenharmony_ci }; 19962306a36Sopenharmony_ci struct sja1105_mac_config_entry *mac; 20062306a36Sopenharmony_ci struct dsa_switch *ds = priv->ds; 20162306a36Sopenharmony_ci struct sja1105_table *table; 20262306a36Sopenharmony_ci struct dsa_port *dp; 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_ci table = &priv->static_config.tables[BLK_IDX_MAC_CONFIG]; 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ci /* Discard previous MAC Configuration Table */ 20762306a36Sopenharmony_ci if (table->entry_count) { 20862306a36Sopenharmony_ci kfree(table->entries); 20962306a36Sopenharmony_ci table->entry_count = 0; 21062306a36Sopenharmony_ci } 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ci table->entries = kcalloc(table->ops->max_entry_count, 21362306a36Sopenharmony_ci table->ops->unpacked_entry_size, GFP_KERNEL); 21462306a36Sopenharmony_ci if (!table->entries) 21562306a36Sopenharmony_ci return -ENOMEM; 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci table->entry_count = table->ops->max_entry_count; 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_ci mac = table->entries; 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_ci list_for_each_entry(dp, &ds->dst->ports, list) { 22262306a36Sopenharmony_ci if (dp->ds != ds) 22362306a36Sopenharmony_ci continue; 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ci mac[dp->index] = default_mac; 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_ci /* Let sja1105_bridge_stp_state_set() keep address learning 22862306a36Sopenharmony_ci * enabled for the DSA ports. CPU ports use software-assisted 22962306a36Sopenharmony_ci * learning to ensure that only FDB entries belonging to the 23062306a36Sopenharmony_ci * bridge are learned, and that they are learned towards all 23162306a36Sopenharmony_ci * CPU ports in a cross-chip topology if multiple CPU ports 23262306a36Sopenharmony_ci * exist. 23362306a36Sopenharmony_ci */ 23462306a36Sopenharmony_ci if (dsa_port_is_dsa(dp)) 23562306a36Sopenharmony_ci dp->learning = true; 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_ci /* Disallow untagged packets from being received on the 23862306a36Sopenharmony_ci * CPU and DSA ports. 23962306a36Sopenharmony_ci */ 24062306a36Sopenharmony_ci if (dsa_port_is_cpu(dp) || dsa_port_is_dsa(dp)) 24162306a36Sopenharmony_ci mac[dp->index].drpuntag = true; 24262306a36Sopenharmony_ci } 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_ci return 0; 24562306a36Sopenharmony_ci} 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_cistatic int sja1105_init_mii_settings(struct sja1105_private *priv) 24862306a36Sopenharmony_ci{ 24962306a36Sopenharmony_ci struct device *dev = &priv->spidev->dev; 25062306a36Sopenharmony_ci struct sja1105_xmii_params_entry *mii; 25162306a36Sopenharmony_ci struct dsa_switch *ds = priv->ds; 25262306a36Sopenharmony_ci struct sja1105_table *table; 25362306a36Sopenharmony_ci int i; 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_ci table = &priv->static_config.tables[BLK_IDX_XMII_PARAMS]; 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_ci /* Discard previous xMII Mode Parameters Table */ 25862306a36Sopenharmony_ci if (table->entry_count) { 25962306a36Sopenharmony_ci kfree(table->entries); 26062306a36Sopenharmony_ci table->entry_count = 0; 26162306a36Sopenharmony_ci } 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_ci table->entries = kcalloc(table->ops->max_entry_count, 26462306a36Sopenharmony_ci table->ops->unpacked_entry_size, GFP_KERNEL); 26562306a36Sopenharmony_ci if (!table->entries) 26662306a36Sopenharmony_ci return -ENOMEM; 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_ci /* Override table based on PHYLINK DT bindings */ 26962306a36Sopenharmony_ci table->entry_count = table->ops->max_entry_count; 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_ci mii = table->entries; 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_ci for (i = 0; i < ds->num_ports; i++) { 27462306a36Sopenharmony_ci sja1105_mii_role_t role = XMII_MAC; 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_ci if (dsa_is_unused_port(priv->ds, i)) 27762306a36Sopenharmony_ci continue; 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci switch (priv->phy_mode[i]) { 28062306a36Sopenharmony_ci case PHY_INTERFACE_MODE_INTERNAL: 28162306a36Sopenharmony_ci if (priv->info->internal_phy[i] == SJA1105_NO_PHY) 28262306a36Sopenharmony_ci goto unsupported; 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_ci mii->xmii_mode[i] = XMII_MODE_MII; 28562306a36Sopenharmony_ci if (priv->info->internal_phy[i] == SJA1105_PHY_BASE_TX) 28662306a36Sopenharmony_ci mii->special[i] = true; 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_ci break; 28962306a36Sopenharmony_ci case PHY_INTERFACE_MODE_REVMII: 29062306a36Sopenharmony_ci role = XMII_PHY; 29162306a36Sopenharmony_ci fallthrough; 29262306a36Sopenharmony_ci case PHY_INTERFACE_MODE_MII: 29362306a36Sopenharmony_ci if (!priv->info->supports_mii[i]) 29462306a36Sopenharmony_ci goto unsupported; 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_ci mii->xmii_mode[i] = XMII_MODE_MII; 29762306a36Sopenharmony_ci break; 29862306a36Sopenharmony_ci case PHY_INTERFACE_MODE_REVRMII: 29962306a36Sopenharmony_ci role = XMII_PHY; 30062306a36Sopenharmony_ci fallthrough; 30162306a36Sopenharmony_ci case PHY_INTERFACE_MODE_RMII: 30262306a36Sopenharmony_ci if (!priv->info->supports_rmii[i]) 30362306a36Sopenharmony_ci goto unsupported; 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_ci mii->xmii_mode[i] = XMII_MODE_RMII; 30662306a36Sopenharmony_ci break; 30762306a36Sopenharmony_ci case PHY_INTERFACE_MODE_RGMII: 30862306a36Sopenharmony_ci case PHY_INTERFACE_MODE_RGMII_ID: 30962306a36Sopenharmony_ci case PHY_INTERFACE_MODE_RGMII_RXID: 31062306a36Sopenharmony_ci case PHY_INTERFACE_MODE_RGMII_TXID: 31162306a36Sopenharmony_ci if (!priv->info->supports_rgmii[i]) 31262306a36Sopenharmony_ci goto unsupported; 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_ci mii->xmii_mode[i] = XMII_MODE_RGMII; 31562306a36Sopenharmony_ci break; 31662306a36Sopenharmony_ci case PHY_INTERFACE_MODE_SGMII: 31762306a36Sopenharmony_ci if (!priv->info->supports_sgmii[i]) 31862306a36Sopenharmony_ci goto unsupported; 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_ci mii->xmii_mode[i] = XMII_MODE_SGMII; 32162306a36Sopenharmony_ci mii->special[i] = true; 32262306a36Sopenharmony_ci break; 32362306a36Sopenharmony_ci case PHY_INTERFACE_MODE_2500BASEX: 32462306a36Sopenharmony_ci if (!priv->info->supports_2500basex[i]) 32562306a36Sopenharmony_ci goto unsupported; 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_ci mii->xmii_mode[i] = XMII_MODE_SGMII; 32862306a36Sopenharmony_ci mii->special[i] = true; 32962306a36Sopenharmony_ci break; 33062306a36Sopenharmony_ciunsupported: 33162306a36Sopenharmony_ci default: 33262306a36Sopenharmony_ci dev_err(dev, "Unsupported PHY mode %s on port %d!\n", 33362306a36Sopenharmony_ci phy_modes(priv->phy_mode[i]), i); 33462306a36Sopenharmony_ci return -EINVAL; 33562306a36Sopenharmony_ci } 33662306a36Sopenharmony_ci 33762306a36Sopenharmony_ci mii->phy_mac[i] = role; 33862306a36Sopenharmony_ci } 33962306a36Sopenharmony_ci return 0; 34062306a36Sopenharmony_ci} 34162306a36Sopenharmony_ci 34262306a36Sopenharmony_cistatic int sja1105_init_static_fdb(struct sja1105_private *priv) 34362306a36Sopenharmony_ci{ 34462306a36Sopenharmony_ci struct sja1105_l2_lookup_entry *l2_lookup; 34562306a36Sopenharmony_ci struct sja1105_table *table; 34662306a36Sopenharmony_ci int port; 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_ci table = &priv->static_config.tables[BLK_IDX_L2_LOOKUP]; 34962306a36Sopenharmony_ci 35062306a36Sopenharmony_ci /* We only populate the FDB table through dynamic L2 Address Lookup 35162306a36Sopenharmony_ci * entries, except for a special entry at the end which is a catch-all 35262306a36Sopenharmony_ci * for unknown multicast and will be used to control flooding domain. 35362306a36Sopenharmony_ci */ 35462306a36Sopenharmony_ci if (table->entry_count) { 35562306a36Sopenharmony_ci kfree(table->entries); 35662306a36Sopenharmony_ci table->entry_count = 0; 35762306a36Sopenharmony_ci } 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_ci if (!priv->info->can_limit_mcast_flood) 36062306a36Sopenharmony_ci return 0; 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_ci table->entries = kcalloc(1, table->ops->unpacked_entry_size, 36362306a36Sopenharmony_ci GFP_KERNEL); 36462306a36Sopenharmony_ci if (!table->entries) 36562306a36Sopenharmony_ci return -ENOMEM; 36662306a36Sopenharmony_ci 36762306a36Sopenharmony_ci table->entry_count = 1; 36862306a36Sopenharmony_ci l2_lookup = table->entries; 36962306a36Sopenharmony_ci 37062306a36Sopenharmony_ci /* All L2 multicast addresses have an odd first octet */ 37162306a36Sopenharmony_ci l2_lookup[0].macaddr = SJA1105_UNKNOWN_MULTICAST; 37262306a36Sopenharmony_ci l2_lookup[0].mask_macaddr = SJA1105_UNKNOWN_MULTICAST; 37362306a36Sopenharmony_ci l2_lookup[0].lockeds = true; 37462306a36Sopenharmony_ci l2_lookup[0].index = SJA1105_MAX_L2_LOOKUP_COUNT - 1; 37562306a36Sopenharmony_ci 37662306a36Sopenharmony_ci /* Flood multicast to every port by default */ 37762306a36Sopenharmony_ci for (port = 0; port < priv->ds->num_ports; port++) 37862306a36Sopenharmony_ci if (!dsa_is_unused_port(priv->ds, port)) 37962306a36Sopenharmony_ci l2_lookup[0].destports |= BIT(port); 38062306a36Sopenharmony_ci 38162306a36Sopenharmony_ci return 0; 38262306a36Sopenharmony_ci} 38362306a36Sopenharmony_ci 38462306a36Sopenharmony_cistatic int sja1105_init_l2_lookup_params(struct sja1105_private *priv) 38562306a36Sopenharmony_ci{ 38662306a36Sopenharmony_ci struct sja1105_l2_lookup_params_entry default_l2_lookup_params = { 38762306a36Sopenharmony_ci /* Learned FDB entries are forgotten after 300 seconds */ 38862306a36Sopenharmony_ci .maxage = SJA1105_AGEING_TIME_MS(300000), 38962306a36Sopenharmony_ci /* All entries within a FDB bin are available for learning */ 39062306a36Sopenharmony_ci .dyn_tbsz = SJA1105ET_FDB_BIN_SIZE, 39162306a36Sopenharmony_ci /* And the P/Q/R/S equivalent setting: */ 39262306a36Sopenharmony_ci .start_dynspc = 0, 39362306a36Sopenharmony_ci /* 2^8 + 2^5 + 2^3 + 2^2 + 2^1 + 1 in Koopman notation */ 39462306a36Sopenharmony_ci .poly = 0x97, 39562306a36Sopenharmony_ci /* Always use Independent VLAN Learning (IVL) */ 39662306a36Sopenharmony_ci .shared_learn = false, 39762306a36Sopenharmony_ci /* Don't discard management traffic based on ENFPORT - 39862306a36Sopenharmony_ci * we don't perform SMAC port enforcement anyway, so 39962306a36Sopenharmony_ci * what we are setting here doesn't matter. 40062306a36Sopenharmony_ci */ 40162306a36Sopenharmony_ci .no_enf_hostprt = false, 40262306a36Sopenharmony_ci /* Don't learn SMAC for mac_fltres1 and mac_fltres0. 40362306a36Sopenharmony_ci * Maybe correlate with no_linklocal_learn from bridge driver? 40462306a36Sopenharmony_ci */ 40562306a36Sopenharmony_ci .no_mgmt_learn = true, 40662306a36Sopenharmony_ci /* P/Q/R/S only */ 40762306a36Sopenharmony_ci .use_static = true, 40862306a36Sopenharmony_ci /* Dynamically learned FDB entries can overwrite other (older) 40962306a36Sopenharmony_ci * dynamic FDB entries 41062306a36Sopenharmony_ci */ 41162306a36Sopenharmony_ci .owr_dyn = true, 41262306a36Sopenharmony_ci .drpnolearn = true, 41362306a36Sopenharmony_ci }; 41462306a36Sopenharmony_ci struct dsa_switch *ds = priv->ds; 41562306a36Sopenharmony_ci int port, num_used_ports = 0; 41662306a36Sopenharmony_ci struct sja1105_table *table; 41762306a36Sopenharmony_ci u64 max_fdb_entries; 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_ci for (port = 0; port < ds->num_ports; port++) 42062306a36Sopenharmony_ci if (!dsa_is_unused_port(ds, port)) 42162306a36Sopenharmony_ci num_used_ports++; 42262306a36Sopenharmony_ci 42362306a36Sopenharmony_ci max_fdb_entries = SJA1105_MAX_L2_LOOKUP_COUNT / num_used_ports; 42462306a36Sopenharmony_ci 42562306a36Sopenharmony_ci for (port = 0; port < ds->num_ports; port++) { 42662306a36Sopenharmony_ci if (dsa_is_unused_port(ds, port)) 42762306a36Sopenharmony_ci continue; 42862306a36Sopenharmony_ci 42962306a36Sopenharmony_ci default_l2_lookup_params.maxaddrp[port] = max_fdb_entries; 43062306a36Sopenharmony_ci } 43162306a36Sopenharmony_ci 43262306a36Sopenharmony_ci table = &priv->static_config.tables[BLK_IDX_L2_LOOKUP_PARAMS]; 43362306a36Sopenharmony_ci 43462306a36Sopenharmony_ci if (table->entry_count) { 43562306a36Sopenharmony_ci kfree(table->entries); 43662306a36Sopenharmony_ci table->entry_count = 0; 43762306a36Sopenharmony_ci } 43862306a36Sopenharmony_ci 43962306a36Sopenharmony_ci table->entries = kcalloc(table->ops->max_entry_count, 44062306a36Sopenharmony_ci table->ops->unpacked_entry_size, GFP_KERNEL); 44162306a36Sopenharmony_ci if (!table->entries) 44262306a36Sopenharmony_ci return -ENOMEM; 44362306a36Sopenharmony_ci 44462306a36Sopenharmony_ci table->entry_count = table->ops->max_entry_count; 44562306a36Sopenharmony_ci 44662306a36Sopenharmony_ci /* This table only has a single entry */ 44762306a36Sopenharmony_ci ((struct sja1105_l2_lookup_params_entry *)table->entries)[0] = 44862306a36Sopenharmony_ci default_l2_lookup_params; 44962306a36Sopenharmony_ci 45062306a36Sopenharmony_ci return 0; 45162306a36Sopenharmony_ci} 45262306a36Sopenharmony_ci 45362306a36Sopenharmony_ci/* Set up a default VLAN for untagged traffic injected from the CPU 45462306a36Sopenharmony_ci * using management routes (e.g. STP, PTP) as opposed to tag_8021q. 45562306a36Sopenharmony_ci * All DT-defined ports are members of this VLAN, and there are no 45662306a36Sopenharmony_ci * restrictions on forwarding (since the CPU selects the destination). 45762306a36Sopenharmony_ci * Frames from this VLAN will always be transmitted as untagged, and 45862306a36Sopenharmony_ci * neither the bridge nor the 8021q module cannot create this VLAN ID. 45962306a36Sopenharmony_ci */ 46062306a36Sopenharmony_cistatic int sja1105_init_static_vlan(struct sja1105_private *priv) 46162306a36Sopenharmony_ci{ 46262306a36Sopenharmony_ci struct sja1105_table *table; 46362306a36Sopenharmony_ci struct sja1105_vlan_lookup_entry pvid = { 46462306a36Sopenharmony_ci .type_entry = SJA1110_VLAN_D_TAG, 46562306a36Sopenharmony_ci .ving_mirr = 0, 46662306a36Sopenharmony_ci .vegr_mirr = 0, 46762306a36Sopenharmony_ci .vmemb_port = 0, 46862306a36Sopenharmony_ci .vlan_bc = 0, 46962306a36Sopenharmony_ci .tag_port = 0, 47062306a36Sopenharmony_ci .vlanid = SJA1105_DEFAULT_VLAN, 47162306a36Sopenharmony_ci }; 47262306a36Sopenharmony_ci struct dsa_switch *ds = priv->ds; 47362306a36Sopenharmony_ci int port; 47462306a36Sopenharmony_ci 47562306a36Sopenharmony_ci table = &priv->static_config.tables[BLK_IDX_VLAN_LOOKUP]; 47662306a36Sopenharmony_ci 47762306a36Sopenharmony_ci if (table->entry_count) { 47862306a36Sopenharmony_ci kfree(table->entries); 47962306a36Sopenharmony_ci table->entry_count = 0; 48062306a36Sopenharmony_ci } 48162306a36Sopenharmony_ci 48262306a36Sopenharmony_ci table->entries = kzalloc(table->ops->unpacked_entry_size, 48362306a36Sopenharmony_ci GFP_KERNEL); 48462306a36Sopenharmony_ci if (!table->entries) 48562306a36Sopenharmony_ci return -ENOMEM; 48662306a36Sopenharmony_ci 48762306a36Sopenharmony_ci table->entry_count = 1; 48862306a36Sopenharmony_ci 48962306a36Sopenharmony_ci for (port = 0; port < ds->num_ports; port++) { 49062306a36Sopenharmony_ci if (dsa_is_unused_port(ds, port)) 49162306a36Sopenharmony_ci continue; 49262306a36Sopenharmony_ci 49362306a36Sopenharmony_ci pvid.vmemb_port |= BIT(port); 49462306a36Sopenharmony_ci pvid.vlan_bc |= BIT(port); 49562306a36Sopenharmony_ci pvid.tag_port &= ~BIT(port); 49662306a36Sopenharmony_ci 49762306a36Sopenharmony_ci if (dsa_is_cpu_port(ds, port) || dsa_is_dsa_port(ds, port)) { 49862306a36Sopenharmony_ci priv->tag_8021q_pvid[port] = SJA1105_DEFAULT_VLAN; 49962306a36Sopenharmony_ci priv->bridge_pvid[port] = SJA1105_DEFAULT_VLAN; 50062306a36Sopenharmony_ci } 50162306a36Sopenharmony_ci } 50262306a36Sopenharmony_ci 50362306a36Sopenharmony_ci ((struct sja1105_vlan_lookup_entry *)table->entries)[0] = pvid; 50462306a36Sopenharmony_ci return 0; 50562306a36Sopenharmony_ci} 50662306a36Sopenharmony_ci 50762306a36Sopenharmony_cistatic int sja1105_init_l2_forwarding(struct sja1105_private *priv) 50862306a36Sopenharmony_ci{ 50962306a36Sopenharmony_ci struct sja1105_l2_forwarding_entry *l2fwd; 51062306a36Sopenharmony_ci struct dsa_switch *ds = priv->ds; 51162306a36Sopenharmony_ci struct dsa_switch_tree *dst; 51262306a36Sopenharmony_ci struct sja1105_table *table; 51362306a36Sopenharmony_ci struct dsa_link *dl; 51462306a36Sopenharmony_ci int port, tc; 51562306a36Sopenharmony_ci int from, to; 51662306a36Sopenharmony_ci 51762306a36Sopenharmony_ci table = &priv->static_config.tables[BLK_IDX_L2_FORWARDING]; 51862306a36Sopenharmony_ci 51962306a36Sopenharmony_ci if (table->entry_count) { 52062306a36Sopenharmony_ci kfree(table->entries); 52162306a36Sopenharmony_ci table->entry_count = 0; 52262306a36Sopenharmony_ci } 52362306a36Sopenharmony_ci 52462306a36Sopenharmony_ci table->entries = kcalloc(table->ops->max_entry_count, 52562306a36Sopenharmony_ci table->ops->unpacked_entry_size, GFP_KERNEL); 52662306a36Sopenharmony_ci if (!table->entries) 52762306a36Sopenharmony_ci return -ENOMEM; 52862306a36Sopenharmony_ci 52962306a36Sopenharmony_ci table->entry_count = table->ops->max_entry_count; 53062306a36Sopenharmony_ci 53162306a36Sopenharmony_ci l2fwd = table->entries; 53262306a36Sopenharmony_ci 53362306a36Sopenharmony_ci /* First 5 entries in the L2 Forwarding Table define the forwarding 53462306a36Sopenharmony_ci * rules and the VLAN PCP to ingress queue mapping. 53562306a36Sopenharmony_ci * Set up the ingress queue mapping first. 53662306a36Sopenharmony_ci */ 53762306a36Sopenharmony_ci for (port = 0; port < ds->num_ports; port++) { 53862306a36Sopenharmony_ci if (dsa_is_unused_port(ds, port)) 53962306a36Sopenharmony_ci continue; 54062306a36Sopenharmony_ci 54162306a36Sopenharmony_ci for (tc = 0; tc < SJA1105_NUM_TC; tc++) 54262306a36Sopenharmony_ci l2fwd[port].vlan_pmap[tc] = tc; 54362306a36Sopenharmony_ci } 54462306a36Sopenharmony_ci 54562306a36Sopenharmony_ci /* Then manage the forwarding domain for user ports. These can forward 54662306a36Sopenharmony_ci * only to the always-on domain (CPU port and DSA links) 54762306a36Sopenharmony_ci */ 54862306a36Sopenharmony_ci for (from = 0; from < ds->num_ports; from++) { 54962306a36Sopenharmony_ci if (!dsa_is_user_port(ds, from)) 55062306a36Sopenharmony_ci continue; 55162306a36Sopenharmony_ci 55262306a36Sopenharmony_ci for (to = 0; to < ds->num_ports; to++) { 55362306a36Sopenharmony_ci if (!dsa_is_cpu_port(ds, to) && 55462306a36Sopenharmony_ci !dsa_is_dsa_port(ds, to)) 55562306a36Sopenharmony_ci continue; 55662306a36Sopenharmony_ci 55762306a36Sopenharmony_ci l2fwd[from].bc_domain |= BIT(to); 55862306a36Sopenharmony_ci l2fwd[from].fl_domain |= BIT(to); 55962306a36Sopenharmony_ci 56062306a36Sopenharmony_ci sja1105_port_allow_traffic(l2fwd, from, to, true); 56162306a36Sopenharmony_ci } 56262306a36Sopenharmony_ci } 56362306a36Sopenharmony_ci 56462306a36Sopenharmony_ci /* Then manage the forwarding domain for DSA links and CPU ports (the 56562306a36Sopenharmony_ci * always-on domain). These can send packets to any enabled port except 56662306a36Sopenharmony_ci * themselves. 56762306a36Sopenharmony_ci */ 56862306a36Sopenharmony_ci for (from = 0; from < ds->num_ports; from++) { 56962306a36Sopenharmony_ci if (!dsa_is_cpu_port(ds, from) && !dsa_is_dsa_port(ds, from)) 57062306a36Sopenharmony_ci continue; 57162306a36Sopenharmony_ci 57262306a36Sopenharmony_ci for (to = 0; to < ds->num_ports; to++) { 57362306a36Sopenharmony_ci if (dsa_is_unused_port(ds, to)) 57462306a36Sopenharmony_ci continue; 57562306a36Sopenharmony_ci 57662306a36Sopenharmony_ci if (from == to) 57762306a36Sopenharmony_ci continue; 57862306a36Sopenharmony_ci 57962306a36Sopenharmony_ci l2fwd[from].bc_domain |= BIT(to); 58062306a36Sopenharmony_ci l2fwd[from].fl_domain |= BIT(to); 58162306a36Sopenharmony_ci 58262306a36Sopenharmony_ci sja1105_port_allow_traffic(l2fwd, from, to, true); 58362306a36Sopenharmony_ci } 58462306a36Sopenharmony_ci } 58562306a36Sopenharmony_ci 58662306a36Sopenharmony_ci /* In odd topologies ("H" connections where there is a DSA link to 58762306a36Sopenharmony_ci * another switch which also has its own CPU port), TX packets can loop 58862306a36Sopenharmony_ci * back into the system (they are flooded from CPU port 1 to the DSA 58962306a36Sopenharmony_ci * link, and from there to CPU port 2). Prevent this from happening by 59062306a36Sopenharmony_ci * cutting RX from DSA links towards our CPU port, if the remote switch 59162306a36Sopenharmony_ci * has its own CPU port and therefore doesn't need ours for network 59262306a36Sopenharmony_ci * stack termination. 59362306a36Sopenharmony_ci */ 59462306a36Sopenharmony_ci dst = ds->dst; 59562306a36Sopenharmony_ci 59662306a36Sopenharmony_ci list_for_each_entry(dl, &dst->rtable, list) { 59762306a36Sopenharmony_ci if (dl->dp->ds != ds || dl->link_dp->cpu_dp == dl->dp->cpu_dp) 59862306a36Sopenharmony_ci continue; 59962306a36Sopenharmony_ci 60062306a36Sopenharmony_ci from = dl->dp->index; 60162306a36Sopenharmony_ci to = dsa_upstream_port(ds, from); 60262306a36Sopenharmony_ci 60362306a36Sopenharmony_ci dev_warn(ds->dev, 60462306a36Sopenharmony_ci "H topology detected, cutting RX from DSA link %d to CPU port %d to prevent TX packet loops\n", 60562306a36Sopenharmony_ci from, to); 60662306a36Sopenharmony_ci 60762306a36Sopenharmony_ci sja1105_port_allow_traffic(l2fwd, from, to, false); 60862306a36Sopenharmony_ci 60962306a36Sopenharmony_ci l2fwd[from].bc_domain &= ~BIT(to); 61062306a36Sopenharmony_ci l2fwd[from].fl_domain &= ~BIT(to); 61162306a36Sopenharmony_ci } 61262306a36Sopenharmony_ci 61362306a36Sopenharmony_ci /* Finally, manage the egress flooding domain. All ports start up with 61462306a36Sopenharmony_ci * flooding enabled, including the CPU port and DSA links. 61562306a36Sopenharmony_ci */ 61662306a36Sopenharmony_ci for (port = 0; port < ds->num_ports; port++) { 61762306a36Sopenharmony_ci if (dsa_is_unused_port(ds, port)) 61862306a36Sopenharmony_ci continue; 61962306a36Sopenharmony_ci 62062306a36Sopenharmony_ci priv->ucast_egress_floods |= BIT(port); 62162306a36Sopenharmony_ci priv->bcast_egress_floods |= BIT(port); 62262306a36Sopenharmony_ci } 62362306a36Sopenharmony_ci 62462306a36Sopenharmony_ci /* Next 8 entries define VLAN PCP mapping from ingress to egress. 62562306a36Sopenharmony_ci * Create a one-to-one mapping. 62662306a36Sopenharmony_ci */ 62762306a36Sopenharmony_ci for (tc = 0; tc < SJA1105_NUM_TC; tc++) { 62862306a36Sopenharmony_ci for (port = 0; port < ds->num_ports; port++) { 62962306a36Sopenharmony_ci if (dsa_is_unused_port(ds, port)) 63062306a36Sopenharmony_ci continue; 63162306a36Sopenharmony_ci 63262306a36Sopenharmony_ci l2fwd[ds->num_ports + tc].vlan_pmap[port] = tc; 63362306a36Sopenharmony_ci } 63462306a36Sopenharmony_ci 63562306a36Sopenharmony_ci l2fwd[ds->num_ports + tc].type_egrpcp2outputq = true; 63662306a36Sopenharmony_ci } 63762306a36Sopenharmony_ci 63862306a36Sopenharmony_ci return 0; 63962306a36Sopenharmony_ci} 64062306a36Sopenharmony_ci 64162306a36Sopenharmony_cistatic int sja1110_init_pcp_remapping(struct sja1105_private *priv) 64262306a36Sopenharmony_ci{ 64362306a36Sopenharmony_ci struct sja1110_pcp_remapping_entry *pcp_remap; 64462306a36Sopenharmony_ci struct dsa_switch *ds = priv->ds; 64562306a36Sopenharmony_ci struct sja1105_table *table; 64662306a36Sopenharmony_ci int port, tc; 64762306a36Sopenharmony_ci 64862306a36Sopenharmony_ci table = &priv->static_config.tables[BLK_IDX_PCP_REMAPPING]; 64962306a36Sopenharmony_ci 65062306a36Sopenharmony_ci /* Nothing to do for SJA1105 */ 65162306a36Sopenharmony_ci if (!table->ops->max_entry_count) 65262306a36Sopenharmony_ci return 0; 65362306a36Sopenharmony_ci 65462306a36Sopenharmony_ci if (table->entry_count) { 65562306a36Sopenharmony_ci kfree(table->entries); 65662306a36Sopenharmony_ci table->entry_count = 0; 65762306a36Sopenharmony_ci } 65862306a36Sopenharmony_ci 65962306a36Sopenharmony_ci table->entries = kcalloc(table->ops->max_entry_count, 66062306a36Sopenharmony_ci table->ops->unpacked_entry_size, GFP_KERNEL); 66162306a36Sopenharmony_ci if (!table->entries) 66262306a36Sopenharmony_ci return -ENOMEM; 66362306a36Sopenharmony_ci 66462306a36Sopenharmony_ci table->entry_count = table->ops->max_entry_count; 66562306a36Sopenharmony_ci 66662306a36Sopenharmony_ci pcp_remap = table->entries; 66762306a36Sopenharmony_ci 66862306a36Sopenharmony_ci /* Repeat the configuration done for vlan_pmap */ 66962306a36Sopenharmony_ci for (port = 0; port < ds->num_ports; port++) { 67062306a36Sopenharmony_ci if (dsa_is_unused_port(ds, port)) 67162306a36Sopenharmony_ci continue; 67262306a36Sopenharmony_ci 67362306a36Sopenharmony_ci for (tc = 0; tc < SJA1105_NUM_TC; tc++) 67462306a36Sopenharmony_ci pcp_remap[port].egrpcp[tc] = tc; 67562306a36Sopenharmony_ci } 67662306a36Sopenharmony_ci 67762306a36Sopenharmony_ci return 0; 67862306a36Sopenharmony_ci} 67962306a36Sopenharmony_ci 68062306a36Sopenharmony_cistatic int sja1105_init_l2_forwarding_params(struct sja1105_private *priv) 68162306a36Sopenharmony_ci{ 68262306a36Sopenharmony_ci struct sja1105_l2_forwarding_params_entry *l2fwd_params; 68362306a36Sopenharmony_ci struct sja1105_table *table; 68462306a36Sopenharmony_ci 68562306a36Sopenharmony_ci table = &priv->static_config.tables[BLK_IDX_L2_FORWARDING_PARAMS]; 68662306a36Sopenharmony_ci 68762306a36Sopenharmony_ci if (table->entry_count) { 68862306a36Sopenharmony_ci kfree(table->entries); 68962306a36Sopenharmony_ci table->entry_count = 0; 69062306a36Sopenharmony_ci } 69162306a36Sopenharmony_ci 69262306a36Sopenharmony_ci table->entries = kcalloc(table->ops->max_entry_count, 69362306a36Sopenharmony_ci table->ops->unpacked_entry_size, GFP_KERNEL); 69462306a36Sopenharmony_ci if (!table->entries) 69562306a36Sopenharmony_ci return -ENOMEM; 69662306a36Sopenharmony_ci 69762306a36Sopenharmony_ci table->entry_count = table->ops->max_entry_count; 69862306a36Sopenharmony_ci 69962306a36Sopenharmony_ci /* This table only has a single entry */ 70062306a36Sopenharmony_ci l2fwd_params = table->entries; 70162306a36Sopenharmony_ci 70262306a36Sopenharmony_ci /* Disallow dynamic reconfiguration of vlan_pmap */ 70362306a36Sopenharmony_ci l2fwd_params->max_dynp = 0; 70462306a36Sopenharmony_ci /* Use a single memory partition for all ingress queues */ 70562306a36Sopenharmony_ci l2fwd_params->part_spc[0] = priv->info->max_frame_mem; 70662306a36Sopenharmony_ci 70762306a36Sopenharmony_ci return 0; 70862306a36Sopenharmony_ci} 70962306a36Sopenharmony_ci 71062306a36Sopenharmony_civoid sja1105_frame_memory_partitioning(struct sja1105_private *priv) 71162306a36Sopenharmony_ci{ 71262306a36Sopenharmony_ci struct sja1105_l2_forwarding_params_entry *l2_fwd_params; 71362306a36Sopenharmony_ci struct sja1105_vl_forwarding_params_entry *vl_fwd_params; 71462306a36Sopenharmony_ci struct sja1105_table *table; 71562306a36Sopenharmony_ci 71662306a36Sopenharmony_ci table = &priv->static_config.tables[BLK_IDX_L2_FORWARDING_PARAMS]; 71762306a36Sopenharmony_ci l2_fwd_params = table->entries; 71862306a36Sopenharmony_ci l2_fwd_params->part_spc[0] = SJA1105_MAX_FRAME_MEMORY; 71962306a36Sopenharmony_ci 72062306a36Sopenharmony_ci /* If we have any critical-traffic virtual links, we need to reserve 72162306a36Sopenharmony_ci * some frame buffer memory for them. At the moment, hardcode the value 72262306a36Sopenharmony_ci * at 100 blocks of 128 bytes of memory each. This leaves 829 blocks 72362306a36Sopenharmony_ci * remaining for best-effort traffic. TODO: figure out a more flexible 72462306a36Sopenharmony_ci * way to perform the frame buffer partitioning. 72562306a36Sopenharmony_ci */ 72662306a36Sopenharmony_ci if (!priv->static_config.tables[BLK_IDX_VL_FORWARDING].entry_count) 72762306a36Sopenharmony_ci return; 72862306a36Sopenharmony_ci 72962306a36Sopenharmony_ci table = &priv->static_config.tables[BLK_IDX_VL_FORWARDING_PARAMS]; 73062306a36Sopenharmony_ci vl_fwd_params = table->entries; 73162306a36Sopenharmony_ci 73262306a36Sopenharmony_ci l2_fwd_params->part_spc[0] -= SJA1105_VL_FRAME_MEMORY; 73362306a36Sopenharmony_ci vl_fwd_params->partspc[0] = SJA1105_VL_FRAME_MEMORY; 73462306a36Sopenharmony_ci} 73562306a36Sopenharmony_ci 73662306a36Sopenharmony_ci/* SJA1110 TDMACONFIGIDX values: 73762306a36Sopenharmony_ci * 73862306a36Sopenharmony_ci * | 100 Mbps ports | 1Gbps ports | 2.5Gbps ports | Disabled ports 73962306a36Sopenharmony_ci * -----+----------------+---------------+---------------+--------------- 74062306a36Sopenharmony_ci * 0 | 0, [5:10] | [1:2] | [3:4] | retag 74162306a36Sopenharmony_ci * 1 |0, [5:10], retag| [1:2] | [3:4] | - 74262306a36Sopenharmony_ci * 2 | 0, [5:10] | [1:3], retag | 4 | - 74362306a36Sopenharmony_ci * 3 | 0, [5:10] |[1:2], 4, retag| 3 | - 74462306a36Sopenharmony_ci * 4 | 0, 2, [5:10] | 1, retag | [3:4] | - 74562306a36Sopenharmony_ci * 5 | 0, 1, [5:10] | 2, retag | [3:4] | - 74662306a36Sopenharmony_ci * 14 | 0, [5:10] | [1:4], retag | - | - 74762306a36Sopenharmony_ci * 15 | [5:10] | [0:4], retag | - | - 74862306a36Sopenharmony_ci */ 74962306a36Sopenharmony_cistatic void sja1110_select_tdmaconfigidx(struct sja1105_private *priv) 75062306a36Sopenharmony_ci{ 75162306a36Sopenharmony_ci struct sja1105_general_params_entry *general_params; 75262306a36Sopenharmony_ci struct sja1105_table *table; 75362306a36Sopenharmony_ci bool port_1_is_base_tx; 75462306a36Sopenharmony_ci bool port_3_is_2500; 75562306a36Sopenharmony_ci bool port_4_is_2500; 75662306a36Sopenharmony_ci u64 tdmaconfigidx; 75762306a36Sopenharmony_ci 75862306a36Sopenharmony_ci if (priv->info->device_id != SJA1110_DEVICE_ID) 75962306a36Sopenharmony_ci return; 76062306a36Sopenharmony_ci 76162306a36Sopenharmony_ci table = &priv->static_config.tables[BLK_IDX_GENERAL_PARAMS]; 76262306a36Sopenharmony_ci general_params = table->entries; 76362306a36Sopenharmony_ci 76462306a36Sopenharmony_ci /* All the settings below are "as opposed to SGMII", which is the 76562306a36Sopenharmony_ci * other pinmuxing option. 76662306a36Sopenharmony_ci */ 76762306a36Sopenharmony_ci port_1_is_base_tx = priv->phy_mode[1] == PHY_INTERFACE_MODE_INTERNAL; 76862306a36Sopenharmony_ci port_3_is_2500 = priv->phy_mode[3] == PHY_INTERFACE_MODE_2500BASEX; 76962306a36Sopenharmony_ci port_4_is_2500 = priv->phy_mode[4] == PHY_INTERFACE_MODE_2500BASEX; 77062306a36Sopenharmony_ci 77162306a36Sopenharmony_ci if (port_1_is_base_tx) 77262306a36Sopenharmony_ci /* Retagging port will operate at 1 Gbps */ 77362306a36Sopenharmony_ci tdmaconfigidx = 5; 77462306a36Sopenharmony_ci else if (port_3_is_2500 && port_4_is_2500) 77562306a36Sopenharmony_ci /* Retagging port will operate at 100 Mbps */ 77662306a36Sopenharmony_ci tdmaconfigidx = 1; 77762306a36Sopenharmony_ci else if (port_3_is_2500) 77862306a36Sopenharmony_ci /* Retagging port will operate at 1 Gbps */ 77962306a36Sopenharmony_ci tdmaconfigidx = 3; 78062306a36Sopenharmony_ci else if (port_4_is_2500) 78162306a36Sopenharmony_ci /* Retagging port will operate at 1 Gbps */ 78262306a36Sopenharmony_ci tdmaconfigidx = 2; 78362306a36Sopenharmony_ci else 78462306a36Sopenharmony_ci /* Retagging port will operate at 1 Gbps */ 78562306a36Sopenharmony_ci tdmaconfigidx = 14; 78662306a36Sopenharmony_ci 78762306a36Sopenharmony_ci general_params->tdmaconfigidx = tdmaconfigidx; 78862306a36Sopenharmony_ci} 78962306a36Sopenharmony_ci 79062306a36Sopenharmony_cistatic int sja1105_init_topology(struct sja1105_private *priv, 79162306a36Sopenharmony_ci struct sja1105_general_params_entry *general_params) 79262306a36Sopenharmony_ci{ 79362306a36Sopenharmony_ci struct dsa_switch *ds = priv->ds; 79462306a36Sopenharmony_ci int port; 79562306a36Sopenharmony_ci 79662306a36Sopenharmony_ci /* The host port is the destination for traffic matching mac_fltres1 79762306a36Sopenharmony_ci * and mac_fltres0 on all ports except itself. Default to an invalid 79862306a36Sopenharmony_ci * value. 79962306a36Sopenharmony_ci */ 80062306a36Sopenharmony_ci general_params->host_port = ds->num_ports; 80162306a36Sopenharmony_ci 80262306a36Sopenharmony_ci /* Link-local traffic received on casc_port will be forwarded 80362306a36Sopenharmony_ci * to host_port without embedding the source port and device ID 80462306a36Sopenharmony_ci * info in the destination MAC address, and no RX timestamps will be 80562306a36Sopenharmony_ci * taken either (presumably because it is a cascaded port and a 80662306a36Sopenharmony_ci * downstream SJA switch already did that). 80762306a36Sopenharmony_ci * To disable the feature, we need to do different things depending on 80862306a36Sopenharmony_ci * switch generation. On SJA1105 we need to set an invalid port, while 80962306a36Sopenharmony_ci * on SJA1110 which support multiple cascaded ports, this field is a 81062306a36Sopenharmony_ci * bitmask so it must be left zero. 81162306a36Sopenharmony_ci */ 81262306a36Sopenharmony_ci if (!priv->info->multiple_cascade_ports) 81362306a36Sopenharmony_ci general_params->casc_port = ds->num_ports; 81462306a36Sopenharmony_ci 81562306a36Sopenharmony_ci for (port = 0; port < ds->num_ports; port++) { 81662306a36Sopenharmony_ci bool is_upstream = dsa_is_upstream_port(ds, port); 81762306a36Sopenharmony_ci bool is_dsa_link = dsa_is_dsa_port(ds, port); 81862306a36Sopenharmony_ci 81962306a36Sopenharmony_ci /* Upstream ports can be dedicated CPU ports or 82062306a36Sopenharmony_ci * upstream-facing DSA links 82162306a36Sopenharmony_ci */ 82262306a36Sopenharmony_ci if (is_upstream) { 82362306a36Sopenharmony_ci if (general_params->host_port == ds->num_ports) { 82462306a36Sopenharmony_ci general_params->host_port = port; 82562306a36Sopenharmony_ci } else { 82662306a36Sopenharmony_ci dev_err(ds->dev, 82762306a36Sopenharmony_ci "Port %llu is already a host port, configuring %d as one too is not supported\n", 82862306a36Sopenharmony_ci general_params->host_port, port); 82962306a36Sopenharmony_ci return -EINVAL; 83062306a36Sopenharmony_ci } 83162306a36Sopenharmony_ci } 83262306a36Sopenharmony_ci 83362306a36Sopenharmony_ci /* Cascade ports are downstream-facing DSA links */ 83462306a36Sopenharmony_ci if (is_dsa_link && !is_upstream) { 83562306a36Sopenharmony_ci if (priv->info->multiple_cascade_ports) { 83662306a36Sopenharmony_ci general_params->casc_port |= BIT(port); 83762306a36Sopenharmony_ci } else if (general_params->casc_port == ds->num_ports) { 83862306a36Sopenharmony_ci general_params->casc_port = port; 83962306a36Sopenharmony_ci } else { 84062306a36Sopenharmony_ci dev_err(ds->dev, 84162306a36Sopenharmony_ci "Port %llu is already a cascade port, configuring %d as one too is not supported\n", 84262306a36Sopenharmony_ci general_params->casc_port, port); 84362306a36Sopenharmony_ci return -EINVAL; 84462306a36Sopenharmony_ci } 84562306a36Sopenharmony_ci } 84662306a36Sopenharmony_ci } 84762306a36Sopenharmony_ci 84862306a36Sopenharmony_ci if (general_params->host_port == ds->num_ports) { 84962306a36Sopenharmony_ci dev_err(ds->dev, "No host port configured\n"); 85062306a36Sopenharmony_ci return -EINVAL; 85162306a36Sopenharmony_ci } 85262306a36Sopenharmony_ci 85362306a36Sopenharmony_ci return 0; 85462306a36Sopenharmony_ci} 85562306a36Sopenharmony_ci 85662306a36Sopenharmony_cistatic int sja1105_init_general_params(struct sja1105_private *priv) 85762306a36Sopenharmony_ci{ 85862306a36Sopenharmony_ci struct sja1105_general_params_entry default_general_params = { 85962306a36Sopenharmony_ci /* Allow dynamic changing of the mirror port */ 86062306a36Sopenharmony_ci .mirr_ptacu = true, 86162306a36Sopenharmony_ci .switchid = priv->ds->index, 86262306a36Sopenharmony_ci /* Priority queue for link-local management frames 86362306a36Sopenharmony_ci * (both ingress to and egress from CPU - PTP, STP etc) 86462306a36Sopenharmony_ci */ 86562306a36Sopenharmony_ci .hostprio = 7, 86662306a36Sopenharmony_ci .mac_fltres1 = SJA1105_LINKLOCAL_FILTER_A, 86762306a36Sopenharmony_ci .mac_flt1 = SJA1105_LINKLOCAL_FILTER_A_MASK, 86862306a36Sopenharmony_ci .incl_srcpt1 = true, 86962306a36Sopenharmony_ci .send_meta1 = true, 87062306a36Sopenharmony_ci .mac_fltres0 = SJA1105_LINKLOCAL_FILTER_B, 87162306a36Sopenharmony_ci .mac_flt0 = SJA1105_LINKLOCAL_FILTER_B_MASK, 87262306a36Sopenharmony_ci .incl_srcpt0 = true, 87362306a36Sopenharmony_ci .send_meta0 = true, 87462306a36Sopenharmony_ci /* Default to an invalid value */ 87562306a36Sopenharmony_ci .mirr_port = priv->ds->num_ports, 87662306a36Sopenharmony_ci /* No TTEthernet */ 87762306a36Sopenharmony_ci .vllupformat = SJA1105_VL_FORMAT_PSFP, 87862306a36Sopenharmony_ci .vlmarker = 0, 87962306a36Sopenharmony_ci .vlmask = 0, 88062306a36Sopenharmony_ci /* Only update correctionField for 1-step PTP (L2 transport) */ 88162306a36Sopenharmony_ci .ignore2stf = 0, 88262306a36Sopenharmony_ci /* Forcefully disable VLAN filtering by telling 88362306a36Sopenharmony_ci * the switch that VLAN has a different EtherType. 88462306a36Sopenharmony_ci */ 88562306a36Sopenharmony_ci .tpid = ETH_P_SJA1105, 88662306a36Sopenharmony_ci .tpid2 = ETH_P_SJA1105, 88762306a36Sopenharmony_ci /* Enable the TTEthernet engine on SJA1110 */ 88862306a36Sopenharmony_ci .tte_en = true, 88962306a36Sopenharmony_ci /* Set up the EtherType for control packets on SJA1110 */ 89062306a36Sopenharmony_ci .header_type = ETH_P_SJA1110, 89162306a36Sopenharmony_ci }; 89262306a36Sopenharmony_ci struct sja1105_general_params_entry *general_params; 89362306a36Sopenharmony_ci struct sja1105_table *table; 89462306a36Sopenharmony_ci int rc; 89562306a36Sopenharmony_ci 89662306a36Sopenharmony_ci rc = sja1105_init_topology(priv, &default_general_params); 89762306a36Sopenharmony_ci if (rc) 89862306a36Sopenharmony_ci return rc; 89962306a36Sopenharmony_ci 90062306a36Sopenharmony_ci table = &priv->static_config.tables[BLK_IDX_GENERAL_PARAMS]; 90162306a36Sopenharmony_ci 90262306a36Sopenharmony_ci if (table->entry_count) { 90362306a36Sopenharmony_ci kfree(table->entries); 90462306a36Sopenharmony_ci table->entry_count = 0; 90562306a36Sopenharmony_ci } 90662306a36Sopenharmony_ci 90762306a36Sopenharmony_ci table->entries = kcalloc(table->ops->max_entry_count, 90862306a36Sopenharmony_ci table->ops->unpacked_entry_size, GFP_KERNEL); 90962306a36Sopenharmony_ci if (!table->entries) 91062306a36Sopenharmony_ci return -ENOMEM; 91162306a36Sopenharmony_ci 91262306a36Sopenharmony_ci table->entry_count = table->ops->max_entry_count; 91362306a36Sopenharmony_ci 91462306a36Sopenharmony_ci general_params = table->entries; 91562306a36Sopenharmony_ci 91662306a36Sopenharmony_ci /* This table only has a single entry */ 91762306a36Sopenharmony_ci general_params[0] = default_general_params; 91862306a36Sopenharmony_ci 91962306a36Sopenharmony_ci sja1110_select_tdmaconfigidx(priv); 92062306a36Sopenharmony_ci 92162306a36Sopenharmony_ci return 0; 92262306a36Sopenharmony_ci} 92362306a36Sopenharmony_ci 92462306a36Sopenharmony_cistatic int sja1105_init_avb_params(struct sja1105_private *priv) 92562306a36Sopenharmony_ci{ 92662306a36Sopenharmony_ci struct sja1105_avb_params_entry *avb; 92762306a36Sopenharmony_ci struct sja1105_table *table; 92862306a36Sopenharmony_ci 92962306a36Sopenharmony_ci table = &priv->static_config.tables[BLK_IDX_AVB_PARAMS]; 93062306a36Sopenharmony_ci 93162306a36Sopenharmony_ci /* Discard previous AVB Parameters Table */ 93262306a36Sopenharmony_ci if (table->entry_count) { 93362306a36Sopenharmony_ci kfree(table->entries); 93462306a36Sopenharmony_ci table->entry_count = 0; 93562306a36Sopenharmony_ci } 93662306a36Sopenharmony_ci 93762306a36Sopenharmony_ci table->entries = kcalloc(table->ops->max_entry_count, 93862306a36Sopenharmony_ci table->ops->unpacked_entry_size, GFP_KERNEL); 93962306a36Sopenharmony_ci if (!table->entries) 94062306a36Sopenharmony_ci return -ENOMEM; 94162306a36Sopenharmony_ci 94262306a36Sopenharmony_ci table->entry_count = table->ops->max_entry_count; 94362306a36Sopenharmony_ci 94462306a36Sopenharmony_ci avb = table->entries; 94562306a36Sopenharmony_ci 94662306a36Sopenharmony_ci /* Configure the MAC addresses for meta frames */ 94762306a36Sopenharmony_ci avb->destmeta = SJA1105_META_DMAC; 94862306a36Sopenharmony_ci avb->srcmeta = SJA1105_META_SMAC; 94962306a36Sopenharmony_ci /* On P/Q/R/S, configure the direction of the PTP_CLK pin as input by 95062306a36Sopenharmony_ci * default. This is because there might be boards with a hardware 95162306a36Sopenharmony_ci * layout where enabling the pin as output might cause an electrical 95262306a36Sopenharmony_ci * clash. On E/T the pin is always an output, which the board designers 95362306a36Sopenharmony_ci * probably already knew, so even if there are going to be electrical 95462306a36Sopenharmony_ci * issues, there's nothing we can do. 95562306a36Sopenharmony_ci */ 95662306a36Sopenharmony_ci avb->cas_master = false; 95762306a36Sopenharmony_ci 95862306a36Sopenharmony_ci return 0; 95962306a36Sopenharmony_ci} 96062306a36Sopenharmony_ci 96162306a36Sopenharmony_ci/* The L2 policing table is 2-stage. The table is looked up for each frame 96262306a36Sopenharmony_ci * according to the ingress port, whether it was broadcast or not, and the 96362306a36Sopenharmony_ci * classified traffic class (given by VLAN PCP). This portion of the lookup is 96462306a36Sopenharmony_ci * fixed, and gives access to the SHARINDX, an indirection register pointing 96562306a36Sopenharmony_ci * within the policing table itself, which is used to resolve the policer that 96662306a36Sopenharmony_ci * will be used for this frame. 96762306a36Sopenharmony_ci * 96862306a36Sopenharmony_ci * Stage 1 Stage 2 96962306a36Sopenharmony_ci * +------------+--------+ +---------------------------------+ 97062306a36Sopenharmony_ci * |Port 0 TC 0 |SHARINDX| | Policer 0: Rate, Burst, MTU | 97162306a36Sopenharmony_ci * +------------+--------+ +---------------------------------+ 97262306a36Sopenharmony_ci * |Port 0 TC 1 |SHARINDX| | Policer 1: Rate, Burst, MTU | 97362306a36Sopenharmony_ci * +------------+--------+ +---------------------------------+ 97462306a36Sopenharmony_ci * ... | Policer 2: Rate, Burst, MTU | 97562306a36Sopenharmony_ci * +------------+--------+ +---------------------------------+ 97662306a36Sopenharmony_ci * |Port 0 TC 7 |SHARINDX| | Policer 3: Rate, Burst, MTU | 97762306a36Sopenharmony_ci * +------------+--------+ +---------------------------------+ 97862306a36Sopenharmony_ci * |Port 1 TC 0 |SHARINDX| | Policer 4: Rate, Burst, MTU | 97962306a36Sopenharmony_ci * +------------+--------+ +---------------------------------+ 98062306a36Sopenharmony_ci * ... | Policer 5: Rate, Burst, MTU | 98162306a36Sopenharmony_ci * +------------+--------+ +---------------------------------+ 98262306a36Sopenharmony_ci * |Port 1 TC 7 |SHARINDX| | Policer 6: Rate, Burst, MTU | 98362306a36Sopenharmony_ci * +------------+--------+ +---------------------------------+ 98462306a36Sopenharmony_ci * ... | Policer 7: Rate, Burst, MTU | 98562306a36Sopenharmony_ci * +------------+--------+ +---------------------------------+ 98662306a36Sopenharmony_ci * |Port 4 TC 7 |SHARINDX| ... 98762306a36Sopenharmony_ci * +------------+--------+ 98862306a36Sopenharmony_ci * |Port 0 BCAST|SHARINDX| ... 98962306a36Sopenharmony_ci * +------------+--------+ 99062306a36Sopenharmony_ci * |Port 1 BCAST|SHARINDX| ... 99162306a36Sopenharmony_ci * +------------+--------+ 99262306a36Sopenharmony_ci * ... ... 99362306a36Sopenharmony_ci * +------------+--------+ +---------------------------------+ 99462306a36Sopenharmony_ci * |Port 4 BCAST|SHARINDX| | Policer 44: Rate, Burst, MTU | 99562306a36Sopenharmony_ci * +------------+--------+ +---------------------------------+ 99662306a36Sopenharmony_ci * 99762306a36Sopenharmony_ci * In this driver, we shall use policers 0-4 as statically alocated port 99862306a36Sopenharmony_ci * (matchall) policers. So we need to make the SHARINDX for all lookups 99962306a36Sopenharmony_ci * corresponding to this ingress port (8 VLAN PCP lookups and 1 broadcast 100062306a36Sopenharmony_ci * lookup) equal. 100162306a36Sopenharmony_ci * The remaining policers (40) shall be dynamically allocated for flower 100262306a36Sopenharmony_ci * policers, where the key is either vlan_prio or dst_mac ff:ff:ff:ff:ff:ff. 100362306a36Sopenharmony_ci */ 100462306a36Sopenharmony_ci#define SJA1105_RATE_MBPS(speed) (((speed) * 64000) / 1000) 100562306a36Sopenharmony_ci 100662306a36Sopenharmony_cistatic int sja1105_init_l2_policing(struct sja1105_private *priv) 100762306a36Sopenharmony_ci{ 100862306a36Sopenharmony_ci struct sja1105_l2_policing_entry *policing; 100962306a36Sopenharmony_ci struct dsa_switch *ds = priv->ds; 101062306a36Sopenharmony_ci struct sja1105_table *table; 101162306a36Sopenharmony_ci int port, tc; 101262306a36Sopenharmony_ci 101362306a36Sopenharmony_ci table = &priv->static_config.tables[BLK_IDX_L2_POLICING]; 101462306a36Sopenharmony_ci 101562306a36Sopenharmony_ci /* Discard previous L2 Policing Table */ 101662306a36Sopenharmony_ci if (table->entry_count) { 101762306a36Sopenharmony_ci kfree(table->entries); 101862306a36Sopenharmony_ci table->entry_count = 0; 101962306a36Sopenharmony_ci } 102062306a36Sopenharmony_ci 102162306a36Sopenharmony_ci table->entries = kcalloc(table->ops->max_entry_count, 102262306a36Sopenharmony_ci table->ops->unpacked_entry_size, GFP_KERNEL); 102362306a36Sopenharmony_ci if (!table->entries) 102462306a36Sopenharmony_ci return -ENOMEM; 102562306a36Sopenharmony_ci 102662306a36Sopenharmony_ci table->entry_count = table->ops->max_entry_count; 102762306a36Sopenharmony_ci 102862306a36Sopenharmony_ci policing = table->entries; 102962306a36Sopenharmony_ci 103062306a36Sopenharmony_ci /* Setup shared indices for the matchall policers */ 103162306a36Sopenharmony_ci for (port = 0; port < ds->num_ports; port++) { 103262306a36Sopenharmony_ci int mcast = (ds->num_ports * (SJA1105_NUM_TC + 1)) + port; 103362306a36Sopenharmony_ci int bcast = (ds->num_ports * SJA1105_NUM_TC) + port; 103462306a36Sopenharmony_ci 103562306a36Sopenharmony_ci for (tc = 0; tc < SJA1105_NUM_TC; tc++) 103662306a36Sopenharmony_ci policing[port * SJA1105_NUM_TC + tc].sharindx = port; 103762306a36Sopenharmony_ci 103862306a36Sopenharmony_ci policing[bcast].sharindx = port; 103962306a36Sopenharmony_ci /* Only SJA1110 has multicast policers */ 104062306a36Sopenharmony_ci if (mcast < table->ops->max_entry_count) 104162306a36Sopenharmony_ci policing[mcast].sharindx = port; 104262306a36Sopenharmony_ci } 104362306a36Sopenharmony_ci 104462306a36Sopenharmony_ci /* Setup the matchall policer parameters */ 104562306a36Sopenharmony_ci for (port = 0; port < ds->num_ports; port++) { 104662306a36Sopenharmony_ci int mtu = VLAN_ETH_FRAME_LEN + ETH_FCS_LEN; 104762306a36Sopenharmony_ci 104862306a36Sopenharmony_ci if (dsa_is_cpu_port(ds, port) || dsa_is_dsa_port(ds, port)) 104962306a36Sopenharmony_ci mtu += VLAN_HLEN; 105062306a36Sopenharmony_ci 105162306a36Sopenharmony_ci policing[port].smax = 65535; /* Burst size in bytes */ 105262306a36Sopenharmony_ci policing[port].rate = SJA1105_RATE_MBPS(1000); 105362306a36Sopenharmony_ci policing[port].maxlen = mtu; 105462306a36Sopenharmony_ci policing[port].partition = 0; 105562306a36Sopenharmony_ci } 105662306a36Sopenharmony_ci 105762306a36Sopenharmony_ci return 0; 105862306a36Sopenharmony_ci} 105962306a36Sopenharmony_ci 106062306a36Sopenharmony_cistatic int sja1105_static_config_load(struct sja1105_private *priv) 106162306a36Sopenharmony_ci{ 106262306a36Sopenharmony_ci int rc; 106362306a36Sopenharmony_ci 106462306a36Sopenharmony_ci sja1105_static_config_free(&priv->static_config); 106562306a36Sopenharmony_ci rc = sja1105_static_config_init(&priv->static_config, 106662306a36Sopenharmony_ci priv->info->static_ops, 106762306a36Sopenharmony_ci priv->info->device_id); 106862306a36Sopenharmony_ci if (rc) 106962306a36Sopenharmony_ci return rc; 107062306a36Sopenharmony_ci 107162306a36Sopenharmony_ci /* Build static configuration */ 107262306a36Sopenharmony_ci rc = sja1105_init_mac_settings(priv); 107362306a36Sopenharmony_ci if (rc < 0) 107462306a36Sopenharmony_ci return rc; 107562306a36Sopenharmony_ci rc = sja1105_init_mii_settings(priv); 107662306a36Sopenharmony_ci if (rc < 0) 107762306a36Sopenharmony_ci return rc; 107862306a36Sopenharmony_ci rc = sja1105_init_static_fdb(priv); 107962306a36Sopenharmony_ci if (rc < 0) 108062306a36Sopenharmony_ci return rc; 108162306a36Sopenharmony_ci rc = sja1105_init_static_vlan(priv); 108262306a36Sopenharmony_ci if (rc < 0) 108362306a36Sopenharmony_ci return rc; 108462306a36Sopenharmony_ci rc = sja1105_init_l2_lookup_params(priv); 108562306a36Sopenharmony_ci if (rc < 0) 108662306a36Sopenharmony_ci return rc; 108762306a36Sopenharmony_ci rc = sja1105_init_l2_forwarding(priv); 108862306a36Sopenharmony_ci if (rc < 0) 108962306a36Sopenharmony_ci return rc; 109062306a36Sopenharmony_ci rc = sja1105_init_l2_forwarding_params(priv); 109162306a36Sopenharmony_ci if (rc < 0) 109262306a36Sopenharmony_ci return rc; 109362306a36Sopenharmony_ci rc = sja1105_init_l2_policing(priv); 109462306a36Sopenharmony_ci if (rc < 0) 109562306a36Sopenharmony_ci return rc; 109662306a36Sopenharmony_ci rc = sja1105_init_general_params(priv); 109762306a36Sopenharmony_ci if (rc < 0) 109862306a36Sopenharmony_ci return rc; 109962306a36Sopenharmony_ci rc = sja1105_init_avb_params(priv); 110062306a36Sopenharmony_ci if (rc < 0) 110162306a36Sopenharmony_ci return rc; 110262306a36Sopenharmony_ci rc = sja1110_init_pcp_remapping(priv); 110362306a36Sopenharmony_ci if (rc < 0) 110462306a36Sopenharmony_ci return rc; 110562306a36Sopenharmony_ci 110662306a36Sopenharmony_ci /* Send initial configuration to hardware via SPI */ 110762306a36Sopenharmony_ci return sja1105_static_config_upload(priv); 110862306a36Sopenharmony_ci} 110962306a36Sopenharmony_ci 111062306a36Sopenharmony_ci/* This is the "new way" for a MAC driver to configure its RGMII delay lines, 111162306a36Sopenharmony_ci * based on the explicit "rx-internal-delay-ps" and "tx-internal-delay-ps" 111262306a36Sopenharmony_ci * properties. It has the advantage of working with fixed links and with PHYs 111362306a36Sopenharmony_ci * that apply RGMII delays too, and the MAC driver needs not perform any 111462306a36Sopenharmony_ci * special checks. 111562306a36Sopenharmony_ci * 111662306a36Sopenharmony_ci * Previously we were acting upon the "phy-mode" property when we were 111762306a36Sopenharmony_ci * operating in fixed-link, basically acting as a PHY, but with a reversed 111862306a36Sopenharmony_ci * interpretation: PHY_INTERFACE_MODE_RGMII_TXID means that the MAC should 111962306a36Sopenharmony_ci * behave as if it is connected to a PHY which has applied RGMII delays in the 112062306a36Sopenharmony_ci * TX direction. So if anything, RX delays should have been added by the MAC, 112162306a36Sopenharmony_ci * but we were adding TX delays. 112262306a36Sopenharmony_ci * 112362306a36Sopenharmony_ci * If the "{rx,tx}-internal-delay-ps" properties are not specified, we fall 112462306a36Sopenharmony_ci * back to the legacy behavior and apply delays on fixed-link ports based on 112562306a36Sopenharmony_ci * the reverse interpretation of the phy-mode. This is a deviation from the 112662306a36Sopenharmony_ci * expected default behavior which is to simply apply no delays. To achieve 112762306a36Sopenharmony_ci * that behavior with the new bindings, it is mandatory to specify 112862306a36Sopenharmony_ci * "{rx,tx}-internal-delay-ps" with a value of 0. 112962306a36Sopenharmony_ci */ 113062306a36Sopenharmony_cistatic int sja1105_parse_rgmii_delays(struct sja1105_private *priv, int port, 113162306a36Sopenharmony_ci struct device_node *port_dn) 113262306a36Sopenharmony_ci{ 113362306a36Sopenharmony_ci phy_interface_t phy_mode = priv->phy_mode[port]; 113462306a36Sopenharmony_ci struct device *dev = &priv->spidev->dev; 113562306a36Sopenharmony_ci int rx_delay = -1, tx_delay = -1; 113662306a36Sopenharmony_ci 113762306a36Sopenharmony_ci if (!phy_interface_mode_is_rgmii(phy_mode)) 113862306a36Sopenharmony_ci return 0; 113962306a36Sopenharmony_ci 114062306a36Sopenharmony_ci of_property_read_u32(port_dn, "rx-internal-delay-ps", &rx_delay); 114162306a36Sopenharmony_ci of_property_read_u32(port_dn, "tx-internal-delay-ps", &tx_delay); 114262306a36Sopenharmony_ci 114362306a36Sopenharmony_ci if (rx_delay == -1 && tx_delay == -1 && priv->fixed_link[port]) { 114462306a36Sopenharmony_ci dev_warn(dev, 114562306a36Sopenharmony_ci "Port %d interpreting RGMII delay settings based on \"phy-mode\" property, " 114662306a36Sopenharmony_ci "please update device tree to specify \"rx-internal-delay-ps\" and " 114762306a36Sopenharmony_ci "\"tx-internal-delay-ps\"", 114862306a36Sopenharmony_ci port); 114962306a36Sopenharmony_ci 115062306a36Sopenharmony_ci if (phy_mode == PHY_INTERFACE_MODE_RGMII_RXID || 115162306a36Sopenharmony_ci phy_mode == PHY_INTERFACE_MODE_RGMII_ID) 115262306a36Sopenharmony_ci rx_delay = 2000; 115362306a36Sopenharmony_ci 115462306a36Sopenharmony_ci if (phy_mode == PHY_INTERFACE_MODE_RGMII_TXID || 115562306a36Sopenharmony_ci phy_mode == PHY_INTERFACE_MODE_RGMII_ID) 115662306a36Sopenharmony_ci tx_delay = 2000; 115762306a36Sopenharmony_ci } 115862306a36Sopenharmony_ci 115962306a36Sopenharmony_ci if (rx_delay < 0) 116062306a36Sopenharmony_ci rx_delay = 0; 116162306a36Sopenharmony_ci if (tx_delay < 0) 116262306a36Sopenharmony_ci tx_delay = 0; 116362306a36Sopenharmony_ci 116462306a36Sopenharmony_ci if ((rx_delay || tx_delay) && !priv->info->setup_rgmii_delay) { 116562306a36Sopenharmony_ci dev_err(dev, "Chip cannot apply RGMII delays\n"); 116662306a36Sopenharmony_ci return -EINVAL; 116762306a36Sopenharmony_ci } 116862306a36Sopenharmony_ci 116962306a36Sopenharmony_ci if ((rx_delay && rx_delay < SJA1105_RGMII_DELAY_MIN_PS) || 117062306a36Sopenharmony_ci (tx_delay && tx_delay < SJA1105_RGMII_DELAY_MIN_PS) || 117162306a36Sopenharmony_ci (rx_delay > SJA1105_RGMII_DELAY_MAX_PS) || 117262306a36Sopenharmony_ci (tx_delay > SJA1105_RGMII_DELAY_MAX_PS)) { 117362306a36Sopenharmony_ci dev_err(dev, 117462306a36Sopenharmony_ci "port %d RGMII delay values out of range, must be between %d and %d ps\n", 117562306a36Sopenharmony_ci port, SJA1105_RGMII_DELAY_MIN_PS, SJA1105_RGMII_DELAY_MAX_PS); 117662306a36Sopenharmony_ci return -ERANGE; 117762306a36Sopenharmony_ci } 117862306a36Sopenharmony_ci 117962306a36Sopenharmony_ci priv->rgmii_rx_delay_ps[port] = rx_delay; 118062306a36Sopenharmony_ci priv->rgmii_tx_delay_ps[port] = tx_delay; 118162306a36Sopenharmony_ci 118262306a36Sopenharmony_ci return 0; 118362306a36Sopenharmony_ci} 118462306a36Sopenharmony_ci 118562306a36Sopenharmony_cistatic int sja1105_parse_ports_node(struct sja1105_private *priv, 118662306a36Sopenharmony_ci struct device_node *ports_node) 118762306a36Sopenharmony_ci{ 118862306a36Sopenharmony_ci struct device *dev = &priv->spidev->dev; 118962306a36Sopenharmony_ci struct device_node *child; 119062306a36Sopenharmony_ci 119162306a36Sopenharmony_ci for_each_available_child_of_node(ports_node, child) { 119262306a36Sopenharmony_ci struct device_node *phy_node; 119362306a36Sopenharmony_ci phy_interface_t phy_mode; 119462306a36Sopenharmony_ci u32 index; 119562306a36Sopenharmony_ci int err; 119662306a36Sopenharmony_ci 119762306a36Sopenharmony_ci /* Get switch port number from DT */ 119862306a36Sopenharmony_ci if (of_property_read_u32(child, "reg", &index) < 0) { 119962306a36Sopenharmony_ci dev_err(dev, "Port number not defined in device tree " 120062306a36Sopenharmony_ci "(property \"reg\")\n"); 120162306a36Sopenharmony_ci of_node_put(child); 120262306a36Sopenharmony_ci return -ENODEV; 120362306a36Sopenharmony_ci } 120462306a36Sopenharmony_ci 120562306a36Sopenharmony_ci /* Get PHY mode from DT */ 120662306a36Sopenharmony_ci err = of_get_phy_mode(child, &phy_mode); 120762306a36Sopenharmony_ci if (err) { 120862306a36Sopenharmony_ci dev_err(dev, "Failed to read phy-mode or " 120962306a36Sopenharmony_ci "phy-interface-type property for port %d\n", 121062306a36Sopenharmony_ci index); 121162306a36Sopenharmony_ci of_node_put(child); 121262306a36Sopenharmony_ci return -ENODEV; 121362306a36Sopenharmony_ci } 121462306a36Sopenharmony_ci 121562306a36Sopenharmony_ci phy_node = of_parse_phandle(child, "phy-handle", 0); 121662306a36Sopenharmony_ci if (!phy_node) { 121762306a36Sopenharmony_ci if (!of_phy_is_fixed_link(child)) { 121862306a36Sopenharmony_ci dev_err(dev, "phy-handle or fixed-link " 121962306a36Sopenharmony_ci "properties missing!\n"); 122062306a36Sopenharmony_ci of_node_put(child); 122162306a36Sopenharmony_ci return -ENODEV; 122262306a36Sopenharmony_ci } 122362306a36Sopenharmony_ci /* phy-handle is missing, but fixed-link isn't. 122462306a36Sopenharmony_ci * So it's a fixed link. Default to PHY role. 122562306a36Sopenharmony_ci */ 122662306a36Sopenharmony_ci priv->fixed_link[index] = true; 122762306a36Sopenharmony_ci } else { 122862306a36Sopenharmony_ci of_node_put(phy_node); 122962306a36Sopenharmony_ci } 123062306a36Sopenharmony_ci 123162306a36Sopenharmony_ci priv->phy_mode[index] = phy_mode; 123262306a36Sopenharmony_ci 123362306a36Sopenharmony_ci err = sja1105_parse_rgmii_delays(priv, index, child); 123462306a36Sopenharmony_ci if (err) { 123562306a36Sopenharmony_ci of_node_put(child); 123662306a36Sopenharmony_ci return err; 123762306a36Sopenharmony_ci } 123862306a36Sopenharmony_ci } 123962306a36Sopenharmony_ci 124062306a36Sopenharmony_ci return 0; 124162306a36Sopenharmony_ci} 124262306a36Sopenharmony_ci 124362306a36Sopenharmony_cistatic int sja1105_parse_dt(struct sja1105_private *priv) 124462306a36Sopenharmony_ci{ 124562306a36Sopenharmony_ci struct device *dev = &priv->spidev->dev; 124662306a36Sopenharmony_ci struct device_node *switch_node = dev->of_node; 124762306a36Sopenharmony_ci struct device_node *ports_node; 124862306a36Sopenharmony_ci int rc; 124962306a36Sopenharmony_ci 125062306a36Sopenharmony_ci ports_node = of_get_child_by_name(switch_node, "ports"); 125162306a36Sopenharmony_ci if (!ports_node) 125262306a36Sopenharmony_ci ports_node = of_get_child_by_name(switch_node, "ethernet-ports"); 125362306a36Sopenharmony_ci if (!ports_node) { 125462306a36Sopenharmony_ci dev_err(dev, "Incorrect bindings: absent \"ports\" node\n"); 125562306a36Sopenharmony_ci return -ENODEV; 125662306a36Sopenharmony_ci } 125762306a36Sopenharmony_ci 125862306a36Sopenharmony_ci rc = sja1105_parse_ports_node(priv, ports_node); 125962306a36Sopenharmony_ci of_node_put(ports_node); 126062306a36Sopenharmony_ci 126162306a36Sopenharmony_ci return rc; 126262306a36Sopenharmony_ci} 126362306a36Sopenharmony_ci 126462306a36Sopenharmony_ci/* Convert link speed from SJA1105 to ethtool encoding */ 126562306a36Sopenharmony_cistatic int sja1105_port_speed_to_ethtool(struct sja1105_private *priv, 126662306a36Sopenharmony_ci u64 speed) 126762306a36Sopenharmony_ci{ 126862306a36Sopenharmony_ci if (speed == priv->info->port_speed[SJA1105_SPEED_10MBPS]) 126962306a36Sopenharmony_ci return SPEED_10; 127062306a36Sopenharmony_ci if (speed == priv->info->port_speed[SJA1105_SPEED_100MBPS]) 127162306a36Sopenharmony_ci return SPEED_100; 127262306a36Sopenharmony_ci if (speed == priv->info->port_speed[SJA1105_SPEED_1000MBPS]) 127362306a36Sopenharmony_ci return SPEED_1000; 127462306a36Sopenharmony_ci if (speed == priv->info->port_speed[SJA1105_SPEED_2500MBPS]) 127562306a36Sopenharmony_ci return SPEED_2500; 127662306a36Sopenharmony_ci return SPEED_UNKNOWN; 127762306a36Sopenharmony_ci} 127862306a36Sopenharmony_ci 127962306a36Sopenharmony_ci/* Set link speed in the MAC configuration for a specific port. */ 128062306a36Sopenharmony_cistatic int sja1105_adjust_port_config(struct sja1105_private *priv, int port, 128162306a36Sopenharmony_ci int speed_mbps) 128262306a36Sopenharmony_ci{ 128362306a36Sopenharmony_ci struct sja1105_mac_config_entry *mac; 128462306a36Sopenharmony_ci struct device *dev = priv->ds->dev; 128562306a36Sopenharmony_ci u64 speed; 128662306a36Sopenharmony_ci int rc; 128762306a36Sopenharmony_ci 128862306a36Sopenharmony_ci /* On P/Q/R/S, one can read from the device via the MAC reconfiguration 128962306a36Sopenharmony_ci * tables. On E/T, MAC reconfig tables are not readable, only writable. 129062306a36Sopenharmony_ci * We have to *know* what the MAC looks like. For the sake of keeping 129162306a36Sopenharmony_ci * the code common, we'll use the static configuration tables as a 129262306a36Sopenharmony_ci * reasonable approximation for both E/T and P/Q/R/S. 129362306a36Sopenharmony_ci */ 129462306a36Sopenharmony_ci mac = priv->static_config.tables[BLK_IDX_MAC_CONFIG].entries; 129562306a36Sopenharmony_ci 129662306a36Sopenharmony_ci switch (speed_mbps) { 129762306a36Sopenharmony_ci case SPEED_UNKNOWN: 129862306a36Sopenharmony_ci /* PHYLINK called sja1105_mac_config() to inform us about 129962306a36Sopenharmony_ci * the state->interface, but AN has not completed and the 130062306a36Sopenharmony_ci * speed is not yet valid. UM10944.pdf says that setting 130162306a36Sopenharmony_ci * SJA1105_SPEED_AUTO at runtime disables the port, so that is 130262306a36Sopenharmony_ci * ok for power consumption in case AN will never complete - 130362306a36Sopenharmony_ci * otherwise PHYLINK should come back with a new update. 130462306a36Sopenharmony_ci */ 130562306a36Sopenharmony_ci speed = priv->info->port_speed[SJA1105_SPEED_AUTO]; 130662306a36Sopenharmony_ci break; 130762306a36Sopenharmony_ci case SPEED_10: 130862306a36Sopenharmony_ci speed = priv->info->port_speed[SJA1105_SPEED_10MBPS]; 130962306a36Sopenharmony_ci break; 131062306a36Sopenharmony_ci case SPEED_100: 131162306a36Sopenharmony_ci speed = priv->info->port_speed[SJA1105_SPEED_100MBPS]; 131262306a36Sopenharmony_ci break; 131362306a36Sopenharmony_ci case SPEED_1000: 131462306a36Sopenharmony_ci speed = priv->info->port_speed[SJA1105_SPEED_1000MBPS]; 131562306a36Sopenharmony_ci break; 131662306a36Sopenharmony_ci case SPEED_2500: 131762306a36Sopenharmony_ci speed = priv->info->port_speed[SJA1105_SPEED_2500MBPS]; 131862306a36Sopenharmony_ci break; 131962306a36Sopenharmony_ci default: 132062306a36Sopenharmony_ci dev_err(dev, "Invalid speed %iMbps\n", speed_mbps); 132162306a36Sopenharmony_ci return -EINVAL; 132262306a36Sopenharmony_ci } 132362306a36Sopenharmony_ci 132462306a36Sopenharmony_ci /* Overwrite SJA1105_SPEED_AUTO from the static MAC configuration 132562306a36Sopenharmony_ci * table, since this will be used for the clocking setup, and we no 132662306a36Sopenharmony_ci * longer need to store it in the static config (already told hardware 132762306a36Sopenharmony_ci * we want auto during upload phase). 132862306a36Sopenharmony_ci * Actually for the SGMII port, the MAC is fixed at 1 Gbps and 132962306a36Sopenharmony_ci * we need to configure the PCS only (if even that). 133062306a36Sopenharmony_ci */ 133162306a36Sopenharmony_ci if (priv->phy_mode[port] == PHY_INTERFACE_MODE_SGMII) 133262306a36Sopenharmony_ci mac[port].speed = priv->info->port_speed[SJA1105_SPEED_1000MBPS]; 133362306a36Sopenharmony_ci else if (priv->phy_mode[port] == PHY_INTERFACE_MODE_2500BASEX) 133462306a36Sopenharmony_ci mac[port].speed = priv->info->port_speed[SJA1105_SPEED_2500MBPS]; 133562306a36Sopenharmony_ci else 133662306a36Sopenharmony_ci mac[port].speed = speed; 133762306a36Sopenharmony_ci 133862306a36Sopenharmony_ci /* Write to the dynamic reconfiguration tables */ 133962306a36Sopenharmony_ci rc = sja1105_dynamic_config_write(priv, BLK_IDX_MAC_CONFIG, port, 134062306a36Sopenharmony_ci &mac[port], true); 134162306a36Sopenharmony_ci if (rc < 0) { 134262306a36Sopenharmony_ci dev_err(dev, "Failed to write MAC config: %d\n", rc); 134362306a36Sopenharmony_ci return rc; 134462306a36Sopenharmony_ci } 134562306a36Sopenharmony_ci 134662306a36Sopenharmony_ci /* Reconfigure the PLLs for the RGMII interfaces (required 125 MHz at 134762306a36Sopenharmony_ci * gigabit, 25 MHz at 100 Mbps and 2.5 MHz at 10 Mbps). For MII and 134862306a36Sopenharmony_ci * RMII no change of the clock setup is required. Actually, changing 134962306a36Sopenharmony_ci * the clock setup does interrupt the clock signal for a certain time 135062306a36Sopenharmony_ci * which causes trouble for all PHYs relying on this signal. 135162306a36Sopenharmony_ci */ 135262306a36Sopenharmony_ci if (!phy_interface_mode_is_rgmii(priv->phy_mode[port])) 135362306a36Sopenharmony_ci return 0; 135462306a36Sopenharmony_ci 135562306a36Sopenharmony_ci return sja1105_clocking_setup_port(priv, port); 135662306a36Sopenharmony_ci} 135762306a36Sopenharmony_ci 135862306a36Sopenharmony_cistatic struct phylink_pcs * 135962306a36Sopenharmony_cisja1105_mac_select_pcs(struct dsa_switch *ds, int port, phy_interface_t iface) 136062306a36Sopenharmony_ci{ 136162306a36Sopenharmony_ci struct sja1105_private *priv = ds->priv; 136262306a36Sopenharmony_ci struct dw_xpcs *xpcs = priv->xpcs[port]; 136362306a36Sopenharmony_ci 136462306a36Sopenharmony_ci if (xpcs) 136562306a36Sopenharmony_ci return &xpcs->pcs; 136662306a36Sopenharmony_ci 136762306a36Sopenharmony_ci return NULL; 136862306a36Sopenharmony_ci} 136962306a36Sopenharmony_ci 137062306a36Sopenharmony_cistatic void sja1105_mac_link_down(struct dsa_switch *ds, int port, 137162306a36Sopenharmony_ci unsigned int mode, 137262306a36Sopenharmony_ci phy_interface_t interface) 137362306a36Sopenharmony_ci{ 137462306a36Sopenharmony_ci sja1105_inhibit_tx(ds->priv, BIT(port), true); 137562306a36Sopenharmony_ci} 137662306a36Sopenharmony_ci 137762306a36Sopenharmony_cistatic void sja1105_mac_link_up(struct dsa_switch *ds, int port, 137862306a36Sopenharmony_ci unsigned int mode, 137962306a36Sopenharmony_ci phy_interface_t interface, 138062306a36Sopenharmony_ci struct phy_device *phydev, 138162306a36Sopenharmony_ci int speed, int duplex, 138262306a36Sopenharmony_ci bool tx_pause, bool rx_pause) 138362306a36Sopenharmony_ci{ 138462306a36Sopenharmony_ci struct sja1105_private *priv = ds->priv; 138562306a36Sopenharmony_ci 138662306a36Sopenharmony_ci sja1105_adjust_port_config(priv, port, speed); 138762306a36Sopenharmony_ci 138862306a36Sopenharmony_ci sja1105_inhibit_tx(priv, BIT(port), false); 138962306a36Sopenharmony_ci} 139062306a36Sopenharmony_ci 139162306a36Sopenharmony_cistatic void sja1105_phylink_get_caps(struct dsa_switch *ds, int port, 139262306a36Sopenharmony_ci struct phylink_config *config) 139362306a36Sopenharmony_ci{ 139462306a36Sopenharmony_ci struct sja1105_private *priv = ds->priv; 139562306a36Sopenharmony_ci struct sja1105_xmii_params_entry *mii; 139662306a36Sopenharmony_ci phy_interface_t phy_mode; 139762306a36Sopenharmony_ci 139862306a36Sopenharmony_ci phy_mode = priv->phy_mode[port]; 139962306a36Sopenharmony_ci if (phy_mode == PHY_INTERFACE_MODE_SGMII || 140062306a36Sopenharmony_ci phy_mode == PHY_INTERFACE_MODE_2500BASEX) { 140162306a36Sopenharmony_ci /* Changing the PHY mode on SERDES ports is possible and makes 140262306a36Sopenharmony_ci * sense, because that is done through the XPCS. We allow 140362306a36Sopenharmony_ci * changes between SGMII and 2500base-X. 140462306a36Sopenharmony_ci */ 140562306a36Sopenharmony_ci if (priv->info->supports_sgmii[port]) 140662306a36Sopenharmony_ci __set_bit(PHY_INTERFACE_MODE_SGMII, 140762306a36Sopenharmony_ci config->supported_interfaces); 140862306a36Sopenharmony_ci 140962306a36Sopenharmony_ci if (priv->info->supports_2500basex[port]) 141062306a36Sopenharmony_ci __set_bit(PHY_INTERFACE_MODE_2500BASEX, 141162306a36Sopenharmony_ci config->supported_interfaces); 141262306a36Sopenharmony_ci } else { 141362306a36Sopenharmony_ci /* The SJA1105 MAC programming model is through the static 141462306a36Sopenharmony_ci * config (the xMII Mode table cannot be dynamically 141562306a36Sopenharmony_ci * reconfigured), and we have to program that early. 141662306a36Sopenharmony_ci */ 141762306a36Sopenharmony_ci __set_bit(phy_mode, config->supported_interfaces); 141862306a36Sopenharmony_ci } 141962306a36Sopenharmony_ci 142062306a36Sopenharmony_ci /* The MAC does not support pause frames, and also doesn't 142162306a36Sopenharmony_ci * support half-duplex traffic modes. 142262306a36Sopenharmony_ci */ 142362306a36Sopenharmony_ci config->mac_capabilities = MAC_10FD | MAC_100FD; 142462306a36Sopenharmony_ci 142562306a36Sopenharmony_ci mii = priv->static_config.tables[BLK_IDX_XMII_PARAMS].entries; 142662306a36Sopenharmony_ci if (mii->xmii_mode[port] == XMII_MODE_RGMII || 142762306a36Sopenharmony_ci mii->xmii_mode[port] == XMII_MODE_SGMII) 142862306a36Sopenharmony_ci config->mac_capabilities |= MAC_1000FD; 142962306a36Sopenharmony_ci 143062306a36Sopenharmony_ci if (priv->info->supports_2500basex[port]) 143162306a36Sopenharmony_ci config->mac_capabilities |= MAC_2500FD; 143262306a36Sopenharmony_ci} 143362306a36Sopenharmony_ci 143462306a36Sopenharmony_cistatic int 143562306a36Sopenharmony_cisja1105_find_static_fdb_entry(struct sja1105_private *priv, int port, 143662306a36Sopenharmony_ci const struct sja1105_l2_lookup_entry *requested) 143762306a36Sopenharmony_ci{ 143862306a36Sopenharmony_ci struct sja1105_l2_lookup_entry *l2_lookup; 143962306a36Sopenharmony_ci struct sja1105_table *table; 144062306a36Sopenharmony_ci int i; 144162306a36Sopenharmony_ci 144262306a36Sopenharmony_ci table = &priv->static_config.tables[BLK_IDX_L2_LOOKUP]; 144362306a36Sopenharmony_ci l2_lookup = table->entries; 144462306a36Sopenharmony_ci 144562306a36Sopenharmony_ci for (i = 0; i < table->entry_count; i++) 144662306a36Sopenharmony_ci if (l2_lookup[i].macaddr == requested->macaddr && 144762306a36Sopenharmony_ci l2_lookup[i].vlanid == requested->vlanid && 144862306a36Sopenharmony_ci l2_lookup[i].destports & BIT(port)) 144962306a36Sopenharmony_ci return i; 145062306a36Sopenharmony_ci 145162306a36Sopenharmony_ci return -1; 145262306a36Sopenharmony_ci} 145362306a36Sopenharmony_ci 145462306a36Sopenharmony_ci/* We want FDB entries added statically through the bridge command to persist 145562306a36Sopenharmony_ci * across switch resets, which are a common thing during normal SJA1105 145662306a36Sopenharmony_ci * operation. So we have to back them up in the static configuration tables 145762306a36Sopenharmony_ci * and hence apply them on next static config upload... yay! 145862306a36Sopenharmony_ci */ 145962306a36Sopenharmony_cistatic int 146062306a36Sopenharmony_cisja1105_static_fdb_change(struct sja1105_private *priv, int port, 146162306a36Sopenharmony_ci const struct sja1105_l2_lookup_entry *requested, 146262306a36Sopenharmony_ci bool keep) 146362306a36Sopenharmony_ci{ 146462306a36Sopenharmony_ci struct sja1105_l2_lookup_entry *l2_lookup; 146562306a36Sopenharmony_ci struct sja1105_table *table; 146662306a36Sopenharmony_ci int rc, match; 146762306a36Sopenharmony_ci 146862306a36Sopenharmony_ci table = &priv->static_config.tables[BLK_IDX_L2_LOOKUP]; 146962306a36Sopenharmony_ci 147062306a36Sopenharmony_ci match = sja1105_find_static_fdb_entry(priv, port, requested); 147162306a36Sopenharmony_ci if (match < 0) { 147262306a36Sopenharmony_ci /* Can't delete a missing entry. */ 147362306a36Sopenharmony_ci if (!keep) 147462306a36Sopenharmony_ci return 0; 147562306a36Sopenharmony_ci 147662306a36Sopenharmony_ci /* No match => new entry */ 147762306a36Sopenharmony_ci rc = sja1105_table_resize(table, table->entry_count + 1); 147862306a36Sopenharmony_ci if (rc) 147962306a36Sopenharmony_ci return rc; 148062306a36Sopenharmony_ci 148162306a36Sopenharmony_ci match = table->entry_count - 1; 148262306a36Sopenharmony_ci } 148362306a36Sopenharmony_ci 148462306a36Sopenharmony_ci /* Assign pointer after the resize (it may be new memory) */ 148562306a36Sopenharmony_ci l2_lookup = table->entries; 148662306a36Sopenharmony_ci 148762306a36Sopenharmony_ci /* We have a match. 148862306a36Sopenharmony_ci * If the job was to add this FDB entry, it's already done (mostly 148962306a36Sopenharmony_ci * anyway, since the port forwarding mask may have changed, case in 149062306a36Sopenharmony_ci * which we update it). 149162306a36Sopenharmony_ci * Otherwise we have to delete it. 149262306a36Sopenharmony_ci */ 149362306a36Sopenharmony_ci if (keep) { 149462306a36Sopenharmony_ci l2_lookup[match] = *requested; 149562306a36Sopenharmony_ci return 0; 149662306a36Sopenharmony_ci } 149762306a36Sopenharmony_ci 149862306a36Sopenharmony_ci /* To remove, the strategy is to overwrite the element with 149962306a36Sopenharmony_ci * the last one, and then reduce the array size by 1 150062306a36Sopenharmony_ci */ 150162306a36Sopenharmony_ci l2_lookup[match] = l2_lookup[table->entry_count - 1]; 150262306a36Sopenharmony_ci return sja1105_table_resize(table, table->entry_count - 1); 150362306a36Sopenharmony_ci} 150462306a36Sopenharmony_ci 150562306a36Sopenharmony_ci/* First-generation switches have a 4-way set associative TCAM that 150662306a36Sopenharmony_ci * holds the FDB entries. An FDB index spans from 0 to 1023 and is comprised of 150762306a36Sopenharmony_ci * a "bin" (grouping of 4 entries) and a "way" (an entry within a bin). 150862306a36Sopenharmony_ci * For the placement of a newly learnt FDB entry, the switch selects the bin 150962306a36Sopenharmony_ci * based on a hash function, and the way within that bin incrementally. 151062306a36Sopenharmony_ci */ 151162306a36Sopenharmony_cistatic int sja1105et_fdb_index(int bin, int way) 151262306a36Sopenharmony_ci{ 151362306a36Sopenharmony_ci return bin * SJA1105ET_FDB_BIN_SIZE + way; 151462306a36Sopenharmony_ci} 151562306a36Sopenharmony_ci 151662306a36Sopenharmony_cistatic int sja1105et_is_fdb_entry_in_bin(struct sja1105_private *priv, int bin, 151762306a36Sopenharmony_ci const u8 *addr, u16 vid, 151862306a36Sopenharmony_ci struct sja1105_l2_lookup_entry *match, 151962306a36Sopenharmony_ci int *last_unused) 152062306a36Sopenharmony_ci{ 152162306a36Sopenharmony_ci int way; 152262306a36Sopenharmony_ci 152362306a36Sopenharmony_ci for (way = 0; way < SJA1105ET_FDB_BIN_SIZE; way++) { 152462306a36Sopenharmony_ci struct sja1105_l2_lookup_entry l2_lookup = {0}; 152562306a36Sopenharmony_ci int index = sja1105et_fdb_index(bin, way); 152662306a36Sopenharmony_ci 152762306a36Sopenharmony_ci /* Skip unused entries, optionally marking them 152862306a36Sopenharmony_ci * into the return value 152962306a36Sopenharmony_ci */ 153062306a36Sopenharmony_ci if (sja1105_dynamic_config_read(priv, BLK_IDX_L2_LOOKUP, 153162306a36Sopenharmony_ci index, &l2_lookup)) { 153262306a36Sopenharmony_ci if (last_unused) 153362306a36Sopenharmony_ci *last_unused = way; 153462306a36Sopenharmony_ci continue; 153562306a36Sopenharmony_ci } 153662306a36Sopenharmony_ci 153762306a36Sopenharmony_ci if (l2_lookup.macaddr == ether_addr_to_u64(addr) && 153862306a36Sopenharmony_ci l2_lookup.vlanid == vid) { 153962306a36Sopenharmony_ci if (match) 154062306a36Sopenharmony_ci *match = l2_lookup; 154162306a36Sopenharmony_ci return way; 154262306a36Sopenharmony_ci } 154362306a36Sopenharmony_ci } 154462306a36Sopenharmony_ci /* Return an invalid entry index if not found */ 154562306a36Sopenharmony_ci return -1; 154662306a36Sopenharmony_ci} 154762306a36Sopenharmony_ci 154862306a36Sopenharmony_ciint sja1105et_fdb_add(struct dsa_switch *ds, int port, 154962306a36Sopenharmony_ci const unsigned char *addr, u16 vid) 155062306a36Sopenharmony_ci{ 155162306a36Sopenharmony_ci struct sja1105_l2_lookup_entry l2_lookup = {0}, tmp; 155262306a36Sopenharmony_ci struct sja1105_private *priv = ds->priv; 155362306a36Sopenharmony_ci struct device *dev = ds->dev; 155462306a36Sopenharmony_ci int last_unused = -1; 155562306a36Sopenharmony_ci int start, end, i; 155662306a36Sopenharmony_ci int bin, way, rc; 155762306a36Sopenharmony_ci 155862306a36Sopenharmony_ci bin = sja1105et_fdb_hash(priv, addr, vid); 155962306a36Sopenharmony_ci 156062306a36Sopenharmony_ci way = sja1105et_is_fdb_entry_in_bin(priv, bin, addr, vid, 156162306a36Sopenharmony_ci &l2_lookup, &last_unused); 156262306a36Sopenharmony_ci if (way >= 0) { 156362306a36Sopenharmony_ci /* We have an FDB entry. Is our port in the destination 156462306a36Sopenharmony_ci * mask? If yes, we need to do nothing. If not, we need 156562306a36Sopenharmony_ci * to rewrite the entry by adding this port to it. 156662306a36Sopenharmony_ci */ 156762306a36Sopenharmony_ci if ((l2_lookup.destports & BIT(port)) && l2_lookup.lockeds) 156862306a36Sopenharmony_ci return 0; 156962306a36Sopenharmony_ci l2_lookup.destports |= BIT(port); 157062306a36Sopenharmony_ci } else { 157162306a36Sopenharmony_ci int index = sja1105et_fdb_index(bin, way); 157262306a36Sopenharmony_ci 157362306a36Sopenharmony_ci /* We don't have an FDB entry. We construct a new one and 157462306a36Sopenharmony_ci * try to find a place for it within the FDB table. 157562306a36Sopenharmony_ci */ 157662306a36Sopenharmony_ci l2_lookup.macaddr = ether_addr_to_u64(addr); 157762306a36Sopenharmony_ci l2_lookup.destports = BIT(port); 157862306a36Sopenharmony_ci l2_lookup.vlanid = vid; 157962306a36Sopenharmony_ci 158062306a36Sopenharmony_ci if (last_unused >= 0) { 158162306a36Sopenharmony_ci way = last_unused; 158262306a36Sopenharmony_ci } else { 158362306a36Sopenharmony_ci /* Bin is full, need to evict somebody. 158462306a36Sopenharmony_ci * Choose victim at random. If you get these messages 158562306a36Sopenharmony_ci * often, you may need to consider changing the 158662306a36Sopenharmony_ci * distribution function: 158762306a36Sopenharmony_ci * static_config[BLK_IDX_L2_LOOKUP_PARAMS].entries->poly 158862306a36Sopenharmony_ci */ 158962306a36Sopenharmony_ci get_random_bytes(&way, sizeof(u8)); 159062306a36Sopenharmony_ci way %= SJA1105ET_FDB_BIN_SIZE; 159162306a36Sopenharmony_ci dev_warn(dev, "Warning, FDB bin %d full while adding entry for %pM. Evicting entry %u.\n", 159262306a36Sopenharmony_ci bin, addr, way); 159362306a36Sopenharmony_ci /* Evict entry */ 159462306a36Sopenharmony_ci sja1105_dynamic_config_write(priv, BLK_IDX_L2_LOOKUP, 159562306a36Sopenharmony_ci index, NULL, false); 159662306a36Sopenharmony_ci } 159762306a36Sopenharmony_ci } 159862306a36Sopenharmony_ci l2_lookup.lockeds = true; 159962306a36Sopenharmony_ci l2_lookup.index = sja1105et_fdb_index(bin, way); 160062306a36Sopenharmony_ci 160162306a36Sopenharmony_ci rc = sja1105_dynamic_config_write(priv, BLK_IDX_L2_LOOKUP, 160262306a36Sopenharmony_ci l2_lookup.index, &l2_lookup, 160362306a36Sopenharmony_ci true); 160462306a36Sopenharmony_ci if (rc < 0) 160562306a36Sopenharmony_ci return rc; 160662306a36Sopenharmony_ci 160762306a36Sopenharmony_ci /* Invalidate a dynamically learned entry if that exists */ 160862306a36Sopenharmony_ci start = sja1105et_fdb_index(bin, 0); 160962306a36Sopenharmony_ci end = sja1105et_fdb_index(bin, way); 161062306a36Sopenharmony_ci 161162306a36Sopenharmony_ci for (i = start; i < end; i++) { 161262306a36Sopenharmony_ci rc = sja1105_dynamic_config_read(priv, BLK_IDX_L2_LOOKUP, 161362306a36Sopenharmony_ci i, &tmp); 161462306a36Sopenharmony_ci if (rc == -ENOENT) 161562306a36Sopenharmony_ci continue; 161662306a36Sopenharmony_ci if (rc) 161762306a36Sopenharmony_ci return rc; 161862306a36Sopenharmony_ci 161962306a36Sopenharmony_ci if (tmp.macaddr != ether_addr_to_u64(addr) || tmp.vlanid != vid) 162062306a36Sopenharmony_ci continue; 162162306a36Sopenharmony_ci 162262306a36Sopenharmony_ci rc = sja1105_dynamic_config_write(priv, BLK_IDX_L2_LOOKUP, 162362306a36Sopenharmony_ci i, NULL, false); 162462306a36Sopenharmony_ci if (rc) 162562306a36Sopenharmony_ci return rc; 162662306a36Sopenharmony_ci 162762306a36Sopenharmony_ci break; 162862306a36Sopenharmony_ci } 162962306a36Sopenharmony_ci 163062306a36Sopenharmony_ci return sja1105_static_fdb_change(priv, port, &l2_lookup, true); 163162306a36Sopenharmony_ci} 163262306a36Sopenharmony_ci 163362306a36Sopenharmony_ciint sja1105et_fdb_del(struct dsa_switch *ds, int port, 163462306a36Sopenharmony_ci const unsigned char *addr, u16 vid) 163562306a36Sopenharmony_ci{ 163662306a36Sopenharmony_ci struct sja1105_l2_lookup_entry l2_lookup = {0}; 163762306a36Sopenharmony_ci struct sja1105_private *priv = ds->priv; 163862306a36Sopenharmony_ci int index, bin, way, rc; 163962306a36Sopenharmony_ci bool keep; 164062306a36Sopenharmony_ci 164162306a36Sopenharmony_ci bin = sja1105et_fdb_hash(priv, addr, vid); 164262306a36Sopenharmony_ci way = sja1105et_is_fdb_entry_in_bin(priv, bin, addr, vid, 164362306a36Sopenharmony_ci &l2_lookup, NULL); 164462306a36Sopenharmony_ci if (way < 0) 164562306a36Sopenharmony_ci return 0; 164662306a36Sopenharmony_ci index = sja1105et_fdb_index(bin, way); 164762306a36Sopenharmony_ci 164862306a36Sopenharmony_ci /* We have an FDB entry. Is our port in the destination mask? If yes, 164962306a36Sopenharmony_ci * we need to remove it. If the resulting port mask becomes empty, we 165062306a36Sopenharmony_ci * need to completely evict the FDB entry. 165162306a36Sopenharmony_ci * Otherwise we just write it back. 165262306a36Sopenharmony_ci */ 165362306a36Sopenharmony_ci l2_lookup.destports &= ~BIT(port); 165462306a36Sopenharmony_ci 165562306a36Sopenharmony_ci if (l2_lookup.destports) 165662306a36Sopenharmony_ci keep = true; 165762306a36Sopenharmony_ci else 165862306a36Sopenharmony_ci keep = false; 165962306a36Sopenharmony_ci 166062306a36Sopenharmony_ci rc = sja1105_dynamic_config_write(priv, BLK_IDX_L2_LOOKUP, 166162306a36Sopenharmony_ci index, &l2_lookup, keep); 166262306a36Sopenharmony_ci if (rc < 0) 166362306a36Sopenharmony_ci return rc; 166462306a36Sopenharmony_ci 166562306a36Sopenharmony_ci return sja1105_static_fdb_change(priv, port, &l2_lookup, keep); 166662306a36Sopenharmony_ci} 166762306a36Sopenharmony_ci 166862306a36Sopenharmony_ciint sja1105pqrs_fdb_add(struct dsa_switch *ds, int port, 166962306a36Sopenharmony_ci const unsigned char *addr, u16 vid) 167062306a36Sopenharmony_ci{ 167162306a36Sopenharmony_ci struct sja1105_l2_lookup_entry l2_lookup = {0}, tmp; 167262306a36Sopenharmony_ci struct sja1105_private *priv = ds->priv; 167362306a36Sopenharmony_ci int rc, i; 167462306a36Sopenharmony_ci 167562306a36Sopenharmony_ci /* Search for an existing entry in the FDB table */ 167662306a36Sopenharmony_ci l2_lookup.macaddr = ether_addr_to_u64(addr); 167762306a36Sopenharmony_ci l2_lookup.vlanid = vid; 167862306a36Sopenharmony_ci l2_lookup.mask_macaddr = GENMASK_ULL(ETH_ALEN * 8 - 1, 0); 167962306a36Sopenharmony_ci l2_lookup.mask_vlanid = VLAN_VID_MASK; 168062306a36Sopenharmony_ci l2_lookup.destports = BIT(port); 168162306a36Sopenharmony_ci 168262306a36Sopenharmony_ci tmp = l2_lookup; 168362306a36Sopenharmony_ci 168462306a36Sopenharmony_ci rc = sja1105_dynamic_config_read(priv, BLK_IDX_L2_LOOKUP, 168562306a36Sopenharmony_ci SJA1105_SEARCH, &tmp); 168662306a36Sopenharmony_ci if (rc == 0 && tmp.index != SJA1105_MAX_L2_LOOKUP_COUNT - 1) { 168762306a36Sopenharmony_ci /* Found a static entry and this port is already in the entry's 168862306a36Sopenharmony_ci * port mask => job done 168962306a36Sopenharmony_ci */ 169062306a36Sopenharmony_ci if ((tmp.destports & BIT(port)) && tmp.lockeds) 169162306a36Sopenharmony_ci return 0; 169262306a36Sopenharmony_ci 169362306a36Sopenharmony_ci l2_lookup = tmp; 169462306a36Sopenharmony_ci 169562306a36Sopenharmony_ci /* l2_lookup.index is populated by the switch in case it 169662306a36Sopenharmony_ci * found something. 169762306a36Sopenharmony_ci */ 169862306a36Sopenharmony_ci l2_lookup.destports |= BIT(port); 169962306a36Sopenharmony_ci goto skip_finding_an_index; 170062306a36Sopenharmony_ci } 170162306a36Sopenharmony_ci 170262306a36Sopenharmony_ci /* Not found, so try to find an unused spot in the FDB. 170362306a36Sopenharmony_ci * This is slightly inefficient because the strategy is knock-knock at 170462306a36Sopenharmony_ci * every possible position from 0 to 1023. 170562306a36Sopenharmony_ci */ 170662306a36Sopenharmony_ci for (i = 0; i < SJA1105_MAX_L2_LOOKUP_COUNT; i++) { 170762306a36Sopenharmony_ci rc = sja1105_dynamic_config_read(priv, BLK_IDX_L2_LOOKUP, 170862306a36Sopenharmony_ci i, NULL); 170962306a36Sopenharmony_ci if (rc < 0) 171062306a36Sopenharmony_ci break; 171162306a36Sopenharmony_ci } 171262306a36Sopenharmony_ci if (i == SJA1105_MAX_L2_LOOKUP_COUNT) { 171362306a36Sopenharmony_ci dev_err(ds->dev, "FDB is full, cannot add entry.\n"); 171462306a36Sopenharmony_ci return -EINVAL; 171562306a36Sopenharmony_ci } 171662306a36Sopenharmony_ci l2_lookup.index = i; 171762306a36Sopenharmony_ci 171862306a36Sopenharmony_ciskip_finding_an_index: 171962306a36Sopenharmony_ci l2_lookup.lockeds = true; 172062306a36Sopenharmony_ci 172162306a36Sopenharmony_ci rc = sja1105_dynamic_config_write(priv, BLK_IDX_L2_LOOKUP, 172262306a36Sopenharmony_ci l2_lookup.index, &l2_lookup, 172362306a36Sopenharmony_ci true); 172462306a36Sopenharmony_ci if (rc < 0) 172562306a36Sopenharmony_ci return rc; 172662306a36Sopenharmony_ci 172762306a36Sopenharmony_ci /* The switch learns dynamic entries and looks up the FDB left to 172862306a36Sopenharmony_ci * right. It is possible that our addition was concurrent with the 172962306a36Sopenharmony_ci * dynamic learning of the same address, so now that the static entry 173062306a36Sopenharmony_ci * has been installed, we are certain that address learning for this 173162306a36Sopenharmony_ci * particular address has been turned off, so the dynamic entry either 173262306a36Sopenharmony_ci * is in the FDB at an index smaller than the static one, or isn't (it 173362306a36Sopenharmony_ci * can also be at a larger index, but in that case it is inactive 173462306a36Sopenharmony_ci * because the static FDB entry will match first, and the dynamic one 173562306a36Sopenharmony_ci * will eventually age out). Search for a dynamically learned address 173662306a36Sopenharmony_ci * prior to our static one and invalidate it. 173762306a36Sopenharmony_ci */ 173862306a36Sopenharmony_ci tmp = l2_lookup; 173962306a36Sopenharmony_ci 174062306a36Sopenharmony_ci rc = sja1105_dynamic_config_read(priv, BLK_IDX_L2_LOOKUP, 174162306a36Sopenharmony_ci SJA1105_SEARCH, &tmp); 174262306a36Sopenharmony_ci if (rc < 0) { 174362306a36Sopenharmony_ci dev_err(ds->dev, 174462306a36Sopenharmony_ci "port %d failed to read back entry for %pM vid %d: %pe\n", 174562306a36Sopenharmony_ci port, addr, vid, ERR_PTR(rc)); 174662306a36Sopenharmony_ci return rc; 174762306a36Sopenharmony_ci } 174862306a36Sopenharmony_ci 174962306a36Sopenharmony_ci if (tmp.index < l2_lookup.index) { 175062306a36Sopenharmony_ci rc = sja1105_dynamic_config_write(priv, BLK_IDX_L2_LOOKUP, 175162306a36Sopenharmony_ci tmp.index, NULL, false); 175262306a36Sopenharmony_ci if (rc < 0) 175362306a36Sopenharmony_ci return rc; 175462306a36Sopenharmony_ci } 175562306a36Sopenharmony_ci 175662306a36Sopenharmony_ci return sja1105_static_fdb_change(priv, port, &l2_lookup, true); 175762306a36Sopenharmony_ci} 175862306a36Sopenharmony_ci 175962306a36Sopenharmony_ciint sja1105pqrs_fdb_del(struct dsa_switch *ds, int port, 176062306a36Sopenharmony_ci const unsigned char *addr, u16 vid) 176162306a36Sopenharmony_ci{ 176262306a36Sopenharmony_ci struct sja1105_l2_lookup_entry l2_lookup = {0}; 176362306a36Sopenharmony_ci struct sja1105_private *priv = ds->priv; 176462306a36Sopenharmony_ci bool keep; 176562306a36Sopenharmony_ci int rc; 176662306a36Sopenharmony_ci 176762306a36Sopenharmony_ci l2_lookup.macaddr = ether_addr_to_u64(addr); 176862306a36Sopenharmony_ci l2_lookup.vlanid = vid; 176962306a36Sopenharmony_ci l2_lookup.mask_macaddr = GENMASK_ULL(ETH_ALEN * 8 - 1, 0); 177062306a36Sopenharmony_ci l2_lookup.mask_vlanid = VLAN_VID_MASK; 177162306a36Sopenharmony_ci l2_lookup.destports = BIT(port); 177262306a36Sopenharmony_ci 177362306a36Sopenharmony_ci rc = sja1105_dynamic_config_read(priv, BLK_IDX_L2_LOOKUP, 177462306a36Sopenharmony_ci SJA1105_SEARCH, &l2_lookup); 177562306a36Sopenharmony_ci if (rc < 0) 177662306a36Sopenharmony_ci return 0; 177762306a36Sopenharmony_ci 177862306a36Sopenharmony_ci l2_lookup.destports &= ~BIT(port); 177962306a36Sopenharmony_ci 178062306a36Sopenharmony_ci /* Decide whether we remove just this port from the FDB entry, 178162306a36Sopenharmony_ci * or if we remove it completely. 178262306a36Sopenharmony_ci */ 178362306a36Sopenharmony_ci if (l2_lookup.destports) 178462306a36Sopenharmony_ci keep = true; 178562306a36Sopenharmony_ci else 178662306a36Sopenharmony_ci keep = false; 178762306a36Sopenharmony_ci 178862306a36Sopenharmony_ci rc = sja1105_dynamic_config_write(priv, BLK_IDX_L2_LOOKUP, 178962306a36Sopenharmony_ci l2_lookup.index, &l2_lookup, keep); 179062306a36Sopenharmony_ci if (rc < 0) 179162306a36Sopenharmony_ci return rc; 179262306a36Sopenharmony_ci 179362306a36Sopenharmony_ci return sja1105_static_fdb_change(priv, port, &l2_lookup, keep); 179462306a36Sopenharmony_ci} 179562306a36Sopenharmony_ci 179662306a36Sopenharmony_cistatic int sja1105_fdb_add(struct dsa_switch *ds, int port, 179762306a36Sopenharmony_ci const unsigned char *addr, u16 vid, 179862306a36Sopenharmony_ci struct dsa_db db) 179962306a36Sopenharmony_ci{ 180062306a36Sopenharmony_ci struct sja1105_private *priv = ds->priv; 180162306a36Sopenharmony_ci int rc; 180262306a36Sopenharmony_ci 180362306a36Sopenharmony_ci if (!vid) { 180462306a36Sopenharmony_ci switch (db.type) { 180562306a36Sopenharmony_ci case DSA_DB_PORT: 180662306a36Sopenharmony_ci vid = dsa_tag_8021q_standalone_vid(db.dp); 180762306a36Sopenharmony_ci break; 180862306a36Sopenharmony_ci case DSA_DB_BRIDGE: 180962306a36Sopenharmony_ci vid = dsa_tag_8021q_bridge_vid(db.bridge.num); 181062306a36Sopenharmony_ci break; 181162306a36Sopenharmony_ci default: 181262306a36Sopenharmony_ci return -EOPNOTSUPP; 181362306a36Sopenharmony_ci } 181462306a36Sopenharmony_ci } 181562306a36Sopenharmony_ci 181662306a36Sopenharmony_ci mutex_lock(&priv->fdb_lock); 181762306a36Sopenharmony_ci rc = priv->info->fdb_add_cmd(ds, port, addr, vid); 181862306a36Sopenharmony_ci mutex_unlock(&priv->fdb_lock); 181962306a36Sopenharmony_ci 182062306a36Sopenharmony_ci return rc; 182162306a36Sopenharmony_ci} 182262306a36Sopenharmony_ci 182362306a36Sopenharmony_cistatic int __sja1105_fdb_del(struct dsa_switch *ds, int port, 182462306a36Sopenharmony_ci const unsigned char *addr, u16 vid, 182562306a36Sopenharmony_ci struct dsa_db db) 182662306a36Sopenharmony_ci{ 182762306a36Sopenharmony_ci struct sja1105_private *priv = ds->priv; 182862306a36Sopenharmony_ci 182962306a36Sopenharmony_ci if (!vid) { 183062306a36Sopenharmony_ci switch (db.type) { 183162306a36Sopenharmony_ci case DSA_DB_PORT: 183262306a36Sopenharmony_ci vid = dsa_tag_8021q_standalone_vid(db.dp); 183362306a36Sopenharmony_ci break; 183462306a36Sopenharmony_ci case DSA_DB_BRIDGE: 183562306a36Sopenharmony_ci vid = dsa_tag_8021q_bridge_vid(db.bridge.num); 183662306a36Sopenharmony_ci break; 183762306a36Sopenharmony_ci default: 183862306a36Sopenharmony_ci return -EOPNOTSUPP; 183962306a36Sopenharmony_ci } 184062306a36Sopenharmony_ci } 184162306a36Sopenharmony_ci 184262306a36Sopenharmony_ci return priv->info->fdb_del_cmd(ds, port, addr, vid); 184362306a36Sopenharmony_ci} 184462306a36Sopenharmony_ci 184562306a36Sopenharmony_cistatic int sja1105_fdb_del(struct dsa_switch *ds, int port, 184662306a36Sopenharmony_ci const unsigned char *addr, u16 vid, 184762306a36Sopenharmony_ci struct dsa_db db) 184862306a36Sopenharmony_ci{ 184962306a36Sopenharmony_ci struct sja1105_private *priv = ds->priv; 185062306a36Sopenharmony_ci int rc; 185162306a36Sopenharmony_ci 185262306a36Sopenharmony_ci mutex_lock(&priv->fdb_lock); 185362306a36Sopenharmony_ci rc = __sja1105_fdb_del(ds, port, addr, vid, db); 185462306a36Sopenharmony_ci mutex_unlock(&priv->fdb_lock); 185562306a36Sopenharmony_ci 185662306a36Sopenharmony_ci return rc; 185762306a36Sopenharmony_ci} 185862306a36Sopenharmony_ci 185962306a36Sopenharmony_cistatic int sja1105_fdb_dump(struct dsa_switch *ds, int port, 186062306a36Sopenharmony_ci dsa_fdb_dump_cb_t *cb, void *data) 186162306a36Sopenharmony_ci{ 186262306a36Sopenharmony_ci struct sja1105_private *priv = ds->priv; 186362306a36Sopenharmony_ci struct device *dev = ds->dev; 186462306a36Sopenharmony_ci int i; 186562306a36Sopenharmony_ci 186662306a36Sopenharmony_ci for (i = 0; i < SJA1105_MAX_L2_LOOKUP_COUNT; i++) { 186762306a36Sopenharmony_ci struct sja1105_l2_lookup_entry l2_lookup = {0}; 186862306a36Sopenharmony_ci u8 macaddr[ETH_ALEN]; 186962306a36Sopenharmony_ci int rc; 187062306a36Sopenharmony_ci 187162306a36Sopenharmony_ci rc = sja1105_dynamic_config_read(priv, BLK_IDX_L2_LOOKUP, 187262306a36Sopenharmony_ci i, &l2_lookup); 187362306a36Sopenharmony_ci /* No fdb entry at i, not an issue */ 187462306a36Sopenharmony_ci if (rc == -ENOENT) 187562306a36Sopenharmony_ci continue; 187662306a36Sopenharmony_ci if (rc) { 187762306a36Sopenharmony_ci dev_err(dev, "Failed to dump FDB: %d\n", rc); 187862306a36Sopenharmony_ci return rc; 187962306a36Sopenharmony_ci } 188062306a36Sopenharmony_ci 188162306a36Sopenharmony_ci /* FDB dump callback is per port. This means we have to 188262306a36Sopenharmony_ci * disregard a valid entry if it's not for this port, even if 188362306a36Sopenharmony_ci * only to revisit it later. This is inefficient because the 188462306a36Sopenharmony_ci * 1024-sized FDB table needs to be traversed 4 times through 188562306a36Sopenharmony_ci * SPI during a 'bridge fdb show' command. 188662306a36Sopenharmony_ci */ 188762306a36Sopenharmony_ci if (!(l2_lookup.destports & BIT(port))) 188862306a36Sopenharmony_ci continue; 188962306a36Sopenharmony_ci 189062306a36Sopenharmony_ci u64_to_ether_addr(l2_lookup.macaddr, macaddr); 189162306a36Sopenharmony_ci 189262306a36Sopenharmony_ci /* Hardware FDB is shared for fdb and mdb, "bridge fdb show" 189362306a36Sopenharmony_ci * only wants to see unicast 189462306a36Sopenharmony_ci */ 189562306a36Sopenharmony_ci if (is_multicast_ether_addr(macaddr)) 189662306a36Sopenharmony_ci continue; 189762306a36Sopenharmony_ci 189862306a36Sopenharmony_ci /* We need to hide the dsa_8021q VLANs from the user. */ 189962306a36Sopenharmony_ci if (vid_is_dsa_8021q(l2_lookup.vlanid)) 190062306a36Sopenharmony_ci l2_lookup.vlanid = 0; 190162306a36Sopenharmony_ci rc = cb(macaddr, l2_lookup.vlanid, l2_lookup.lockeds, data); 190262306a36Sopenharmony_ci if (rc) 190362306a36Sopenharmony_ci return rc; 190462306a36Sopenharmony_ci } 190562306a36Sopenharmony_ci return 0; 190662306a36Sopenharmony_ci} 190762306a36Sopenharmony_ci 190862306a36Sopenharmony_cistatic void sja1105_fast_age(struct dsa_switch *ds, int port) 190962306a36Sopenharmony_ci{ 191062306a36Sopenharmony_ci struct dsa_port *dp = dsa_to_port(ds, port); 191162306a36Sopenharmony_ci struct sja1105_private *priv = ds->priv; 191262306a36Sopenharmony_ci struct dsa_db db = { 191362306a36Sopenharmony_ci .type = DSA_DB_BRIDGE, 191462306a36Sopenharmony_ci .bridge = { 191562306a36Sopenharmony_ci .dev = dsa_port_bridge_dev_get(dp), 191662306a36Sopenharmony_ci .num = dsa_port_bridge_num_get(dp), 191762306a36Sopenharmony_ci }, 191862306a36Sopenharmony_ci }; 191962306a36Sopenharmony_ci int i; 192062306a36Sopenharmony_ci 192162306a36Sopenharmony_ci mutex_lock(&priv->fdb_lock); 192262306a36Sopenharmony_ci 192362306a36Sopenharmony_ci for (i = 0; i < SJA1105_MAX_L2_LOOKUP_COUNT; i++) { 192462306a36Sopenharmony_ci struct sja1105_l2_lookup_entry l2_lookup = {0}; 192562306a36Sopenharmony_ci u8 macaddr[ETH_ALEN]; 192662306a36Sopenharmony_ci int rc; 192762306a36Sopenharmony_ci 192862306a36Sopenharmony_ci rc = sja1105_dynamic_config_read(priv, BLK_IDX_L2_LOOKUP, 192962306a36Sopenharmony_ci i, &l2_lookup); 193062306a36Sopenharmony_ci /* No fdb entry at i, not an issue */ 193162306a36Sopenharmony_ci if (rc == -ENOENT) 193262306a36Sopenharmony_ci continue; 193362306a36Sopenharmony_ci if (rc) { 193462306a36Sopenharmony_ci dev_err(ds->dev, "Failed to read FDB: %pe\n", 193562306a36Sopenharmony_ci ERR_PTR(rc)); 193662306a36Sopenharmony_ci break; 193762306a36Sopenharmony_ci } 193862306a36Sopenharmony_ci 193962306a36Sopenharmony_ci if (!(l2_lookup.destports & BIT(port))) 194062306a36Sopenharmony_ci continue; 194162306a36Sopenharmony_ci 194262306a36Sopenharmony_ci /* Don't delete static FDB entries */ 194362306a36Sopenharmony_ci if (l2_lookup.lockeds) 194462306a36Sopenharmony_ci continue; 194562306a36Sopenharmony_ci 194662306a36Sopenharmony_ci u64_to_ether_addr(l2_lookup.macaddr, macaddr); 194762306a36Sopenharmony_ci 194862306a36Sopenharmony_ci rc = __sja1105_fdb_del(ds, port, macaddr, l2_lookup.vlanid, db); 194962306a36Sopenharmony_ci if (rc) { 195062306a36Sopenharmony_ci dev_err(ds->dev, 195162306a36Sopenharmony_ci "Failed to delete FDB entry %pM vid %lld: %pe\n", 195262306a36Sopenharmony_ci macaddr, l2_lookup.vlanid, ERR_PTR(rc)); 195362306a36Sopenharmony_ci break; 195462306a36Sopenharmony_ci } 195562306a36Sopenharmony_ci } 195662306a36Sopenharmony_ci 195762306a36Sopenharmony_ci mutex_unlock(&priv->fdb_lock); 195862306a36Sopenharmony_ci} 195962306a36Sopenharmony_ci 196062306a36Sopenharmony_cistatic int sja1105_mdb_add(struct dsa_switch *ds, int port, 196162306a36Sopenharmony_ci const struct switchdev_obj_port_mdb *mdb, 196262306a36Sopenharmony_ci struct dsa_db db) 196362306a36Sopenharmony_ci{ 196462306a36Sopenharmony_ci return sja1105_fdb_add(ds, port, mdb->addr, mdb->vid, db); 196562306a36Sopenharmony_ci} 196662306a36Sopenharmony_ci 196762306a36Sopenharmony_cistatic int sja1105_mdb_del(struct dsa_switch *ds, int port, 196862306a36Sopenharmony_ci const struct switchdev_obj_port_mdb *mdb, 196962306a36Sopenharmony_ci struct dsa_db db) 197062306a36Sopenharmony_ci{ 197162306a36Sopenharmony_ci return sja1105_fdb_del(ds, port, mdb->addr, mdb->vid, db); 197262306a36Sopenharmony_ci} 197362306a36Sopenharmony_ci 197462306a36Sopenharmony_ci/* Common function for unicast and broadcast flood configuration. 197562306a36Sopenharmony_ci * Flooding is configured between each {ingress, egress} port pair, and since 197662306a36Sopenharmony_ci * the bridge's semantics are those of "egress flooding", it means we must 197762306a36Sopenharmony_ci * enable flooding towards this port from all ingress ports that are in the 197862306a36Sopenharmony_ci * same forwarding domain. 197962306a36Sopenharmony_ci */ 198062306a36Sopenharmony_cistatic int sja1105_manage_flood_domains(struct sja1105_private *priv) 198162306a36Sopenharmony_ci{ 198262306a36Sopenharmony_ci struct sja1105_l2_forwarding_entry *l2_fwd; 198362306a36Sopenharmony_ci struct dsa_switch *ds = priv->ds; 198462306a36Sopenharmony_ci int from, to, rc; 198562306a36Sopenharmony_ci 198662306a36Sopenharmony_ci l2_fwd = priv->static_config.tables[BLK_IDX_L2_FORWARDING].entries; 198762306a36Sopenharmony_ci 198862306a36Sopenharmony_ci for (from = 0; from < ds->num_ports; from++) { 198962306a36Sopenharmony_ci u64 fl_domain = 0, bc_domain = 0; 199062306a36Sopenharmony_ci 199162306a36Sopenharmony_ci for (to = 0; to < priv->ds->num_ports; to++) { 199262306a36Sopenharmony_ci if (!sja1105_can_forward(l2_fwd, from, to)) 199362306a36Sopenharmony_ci continue; 199462306a36Sopenharmony_ci 199562306a36Sopenharmony_ci if (priv->ucast_egress_floods & BIT(to)) 199662306a36Sopenharmony_ci fl_domain |= BIT(to); 199762306a36Sopenharmony_ci if (priv->bcast_egress_floods & BIT(to)) 199862306a36Sopenharmony_ci bc_domain |= BIT(to); 199962306a36Sopenharmony_ci } 200062306a36Sopenharmony_ci 200162306a36Sopenharmony_ci /* Nothing changed, nothing to do */ 200262306a36Sopenharmony_ci if (l2_fwd[from].fl_domain == fl_domain && 200362306a36Sopenharmony_ci l2_fwd[from].bc_domain == bc_domain) 200462306a36Sopenharmony_ci continue; 200562306a36Sopenharmony_ci 200662306a36Sopenharmony_ci l2_fwd[from].fl_domain = fl_domain; 200762306a36Sopenharmony_ci l2_fwd[from].bc_domain = bc_domain; 200862306a36Sopenharmony_ci 200962306a36Sopenharmony_ci rc = sja1105_dynamic_config_write(priv, BLK_IDX_L2_FORWARDING, 201062306a36Sopenharmony_ci from, &l2_fwd[from], true); 201162306a36Sopenharmony_ci if (rc < 0) 201262306a36Sopenharmony_ci return rc; 201362306a36Sopenharmony_ci } 201462306a36Sopenharmony_ci 201562306a36Sopenharmony_ci return 0; 201662306a36Sopenharmony_ci} 201762306a36Sopenharmony_ci 201862306a36Sopenharmony_cistatic int sja1105_bridge_member(struct dsa_switch *ds, int port, 201962306a36Sopenharmony_ci struct dsa_bridge bridge, bool member) 202062306a36Sopenharmony_ci{ 202162306a36Sopenharmony_ci struct sja1105_l2_forwarding_entry *l2_fwd; 202262306a36Sopenharmony_ci struct sja1105_private *priv = ds->priv; 202362306a36Sopenharmony_ci int i, rc; 202462306a36Sopenharmony_ci 202562306a36Sopenharmony_ci l2_fwd = priv->static_config.tables[BLK_IDX_L2_FORWARDING].entries; 202662306a36Sopenharmony_ci 202762306a36Sopenharmony_ci for (i = 0; i < ds->num_ports; i++) { 202862306a36Sopenharmony_ci /* Add this port to the forwarding matrix of the 202962306a36Sopenharmony_ci * other ports in the same bridge, and viceversa. 203062306a36Sopenharmony_ci */ 203162306a36Sopenharmony_ci if (!dsa_is_user_port(ds, i)) 203262306a36Sopenharmony_ci continue; 203362306a36Sopenharmony_ci /* For the ports already under the bridge, only one thing needs 203462306a36Sopenharmony_ci * to be done, and that is to add this port to their 203562306a36Sopenharmony_ci * reachability domain. So we can perform the SPI write for 203662306a36Sopenharmony_ci * them immediately. However, for this port itself (the one 203762306a36Sopenharmony_ci * that is new to the bridge), we need to add all other ports 203862306a36Sopenharmony_ci * to its reachability domain. So we do that incrementally in 203962306a36Sopenharmony_ci * this loop, and perform the SPI write only at the end, once 204062306a36Sopenharmony_ci * the domain contains all other bridge ports. 204162306a36Sopenharmony_ci */ 204262306a36Sopenharmony_ci if (i == port) 204362306a36Sopenharmony_ci continue; 204462306a36Sopenharmony_ci if (!dsa_port_offloads_bridge(dsa_to_port(ds, i), &bridge)) 204562306a36Sopenharmony_ci continue; 204662306a36Sopenharmony_ci sja1105_port_allow_traffic(l2_fwd, i, port, member); 204762306a36Sopenharmony_ci sja1105_port_allow_traffic(l2_fwd, port, i, member); 204862306a36Sopenharmony_ci 204962306a36Sopenharmony_ci rc = sja1105_dynamic_config_write(priv, BLK_IDX_L2_FORWARDING, 205062306a36Sopenharmony_ci i, &l2_fwd[i], true); 205162306a36Sopenharmony_ci if (rc < 0) 205262306a36Sopenharmony_ci return rc; 205362306a36Sopenharmony_ci } 205462306a36Sopenharmony_ci 205562306a36Sopenharmony_ci rc = sja1105_dynamic_config_write(priv, BLK_IDX_L2_FORWARDING, 205662306a36Sopenharmony_ci port, &l2_fwd[port], true); 205762306a36Sopenharmony_ci if (rc) 205862306a36Sopenharmony_ci return rc; 205962306a36Sopenharmony_ci 206062306a36Sopenharmony_ci rc = sja1105_commit_pvid(ds, port); 206162306a36Sopenharmony_ci if (rc) 206262306a36Sopenharmony_ci return rc; 206362306a36Sopenharmony_ci 206462306a36Sopenharmony_ci return sja1105_manage_flood_domains(priv); 206562306a36Sopenharmony_ci} 206662306a36Sopenharmony_ci 206762306a36Sopenharmony_cistatic void sja1105_bridge_stp_state_set(struct dsa_switch *ds, int port, 206862306a36Sopenharmony_ci u8 state) 206962306a36Sopenharmony_ci{ 207062306a36Sopenharmony_ci struct dsa_port *dp = dsa_to_port(ds, port); 207162306a36Sopenharmony_ci struct sja1105_private *priv = ds->priv; 207262306a36Sopenharmony_ci struct sja1105_mac_config_entry *mac; 207362306a36Sopenharmony_ci 207462306a36Sopenharmony_ci mac = priv->static_config.tables[BLK_IDX_MAC_CONFIG].entries; 207562306a36Sopenharmony_ci 207662306a36Sopenharmony_ci switch (state) { 207762306a36Sopenharmony_ci case BR_STATE_DISABLED: 207862306a36Sopenharmony_ci case BR_STATE_BLOCKING: 207962306a36Sopenharmony_ci /* From UM10944 description of DRPDTAG (why put this there?): 208062306a36Sopenharmony_ci * "Management traffic flows to the port regardless of the state 208162306a36Sopenharmony_ci * of the INGRESS flag". So BPDUs are still be allowed to pass. 208262306a36Sopenharmony_ci * At the moment no difference between DISABLED and BLOCKING. 208362306a36Sopenharmony_ci */ 208462306a36Sopenharmony_ci mac[port].ingress = false; 208562306a36Sopenharmony_ci mac[port].egress = false; 208662306a36Sopenharmony_ci mac[port].dyn_learn = false; 208762306a36Sopenharmony_ci break; 208862306a36Sopenharmony_ci case BR_STATE_LISTENING: 208962306a36Sopenharmony_ci mac[port].ingress = true; 209062306a36Sopenharmony_ci mac[port].egress = false; 209162306a36Sopenharmony_ci mac[port].dyn_learn = false; 209262306a36Sopenharmony_ci break; 209362306a36Sopenharmony_ci case BR_STATE_LEARNING: 209462306a36Sopenharmony_ci mac[port].ingress = true; 209562306a36Sopenharmony_ci mac[port].egress = false; 209662306a36Sopenharmony_ci mac[port].dyn_learn = dp->learning; 209762306a36Sopenharmony_ci break; 209862306a36Sopenharmony_ci case BR_STATE_FORWARDING: 209962306a36Sopenharmony_ci mac[port].ingress = true; 210062306a36Sopenharmony_ci mac[port].egress = true; 210162306a36Sopenharmony_ci mac[port].dyn_learn = dp->learning; 210262306a36Sopenharmony_ci break; 210362306a36Sopenharmony_ci default: 210462306a36Sopenharmony_ci dev_err(ds->dev, "invalid STP state: %d\n", state); 210562306a36Sopenharmony_ci return; 210662306a36Sopenharmony_ci } 210762306a36Sopenharmony_ci 210862306a36Sopenharmony_ci sja1105_dynamic_config_write(priv, BLK_IDX_MAC_CONFIG, port, 210962306a36Sopenharmony_ci &mac[port], true); 211062306a36Sopenharmony_ci} 211162306a36Sopenharmony_ci 211262306a36Sopenharmony_cistatic int sja1105_bridge_join(struct dsa_switch *ds, int port, 211362306a36Sopenharmony_ci struct dsa_bridge bridge, 211462306a36Sopenharmony_ci bool *tx_fwd_offload, 211562306a36Sopenharmony_ci struct netlink_ext_ack *extack) 211662306a36Sopenharmony_ci{ 211762306a36Sopenharmony_ci int rc; 211862306a36Sopenharmony_ci 211962306a36Sopenharmony_ci rc = sja1105_bridge_member(ds, port, bridge, true); 212062306a36Sopenharmony_ci if (rc) 212162306a36Sopenharmony_ci return rc; 212262306a36Sopenharmony_ci 212362306a36Sopenharmony_ci rc = dsa_tag_8021q_bridge_join(ds, port, bridge); 212462306a36Sopenharmony_ci if (rc) { 212562306a36Sopenharmony_ci sja1105_bridge_member(ds, port, bridge, false); 212662306a36Sopenharmony_ci return rc; 212762306a36Sopenharmony_ci } 212862306a36Sopenharmony_ci 212962306a36Sopenharmony_ci *tx_fwd_offload = true; 213062306a36Sopenharmony_ci 213162306a36Sopenharmony_ci return 0; 213262306a36Sopenharmony_ci} 213362306a36Sopenharmony_ci 213462306a36Sopenharmony_cistatic void sja1105_bridge_leave(struct dsa_switch *ds, int port, 213562306a36Sopenharmony_ci struct dsa_bridge bridge) 213662306a36Sopenharmony_ci{ 213762306a36Sopenharmony_ci dsa_tag_8021q_bridge_leave(ds, port, bridge); 213862306a36Sopenharmony_ci sja1105_bridge_member(ds, port, bridge, false); 213962306a36Sopenharmony_ci} 214062306a36Sopenharmony_ci 214162306a36Sopenharmony_ci#define BYTES_PER_KBIT (1000LL / 8) 214262306a36Sopenharmony_ci/* Port 0 (the uC port) does not have CBS shapers */ 214362306a36Sopenharmony_ci#define SJA1110_FIXED_CBS(port, prio) ((((port) - 1) * SJA1105_NUM_TC) + (prio)) 214462306a36Sopenharmony_ci 214562306a36Sopenharmony_cistatic int sja1105_find_cbs_shaper(struct sja1105_private *priv, 214662306a36Sopenharmony_ci int port, int prio) 214762306a36Sopenharmony_ci{ 214862306a36Sopenharmony_ci int i; 214962306a36Sopenharmony_ci 215062306a36Sopenharmony_ci if (priv->info->fixed_cbs_mapping) { 215162306a36Sopenharmony_ci i = SJA1110_FIXED_CBS(port, prio); 215262306a36Sopenharmony_ci if (i >= 0 && i < priv->info->num_cbs_shapers) 215362306a36Sopenharmony_ci return i; 215462306a36Sopenharmony_ci 215562306a36Sopenharmony_ci return -1; 215662306a36Sopenharmony_ci } 215762306a36Sopenharmony_ci 215862306a36Sopenharmony_ci for (i = 0; i < priv->info->num_cbs_shapers; i++) 215962306a36Sopenharmony_ci if (priv->cbs[i].port == port && priv->cbs[i].prio == prio) 216062306a36Sopenharmony_ci return i; 216162306a36Sopenharmony_ci 216262306a36Sopenharmony_ci return -1; 216362306a36Sopenharmony_ci} 216462306a36Sopenharmony_ci 216562306a36Sopenharmony_cistatic int sja1105_find_unused_cbs_shaper(struct sja1105_private *priv) 216662306a36Sopenharmony_ci{ 216762306a36Sopenharmony_ci int i; 216862306a36Sopenharmony_ci 216962306a36Sopenharmony_ci if (priv->info->fixed_cbs_mapping) 217062306a36Sopenharmony_ci return -1; 217162306a36Sopenharmony_ci 217262306a36Sopenharmony_ci for (i = 0; i < priv->info->num_cbs_shapers; i++) 217362306a36Sopenharmony_ci if (!priv->cbs[i].idle_slope && !priv->cbs[i].send_slope) 217462306a36Sopenharmony_ci return i; 217562306a36Sopenharmony_ci 217662306a36Sopenharmony_ci return -1; 217762306a36Sopenharmony_ci} 217862306a36Sopenharmony_ci 217962306a36Sopenharmony_cistatic int sja1105_delete_cbs_shaper(struct sja1105_private *priv, int port, 218062306a36Sopenharmony_ci int prio) 218162306a36Sopenharmony_ci{ 218262306a36Sopenharmony_ci int i; 218362306a36Sopenharmony_ci 218462306a36Sopenharmony_ci for (i = 0; i < priv->info->num_cbs_shapers; i++) { 218562306a36Sopenharmony_ci struct sja1105_cbs_entry *cbs = &priv->cbs[i]; 218662306a36Sopenharmony_ci 218762306a36Sopenharmony_ci if (cbs->port == port && cbs->prio == prio) { 218862306a36Sopenharmony_ci memset(cbs, 0, sizeof(*cbs)); 218962306a36Sopenharmony_ci return sja1105_dynamic_config_write(priv, BLK_IDX_CBS, 219062306a36Sopenharmony_ci i, cbs, true); 219162306a36Sopenharmony_ci } 219262306a36Sopenharmony_ci } 219362306a36Sopenharmony_ci 219462306a36Sopenharmony_ci return 0; 219562306a36Sopenharmony_ci} 219662306a36Sopenharmony_ci 219762306a36Sopenharmony_cistatic int sja1105_setup_tc_cbs(struct dsa_switch *ds, int port, 219862306a36Sopenharmony_ci struct tc_cbs_qopt_offload *offload) 219962306a36Sopenharmony_ci{ 220062306a36Sopenharmony_ci struct sja1105_private *priv = ds->priv; 220162306a36Sopenharmony_ci struct sja1105_cbs_entry *cbs; 220262306a36Sopenharmony_ci s64 port_transmit_rate_kbps; 220362306a36Sopenharmony_ci int index; 220462306a36Sopenharmony_ci 220562306a36Sopenharmony_ci if (!offload->enable) 220662306a36Sopenharmony_ci return sja1105_delete_cbs_shaper(priv, port, offload->queue); 220762306a36Sopenharmony_ci 220862306a36Sopenharmony_ci /* The user may be replacing an existing shaper */ 220962306a36Sopenharmony_ci index = sja1105_find_cbs_shaper(priv, port, offload->queue); 221062306a36Sopenharmony_ci if (index < 0) { 221162306a36Sopenharmony_ci /* That isn't the case - see if we can allocate a new one */ 221262306a36Sopenharmony_ci index = sja1105_find_unused_cbs_shaper(priv); 221362306a36Sopenharmony_ci if (index < 0) 221462306a36Sopenharmony_ci return -ENOSPC; 221562306a36Sopenharmony_ci } 221662306a36Sopenharmony_ci 221762306a36Sopenharmony_ci cbs = &priv->cbs[index]; 221862306a36Sopenharmony_ci cbs->port = port; 221962306a36Sopenharmony_ci cbs->prio = offload->queue; 222062306a36Sopenharmony_ci /* locredit and sendslope are negative by definition. In hardware, 222162306a36Sopenharmony_ci * positive values must be provided, and the negative sign is implicit. 222262306a36Sopenharmony_ci */ 222362306a36Sopenharmony_ci cbs->credit_hi = offload->hicredit; 222462306a36Sopenharmony_ci cbs->credit_lo = abs(offload->locredit); 222562306a36Sopenharmony_ci /* User space is in kbits/sec, while the hardware in bytes/sec times 222662306a36Sopenharmony_ci * link speed. Since the given offload->sendslope is good only for the 222762306a36Sopenharmony_ci * current link speed anyway, and user space is likely to reprogram it 222862306a36Sopenharmony_ci * when that changes, don't even bother to track the port's link speed, 222962306a36Sopenharmony_ci * but deduce the port transmit rate from idleslope - sendslope. 223062306a36Sopenharmony_ci */ 223162306a36Sopenharmony_ci port_transmit_rate_kbps = offload->idleslope - offload->sendslope; 223262306a36Sopenharmony_ci cbs->idle_slope = div_s64(offload->idleslope * BYTES_PER_KBIT, 223362306a36Sopenharmony_ci port_transmit_rate_kbps); 223462306a36Sopenharmony_ci cbs->send_slope = div_s64(abs(offload->sendslope * BYTES_PER_KBIT), 223562306a36Sopenharmony_ci port_transmit_rate_kbps); 223662306a36Sopenharmony_ci /* Convert the negative values from 64-bit 2's complement 223762306a36Sopenharmony_ci * to 32-bit 2's complement (for the case of 0x80000000 whose 223862306a36Sopenharmony_ci * negative is still negative). 223962306a36Sopenharmony_ci */ 224062306a36Sopenharmony_ci cbs->credit_lo &= GENMASK_ULL(31, 0); 224162306a36Sopenharmony_ci cbs->send_slope &= GENMASK_ULL(31, 0); 224262306a36Sopenharmony_ci 224362306a36Sopenharmony_ci return sja1105_dynamic_config_write(priv, BLK_IDX_CBS, index, cbs, 224462306a36Sopenharmony_ci true); 224562306a36Sopenharmony_ci} 224662306a36Sopenharmony_ci 224762306a36Sopenharmony_cistatic int sja1105_reload_cbs(struct sja1105_private *priv) 224862306a36Sopenharmony_ci{ 224962306a36Sopenharmony_ci int rc = 0, i; 225062306a36Sopenharmony_ci 225162306a36Sopenharmony_ci /* The credit based shapers are only allocated if 225262306a36Sopenharmony_ci * CONFIG_NET_SCH_CBS is enabled. 225362306a36Sopenharmony_ci */ 225462306a36Sopenharmony_ci if (!priv->cbs) 225562306a36Sopenharmony_ci return 0; 225662306a36Sopenharmony_ci 225762306a36Sopenharmony_ci for (i = 0; i < priv->info->num_cbs_shapers; i++) { 225862306a36Sopenharmony_ci struct sja1105_cbs_entry *cbs = &priv->cbs[i]; 225962306a36Sopenharmony_ci 226062306a36Sopenharmony_ci if (!cbs->idle_slope && !cbs->send_slope) 226162306a36Sopenharmony_ci continue; 226262306a36Sopenharmony_ci 226362306a36Sopenharmony_ci rc = sja1105_dynamic_config_write(priv, BLK_IDX_CBS, i, cbs, 226462306a36Sopenharmony_ci true); 226562306a36Sopenharmony_ci if (rc) 226662306a36Sopenharmony_ci break; 226762306a36Sopenharmony_ci } 226862306a36Sopenharmony_ci 226962306a36Sopenharmony_ci return rc; 227062306a36Sopenharmony_ci} 227162306a36Sopenharmony_ci 227262306a36Sopenharmony_cistatic const char * const sja1105_reset_reasons[] = { 227362306a36Sopenharmony_ci [SJA1105_VLAN_FILTERING] = "VLAN filtering", 227462306a36Sopenharmony_ci [SJA1105_AGEING_TIME] = "Ageing time", 227562306a36Sopenharmony_ci [SJA1105_SCHEDULING] = "Time-aware scheduling", 227662306a36Sopenharmony_ci [SJA1105_BEST_EFFORT_POLICING] = "Best-effort policing", 227762306a36Sopenharmony_ci [SJA1105_VIRTUAL_LINKS] = "Virtual links", 227862306a36Sopenharmony_ci}; 227962306a36Sopenharmony_ci 228062306a36Sopenharmony_ci/* For situations where we need to change a setting at runtime that is only 228162306a36Sopenharmony_ci * available through the static configuration, resetting the switch in order 228262306a36Sopenharmony_ci * to upload the new static config is unavoidable. Back up the settings we 228362306a36Sopenharmony_ci * modify at runtime (currently only MAC) and restore them after uploading, 228462306a36Sopenharmony_ci * such that this operation is relatively seamless. 228562306a36Sopenharmony_ci */ 228662306a36Sopenharmony_ciint sja1105_static_config_reload(struct sja1105_private *priv, 228762306a36Sopenharmony_ci enum sja1105_reset_reason reason) 228862306a36Sopenharmony_ci{ 228962306a36Sopenharmony_ci struct ptp_system_timestamp ptp_sts_before; 229062306a36Sopenharmony_ci struct ptp_system_timestamp ptp_sts_after; 229162306a36Sopenharmony_ci int speed_mbps[SJA1105_MAX_NUM_PORTS]; 229262306a36Sopenharmony_ci u16 bmcr[SJA1105_MAX_NUM_PORTS] = {0}; 229362306a36Sopenharmony_ci struct sja1105_mac_config_entry *mac; 229462306a36Sopenharmony_ci struct dsa_switch *ds = priv->ds; 229562306a36Sopenharmony_ci s64 t1, t2, t3, t4; 229662306a36Sopenharmony_ci s64 t12, t34; 229762306a36Sopenharmony_ci int rc, i; 229862306a36Sopenharmony_ci s64 now; 229962306a36Sopenharmony_ci 230062306a36Sopenharmony_ci mutex_lock(&priv->fdb_lock); 230162306a36Sopenharmony_ci mutex_lock(&priv->mgmt_lock); 230262306a36Sopenharmony_ci 230362306a36Sopenharmony_ci mac = priv->static_config.tables[BLK_IDX_MAC_CONFIG].entries; 230462306a36Sopenharmony_ci 230562306a36Sopenharmony_ci /* Back up the dynamic link speed changed by sja1105_adjust_port_config 230662306a36Sopenharmony_ci * in order to temporarily restore it to SJA1105_SPEED_AUTO - which the 230762306a36Sopenharmony_ci * switch wants to see in the static config in order to allow us to 230862306a36Sopenharmony_ci * change it through the dynamic interface later. 230962306a36Sopenharmony_ci */ 231062306a36Sopenharmony_ci for (i = 0; i < ds->num_ports; i++) { 231162306a36Sopenharmony_ci speed_mbps[i] = sja1105_port_speed_to_ethtool(priv, 231262306a36Sopenharmony_ci mac[i].speed); 231362306a36Sopenharmony_ci mac[i].speed = priv->info->port_speed[SJA1105_SPEED_AUTO]; 231462306a36Sopenharmony_ci 231562306a36Sopenharmony_ci if (priv->xpcs[i]) 231662306a36Sopenharmony_ci bmcr[i] = mdiobus_c45_read(priv->mdio_pcs, i, 231762306a36Sopenharmony_ci MDIO_MMD_VEND2, MDIO_CTRL1); 231862306a36Sopenharmony_ci } 231962306a36Sopenharmony_ci 232062306a36Sopenharmony_ci /* No PTP operations can run right now */ 232162306a36Sopenharmony_ci mutex_lock(&priv->ptp_data.lock); 232262306a36Sopenharmony_ci 232362306a36Sopenharmony_ci rc = __sja1105_ptp_gettimex(ds, &now, &ptp_sts_before); 232462306a36Sopenharmony_ci if (rc < 0) { 232562306a36Sopenharmony_ci mutex_unlock(&priv->ptp_data.lock); 232662306a36Sopenharmony_ci goto out; 232762306a36Sopenharmony_ci } 232862306a36Sopenharmony_ci 232962306a36Sopenharmony_ci /* Reset switch and send updated static configuration */ 233062306a36Sopenharmony_ci rc = sja1105_static_config_upload(priv); 233162306a36Sopenharmony_ci if (rc < 0) { 233262306a36Sopenharmony_ci mutex_unlock(&priv->ptp_data.lock); 233362306a36Sopenharmony_ci goto out; 233462306a36Sopenharmony_ci } 233562306a36Sopenharmony_ci 233662306a36Sopenharmony_ci rc = __sja1105_ptp_settime(ds, 0, &ptp_sts_after); 233762306a36Sopenharmony_ci if (rc < 0) { 233862306a36Sopenharmony_ci mutex_unlock(&priv->ptp_data.lock); 233962306a36Sopenharmony_ci goto out; 234062306a36Sopenharmony_ci } 234162306a36Sopenharmony_ci 234262306a36Sopenharmony_ci t1 = timespec64_to_ns(&ptp_sts_before.pre_ts); 234362306a36Sopenharmony_ci t2 = timespec64_to_ns(&ptp_sts_before.post_ts); 234462306a36Sopenharmony_ci t3 = timespec64_to_ns(&ptp_sts_after.pre_ts); 234562306a36Sopenharmony_ci t4 = timespec64_to_ns(&ptp_sts_after.post_ts); 234662306a36Sopenharmony_ci /* Mid point, corresponds to pre-reset PTPCLKVAL */ 234762306a36Sopenharmony_ci t12 = t1 + (t2 - t1) / 2; 234862306a36Sopenharmony_ci /* Mid point, corresponds to post-reset PTPCLKVAL, aka 0 */ 234962306a36Sopenharmony_ci t34 = t3 + (t4 - t3) / 2; 235062306a36Sopenharmony_ci /* Advance PTPCLKVAL by the time it took since its readout */ 235162306a36Sopenharmony_ci now += (t34 - t12); 235262306a36Sopenharmony_ci 235362306a36Sopenharmony_ci __sja1105_ptp_adjtime(ds, now); 235462306a36Sopenharmony_ci 235562306a36Sopenharmony_ci mutex_unlock(&priv->ptp_data.lock); 235662306a36Sopenharmony_ci 235762306a36Sopenharmony_ci dev_info(priv->ds->dev, 235862306a36Sopenharmony_ci "Reset switch and programmed static config. Reason: %s\n", 235962306a36Sopenharmony_ci sja1105_reset_reasons[reason]); 236062306a36Sopenharmony_ci 236162306a36Sopenharmony_ci /* Configure the CGU (PLLs) for MII and RMII PHYs. 236262306a36Sopenharmony_ci * For these interfaces there is no dynamic configuration 236362306a36Sopenharmony_ci * needed, since PLLs have same settings at all speeds. 236462306a36Sopenharmony_ci */ 236562306a36Sopenharmony_ci if (priv->info->clocking_setup) { 236662306a36Sopenharmony_ci rc = priv->info->clocking_setup(priv); 236762306a36Sopenharmony_ci if (rc < 0) 236862306a36Sopenharmony_ci goto out; 236962306a36Sopenharmony_ci } 237062306a36Sopenharmony_ci 237162306a36Sopenharmony_ci for (i = 0; i < ds->num_ports; i++) { 237262306a36Sopenharmony_ci struct dw_xpcs *xpcs = priv->xpcs[i]; 237362306a36Sopenharmony_ci unsigned int neg_mode; 237462306a36Sopenharmony_ci 237562306a36Sopenharmony_ci rc = sja1105_adjust_port_config(priv, i, speed_mbps[i]); 237662306a36Sopenharmony_ci if (rc < 0) 237762306a36Sopenharmony_ci goto out; 237862306a36Sopenharmony_ci 237962306a36Sopenharmony_ci if (!xpcs) 238062306a36Sopenharmony_ci continue; 238162306a36Sopenharmony_ci 238262306a36Sopenharmony_ci if (bmcr[i] & BMCR_ANENABLE) 238362306a36Sopenharmony_ci neg_mode = PHYLINK_PCS_NEG_INBAND_ENABLED; 238462306a36Sopenharmony_ci else 238562306a36Sopenharmony_ci neg_mode = PHYLINK_PCS_NEG_OUTBAND; 238662306a36Sopenharmony_ci 238762306a36Sopenharmony_ci rc = xpcs_do_config(xpcs, priv->phy_mode[i], NULL, neg_mode); 238862306a36Sopenharmony_ci if (rc < 0) 238962306a36Sopenharmony_ci goto out; 239062306a36Sopenharmony_ci 239162306a36Sopenharmony_ci if (neg_mode == PHYLINK_PCS_NEG_OUTBAND) { 239262306a36Sopenharmony_ci int speed = SPEED_UNKNOWN; 239362306a36Sopenharmony_ci 239462306a36Sopenharmony_ci if (priv->phy_mode[i] == PHY_INTERFACE_MODE_2500BASEX) 239562306a36Sopenharmony_ci speed = SPEED_2500; 239662306a36Sopenharmony_ci else if (bmcr[i] & BMCR_SPEED1000) 239762306a36Sopenharmony_ci speed = SPEED_1000; 239862306a36Sopenharmony_ci else if (bmcr[i] & BMCR_SPEED100) 239962306a36Sopenharmony_ci speed = SPEED_100; 240062306a36Sopenharmony_ci else 240162306a36Sopenharmony_ci speed = SPEED_10; 240262306a36Sopenharmony_ci 240362306a36Sopenharmony_ci xpcs_link_up(&xpcs->pcs, neg_mode, priv->phy_mode[i], 240462306a36Sopenharmony_ci speed, DUPLEX_FULL); 240562306a36Sopenharmony_ci } 240662306a36Sopenharmony_ci } 240762306a36Sopenharmony_ci 240862306a36Sopenharmony_ci rc = sja1105_reload_cbs(priv); 240962306a36Sopenharmony_ci if (rc < 0) 241062306a36Sopenharmony_ci goto out; 241162306a36Sopenharmony_ciout: 241262306a36Sopenharmony_ci mutex_unlock(&priv->mgmt_lock); 241362306a36Sopenharmony_ci mutex_unlock(&priv->fdb_lock); 241462306a36Sopenharmony_ci 241562306a36Sopenharmony_ci return rc; 241662306a36Sopenharmony_ci} 241762306a36Sopenharmony_ci 241862306a36Sopenharmony_cistatic enum dsa_tag_protocol 241962306a36Sopenharmony_cisja1105_get_tag_protocol(struct dsa_switch *ds, int port, 242062306a36Sopenharmony_ci enum dsa_tag_protocol mp) 242162306a36Sopenharmony_ci{ 242262306a36Sopenharmony_ci struct sja1105_private *priv = ds->priv; 242362306a36Sopenharmony_ci 242462306a36Sopenharmony_ci return priv->info->tag_proto; 242562306a36Sopenharmony_ci} 242662306a36Sopenharmony_ci 242762306a36Sopenharmony_ci/* The TPID setting belongs to the General Parameters table, 242862306a36Sopenharmony_ci * which can only be partially reconfigured at runtime (and not the TPID). 242962306a36Sopenharmony_ci * So a switch reset is required. 243062306a36Sopenharmony_ci */ 243162306a36Sopenharmony_ciint sja1105_vlan_filtering(struct dsa_switch *ds, int port, bool enabled, 243262306a36Sopenharmony_ci struct netlink_ext_ack *extack) 243362306a36Sopenharmony_ci{ 243462306a36Sopenharmony_ci struct sja1105_general_params_entry *general_params; 243562306a36Sopenharmony_ci struct sja1105_private *priv = ds->priv; 243662306a36Sopenharmony_ci struct sja1105_table *table; 243762306a36Sopenharmony_ci struct sja1105_rule *rule; 243862306a36Sopenharmony_ci u16 tpid, tpid2; 243962306a36Sopenharmony_ci int rc; 244062306a36Sopenharmony_ci 244162306a36Sopenharmony_ci list_for_each_entry(rule, &priv->flow_block.rules, list) { 244262306a36Sopenharmony_ci if (rule->type == SJA1105_RULE_VL) { 244362306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, 244462306a36Sopenharmony_ci "Cannot change VLAN filtering with active VL rules"); 244562306a36Sopenharmony_ci return -EBUSY; 244662306a36Sopenharmony_ci } 244762306a36Sopenharmony_ci } 244862306a36Sopenharmony_ci 244962306a36Sopenharmony_ci if (enabled) { 245062306a36Sopenharmony_ci /* Enable VLAN filtering. */ 245162306a36Sopenharmony_ci tpid = ETH_P_8021Q; 245262306a36Sopenharmony_ci tpid2 = ETH_P_8021AD; 245362306a36Sopenharmony_ci } else { 245462306a36Sopenharmony_ci /* Disable VLAN filtering. */ 245562306a36Sopenharmony_ci tpid = ETH_P_SJA1105; 245662306a36Sopenharmony_ci tpid2 = ETH_P_SJA1105; 245762306a36Sopenharmony_ci } 245862306a36Sopenharmony_ci 245962306a36Sopenharmony_ci table = &priv->static_config.tables[BLK_IDX_GENERAL_PARAMS]; 246062306a36Sopenharmony_ci general_params = table->entries; 246162306a36Sopenharmony_ci /* EtherType used to identify inner tagged (C-tag) VLAN traffic */ 246262306a36Sopenharmony_ci general_params->tpid = tpid; 246362306a36Sopenharmony_ci /* EtherType used to identify outer tagged (S-tag) VLAN traffic */ 246462306a36Sopenharmony_ci general_params->tpid2 = tpid2; 246562306a36Sopenharmony_ci 246662306a36Sopenharmony_ci for (port = 0; port < ds->num_ports; port++) { 246762306a36Sopenharmony_ci if (dsa_is_unused_port(ds, port)) 246862306a36Sopenharmony_ci continue; 246962306a36Sopenharmony_ci 247062306a36Sopenharmony_ci rc = sja1105_commit_pvid(ds, port); 247162306a36Sopenharmony_ci if (rc) 247262306a36Sopenharmony_ci return rc; 247362306a36Sopenharmony_ci } 247462306a36Sopenharmony_ci 247562306a36Sopenharmony_ci rc = sja1105_static_config_reload(priv, SJA1105_VLAN_FILTERING); 247662306a36Sopenharmony_ci if (rc) 247762306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "Failed to change VLAN Ethertype"); 247862306a36Sopenharmony_ci 247962306a36Sopenharmony_ci return rc; 248062306a36Sopenharmony_ci} 248162306a36Sopenharmony_ci 248262306a36Sopenharmony_cistatic int sja1105_vlan_add(struct sja1105_private *priv, int port, u16 vid, 248362306a36Sopenharmony_ci u16 flags, bool allowed_ingress) 248462306a36Sopenharmony_ci{ 248562306a36Sopenharmony_ci struct sja1105_vlan_lookup_entry *vlan; 248662306a36Sopenharmony_ci struct sja1105_table *table; 248762306a36Sopenharmony_ci int match, rc; 248862306a36Sopenharmony_ci 248962306a36Sopenharmony_ci table = &priv->static_config.tables[BLK_IDX_VLAN_LOOKUP]; 249062306a36Sopenharmony_ci 249162306a36Sopenharmony_ci match = sja1105_is_vlan_configured(priv, vid); 249262306a36Sopenharmony_ci if (match < 0) { 249362306a36Sopenharmony_ci rc = sja1105_table_resize(table, table->entry_count + 1); 249462306a36Sopenharmony_ci if (rc) 249562306a36Sopenharmony_ci return rc; 249662306a36Sopenharmony_ci match = table->entry_count - 1; 249762306a36Sopenharmony_ci } 249862306a36Sopenharmony_ci 249962306a36Sopenharmony_ci /* Assign pointer after the resize (it's new memory) */ 250062306a36Sopenharmony_ci vlan = table->entries; 250162306a36Sopenharmony_ci 250262306a36Sopenharmony_ci vlan[match].type_entry = SJA1110_VLAN_D_TAG; 250362306a36Sopenharmony_ci vlan[match].vlanid = vid; 250462306a36Sopenharmony_ci vlan[match].vlan_bc |= BIT(port); 250562306a36Sopenharmony_ci 250662306a36Sopenharmony_ci if (allowed_ingress) 250762306a36Sopenharmony_ci vlan[match].vmemb_port |= BIT(port); 250862306a36Sopenharmony_ci else 250962306a36Sopenharmony_ci vlan[match].vmemb_port &= ~BIT(port); 251062306a36Sopenharmony_ci 251162306a36Sopenharmony_ci if (flags & BRIDGE_VLAN_INFO_UNTAGGED) 251262306a36Sopenharmony_ci vlan[match].tag_port &= ~BIT(port); 251362306a36Sopenharmony_ci else 251462306a36Sopenharmony_ci vlan[match].tag_port |= BIT(port); 251562306a36Sopenharmony_ci 251662306a36Sopenharmony_ci return sja1105_dynamic_config_write(priv, BLK_IDX_VLAN_LOOKUP, vid, 251762306a36Sopenharmony_ci &vlan[match], true); 251862306a36Sopenharmony_ci} 251962306a36Sopenharmony_ci 252062306a36Sopenharmony_cistatic int sja1105_vlan_del(struct sja1105_private *priv, int port, u16 vid) 252162306a36Sopenharmony_ci{ 252262306a36Sopenharmony_ci struct sja1105_vlan_lookup_entry *vlan; 252362306a36Sopenharmony_ci struct sja1105_table *table; 252462306a36Sopenharmony_ci bool keep = true; 252562306a36Sopenharmony_ci int match, rc; 252662306a36Sopenharmony_ci 252762306a36Sopenharmony_ci table = &priv->static_config.tables[BLK_IDX_VLAN_LOOKUP]; 252862306a36Sopenharmony_ci 252962306a36Sopenharmony_ci match = sja1105_is_vlan_configured(priv, vid); 253062306a36Sopenharmony_ci /* Can't delete a missing entry. */ 253162306a36Sopenharmony_ci if (match < 0) 253262306a36Sopenharmony_ci return 0; 253362306a36Sopenharmony_ci 253462306a36Sopenharmony_ci /* Assign pointer after the resize (it's new memory) */ 253562306a36Sopenharmony_ci vlan = table->entries; 253662306a36Sopenharmony_ci 253762306a36Sopenharmony_ci vlan[match].vlanid = vid; 253862306a36Sopenharmony_ci vlan[match].vlan_bc &= ~BIT(port); 253962306a36Sopenharmony_ci vlan[match].vmemb_port &= ~BIT(port); 254062306a36Sopenharmony_ci /* Also unset tag_port, just so we don't have a confusing bitmap 254162306a36Sopenharmony_ci * (no practical purpose). 254262306a36Sopenharmony_ci */ 254362306a36Sopenharmony_ci vlan[match].tag_port &= ~BIT(port); 254462306a36Sopenharmony_ci 254562306a36Sopenharmony_ci /* If there's no port left as member of this VLAN, 254662306a36Sopenharmony_ci * it's time for it to go. 254762306a36Sopenharmony_ci */ 254862306a36Sopenharmony_ci if (!vlan[match].vmemb_port) 254962306a36Sopenharmony_ci keep = false; 255062306a36Sopenharmony_ci 255162306a36Sopenharmony_ci rc = sja1105_dynamic_config_write(priv, BLK_IDX_VLAN_LOOKUP, vid, 255262306a36Sopenharmony_ci &vlan[match], keep); 255362306a36Sopenharmony_ci if (rc < 0) 255462306a36Sopenharmony_ci return rc; 255562306a36Sopenharmony_ci 255662306a36Sopenharmony_ci if (!keep) 255762306a36Sopenharmony_ci return sja1105_table_delete_entry(table, match); 255862306a36Sopenharmony_ci 255962306a36Sopenharmony_ci return 0; 256062306a36Sopenharmony_ci} 256162306a36Sopenharmony_ci 256262306a36Sopenharmony_cistatic int sja1105_bridge_vlan_add(struct dsa_switch *ds, int port, 256362306a36Sopenharmony_ci const struct switchdev_obj_port_vlan *vlan, 256462306a36Sopenharmony_ci struct netlink_ext_ack *extack) 256562306a36Sopenharmony_ci{ 256662306a36Sopenharmony_ci struct sja1105_private *priv = ds->priv; 256762306a36Sopenharmony_ci u16 flags = vlan->flags; 256862306a36Sopenharmony_ci int rc; 256962306a36Sopenharmony_ci 257062306a36Sopenharmony_ci /* Be sure to deny alterations to the configuration done by tag_8021q. 257162306a36Sopenharmony_ci */ 257262306a36Sopenharmony_ci if (vid_is_dsa_8021q(vlan->vid)) { 257362306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, 257462306a36Sopenharmony_ci "Range 3072-4095 reserved for dsa_8021q operation"); 257562306a36Sopenharmony_ci return -EBUSY; 257662306a36Sopenharmony_ci } 257762306a36Sopenharmony_ci 257862306a36Sopenharmony_ci /* Always install bridge VLANs as egress-tagged on CPU and DSA ports */ 257962306a36Sopenharmony_ci if (dsa_is_cpu_port(ds, port) || dsa_is_dsa_port(ds, port)) 258062306a36Sopenharmony_ci flags = 0; 258162306a36Sopenharmony_ci 258262306a36Sopenharmony_ci rc = sja1105_vlan_add(priv, port, vlan->vid, flags, true); 258362306a36Sopenharmony_ci if (rc) 258462306a36Sopenharmony_ci return rc; 258562306a36Sopenharmony_ci 258662306a36Sopenharmony_ci if (vlan->flags & BRIDGE_VLAN_INFO_PVID) 258762306a36Sopenharmony_ci priv->bridge_pvid[port] = vlan->vid; 258862306a36Sopenharmony_ci 258962306a36Sopenharmony_ci return sja1105_commit_pvid(ds, port); 259062306a36Sopenharmony_ci} 259162306a36Sopenharmony_ci 259262306a36Sopenharmony_cistatic int sja1105_bridge_vlan_del(struct dsa_switch *ds, int port, 259362306a36Sopenharmony_ci const struct switchdev_obj_port_vlan *vlan) 259462306a36Sopenharmony_ci{ 259562306a36Sopenharmony_ci struct sja1105_private *priv = ds->priv; 259662306a36Sopenharmony_ci int rc; 259762306a36Sopenharmony_ci 259862306a36Sopenharmony_ci rc = sja1105_vlan_del(priv, port, vlan->vid); 259962306a36Sopenharmony_ci if (rc) 260062306a36Sopenharmony_ci return rc; 260162306a36Sopenharmony_ci 260262306a36Sopenharmony_ci /* In case the pvid was deleted, make sure that untagged packets will 260362306a36Sopenharmony_ci * be dropped. 260462306a36Sopenharmony_ci */ 260562306a36Sopenharmony_ci return sja1105_commit_pvid(ds, port); 260662306a36Sopenharmony_ci} 260762306a36Sopenharmony_ci 260862306a36Sopenharmony_cistatic int sja1105_dsa_8021q_vlan_add(struct dsa_switch *ds, int port, u16 vid, 260962306a36Sopenharmony_ci u16 flags) 261062306a36Sopenharmony_ci{ 261162306a36Sopenharmony_ci struct sja1105_private *priv = ds->priv; 261262306a36Sopenharmony_ci bool allowed_ingress = true; 261362306a36Sopenharmony_ci int rc; 261462306a36Sopenharmony_ci 261562306a36Sopenharmony_ci /* Prevent attackers from trying to inject a DSA tag from 261662306a36Sopenharmony_ci * the outside world. 261762306a36Sopenharmony_ci */ 261862306a36Sopenharmony_ci if (dsa_is_user_port(ds, port)) 261962306a36Sopenharmony_ci allowed_ingress = false; 262062306a36Sopenharmony_ci 262162306a36Sopenharmony_ci rc = sja1105_vlan_add(priv, port, vid, flags, allowed_ingress); 262262306a36Sopenharmony_ci if (rc) 262362306a36Sopenharmony_ci return rc; 262462306a36Sopenharmony_ci 262562306a36Sopenharmony_ci if (flags & BRIDGE_VLAN_INFO_PVID) 262662306a36Sopenharmony_ci priv->tag_8021q_pvid[port] = vid; 262762306a36Sopenharmony_ci 262862306a36Sopenharmony_ci return sja1105_commit_pvid(ds, port); 262962306a36Sopenharmony_ci} 263062306a36Sopenharmony_ci 263162306a36Sopenharmony_cistatic int sja1105_dsa_8021q_vlan_del(struct dsa_switch *ds, int port, u16 vid) 263262306a36Sopenharmony_ci{ 263362306a36Sopenharmony_ci struct sja1105_private *priv = ds->priv; 263462306a36Sopenharmony_ci 263562306a36Sopenharmony_ci return sja1105_vlan_del(priv, port, vid); 263662306a36Sopenharmony_ci} 263762306a36Sopenharmony_ci 263862306a36Sopenharmony_cistatic int sja1105_prechangeupper(struct dsa_switch *ds, int port, 263962306a36Sopenharmony_ci struct netdev_notifier_changeupper_info *info) 264062306a36Sopenharmony_ci{ 264162306a36Sopenharmony_ci struct netlink_ext_ack *extack = info->info.extack; 264262306a36Sopenharmony_ci struct net_device *upper = info->upper_dev; 264362306a36Sopenharmony_ci struct dsa_switch_tree *dst = ds->dst; 264462306a36Sopenharmony_ci struct dsa_port *dp; 264562306a36Sopenharmony_ci 264662306a36Sopenharmony_ci if (is_vlan_dev(upper)) { 264762306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "8021q uppers are not supported"); 264862306a36Sopenharmony_ci return -EBUSY; 264962306a36Sopenharmony_ci } 265062306a36Sopenharmony_ci 265162306a36Sopenharmony_ci if (netif_is_bridge_master(upper)) { 265262306a36Sopenharmony_ci list_for_each_entry(dp, &dst->ports, list) { 265362306a36Sopenharmony_ci struct net_device *br = dsa_port_bridge_dev_get(dp); 265462306a36Sopenharmony_ci 265562306a36Sopenharmony_ci if (br && br != upper && br_vlan_enabled(br)) { 265662306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, 265762306a36Sopenharmony_ci "Only one VLAN-aware bridge is supported"); 265862306a36Sopenharmony_ci return -EBUSY; 265962306a36Sopenharmony_ci } 266062306a36Sopenharmony_ci } 266162306a36Sopenharmony_ci } 266262306a36Sopenharmony_ci 266362306a36Sopenharmony_ci return 0; 266462306a36Sopenharmony_ci} 266562306a36Sopenharmony_ci 266662306a36Sopenharmony_cistatic int sja1105_mgmt_xmit(struct dsa_switch *ds, int port, int slot, 266762306a36Sopenharmony_ci struct sk_buff *skb, bool takets) 266862306a36Sopenharmony_ci{ 266962306a36Sopenharmony_ci struct sja1105_mgmt_entry mgmt_route = {0}; 267062306a36Sopenharmony_ci struct sja1105_private *priv = ds->priv; 267162306a36Sopenharmony_ci struct ethhdr *hdr; 267262306a36Sopenharmony_ci int timeout = 10; 267362306a36Sopenharmony_ci int rc; 267462306a36Sopenharmony_ci 267562306a36Sopenharmony_ci hdr = eth_hdr(skb); 267662306a36Sopenharmony_ci 267762306a36Sopenharmony_ci mgmt_route.macaddr = ether_addr_to_u64(hdr->h_dest); 267862306a36Sopenharmony_ci mgmt_route.destports = BIT(port); 267962306a36Sopenharmony_ci mgmt_route.enfport = 1; 268062306a36Sopenharmony_ci mgmt_route.tsreg = 0; 268162306a36Sopenharmony_ci mgmt_route.takets = takets; 268262306a36Sopenharmony_ci 268362306a36Sopenharmony_ci rc = sja1105_dynamic_config_write(priv, BLK_IDX_MGMT_ROUTE, 268462306a36Sopenharmony_ci slot, &mgmt_route, true); 268562306a36Sopenharmony_ci if (rc < 0) { 268662306a36Sopenharmony_ci kfree_skb(skb); 268762306a36Sopenharmony_ci return rc; 268862306a36Sopenharmony_ci } 268962306a36Sopenharmony_ci 269062306a36Sopenharmony_ci /* Transfer skb to the host port. */ 269162306a36Sopenharmony_ci dsa_enqueue_skb(skb, dsa_to_port(ds, port)->slave); 269262306a36Sopenharmony_ci 269362306a36Sopenharmony_ci /* Wait until the switch has processed the frame */ 269462306a36Sopenharmony_ci do { 269562306a36Sopenharmony_ci rc = sja1105_dynamic_config_read(priv, BLK_IDX_MGMT_ROUTE, 269662306a36Sopenharmony_ci slot, &mgmt_route); 269762306a36Sopenharmony_ci if (rc < 0) { 269862306a36Sopenharmony_ci dev_err_ratelimited(priv->ds->dev, 269962306a36Sopenharmony_ci "failed to poll for mgmt route\n"); 270062306a36Sopenharmony_ci continue; 270162306a36Sopenharmony_ci } 270262306a36Sopenharmony_ci 270362306a36Sopenharmony_ci /* UM10944: The ENFPORT flag of the respective entry is 270462306a36Sopenharmony_ci * cleared when a match is found. The host can use this 270562306a36Sopenharmony_ci * flag as an acknowledgment. 270662306a36Sopenharmony_ci */ 270762306a36Sopenharmony_ci cpu_relax(); 270862306a36Sopenharmony_ci } while (mgmt_route.enfport && --timeout); 270962306a36Sopenharmony_ci 271062306a36Sopenharmony_ci if (!timeout) { 271162306a36Sopenharmony_ci /* Clean up the management route so that a follow-up 271262306a36Sopenharmony_ci * frame may not match on it by mistake. 271362306a36Sopenharmony_ci * This is only hardware supported on P/Q/R/S - on E/T it is 271462306a36Sopenharmony_ci * a no-op and we are silently discarding the -EOPNOTSUPP. 271562306a36Sopenharmony_ci */ 271662306a36Sopenharmony_ci sja1105_dynamic_config_write(priv, BLK_IDX_MGMT_ROUTE, 271762306a36Sopenharmony_ci slot, &mgmt_route, false); 271862306a36Sopenharmony_ci dev_err_ratelimited(priv->ds->dev, "xmit timed out\n"); 271962306a36Sopenharmony_ci } 272062306a36Sopenharmony_ci 272162306a36Sopenharmony_ci return NETDEV_TX_OK; 272262306a36Sopenharmony_ci} 272362306a36Sopenharmony_ci 272462306a36Sopenharmony_ci#define work_to_xmit_work(w) \ 272562306a36Sopenharmony_ci container_of((w), struct sja1105_deferred_xmit_work, work) 272662306a36Sopenharmony_ci 272762306a36Sopenharmony_ci/* Deferred work is unfortunately necessary because setting up the management 272862306a36Sopenharmony_ci * route cannot be done from atomit context (SPI transfer takes a sleepable 272962306a36Sopenharmony_ci * lock on the bus) 273062306a36Sopenharmony_ci */ 273162306a36Sopenharmony_cistatic void sja1105_port_deferred_xmit(struct kthread_work *work) 273262306a36Sopenharmony_ci{ 273362306a36Sopenharmony_ci struct sja1105_deferred_xmit_work *xmit_work = work_to_xmit_work(work); 273462306a36Sopenharmony_ci struct sk_buff *clone, *skb = xmit_work->skb; 273562306a36Sopenharmony_ci struct dsa_switch *ds = xmit_work->dp->ds; 273662306a36Sopenharmony_ci struct sja1105_private *priv = ds->priv; 273762306a36Sopenharmony_ci int port = xmit_work->dp->index; 273862306a36Sopenharmony_ci 273962306a36Sopenharmony_ci clone = SJA1105_SKB_CB(skb)->clone; 274062306a36Sopenharmony_ci 274162306a36Sopenharmony_ci mutex_lock(&priv->mgmt_lock); 274262306a36Sopenharmony_ci 274362306a36Sopenharmony_ci sja1105_mgmt_xmit(ds, port, 0, skb, !!clone); 274462306a36Sopenharmony_ci 274562306a36Sopenharmony_ci /* The clone, if there, was made by dsa_skb_tx_timestamp */ 274662306a36Sopenharmony_ci if (clone) 274762306a36Sopenharmony_ci sja1105_ptp_txtstamp_skb(ds, port, clone); 274862306a36Sopenharmony_ci 274962306a36Sopenharmony_ci mutex_unlock(&priv->mgmt_lock); 275062306a36Sopenharmony_ci 275162306a36Sopenharmony_ci kfree(xmit_work); 275262306a36Sopenharmony_ci} 275362306a36Sopenharmony_ci 275462306a36Sopenharmony_cistatic int sja1105_connect_tag_protocol(struct dsa_switch *ds, 275562306a36Sopenharmony_ci enum dsa_tag_protocol proto) 275662306a36Sopenharmony_ci{ 275762306a36Sopenharmony_ci struct sja1105_private *priv = ds->priv; 275862306a36Sopenharmony_ci struct sja1105_tagger_data *tagger_data; 275962306a36Sopenharmony_ci 276062306a36Sopenharmony_ci if (proto != priv->info->tag_proto) 276162306a36Sopenharmony_ci return -EPROTONOSUPPORT; 276262306a36Sopenharmony_ci 276362306a36Sopenharmony_ci tagger_data = sja1105_tagger_data(ds); 276462306a36Sopenharmony_ci tagger_data->xmit_work_fn = sja1105_port_deferred_xmit; 276562306a36Sopenharmony_ci tagger_data->meta_tstamp_handler = sja1110_process_meta_tstamp; 276662306a36Sopenharmony_ci 276762306a36Sopenharmony_ci return 0; 276862306a36Sopenharmony_ci} 276962306a36Sopenharmony_ci 277062306a36Sopenharmony_ci/* The MAXAGE setting belongs to the L2 Forwarding Parameters table, 277162306a36Sopenharmony_ci * which cannot be reconfigured at runtime. So a switch reset is required. 277262306a36Sopenharmony_ci */ 277362306a36Sopenharmony_cistatic int sja1105_set_ageing_time(struct dsa_switch *ds, 277462306a36Sopenharmony_ci unsigned int ageing_time) 277562306a36Sopenharmony_ci{ 277662306a36Sopenharmony_ci struct sja1105_l2_lookup_params_entry *l2_lookup_params; 277762306a36Sopenharmony_ci struct sja1105_private *priv = ds->priv; 277862306a36Sopenharmony_ci struct sja1105_table *table; 277962306a36Sopenharmony_ci unsigned int maxage; 278062306a36Sopenharmony_ci 278162306a36Sopenharmony_ci table = &priv->static_config.tables[BLK_IDX_L2_LOOKUP_PARAMS]; 278262306a36Sopenharmony_ci l2_lookup_params = table->entries; 278362306a36Sopenharmony_ci 278462306a36Sopenharmony_ci maxage = SJA1105_AGEING_TIME_MS(ageing_time); 278562306a36Sopenharmony_ci 278662306a36Sopenharmony_ci if (l2_lookup_params->maxage == maxage) 278762306a36Sopenharmony_ci return 0; 278862306a36Sopenharmony_ci 278962306a36Sopenharmony_ci l2_lookup_params->maxage = maxage; 279062306a36Sopenharmony_ci 279162306a36Sopenharmony_ci return sja1105_static_config_reload(priv, SJA1105_AGEING_TIME); 279262306a36Sopenharmony_ci} 279362306a36Sopenharmony_ci 279462306a36Sopenharmony_cistatic int sja1105_change_mtu(struct dsa_switch *ds, int port, int new_mtu) 279562306a36Sopenharmony_ci{ 279662306a36Sopenharmony_ci struct sja1105_l2_policing_entry *policing; 279762306a36Sopenharmony_ci struct sja1105_private *priv = ds->priv; 279862306a36Sopenharmony_ci 279962306a36Sopenharmony_ci new_mtu += VLAN_ETH_HLEN + ETH_FCS_LEN; 280062306a36Sopenharmony_ci 280162306a36Sopenharmony_ci if (dsa_is_cpu_port(ds, port) || dsa_is_dsa_port(ds, port)) 280262306a36Sopenharmony_ci new_mtu += VLAN_HLEN; 280362306a36Sopenharmony_ci 280462306a36Sopenharmony_ci policing = priv->static_config.tables[BLK_IDX_L2_POLICING].entries; 280562306a36Sopenharmony_ci 280662306a36Sopenharmony_ci if (policing[port].maxlen == new_mtu) 280762306a36Sopenharmony_ci return 0; 280862306a36Sopenharmony_ci 280962306a36Sopenharmony_ci policing[port].maxlen = new_mtu; 281062306a36Sopenharmony_ci 281162306a36Sopenharmony_ci return sja1105_static_config_reload(priv, SJA1105_BEST_EFFORT_POLICING); 281262306a36Sopenharmony_ci} 281362306a36Sopenharmony_ci 281462306a36Sopenharmony_cistatic int sja1105_get_max_mtu(struct dsa_switch *ds, int port) 281562306a36Sopenharmony_ci{ 281662306a36Sopenharmony_ci return 2043 - VLAN_ETH_HLEN - ETH_FCS_LEN; 281762306a36Sopenharmony_ci} 281862306a36Sopenharmony_ci 281962306a36Sopenharmony_cistatic int sja1105_port_setup_tc(struct dsa_switch *ds, int port, 282062306a36Sopenharmony_ci enum tc_setup_type type, 282162306a36Sopenharmony_ci void *type_data) 282262306a36Sopenharmony_ci{ 282362306a36Sopenharmony_ci switch (type) { 282462306a36Sopenharmony_ci case TC_SETUP_QDISC_TAPRIO: 282562306a36Sopenharmony_ci return sja1105_setup_tc_taprio(ds, port, type_data); 282662306a36Sopenharmony_ci case TC_SETUP_QDISC_CBS: 282762306a36Sopenharmony_ci return sja1105_setup_tc_cbs(ds, port, type_data); 282862306a36Sopenharmony_ci default: 282962306a36Sopenharmony_ci return -EOPNOTSUPP; 283062306a36Sopenharmony_ci } 283162306a36Sopenharmony_ci} 283262306a36Sopenharmony_ci 283362306a36Sopenharmony_ci/* We have a single mirror (@to) port, but can configure ingress and egress 283462306a36Sopenharmony_ci * mirroring on all other (@from) ports. 283562306a36Sopenharmony_ci * We need to allow mirroring rules only as long as the @to port is always the 283662306a36Sopenharmony_ci * same, and we need to unset the @to port from mirr_port only when there is no 283762306a36Sopenharmony_ci * mirroring rule that references it. 283862306a36Sopenharmony_ci */ 283962306a36Sopenharmony_cistatic int sja1105_mirror_apply(struct sja1105_private *priv, int from, int to, 284062306a36Sopenharmony_ci bool ingress, bool enabled) 284162306a36Sopenharmony_ci{ 284262306a36Sopenharmony_ci struct sja1105_general_params_entry *general_params; 284362306a36Sopenharmony_ci struct sja1105_mac_config_entry *mac; 284462306a36Sopenharmony_ci struct dsa_switch *ds = priv->ds; 284562306a36Sopenharmony_ci struct sja1105_table *table; 284662306a36Sopenharmony_ci bool already_enabled; 284762306a36Sopenharmony_ci u64 new_mirr_port; 284862306a36Sopenharmony_ci int rc; 284962306a36Sopenharmony_ci 285062306a36Sopenharmony_ci table = &priv->static_config.tables[BLK_IDX_GENERAL_PARAMS]; 285162306a36Sopenharmony_ci general_params = table->entries; 285262306a36Sopenharmony_ci 285362306a36Sopenharmony_ci mac = priv->static_config.tables[BLK_IDX_MAC_CONFIG].entries; 285462306a36Sopenharmony_ci 285562306a36Sopenharmony_ci already_enabled = (general_params->mirr_port != ds->num_ports); 285662306a36Sopenharmony_ci if (already_enabled && enabled && general_params->mirr_port != to) { 285762306a36Sopenharmony_ci dev_err(priv->ds->dev, 285862306a36Sopenharmony_ci "Delete mirroring rules towards port %llu first\n", 285962306a36Sopenharmony_ci general_params->mirr_port); 286062306a36Sopenharmony_ci return -EBUSY; 286162306a36Sopenharmony_ci } 286262306a36Sopenharmony_ci 286362306a36Sopenharmony_ci new_mirr_port = to; 286462306a36Sopenharmony_ci if (!enabled) { 286562306a36Sopenharmony_ci bool keep = false; 286662306a36Sopenharmony_ci int port; 286762306a36Sopenharmony_ci 286862306a36Sopenharmony_ci /* Anybody still referencing mirr_port? */ 286962306a36Sopenharmony_ci for (port = 0; port < ds->num_ports; port++) { 287062306a36Sopenharmony_ci if (mac[port].ing_mirr || mac[port].egr_mirr) { 287162306a36Sopenharmony_ci keep = true; 287262306a36Sopenharmony_ci break; 287362306a36Sopenharmony_ci } 287462306a36Sopenharmony_ci } 287562306a36Sopenharmony_ci /* Unset already_enabled for next time */ 287662306a36Sopenharmony_ci if (!keep) 287762306a36Sopenharmony_ci new_mirr_port = ds->num_ports; 287862306a36Sopenharmony_ci } 287962306a36Sopenharmony_ci if (new_mirr_port != general_params->mirr_port) { 288062306a36Sopenharmony_ci general_params->mirr_port = new_mirr_port; 288162306a36Sopenharmony_ci 288262306a36Sopenharmony_ci rc = sja1105_dynamic_config_write(priv, BLK_IDX_GENERAL_PARAMS, 288362306a36Sopenharmony_ci 0, general_params, true); 288462306a36Sopenharmony_ci if (rc < 0) 288562306a36Sopenharmony_ci return rc; 288662306a36Sopenharmony_ci } 288762306a36Sopenharmony_ci 288862306a36Sopenharmony_ci if (ingress) 288962306a36Sopenharmony_ci mac[from].ing_mirr = enabled; 289062306a36Sopenharmony_ci else 289162306a36Sopenharmony_ci mac[from].egr_mirr = enabled; 289262306a36Sopenharmony_ci 289362306a36Sopenharmony_ci return sja1105_dynamic_config_write(priv, BLK_IDX_MAC_CONFIG, from, 289462306a36Sopenharmony_ci &mac[from], true); 289562306a36Sopenharmony_ci} 289662306a36Sopenharmony_ci 289762306a36Sopenharmony_cistatic int sja1105_mirror_add(struct dsa_switch *ds, int port, 289862306a36Sopenharmony_ci struct dsa_mall_mirror_tc_entry *mirror, 289962306a36Sopenharmony_ci bool ingress, struct netlink_ext_ack *extack) 290062306a36Sopenharmony_ci{ 290162306a36Sopenharmony_ci return sja1105_mirror_apply(ds->priv, port, mirror->to_local_port, 290262306a36Sopenharmony_ci ingress, true); 290362306a36Sopenharmony_ci} 290462306a36Sopenharmony_ci 290562306a36Sopenharmony_cistatic void sja1105_mirror_del(struct dsa_switch *ds, int port, 290662306a36Sopenharmony_ci struct dsa_mall_mirror_tc_entry *mirror) 290762306a36Sopenharmony_ci{ 290862306a36Sopenharmony_ci sja1105_mirror_apply(ds->priv, port, mirror->to_local_port, 290962306a36Sopenharmony_ci mirror->ingress, false); 291062306a36Sopenharmony_ci} 291162306a36Sopenharmony_ci 291262306a36Sopenharmony_cistatic int sja1105_port_policer_add(struct dsa_switch *ds, int port, 291362306a36Sopenharmony_ci struct dsa_mall_policer_tc_entry *policer) 291462306a36Sopenharmony_ci{ 291562306a36Sopenharmony_ci struct sja1105_l2_policing_entry *policing; 291662306a36Sopenharmony_ci struct sja1105_private *priv = ds->priv; 291762306a36Sopenharmony_ci 291862306a36Sopenharmony_ci policing = priv->static_config.tables[BLK_IDX_L2_POLICING].entries; 291962306a36Sopenharmony_ci 292062306a36Sopenharmony_ci /* In hardware, every 8 microseconds the credit level is incremented by 292162306a36Sopenharmony_ci * the value of RATE bytes divided by 64, up to a maximum of SMAX 292262306a36Sopenharmony_ci * bytes. 292362306a36Sopenharmony_ci */ 292462306a36Sopenharmony_ci policing[port].rate = div_u64(512 * policer->rate_bytes_per_sec, 292562306a36Sopenharmony_ci 1000000); 292662306a36Sopenharmony_ci policing[port].smax = policer->burst; 292762306a36Sopenharmony_ci 292862306a36Sopenharmony_ci return sja1105_static_config_reload(priv, SJA1105_BEST_EFFORT_POLICING); 292962306a36Sopenharmony_ci} 293062306a36Sopenharmony_ci 293162306a36Sopenharmony_cistatic void sja1105_port_policer_del(struct dsa_switch *ds, int port) 293262306a36Sopenharmony_ci{ 293362306a36Sopenharmony_ci struct sja1105_l2_policing_entry *policing; 293462306a36Sopenharmony_ci struct sja1105_private *priv = ds->priv; 293562306a36Sopenharmony_ci 293662306a36Sopenharmony_ci policing = priv->static_config.tables[BLK_IDX_L2_POLICING].entries; 293762306a36Sopenharmony_ci 293862306a36Sopenharmony_ci policing[port].rate = SJA1105_RATE_MBPS(1000); 293962306a36Sopenharmony_ci policing[port].smax = 65535; 294062306a36Sopenharmony_ci 294162306a36Sopenharmony_ci sja1105_static_config_reload(priv, SJA1105_BEST_EFFORT_POLICING); 294262306a36Sopenharmony_ci} 294362306a36Sopenharmony_ci 294462306a36Sopenharmony_cistatic int sja1105_port_set_learning(struct sja1105_private *priv, int port, 294562306a36Sopenharmony_ci bool enabled) 294662306a36Sopenharmony_ci{ 294762306a36Sopenharmony_ci struct sja1105_mac_config_entry *mac; 294862306a36Sopenharmony_ci 294962306a36Sopenharmony_ci mac = priv->static_config.tables[BLK_IDX_MAC_CONFIG].entries; 295062306a36Sopenharmony_ci 295162306a36Sopenharmony_ci mac[port].dyn_learn = enabled; 295262306a36Sopenharmony_ci 295362306a36Sopenharmony_ci return sja1105_dynamic_config_write(priv, BLK_IDX_MAC_CONFIG, port, 295462306a36Sopenharmony_ci &mac[port], true); 295562306a36Sopenharmony_ci} 295662306a36Sopenharmony_ci 295762306a36Sopenharmony_cistatic int sja1105_port_ucast_bcast_flood(struct sja1105_private *priv, int to, 295862306a36Sopenharmony_ci struct switchdev_brport_flags flags) 295962306a36Sopenharmony_ci{ 296062306a36Sopenharmony_ci if (flags.mask & BR_FLOOD) { 296162306a36Sopenharmony_ci if (flags.val & BR_FLOOD) 296262306a36Sopenharmony_ci priv->ucast_egress_floods |= BIT(to); 296362306a36Sopenharmony_ci else 296462306a36Sopenharmony_ci priv->ucast_egress_floods &= ~BIT(to); 296562306a36Sopenharmony_ci } 296662306a36Sopenharmony_ci 296762306a36Sopenharmony_ci if (flags.mask & BR_BCAST_FLOOD) { 296862306a36Sopenharmony_ci if (flags.val & BR_BCAST_FLOOD) 296962306a36Sopenharmony_ci priv->bcast_egress_floods |= BIT(to); 297062306a36Sopenharmony_ci else 297162306a36Sopenharmony_ci priv->bcast_egress_floods &= ~BIT(to); 297262306a36Sopenharmony_ci } 297362306a36Sopenharmony_ci 297462306a36Sopenharmony_ci return sja1105_manage_flood_domains(priv); 297562306a36Sopenharmony_ci} 297662306a36Sopenharmony_ci 297762306a36Sopenharmony_cistatic int sja1105_port_mcast_flood(struct sja1105_private *priv, int to, 297862306a36Sopenharmony_ci struct switchdev_brport_flags flags, 297962306a36Sopenharmony_ci struct netlink_ext_ack *extack) 298062306a36Sopenharmony_ci{ 298162306a36Sopenharmony_ci struct sja1105_l2_lookup_entry *l2_lookup; 298262306a36Sopenharmony_ci struct sja1105_table *table; 298362306a36Sopenharmony_ci int match, rc; 298462306a36Sopenharmony_ci 298562306a36Sopenharmony_ci mutex_lock(&priv->fdb_lock); 298662306a36Sopenharmony_ci 298762306a36Sopenharmony_ci table = &priv->static_config.tables[BLK_IDX_L2_LOOKUP]; 298862306a36Sopenharmony_ci l2_lookup = table->entries; 298962306a36Sopenharmony_ci 299062306a36Sopenharmony_ci for (match = 0; match < table->entry_count; match++) 299162306a36Sopenharmony_ci if (l2_lookup[match].macaddr == SJA1105_UNKNOWN_MULTICAST && 299262306a36Sopenharmony_ci l2_lookup[match].mask_macaddr == SJA1105_UNKNOWN_MULTICAST) 299362306a36Sopenharmony_ci break; 299462306a36Sopenharmony_ci 299562306a36Sopenharmony_ci if (match == table->entry_count) { 299662306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, 299762306a36Sopenharmony_ci "Could not find FDB entry for unknown multicast"); 299862306a36Sopenharmony_ci rc = -ENOSPC; 299962306a36Sopenharmony_ci goto out; 300062306a36Sopenharmony_ci } 300162306a36Sopenharmony_ci 300262306a36Sopenharmony_ci if (flags.val & BR_MCAST_FLOOD) 300362306a36Sopenharmony_ci l2_lookup[match].destports |= BIT(to); 300462306a36Sopenharmony_ci else 300562306a36Sopenharmony_ci l2_lookup[match].destports &= ~BIT(to); 300662306a36Sopenharmony_ci 300762306a36Sopenharmony_ci rc = sja1105_dynamic_config_write(priv, BLK_IDX_L2_LOOKUP, 300862306a36Sopenharmony_ci l2_lookup[match].index, 300962306a36Sopenharmony_ci &l2_lookup[match], true); 301062306a36Sopenharmony_ciout: 301162306a36Sopenharmony_ci mutex_unlock(&priv->fdb_lock); 301262306a36Sopenharmony_ci 301362306a36Sopenharmony_ci return rc; 301462306a36Sopenharmony_ci} 301562306a36Sopenharmony_ci 301662306a36Sopenharmony_cistatic int sja1105_port_pre_bridge_flags(struct dsa_switch *ds, int port, 301762306a36Sopenharmony_ci struct switchdev_brport_flags flags, 301862306a36Sopenharmony_ci struct netlink_ext_ack *extack) 301962306a36Sopenharmony_ci{ 302062306a36Sopenharmony_ci struct sja1105_private *priv = ds->priv; 302162306a36Sopenharmony_ci 302262306a36Sopenharmony_ci if (flags.mask & ~(BR_LEARNING | BR_FLOOD | BR_MCAST_FLOOD | 302362306a36Sopenharmony_ci BR_BCAST_FLOOD)) 302462306a36Sopenharmony_ci return -EINVAL; 302562306a36Sopenharmony_ci 302662306a36Sopenharmony_ci if (flags.mask & (BR_FLOOD | BR_MCAST_FLOOD) && 302762306a36Sopenharmony_ci !priv->info->can_limit_mcast_flood) { 302862306a36Sopenharmony_ci bool multicast = !!(flags.val & BR_MCAST_FLOOD); 302962306a36Sopenharmony_ci bool unicast = !!(flags.val & BR_FLOOD); 303062306a36Sopenharmony_ci 303162306a36Sopenharmony_ci if (unicast != multicast) { 303262306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, 303362306a36Sopenharmony_ci "This chip cannot configure multicast flooding independently of unicast"); 303462306a36Sopenharmony_ci return -EINVAL; 303562306a36Sopenharmony_ci } 303662306a36Sopenharmony_ci } 303762306a36Sopenharmony_ci 303862306a36Sopenharmony_ci return 0; 303962306a36Sopenharmony_ci} 304062306a36Sopenharmony_ci 304162306a36Sopenharmony_cistatic int sja1105_port_bridge_flags(struct dsa_switch *ds, int port, 304262306a36Sopenharmony_ci struct switchdev_brport_flags flags, 304362306a36Sopenharmony_ci struct netlink_ext_ack *extack) 304462306a36Sopenharmony_ci{ 304562306a36Sopenharmony_ci struct sja1105_private *priv = ds->priv; 304662306a36Sopenharmony_ci int rc; 304762306a36Sopenharmony_ci 304862306a36Sopenharmony_ci if (flags.mask & BR_LEARNING) { 304962306a36Sopenharmony_ci bool learn_ena = !!(flags.val & BR_LEARNING); 305062306a36Sopenharmony_ci 305162306a36Sopenharmony_ci rc = sja1105_port_set_learning(priv, port, learn_ena); 305262306a36Sopenharmony_ci if (rc) 305362306a36Sopenharmony_ci return rc; 305462306a36Sopenharmony_ci } 305562306a36Sopenharmony_ci 305662306a36Sopenharmony_ci if (flags.mask & (BR_FLOOD | BR_BCAST_FLOOD)) { 305762306a36Sopenharmony_ci rc = sja1105_port_ucast_bcast_flood(priv, port, flags); 305862306a36Sopenharmony_ci if (rc) 305962306a36Sopenharmony_ci return rc; 306062306a36Sopenharmony_ci } 306162306a36Sopenharmony_ci 306262306a36Sopenharmony_ci /* For chips that can't offload BR_MCAST_FLOOD independently, there 306362306a36Sopenharmony_ci * is nothing to do here, we ensured the configuration is in sync by 306462306a36Sopenharmony_ci * offloading BR_FLOOD. 306562306a36Sopenharmony_ci */ 306662306a36Sopenharmony_ci if (flags.mask & BR_MCAST_FLOOD && priv->info->can_limit_mcast_flood) { 306762306a36Sopenharmony_ci rc = sja1105_port_mcast_flood(priv, port, flags, 306862306a36Sopenharmony_ci extack); 306962306a36Sopenharmony_ci if (rc) 307062306a36Sopenharmony_ci return rc; 307162306a36Sopenharmony_ci } 307262306a36Sopenharmony_ci 307362306a36Sopenharmony_ci return 0; 307462306a36Sopenharmony_ci} 307562306a36Sopenharmony_ci 307662306a36Sopenharmony_ci/* The programming model for the SJA1105 switch is "all-at-once" via static 307762306a36Sopenharmony_ci * configuration tables. Some of these can be dynamically modified at runtime, 307862306a36Sopenharmony_ci * but not the xMII mode parameters table. 307962306a36Sopenharmony_ci * Furthermode, some PHYs may not have crystals for generating their clocks 308062306a36Sopenharmony_ci * (e.g. RMII). Instead, their 50MHz clock is supplied via the SJA1105 port's 308162306a36Sopenharmony_ci * ref_clk pin. So port clocking needs to be initialized early, before 308262306a36Sopenharmony_ci * connecting to PHYs is attempted, otherwise they won't respond through MDIO. 308362306a36Sopenharmony_ci * Setting correct PHY link speed does not matter now. 308462306a36Sopenharmony_ci * But dsa_slave_phy_setup is called later than sja1105_setup, so the PHY 308562306a36Sopenharmony_ci * bindings are not yet parsed by DSA core. We need to parse early so that we 308662306a36Sopenharmony_ci * can populate the xMII mode parameters table. 308762306a36Sopenharmony_ci */ 308862306a36Sopenharmony_cistatic int sja1105_setup(struct dsa_switch *ds) 308962306a36Sopenharmony_ci{ 309062306a36Sopenharmony_ci struct sja1105_private *priv = ds->priv; 309162306a36Sopenharmony_ci int rc; 309262306a36Sopenharmony_ci 309362306a36Sopenharmony_ci if (priv->info->disable_microcontroller) { 309462306a36Sopenharmony_ci rc = priv->info->disable_microcontroller(priv); 309562306a36Sopenharmony_ci if (rc < 0) { 309662306a36Sopenharmony_ci dev_err(ds->dev, 309762306a36Sopenharmony_ci "Failed to disable microcontroller: %pe\n", 309862306a36Sopenharmony_ci ERR_PTR(rc)); 309962306a36Sopenharmony_ci return rc; 310062306a36Sopenharmony_ci } 310162306a36Sopenharmony_ci } 310262306a36Sopenharmony_ci 310362306a36Sopenharmony_ci /* Create and send configuration down to device */ 310462306a36Sopenharmony_ci rc = sja1105_static_config_load(priv); 310562306a36Sopenharmony_ci if (rc < 0) { 310662306a36Sopenharmony_ci dev_err(ds->dev, "Failed to load static config: %d\n", rc); 310762306a36Sopenharmony_ci return rc; 310862306a36Sopenharmony_ci } 310962306a36Sopenharmony_ci 311062306a36Sopenharmony_ci /* Configure the CGU (PHY link modes and speeds) */ 311162306a36Sopenharmony_ci if (priv->info->clocking_setup) { 311262306a36Sopenharmony_ci rc = priv->info->clocking_setup(priv); 311362306a36Sopenharmony_ci if (rc < 0) { 311462306a36Sopenharmony_ci dev_err(ds->dev, 311562306a36Sopenharmony_ci "Failed to configure MII clocking: %pe\n", 311662306a36Sopenharmony_ci ERR_PTR(rc)); 311762306a36Sopenharmony_ci goto out_static_config_free; 311862306a36Sopenharmony_ci } 311962306a36Sopenharmony_ci } 312062306a36Sopenharmony_ci 312162306a36Sopenharmony_ci sja1105_tas_setup(ds); 312262306a36Sopenharmony_ci sja1105_flower_setup(ds); 312362306a36Sopenharmony_ci 312462306a36Sopenharmony_ci rc = sja1105_ptp_clock_register(ds); 312562306a36Sopenharmony_ci if (rc < 0) { 312662306a36Sopenharmony_ci dev_err(ds->dev, "Failed to register PTP clock: %d\n", rc); 312762306a36Sopenharmony_ci goto out_flower_teardown; 312862306a36Sopenharmony_ci } 312962306a36Sopenharmony_ci 313062306a36Sopenharmony_ci rc = sja1105_mdiobus_register(ds); 313162306a36Sopenharmony_ci if (rc < 0) { 313262306a36Sopenharmony_ci dev_err(ds->dev, "Failed to register MDIO bus: %pe\n", 313362306a36Sopenharmony_ci ERR_PTR(rc)); 313462306a36Sopenharmony_ci goto out_ptp_clock_unregister; 313562306a36Sopenharmony_ci } 313662306a36Sopenharmony_ci 313762306a36Sopenharmony_ci rc = sja1105_devlink_setup(ds); 313862306a36Sopenharmony_ci if (rc < 0) 313962306a36Sopenharmony_ci goto out_mdiobus_unregister; 314062306a36Sopenharmony_ci 314162306a36Sopenharmony_ci rtnl_lock(); 314262306a36Sopenharmony_ci rc = dsa_tag_8021q_register(ds, htons(ETH_P_8021Q)); 314362306a36Sopenharmony_ci rtnl_unlock(); 314462306a36Sopenharmony_ci if (rc) 314562306a36Sopenharmony_ci goto out_devlink_teardown; 314662306a36Sopenharmony_ci 314762306a36Sopenharmony_ci /* On SJA1105, VLAN filtering per se is always enabled in hardware. 314862306a36Sopenharmony_ci * The only thing we can do to disable it is lie about what the 802.1Q 314962306a36Sopenharmony_ci * EtherType is. 315062306a36Sopenharmony_ci * So it will still try to apply VLAN filtering, but all ingress 315162306a36Sopenharmony_ci * traffic (except frames received with EtherType of ETH_P_SJA1105) 315262306a36Sopenharmony_ci * will be internally tagged with a distorted VLAN header where the 315362306a36Sopenharmony_ci * TPID is ETH_P_SJA1105, and the VLAN ID is the port pvid. 315462306a36Sopenharmony_ci */ 315562306a36Sopenharmony_ci ds->vlan_filtering_is_global = true; 315662306a36Sopenharmony_ci ds->untag_bridge_pvid = true; 315762306a36Sopenharmony_ci ds->fdb_isolation = true; 315862306a36Sopenharmony_ci /* tag_8021q has 3 bits for the VBID, and the value 0 is reserved */ 315962306a36Sopenharmony_ci ds->max_num_bridges = 7; 316062306a36Sopenharmony_ci 316162306a36Sopenharmony_ci /* Advertise the 8 egress queues */ 316262306a36Sopenharmony_ci ds->num_tx_queues = SJA1105_NUM_TC; 316362306a36Sopenharmony_ci 316462306a36Sopenharmony_ci ds->mtu_enforcement_ingress = true; 316562306a36Sopenharmony_ci ds->assisted_learning_on_cpu_port = true; 316662306a36Sopenharmony_ci 316762306a36Sopenharmony_ci return 0; 316862306a36Sopenharmony_ci 316962306a36Sopenharmony_ciout_devlink_teardown: 317062306a36Sopenharmony_ci sja1105_devlink_teardown(ds); 317162306a36Sopenharmony_ciout_mdiobus_unregister: 317262306a36Sopenharmony_ci sja1105_mdiobus_unregister(ds); 317362306a36Sopenharmony_ciout_ptp_clock_unregister: 317462306a36Sopenharmony_ci sja1105_ptp_clock_unregister(ds); 317562306a36Sopenharmony_ciout_flower_teardown: 317662306a36Sopenharmony_ci sja1105_flower_teardown(ds); 317762306a36Sopenharmony_ci sja1105_tas_teardown(ds); 317862306a36Sopenharmony_ciout_static_config_free: 317962306a36Sopenharmony_ci sja1105_static_config_free(&priv->static_config); 318062306a36Sopenharmony_ci 318162306a36Sopenharmony_ci return rc; 318262306a36Sopenharmony_ci} 318362306a36Sopenharmony_ci 318462306a36Sopenharmony_cistatic void sja1105_teardown(struct dsa_switch *ds) 318562306a36Sopenharmony_ci{ 318662306a36Sopenharmony_ci struct sja1105_private *priv = ds->priv; 318762306a36Sopenharmony_ci 318862306a36Sopenharmony_ci rtnl_lock(); 318962306a36Sopenharmony_ci dsa_tag_8021q_unregister(ds); 319062306a36Sopenharmony_ci rtnl_unlock(); 319162306a36Sopenharmony_ci 319262306a36Sopenharmony_ci sja1105_devlink_teardown(ds); 319362306a36Sopenharmony_ci sja1105_mdiobus_unregister(ds); 319462306a36Sopenharmony_ci sja1105_ptp_clock_unregister(ds); 319562306a36Sopenharmony_ci sja1105_flower_teardown(ds); 319662306a36Sopenharmony_ci sja1105_tas_teardown(ds); 319762306a36Sopenharmony_ci sja1105_static_config_free(&priv->static_config); 319862306a36Sopenharmony_ci} 319962306a36Sopenharmony_ci 320062306a36Sopenharmony_cistatic const struct dsa_switch_ops sja1105_switch_ops = { 320162306a36Sopenharmony_ci .get_tag_protocol = sja1105_get_tag_protocol, 320262306a36Sopenharmony_ci .connect_tag_protocol = sja1105_connect_tag_protocol, 320362306a36Sopenharmony_ci .setup = sja1105_setup, 320462306a36Sopenharmony_ci .teardown = sja1105_teardown, 320562306a36Sopenharmony_ci .set_ageing_time = sja1105_set_ageing_time, 320662306a36Sopenharmony_ci .port_change_mtu = sja1105_change_mtu, 320762306a36Sopenharmony_ci .port_max_mtu = sja1105_get_max_mtu, 320862306a36Sopenharmony_ci .phylink_get_caps = sja1105_phylink_get_caps, 320962306a36Sopenharmony_ci .phylink_mac_select_pcs = sja1105_mac_select_pcs, 321062306a36Sopenharmony_ci .phylink_mac_link_up = sja1105_mac_link_up, 321162306a36Sopenharmony_ci .phylink_mac_link_down = sja1105_mac_link_down, 321262306a36Sopenharmony_ci .get_strings = sja1105_get_strings, 321362306a36Sopenharmony_ci .get_ethtool_stats = sja1105_get_ethtool_stats, 321462306a36Sopenharmony_ci .get_sset_count = sja1105_get_sset_count, 321562306a36Sopenharmony_ci .get_ts_info = sja1105_get_ts_info, 321662306a36Sopenharmony_ci .port_fdb_dump = sja1105_fdb_dump, 321762306a36Sopenharmony_ci .port_fdb_add = sja1105_fdb_add, 321862306a36Sopenharmony_ci .port_fdb_del = sja1105_fdb_del, 321962306a36Sopenharmony_ci .port_fast_age = sja1105_fast_age, 322062306a36Sopenharmony_ci .port_bridge_join = sja1105_bridge_join, 322162306a36Sopenharmony_ci .port_bridge_leave = sja1105_bridge_leave, 322262306a36Sopenharmony_ci .port_pre_bridge_flags = sja1105_port_pre_bridge_flags, 322362306a36Sopenharmony_ci .port_bridge_flags = sja1105_port_bridge_flags, 322462306a36Sopenharmony_ci .port_stp_state_set = sja1105_bridge_stp_state_set, 322562306a36Sopenharmony_ci .port_vlan_filtering = sja1105_vlan_filtering, 322662306a36Sopenharmony_ci .port_vlan_add = sja1105_bridge_vlan_add, 322762306a36Sopenharmony_ci .port_vlan_del = sja1105_bridge_vlan_del, 322862306a36Sopenharmony_ci .port_mdb_add = sja1105_mdb_add, 322962306a36Sopenharmony_ci .port_mdb_del = sja1105_mdb_del, 323062306a36Sopenharmony_ci .port_hwtstamp_get = sja1105_hwtstamp_get, 323162306a36Sopenharmony_ci .port_hwtstamp_set = sja1105_hwtstamp_set, 323262306a36Sopenharmony_ci .port_rxtstamp = sja1105_port_rxtstamp, 323362306a36Sopenharmony_ci .port_txtstamp = sja1105_port_txtstamp, 323462306a36Sopenharmony_ci .port_setup_tc = sja1105_port_setup_tc, 323562306a36Sopenharmony_ci .port_mirror_add = sja1105_mirror_add, 323662306a36Sopenharmony_ci .port_mirror_del = sja1105_mirror_del, 323762306a36Sopenharmony_ci .port_policer_add = sja1105_port_policer_add, 323862306a36Sopenharmony_ci .port_policer_del = sja1105_port_policer_del, 323962306a36Sopenharmony_ci .cls_flower_add = sja1105_cls_flower_add, 324062306a36Sopenharmony_ci .cls_flower_del = sja1105_cls_flower_del, 324162306a36Sopenharmony_ci .cls_flower_stats = sja1105_cls_flower_stats, 324262306a36Sopenharmony_ci .devlink_info_get = sja1105_devlink_info_get, 324362306a36Sopenharmony_ci .tag_8021q_vlan_add = sja1105_dsa_8021q_vlan_add, 324462306a36Sopenharmony_ci .tag_8021q_vlan_del = sja1105_dsa_8021q_vlan_del, 324562306a36Sopenharmony_ci .port_prechangeupper = sja1105_prechangeupper, 324662306a36Sopenharmony_ci}; 324762306a36Sopenharmony_ci 324862306a36Sopenharmony_cistatic const struct of_device_id sja1105_dt_ids[]; 324962306a36Sopenharmony_ci 325062306a36Sopenharmony_cistatic int sja1105_check_device_id(struct sja1105_private *priv) 325162306a36Sopenharmony_ci{ 325262306a36Sopenharmony_ci const struct sja1105_regs *regs = priv->info->regs; 325362306a36Sopenharmony_ci u8 prod_id[SJA1105_SIZE_DEVICE_ID] = {0}; 325462306a36Sopenharmony_ci struct device *dev = &priv->spidev->dev; 325562306a36Sopenharmony_ci const struct of_device_id *match; 325662306a36Sopenharmony_ci u32 device_id; 325762306a36Sopenharmony_ci u64 part_no; 325862306a36Sopenharmony_ci int rc; 325962306a36Sopenharmony_ci 326062306a36Sopenharmony_ci rc = sja1105_xfer_u32(priv, SPI_READ, regs->device_id, &device_id, 326162306a36Sopenharmony_ci NULL); 326262306a36Sopenharmony_ci if (rc < 0) 326362306a36Sopenharmony_ci return rc; 326462306a36Sopenharmony_ci 326562306a36Sopenharmony_ci rc = sja1105_xfer_buf(priv, SPI_READ, regs->prod_id, prod_id, 326662306a36Sopenharmony_ci SJA1105_SIZE_DEVICE_ID); 326762306a36Sopenharmony_ci if (rc < 0) 326862306a36Sopenharmony_ci return rc; 326962306a36Sopenharmony_ci 327062306a36Sopenharmony_ci sja1105_unpack(prod_id, &part_no, 19, 4, SJA1105_SIZE_DEVICE_ID); 327162306a36Sopenharmony_ci 327262306a36Sopenharmony_ci for (match = sja1105_dt_ids; match->compatible[0]; match++) { 327362306a36Sopenharmony_ci const struct sja1105_info *info = match->data; 327462306a36Sopenharmony_ci 327562306a36Sopenharmony_ci /* Is what's been probed in our match table at all? */ 327662306a36Sopenharmony_ci if (info->device_id != device_id || info->part_no != part_no) 327762306a36Sopenharmony_ci continue; 327862306a36Sopenharmony_ci 327962306a36Sopenharmony_ci /* But is it what's in the device tree? */ 328062306a36Sopenharmony_ci if (priv->info->device_id != device_id || 328162306a36Sopenharmony_ci priv->info->part_no != part_no) { 328262306a36Sopenharmony_ci dev_warn(dev, "Device tree specifies chip %s but found %s, please fix it!\n", 328362306a36Sopenharmony_ci priv->info->name, info->name); 328462306a36Sopenharmony_ci /* It isn't. No problem, pick that up. */ 328562306a36Sopenharmony_ci priv->info = info; 328662306a36Sopenharmony_ci } 328762306a36Sopenharmony_ci 328862306a36Sopenharmony_ci return 0; 328962306a36Sopenharmony_ci } 329062306a36Sopenharmony_ci 329162306a36Sopenharmony_ci dev_err(dev, "Unexpected {device ID, part number}: 0x%x 0x%llx\n", 329262306a36Sopenharmony_ci device_id, part_no); 329362306a36Sopenharmony_ci 329462306a36Sopenharmony_ci return -ENODEV; 329562306a36Sopenharmony_ci} 329662306a36Sopenharmony_ci 329762306a36Sopenharmony_cistatic int sja1105_probe(struct spi_device *spi) 329862306a36Sopenharmony_ci{ 329962306a36Sopenharmony_ci struct device *dev = &spi->dev; 330062306a36Sopenharmony_ci struct sja1105_private *priv; 330162306a36Sopenharmony_ci size_t max_xfer, max_msg; 330262306a36Sopenharmony_ci struct dsa_switch *ds; 330362306a36Sopenharmony_ci int rc; 330462306a36Sopenharmony_ci 330562306a36Sopenharmony_ci if (!dev->of_node) { 330662306a36Sopenharmony_ci dev_err(dev, "No DTS bindings for SJA1105 driver\n"); 330762306a36Sopenharmony_ci return -EINVAL; 330862306a36Sopenharmony_ci } 330962306a36Sopenharmony_ci 331062306a36Sopenharmony_ci rc = sja1105_hw_reset(dev, 1, 1); 331162306a36Sopenharmony_ci if (rc) 331262306a36Sopenharmony_ci return rc; 331362306a36Sopenharmony_ci 331462306a36Sopenharmony_ci priv = devm_kzalloc(dev, sizeof(struct sja1105_private), GFP_KERNEL); 331562306a36Sopenharmony_ci if (!priv) 331662306a36Sopenharmony_ci return -ENOMEM; 331762306a36Sopenharmony_ci 331862306a36Sopenharmony_ci /* Populate our driver private structure (priv) based on 331962306a36Sopenharmony_ci * the device tree node that was probed (spi) 332062306a36Sopenharmony_ci */ 332162306a36Sopenharmony_ci priv->spidev = spi; 332262306a36Sopenharmony_ci spi_set_drvdata(spi, priv); 332362306a36Sopenharmony_ci 332462306a36Sopenharmony_ci /* Configure the SPI bus */ 332562306a36Sopenharmony_ci spi->bits_per_word = 8; 332662306a36Sopenharmony_ci rc = spi_setup(spi); 332762306a36Sopenharmony_ci if (rc < 0) { 332862306a36Sopenharmony_ci dev_err(dev, "Could not init SPI\n"); 332962306a36Sopenharmony_ci return rc; 333062306a36Sopenharmony_ci } 333162306a36Sopenharmony_ci 333262306a36Sopenharmony_ci /* In sja1105_xfer, we send spi_messages composed of two spi_transfers: 333362306a36Sopenharmony_ci * a small one for the message header and another one for the current 333462306a36Sopenharmony_ci * chunk of the packed buffer. 333562306a36Sopenharmony_ci * Check that the restrictions imposed by the SPI controller are 333662306a36Sopenharmony_ci * respected: the chunk buffer is smaller than the max transfer size, 333762306a36Sopenharmony_ci * and the total length of the chunk plus its message header is smaller 333862306a36Sopenharmony_ci * than the max message size. 333962306a36Sopenharmony_ci * We do that during probe time since the maximum transfer size is a 334062306a36Sopenharmony_ci * runtime invariant. 334162306a36Sopenharmony_ci */ 334262306a36Sopenharmony_ci max_xfer = spi_max_transfer_size(spi); 334362306a36Sopenharmony_ci max_msg = spi_max_message_size(spi); 334462306a36Sopenharmony_ci 334562306a36Sopenharmony_ci /* We need to send at least one 64-bit word of SPI payload per message 334662306a36Sopenharmony_ci * in order to be able to make useful progress. 334762306a36Sopenharmony_ci */ 334862306a36Sopenharmony_ci if (max_msg < SJA1105_SIZE_SPI_MSG_HEADER + 8) { 334962306a36Sopenharmony_ci dev_err(dev, "SPI master cannot send large enough buffers, aborting\n"); 335062306a36Sopenharmony_ci return -EINVAL; 335162306a36Sopenharmony_ci } 335262306a36Sopenharmony_ci 335362306a36Sopenharmony_ci priv->max_xfer_len = SJA1105_SIZE_SPI_MSG_MAXLEN; 335462306a36Sopenharmony_ci if (priv->max_xfer_len > max_xfer) 335562306a36Sopenharmony_ci priv->max_xfer_len = max_xfer; 335662306a36Sopenharmony_ci if (priv->max_xfer_len > max_msg - SJA1105_SIZE_SPI_MSG_HEADER) 335762306a36Sopenharmony_ci priv->max_xfer_len = max_msg - SJA1105_SIZE_SPI_MSG_HEADER; 335862306a36Sopenharmony_ci 335962306a36Sopenharmony_ci priv->info = of_device_get_match_data(dev); 336062306a36Sopenharmony_ci 336162306a36Sopenharmony_ci /* Detect hardware device */ 336262306a36Sopenharmony_ci rc = sja1105_check_device_id(priv); 336362306a36Sopenharmony_ci if (rc < 0) { 336462306a36Sopenharmony_ci dev_err(dev, "Device ID check failed: %d\n", rc); 336562306a36Sopenharmony_ci return rc; 336662306a36Sopenharmony_ci } 336762306a36Sopenharmony_ci 336862306a36Sopenharmony_ci dev_info(dev, "Probed switch chip: %s\n", priv->info->name); 336962306a36Sopenharmony_ci 337062306a36Sopenharmony_ci ds = devm_kzalloc(dev, sizeof(*ds), GFP_KERNEL); 337162306a36Sopenharmony_ci if (!ds) 337262306a36Sopenharmony_ci return -ENOMEM; 337362306a36Sopenharmony_ci 337462306a36Sopenharmony_ci ds->dev = dev; 337562306a36Sopenharmony_ci ds->num_ports = priv->info->num_ports; 337662306a36Sopenharmony_ci ds->ops = &sja1105_switch_ops; 337762306a36Sopenharmony_ci ds->priv = priv; 337862306a36Sopenharmony_ci priv->ds = ds; 337962306a36Sopenharmony_ci 338062306a36Sopenharmony_ci mutex_init(&priv->ptp_data.lock); 338162306a36Sopenharmony_ci mutex_init(&priv->dynamic_config_lock); 338262306a36Sopenharmony_ci mutex_init(&priv->mgmt_lock); 338362306a36Sopenharmony_ci mutex_init(&priv->fdb_lock); 338462306a36Sopenharmony_ci spin_lock_init(&priv->ts_id_lock); 338562306a36Sopenharmony_ci 338662306a36Sopenharmony_ci rc = sja1105_parse_dt(priv); 338762306a36Sopenharmony_ci if (rc < 0) { 338862306a36Sopenharmony_ci dev_err(ds->dev, "Failed to parse DT: %d\n", rc); 338962306a36Sopenharmony_ci return rc; 339062306a36Sopenharmony_ci } 339162306a36Sopenharmony_ci 339262306a36Sopenharmony_ci if (IS_ENABLED(CONFIG_NET_SCH_CBS)) { 339362306a36Sopenharmony_ci priv->cbs = devm_kcalloc(dev, priv->info->num_cbs_shapers, 339462306a36Sopenharmony_ci sizeof(struct sja1105_cbs_entry), 339562306a36Sopenharmony_ci GFP_KERNEL); 339662306a36Sopenharmony_ci if (!priv->cbs) 339762306a36Sopenharmony_ci return -ENOMEM; 339862306a36Sopenharmony_ci } 339962306a36Sopenharmony_ci 340062306a36Sopenharmony_ci return dsa_register_switch(priv->ds); 340162306a36Sopenharmony_ci} 340262306a36Sopenharmony_ci 340362306a36Sopenharmony_cistatic void sja1105_remove(struct spi_device *spi) 340462306a36Sopenharmony_ci{ 340562306a36Sopenharmony_ci struct sja1105_private *priv = spi_get_drvdata(spi); 340662306a36Sopenharmony_ci 340762306a36Sopenharmony_ci if (!priv) 340862306a36Sopenharmony_ci return; 340962306a36Sopenharmony_ci 341062306a36Sopenharmony_ci dsa_unregister_switch(priv->ds); 341162306a36Sopenharmony_ci} 341262306a36Sopenharmony_ci 341362306a36Sopenharmony_cistatic void sja1105_shutdown(struct spi_device *spi) 341462306a36Sopenharmony_ci{ 341562306a36Sopenharmony_ci struct sja1105_private *priv = spi_get_drvdata(spi); 341662306a36Sopenharmony_ci 341762306a36Sopenharmony_ci if (!priv) 341862306a36Sopenharmony_ci return; 341962306a36Sopenharmony_ci 342062306a36Sopenharmony_ci dsa_switch_shutdown(priv->ds); 342162306a36Sopenharmony_ci 342262306a36Sopenharmony_ci spi_set_drvdata(spi, NULL); 342362306a36Sopenharmony_ci} 342462306a36Sopenharmony_ci 342562306a36Sopenharmony_cistatic const struct of_device_id sja1105_dt_ids[] = { 342662306a36Sopenharmony_ci { .compatible = "nxp,sja1105e", .data = &sja1105e_info }, 342762306a36Sopenharmony_ci { .compatible = "nxp,sja1105t", .data = &sja1105t_info }, 342862306a36Sopenharmony_ci { .compatible = "nxp,sja1105p", .data = &sja1105p_info }, 342962306a36Sopenharmony_ci { .compatible = "nxp,sja1105q", .data = &sja1105q_info }, 343062306a36Sopenharmony_ci { .compatible = "nxp,sja1105r", .data = &sja1105r_info }, 343162306a36Sopenharmony_ci { .compatible = "nxp,sja1105s", .data = &sja1105s_info }, 343262306a36Sopenharmony_ci { .compatible = "nxp,sja1110a", .data = &sja1110a_info }, 343362306a36Sopenharmony_ci { .compatible = "nxp,sja1110b", .data = &sja1110b_info }, 343462306a36Sopenharmony_ci { .compatible = "nxp,sja1110c", .data = &sja1110c_info }, 343562306a36Sopenharmony_ci { .compatible = "nxp,sja1110d", .data = &sja1110d_info }, 343662306a36Sopenharmony_ci { /* sentinel */ }, 343762306a36Sopenharmony_ci}; 343862306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, sja1105_dt_ids); 343962306a36Sopenharmony_ci 344062306a36Sopenharmony_cistatic const struct spi_device_id sja1105_spi_ids[] = { 344162306a36Sopenharmony_ci { "sja1105e" }, 344262306a36Sopenharmony_ci { "sja1105t" }, 344362306a36Sopenharmony_ci { "sja1105p" }, 344462306a36Sopenharmony_ci { "sja1105q" }, 344562306a36Sopenharmony_ci { "sja1105r" }, 344662306a36Sopenharmony_ci { "sja1105s" }, 344762306a36Sopenharmony_ci { "sja1110a" }, 344862306a36Sopenharmony_ci { "sja1110b" }, 344962306a36Sopenharmony_ci { "sja1110c" }, 345062306a36Sopenharmony_ci { "sja1110d" }, 345162306a36Sopenharmony_ci { }, 345262306a36Sopenharmony_ci}; 345362306a36Sopenharmony_ciMODULE_DEVICE_TABLE(spi, sja1105_spi_ids); 345462306a36Sopenharmony_ci 345562306a36Sopenharmony_cistatic struct spi_driver sja1105_driver = { 345662306a36Sopenharmony_ci .driver = { 345762306a36Sopenharmony_ci .name = "sja1105", 345862306a36Sopenharmony_ci .owner = THIS_MODULE, 345962306a36Sopenharmony_ci .of_match_table = of_match_ptr(sja1105_dt_ids), 346062306a36Sopenharmony_ci }, 346162306a36Sopenharmony_ci .id_table = sja1105_spi_ids, 346262306a36Sopenharmony_ci .probe = sja1105_probe, 346362306a36Sopenharmony_ci .remove = sja1105_remove, 346462306a36Sopenharmony_ci .shutdown = sja1105_shutdown, 346562306a36Sopenharmony_ci}; 346662306a36Sopenharmony_ci 346762306a36Sopenharmony_cimodule_spi_driver(sja1105_driver); 346862306a36Sopenharmony_ci 346962306a36Sopenharmony_ciMODULE_AUTHOR("Vladimir Oltean <olteanv@gmail.com>"); 347062306a36Sopenharmony_ciMODULE_AUTHOR("Georg Waibel <georg.waibel@sensor-technik.de>"); 347162306a36Sopenharmony_ciMODULE_DESCRIPTION("SJA1105 Driver"); 347262306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 3473