18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* Copyright (c) 2018, Sensor-Technik Wiedemann GmbH 38c2ecf20Sopenharmony_ci * Copyright (c) 2018-2019, Vladimir Oltean <olteanv@gmail.com> 48c2ecf20Sopenharmony_ci */ 58c2ecf20Sopenharmony_ci 68c2ecf20Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include <linux/delay.h> 98c2ecf20Sopenharmony_ci#include <linux/module.h> 108c2ecf20Sopenharmony_ci#include <linux/printk.h> 118c2ecf20Sopenharmony_ci#include <linux/spi/spi.h> 128c2ecf20Sopenharmony_ci#include <linux/errno.h> 138c2ecf20Sopenharmony_ci#include <linux/gpio/consumer.h> 148c2ecf20Sopenharmony_ci#include <linux/phylink.h> 158c2ecf20Sopenharmony_ci#include <linux/of.h> 168c2ecf20Sopenharmony_ci#include <linux/of_net.h> 178c2ecf20Sopenharmony_ci#include <linux/of_mdio.h> 188c2ecf20Sopenharmony_ci#include <linux/of_device.h> 198c2ecf20Sopenharmony_ci#include <linux/netdev_features.h> 208c2ecf20Sopenharmony_ci#include <linux/netdevice.h> 218c2ecf20Sopenharmony_ci#include <linux/if_bridge.h> 228c2ecf20Sopenharmony_ci#include <linux/if_ether.h> 238c2ecf20Sopenharmony_ci#include <linux/dsa/8021q.h> 248c2ecf20Sopenharmony_ci#include "sja1105.h" 258c2ecf20Sopenharmony_ci#include "sja1105_sgmii.h" 268c2ecf20Sopenharmony_ci#include "sja1105_tas.h" 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci#define SJA1105_DEFAULT_VLAN (VLAN_N_VID - 1) 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_cistatic const struct dsa_switch_ops sja1105_switch_ops; 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_cistatic void sja1105_hw_reset(struct gpio_desc *gpio, unsigned int pulse_len, 338c2ecf20Sopenharmony_ci unsigned int startup_delay) 348c2ecf20Sopenharmony_ci{ 358c2ecf20Sopenharmony_ci gpiod_set_value_cansleep(gpio, 1); 368c2ecf20Sopenharmony_ci /* Wait for minimum reset pulse length */ 378c2ecf20Sopenharmony_ci msleep(pulse_len); 388c2ecf20Sopenharmony_ci gpiod_set_value_cansleep(gpio, 0); 398c2ecf20Sopenharmony_ci /* Wait until chip is ready after reset */ 408c2ecf20Sopenharmony_ci msleep(startup_delay); 418c2ecf20Sopenharmony_ci} 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_cistatic void 448c2ecf20Sopenharmony_cisja1105_port_allow_traffic(struct sja1105_l2_forwarding_entry *l2_fwd, 458c2ecf20Sopenharmony_ci int from, int to, bool allow) 468c2ecf20Sopenharmony_ci{ 478c2ecf20Sopenharmony_ci if (allow) { 488c2ecf20Sopenharmony_ci l2_fwd[from].bc_domain |= BIT(to); 498c2ecf20Sopenharmony_ci l2_fwd[from].reach_port |= BIT(to); 508c2ecf20Sopenharmony_ci l2_fwd[from].fl_domain |= BIT(to); 518c2ecf20Sopenharmony_ci } else { 528c2ecf20Sopenharmony_ci l2_fwd[from].bc_domain &= ~BIT(to); 538c2ecf20Sopenharmony_ci l2_fwd[from].reach_port &= ~BIT(to); 548c2ecf20Sopenharmony_ci l2_fwd[from].fl_domain &= ~BIT(to); 558c2ecf20Sopenharmony_ci } 568c2ecf20Sopenharmony_ci} 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_ci/* Structure used to temporarily transport device tree 598c2ecf20Sopenharmony_ci * settings into sja1105_setup 608c2ecf20Sopenharmony_ci */ 618c2ecf20Sopenharmony_cistruct sja1105_dt_port { 628c2ecf20Sopenharmony_ci phy_interface_t phy_mode; 638c2ecf20Sopenharmony_ci sja1105_mii_role_t role; 648c2ecf20Sopenharmony_ci}; 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_cistatic int sja1105_init_mac_settings(struct sja1105_private *priv) 678c2ecf20Sopenharmony_ci{ 688c2ecf20Sopenharmony_ci struct sja1105_mac_config_entry default_mac = { 698c2ecf20Sopenharmony_ci /* Enable all 8 priority queues on egress. 708c2ecf20Sopenharmony_ci * Every queue i holds top[i] - base[i] frames. 718c2ecf20Sopenharmony_ci * Sum of top[i] - base[i] is 511 (max hardware limit). 728c2ecf20Sopenharmony_ci */ 738c2ecf20Sopenharmony_ci .top = {0x3F, 0x7F, 0xBF, 0xFF, 0x13F, 0x17F, 0x1BF, 0x1FF}, 748c2ecf20Sopenharmony_ci .base = {0x0, 0x40, 0x80, 0xC0, 0x100, 0x140, 0x180, 0x1C0}, 758c2ecf20Sopenharmony_ci .enabled = {true, true, true, true, true, true, true, true}, 768c2ecf20Sopenharmony_ci /* Keep standard IFG of 12 bytes on egress. */ 778c2ecf20Sopenharmony_ci .ifg = 0, 788c2ecf20Sopenharmony_ci /* Always put the MAC speed in automatic mode, where it can be 798c2ecf20Sopenharmony_ci * adjusted at runtime by PHYLINK. 808c2ecf20Sopenharmony_ci */ 818c2ecf20Sopenharmony_ci .speed = SJA1105_SPEED_AUTO, 828c2ecf20Sopenharmony_ci /* No static correction for 1-step 1588 events */ 838c2ecf20Sopenharmony_ci .tp_delin = 0, 848c2ecf20Sopenharmony_ci .tp_delout = 0, 858c2ecf20Sopenharmony_ci /* Disable aging for critical TTEthernet traffic */ 868c2ecf20Sopenharmony_ci .maxage = 0xFF, 878c2ecf20Sopenharmony_ci /* Internal VLAN (pvid) to apply to untagged ingress */ 888c2ecf20Sopenharmony_ci .vlanprio = 0, 898c2ecf20Sopenharmony_ci .vlanid = 1, 908c2ecf20Sopenharmony_ci .ing_mirr = false, 918c2ecf20Sopenharmony_ci .egr_mirr = false, 928c2ecf20Sopenharmony_ci /* Don't drop traffic with other EtherType than ETH_P_IP */ 938c2ecf20Sopenharmony_ci .drpnona664 = false, 948c2ecf20Sopenharmony_ci /* Don't drop double-tagged traffic */ 958c2ecf20Sopenharmony_ci .drpdtag = false, 968c2ecf20Sopenharmony_ci /* Don't drop untagged traffic */ 978c2ecf20Sopenharmony_ci .drpuntag = false, 988c2ecf20Sopenharmony_ci /* Don't retag 802.1p (VID 0) traffic with the pvid */ 998c2ecf20Sopenharmony_ci .retag = false, 1008c2ecf20Sopenharmony_ci /* Disable learning and I/O on user ports by default - 1018c2ecf20Sopenharmony_ci * STP will enable it. 1028c2ecf20Sopenharmony_ci */ 1038c2ecf20Sopenharmony_ci .dyn_learn = false, 1048c2ecf20Sopenharmony_ci .egress = false, 1058c2ecf20Sopenharmony_ci .ingress = false, 1068c2ecf20Sopenharmony_ci }; 1078c2ecf20Sopenharmony_ci struct sja1105_mac_config_entry *mac; 1088c2ecf20Sopenharmony_ci struct sja1105_table *table; 1098c2ecf20Sopenharmony_ci int i; 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci table = &priv->static_config.tables[BLK_IDX_MAC_CONFIG]; 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci /* Discard previous MAC Configuration Table */ 1148c2ecf20Sopenharmony_ci if (table->entry_count) { 1158c2ecf20Sopenharmony_ci kfree(table->entries); 1168c2ecf20Sopenharmony_ci table->entry_count = 0; 1178c2ecf20Sopenharmony_ci } 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci table->entries = kcalloc(SJA1105_NUM_PORTS, 1208c2ecf20Sopenharmony_ci table->ops->unpacked_entry_size, GFP_KERNEL); 1218c2ecf20Sopenharmony_ci if (!table->entries) 1228c2ecf20Sopenharmony_ci return -ENOMEM; 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci table->entry_count = SJA1105_NUM_PORTS; 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci mac = table->entries; 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci for (i = 0; i < SJA1105_NUM_PORTS; i++) { 1298c2ecf20Sopenharmony_ci mac[i] = default_mac; 1308c2ecf20Sopenharmony_ci if (i == dsa_upstream_port(priv->ds, i)) { 1318c2ecf20Sopenharmony_ci /* STP doesn't get called for CPU port, so we need to 1328c2ecf20Sopenharmony_ci * set the I/O parameters statically. 1338c2ecf20Sopenharmony_ci */ 1348c2ecf20Sopenharmony_ci mac[i].dyn_learn = true; 1358c2ecf20Sopenharmony_ci mac[i].ingress = true; 1368c2ecf20Sopenharmony_ci mac[i].egress = true; 1378c2ecf20Sopenharmony_ci } 1388c2ecf20Sopenharmony_ci } 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci return 0; 1418c2ecf20Sopenharmony_ci} 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_cistatic bool sja1105_supports_sgmii(struct sja1105_private *priv, int port) 1448c2ecf20Sopenharmony_ci{ 1458c2ecf20Sopenharmony_ci if (priv->info->part_no != SJA1105R_PART_NO && 1468c2ecf20Sopenharmony_ci priv->info->part_no != SJA1105S_PART_NO) 1478c2ecf20Sopenharmony_ci return false; 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci if (port != SJA1105_SGMII_PORT) 1508c2ecf20Sopenharmony_ci return false; 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci if (dsa_is_unused_port(priv->ds, port)) 1538c2ecf20Sopenharmony_ci return false; 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci return true; 1568c2ecf20Sopenharmony_ci} 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_cistatic int sja1105_init_mii_settings(struct sja1105_private *priv, 1598c2ecf20Sopenharmony_ci struct sja1105_dt_port *ports) 1608c2ecf20Sopenharmony_ci{ 1618c2ecf20Sopenharmony_ci struct device *dev = &priv->spidev->dev; 1628c2ecf20Sopenharmony_ci struct sja1105_xmii_params_entry *mii; 1638c2ecf20Sopenharmony_ci struct sja1105_table *table; 1648c2ecf20Sopenharmony_ci int i; 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_ci table = &priv->static_config.tables[BLK_IDX_XMII_PARAMS]; 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci /* Discard previous xMII Mode Parameters Table */ 1698c2ecf20Sopenharmony_ci if (table->entry_count) { 1708c2ecf20Sopenharmony_ci kfree(table->entries); 1718c2ecf20Sopenharmony_ci table->entry_count = 0; 1728c2ecf20Sopenharmony_ci } 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci table->entries = kcalloc(SJA1105_MAX_XMII_PARAMS_COUNT, 1758c2ecf20Sopenharmony_ci table->ops->unpacked_entry_size, GFP_KERNEL); 1768c2ecf20Sopenharmony_ci if (!table->entries) 1778c2ecf20Sopenharmony_ci return -ENOMEM; 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci /* Override table based on PHYLINK DT bindings */ 1808c2ecf20Sopenharmony_ci table->entry_count = SJA1105_MAX_XMII_PARAMS_COUNT; 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci mii = table->entries; 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_ci for (i = 0; i < SJA1105_NUM_PORTS; i++) { 1858c2ecf20Sopenharmony_ci if (dsa_is_unused_port(priv->ds, i)) 1868c2ecf20Sopenharmony_ci continue; 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ci switch (ports[i].phy_mode) { 1898c2ecf20Sopenharmony_ci case PHY_INTERFACE_MODE_MII: 1908c2ecf20Sopenharmony_ci mii->xmii_mode[i] = XMII_MODE_MII; 1918c2ecf20Sopenharmony_ci break; 1928c2ecf20Sopenharmony_ci case PHY_INTERFACE_MODE_RMII: 1938c2ecf20Sopenharmony_ci mii->xmii_mode[i] = XMII_MODE_RMII; 1948c2ecf20Sopenharmony_ci break; 1958c2ecf20Sopenharmony_ci case PHY_INTERFACE_MODE_RGMII: 1968c2ecf20Sopenharmony_ci case PHY_INTERFACE_MODE_RGMII_ID: 1978c2ecf20Sopenharmony_ci case PHY_INTERFACE_MODE_RGMII_RXID: 1988c2ecf20Sopenharmony_ci case PHY_INTERFACE_MODE_RGMII_TXID: 1998c2ecf20Sopenharmony_ci mii->xmii_mode[i] = XMII_MODE_RGMII; 2008c2ecf20Sopenharmony_ci break; 2018c2ecf20Sopenharmony_ci case PHY_INTERFACE_MODE_SGMII: 2028c2ecf20Sopenharmony_ci if (!sja1105_supports_sgmii(priv, i)) 2038c2ecf20Sopenharmony_ci return -EINVAL; 2048c2ecf20Sopenharmony_ci mii->xmii_mode[i] = XMII_MODE_SGMII; 2058c2ecf20Sopenharmony_ci break; 2068c2ecf20Sopenharmony_ci default: 2078c2ecf20Sopenharmony_ci dev_err(dev, "Unsupported PHY mode %s!\n", 2088c2ecf20Sopenharmony_ci phy_modes(ports[i].phy_mode)); 2098c2ecf20Sopenharmony_ci return -EINVAL; 2108c2ecf20Sopenharmony_ci } 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci /* Even though the SerDes port is able to drive SGMII autoneg 2138c2ecf20Sopenharmony_ci * like a PHY would, from the perspective of the XMII tables, 2148c2ecf20Sopenharmony_ci * the SGMII port should always be put in MAC mode. 2158c2ecf20Sopenharmony_ci */ 2168c2ecf20Sopenharmony_ci if (ports[i].phy_mode == PHY_INTERFACE_MODE_SGMII) 2178c2ecf20Sopenharmony_ci mii->phy_mac[i] = XMII_MAC; 2188c2ecf20Sopenharmony_ci else 2198c2ecf20Sopenharmony_ci mii->phy_mac[i] = ports[i].role; 2208c2ecf20Sopenharmony_ci } 2218c2ecf20Sopenharmony_ci return 0; 2228c2ecf20Sopenharmony_ci} 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_cistatic int sja1105_init_static_fdb(struct sja1105_private *priv) 2258c2ecf20Sopenharmony_ci{ 2268c2ecf20Sopenharmony_ci struct sja1105_table *table; 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_ci table = &priv->static_config.tables[BLK_IDX_L2_LOOKUP]; 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_ci /* We only populate the FDB table through dynamic 2318c2ecf20Sopenharmony_ci * L2 Address Lookup entries 2328c2ecf20Sopenharmony_ci */ 2338c2ecf20Sopenharmony_ci if (table->entry_count) { 2348c2ecf20Sopenharmony_ci kfree(table->entries); 2358c2ecf20Sopenharmony_ci table->entry_count = 0; 2368c2ecf20Sopenharmony_ci } 2378c2ecf20Sopenharmony_ci return 0; 2388c2ecf20Sopenharmony_ci} 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_cistatic int sja1105_init_l2_lookup_params(struct sja1105_private *priv) 2418c2ecf20Sopenharmony_ci{ 2428c2ecf20Sopenharmony_ci struct sja1105_table *table; 2438c2ecf20Sopenharmony_ci u64 max_fdb_entries = SJA1105_MAX_L2_LOOKUP_COUNT / SJA1105_NUM_PORTS; 2448c2ecf20Sopenharmony_ci struct sja1105_l2_lookup_params_entry default_l2_lookup_params = { 2458c2ecf20Sopenharmony_ci /* Learned FDB entries are forgotten after 300 seconds */ 2468c2ecf20Sopenharmony_ci .maxage = SJA1105_AGEING_TIME_MS(300000), 2478c2ecf20Sopenharmony_ci /* All entries within a FDB bin are available for learning */ 2488c2ecf20Sopenharmony_ci .dyn_tbsz = SJA1105ET_FDB_BIN_SIZE, 2498c2ecf20Sopenharmony_ci /* And the P/Q/R/S equivalent setting: */ 2508c2ecf20Sopenharmony_ci .start_dynspc = 0, 2518c2ecf20Sopenharmony_ci .maxaddrp = {max_fdb_entries, max_fdb_entries, max_fdb_entries, 2528c2ecf20Sopenharmony_ci max_fdb_entries, max_fdb_entries, }, 2538c2ecf20Sopenharmony_ci /* 2^8 + 2^5 + 2^3 + 2^2 + 2^1 + 1 in Koopman notation */ 2548c2ecf20Sopenharmony_ci .poly = 0x97, 2558c2ecf20Sopenharmony_ci /* This selects between Independent VLAN Learning (IVL) and 2568c2ecf20Sopenharmony_ci * Shared VLAN Learning (SVL) 2578c2ecf20Sopenharmony_ci */ 2588c2ecf20Sopenharmony_ci .shared_learn = true, 2598c2ecf20Sopenharmony_ci /* Don't discard management traffic based on ENFPORT - 2608c2ecf20Sopenharmony_ci * we don't perform SMAC port enforcement anyway, so 2618c2ecf20Sopenharmony_ci * what we are setting here doesn't matter. 2628c2ecf20Sopenharmony_ci */ 2638c2ecf20Sopenharmony_ci .no_enf_hostprt = false, 2648c2ecf20Sopenharmony_ci /* Don't learn SMAC for mac_fltres1 and mac_fltres0. 2658c2ecf20Sopenharmony_ci * Maybe correlate with no_linklocal_learn from bridge driver? 2668c2ecf20Sopenharmony_ci */ 2678c2ecf20Sopenharmony_ci .no_mgmt_learn = true, 2688c2ecf20Sopenharmony_ci /* P/Q/R/S only */ 2698c2ecf20Sopenharmony_ci .use_static = true, 2708c2ecf20Sopenharmony_ci /* Dynamically learned FDB entries can overwrite other (older) 2718c2ecf20Sopenharmony_ci * dynamic FDB entries 2728c2ecf20Sopenharmony_ci */ 2738c2ecf20Sopenharmony_ci .owr_dyn = true, 2748c2ecf20Sopenharmony_ci .drpnolearn = true, 2758c2ecf20Sopenharmony_ci }; 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_ci table = &priv->static_config.tables[BLK_IDX_L2_LOOKUP_PARAMS]; 2788c2ecf20Sopenharmony_ci 2798c2ecf20Sopenharmony_ci if (table->entry_count) { 2808c2ecf20Sopenharmony_ci kfree(table->entries); 2818c2ecf20Sopenharmony_ci table->entry_count = 0; 2828c2ecf20Sopenharmony_ci } 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_ci table->entries = kcalloc(SJA1105_MAX_L2_LOOKUP_PARAMS_COUNT, 2858c2ecf20Sopenharmony_ci table->ops->unpacked_entry_size, GFP_KERNEL); 2868c2ecf20Sopenharmony_ci if (!table->entries) 2878c2ecf20Sopenharmony_ci return -ENOMEM; 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_ci table->entry_count = SJA1105_MAX_L2_LOOKUP_PARAMS_COUNT; 2908c2ecf20Sopenharmony_ci 2918c2ecf20Sopenharmony_ci /* This table only has a single entry */ 2928c2ecf20Sopenharmony_ci ((struct sja1105_l2_lookup_params_entry *)table->entries)[0] = 2938c2ecf20Sopenharmony_ci default_l2_lookup_params; 2948c2ecf20Sopenharmony_ci 2958c2ecf20Sopenharmony_ci return 0; 2968c2ecf20Sopenharmony_ci} 2978c2ecf20Sopenharmony_ci 2988c2ecf20Sopenharmony_ci/* Set up a default VLAN for untagged traffic injected from the CPU 2998c2ecf20Sopenharmony_ci * using management routes (e.g. STP, PTP) as opposed to tag_8021q. 3008c2ecf20Sopenharmony_ci * All DT-defined ports are members of this VLAN, and there are no 3018c2ecf20Sopenharmony_ci * restrictions on forwarding (since the CPU selects the destination). 3028c2ecf20Sopenharmony_ci * Frames from this VLAN will always be transmitted as untagged, and 3038c2ecf20Sopenharmony_ci * neither the bridge nor the 8021q module cannot create this VLAN ID. 3048c2ecf20Sopenharmony_ci */ 3058c2ecf20Sopenharmony_cistatic int sja1105_init_static_vlan(struct sja1105_private *priv) 3068c2ecf20Sopenharmony_ci{ 3078c2ecf20Sopenharmony_ci struct sja1105_table *table; 3088c2ecf20Sopenharmony_ci struct sja1105_vlan_lookup_entry pvid = { 3098c2ecf20Sopenharmony_ci .ving_mirr = 0, 3108c2ecf20Sopenharmony_ci .vegr_mirr = 0, 3118c2ecf20Sopenharmony_ci .vmemb_port = 0, 3128c2ecf20Sopenharmony_ci .vlan_bc = 0, 3138c2ecf20Sopenharmony_ci .tag_port = 0, 3148c2ecf20Sopenharmony_ci .vlanid = SJA1105_DEFAULT_VLAN, 3158c2ecf20Sopenharmony_ci }; 3168c2ecf20Sopenharmony_ci struct dsa_switch *ds = priv->ds; 3178c2ecf20Sopenharmony_ci int port; 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_ci table = &priv->static_config.tables[BLK_IDX_VLAN_LOOKUP]; 3208c2ecf20Sopenharmony_ci 3218c2ecf20Sopenharmony_ci if (table->entry_count) { 3228c2ecf20Sopenharmony_ci kfree(table->entries); 3238c2ecf20Sopenharmony_ci table->entry_count = 0; 3248c2ecf20Sopenharmony_ci } 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_ci table->entries = kcalloc(1, table->ops->unpacked_entry_size, 3278c2ecf20Sopenharmony_ci GFP_KERNEL); 3288c2ecf20Sopenharmony_ci if (!table->entries) 3298c2ecf20Sopenharmony_ci return -ENOMEM; 3308c2ecf20Sopenharmony_ci 3318c2ecf20Sopenharmony_ci table->entry_count = 1; 3328c2ecf20Sopenharmony_ci 3338c2ecf20Sopenharmony_ci for (port = 0; port < ds->num_ports; port++) { 3348c2ecf20Sopenharmony_ci struct sja1105_bridge_vlan *v; 3358c2ecf20Sopenharmony_ci 3368c2ecf20Sopenharmony_ci if (dsa_is_unused_port(ds, port)) 3378c2ecf20Sopenharmony_ci continue; 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_ci pvid.vmemb_port |= BIT(port); 3408c2ecf20Sopenharmony_ci pvid.vlan_bc |= BIT(port); 3418c2ecf20Sopenharmony_ci pvid.tag_port &= ~BIT(port); 3428c2ecf20Sopenharmony_ci 3438c2ecf20Sopenharmony_ci v = kzalloc(sizeof(*v), GFP_KERNEL); 3448c2ecf20Sopenharmony_ci if (!v) 3458c2ecf20Sopenharmony_ci return -ENOMEM; 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_ci v->port = port; 3488c2ecf20Sopenharmony_ci v->vid = SJA1105_DEFAULT_VLAN; 3498c2ecf20Sopenharmony_ci v->untagged = true; 3508c2ecf20Sopenharmony_ci if (dsa_is_cpu_port(ds, port)) 3518c2ecf20Sopenharmony_ci v->pvid = true; 3528c2ecf20Sopenharmony_ci list_add(&v->list, &priv->dsa_8021q_vlans); 3538c2ecf20Sopenharmony_ci 3548c2ecf20Sopenharmony_ci v = kmemdup(v, sizeof(*v), GFP_KERNEL); 3558c2ecf20Sopenharmony_ci if (!v) 3568c2ecf20Sopenharmony_ci return -ENOMEM; 3578c2ecf20Sopenharmony_ci 3588c2ecf20Sopenharmony_ci list_add(&v->list, &priv->bridge_vlans); 3598c2ecf20Sopenharmony_ci } 3608c2ecf20Sopenharmony_ci 3618c2ecf20Sopenharmony_ci ((struct sja1105_vlan_lookup_entry *)table->entries)[0] = pvid; 3628c2ecf20Sopenharmony_ci return 0; 3638c2ecf20Sopenharmony_ci} 3648c2ecf20Sopenharmony_ci 3658c2ecf20Sopenharmony_cistatic int sja1105_init_l2_forwarding(struct sja1105_private *priv) 3668c2ecf20Sopenharmony_ci{ 3678c2ecf20Sopenharmony_ci struct sja1105_l2_forwarding_entry *l2fwd; 3688c2ecf20Sopenharmony_ci struct sja1105_table *table; 3698c2ecf20Sopenharmony_ci int i, j; 3708c2ecf20Sopenharmony_ci 3718c2ecf20Sopenharmony_ci table = &priv->static_config.tables[BLK_IDX_L2_FORWARDING]; 3728c2ecf20Sopenharmony_ci 3738c2ecf20Sopenharmony_ci if (table->entry_count) { 3748c2ecf20Sopenharmony_ci kfree(table->entries); 3758c2ecf20Sopenharmony_ci table->entry_count = 0; 3768c2ecf20Sopenharmony_ci } 3778c2ecf20Sopenharmony_ci 3788c2ecf20Sopenharmony_ci table->entries = kcalloc(SJA1105_MAX_L2_FORWARDING_COUNT, 3798c2ecf20Sopenharmony_ci table->ops->unpacked_entry_size, GFP_KERNEL); 3808c2ecf20Sopenharmony_ci if (!table->entries) 3818c2ecf20Sopenharmony_ci return -ENOMEM; 3828c2ecf20Sopenharmony_ci 3838c2ecf20Sopenharmony_ci table->entry_count = SJA1105_MAX_L2_FORWARDING_COUNT; 3848c2ecf20Sopenharmony_ci 3858c2ecf20Sopenharmony_ci l2fwd = table->entries; 3868c2ecf20Sopenharmony_ci 3878c2ecf20Sopenharmony_ci /* First 5 entries define the forwarding rules */ 3888c2ecf20Sopenharmony_ci for (i = 0; i < SJA1105_NUM_PORTS; i++) { 3898c2ecf20Sopenharmony_ci unsigned int upstream = dsa_upstream_port(priv->ds, i); 3908c2ecf20Sopenharmony_ci 3918c2ecf20Sopenharmony_ci for (j = 0; j < SJA1105_NUM_TC; j++) 3928c2ecf20Sopenharmony_ci l2fwd[i].vlan_pmap[j] = j; 3938c2ecf20Sopenharmony_ci 3948c2ecf20Sopenharmony_ci if (i == upstream) 3958c2ecf20Sopenharmony_ci continue; 3968c2ecf20Sopenharmony_ci 3978c2ecf20Sopenharmony_ci sja1105_port_allow_traffic(l2fwd, i, upstream, true); 3988c2ecf20Sopenharmony_ci sja1105_port_allow_traffic(l2fwd, upstream, i, true); 3998c2ecf20Sopenharmony_ci } 4008c2ecf20Sopenharmony_ci /* Next 8 entries define VLAN PCP mapping from ingress to egress. 4018c2ecf20Sopenharmony_ci * Create a one-to-one mapping. 4028c2ecf20Sopenharmony_ci */ 4038c2ecf20Sopenharmony_ci for (i = 0; i < SJA1105_NUM_TC; i++) 4048c2ecf20Sopenharmony_ci for (j = 0; j < SJA1105_NUM_PORTS; j++) 4058c2ecf20Sopenharmony_ci l2fwd[SJA1105_NUM_PORTS + i].vlan_pmap[j] = i; 4068c2ecf20Sopenharmony_ci 4078c2ecf20Sopenharmony_ci return 0; 4088c2ecf20Sopenharmony_ci} 4098c2ecf20Sopenharmony_ci 4108c2ecf20Sopenharmony_cistatic int sja1105_init_l2_forwarding_params(struct sja1105_private *priv) 4118c2ecf20Sopenharmony_ci{ 4128c2ecf20Sopenharmony_ci struct sja1105_l2_forwarding_params_entry default_l2fwd_params = { 4138c2ecf20Sopenharmony_ci /* Disallow dynamic reconfiguration of vlan_pmap */ 4148c2ecf20Sopenharmony_ci .max_dynp = 0, 4158c2ecf20Sopenharmony_ci /* Use a single memory partition for all ingress queues */ 4168c2ecf20Sopenharmony_ci .part_spc = { SJA1105_MAX_FRAME_MEMORY, 0, 0, 0, 0, 0, 0, 0 }, 4178c2ecf20Sopenharmony_ci }; 4188c2ecf20Sopenharmony_ci struct sja1105_table *table; 4198c2ecf20Sopenharmony_ci 4208c2ecf20Sopenharmony_ci table = &priv->static_config.tables[BLK_IDX_L2_FORWARDING_PARAMS]; 4218c2ecf20Sopenharmony_ci 4228c2ecf20Sopenharmony_ci if (table->entry_count) { 4238c2ecf20Sopenharmony_ci kfree(table->entries); 4248c2ecf20Sopenharmony_ci table->entry_count = 0; 4258c2ecf20Sopenharmony_ci } 4268c2ecf20Sopenharmony_ci 4278c2ecf20Sopenharmony_ci table->entries = kcalloc(SJA1105_MAX_L2_FORWARDING_PARAMS_COUNT, 4288c2ecf20Sopenharmony_ci table->ops->unpacked_entry_size, GFP_KERNEL); 4298c2ecf20Sopenharmony_ci if (!table->entries) 4308c2ecf20Sopenharmony_ci return -ENOMEM; 4318c2ecf20Sopenharmony_ci 4328c2ecf20Sopenharmony_ci table->entry_count = SJA1105_MAX_L2_FORWARDING_PARAMS_COUNT; 4338c2ecf20Sopenharmony_ci 4348c2ecf20Sopenharmony_ci /* This table only has a single entry */ 4358c2ecf20Sopenharmony_ci ((struct sja1105_l2_forwarding_params_entry *)table->entries)[0] = 4368c2ecf20Sopenharmony_ci default_l2fwd_params; 4378c2ecf20Sopenharmony_ci 4388c2ecf20Sopenharmony_ci return 0; 4398c2ecf20Sopenharmony_ci} 4408c2ecf20Sopenharmony_ci 4418c2ecf20Sopenharmony_civoid sja1105_frame_memory_partitioning(struct sja1105_private *priv) 4428c2ecf20Sopenharmony_ci{ 4438c2ecf20Sopenharmony_ci struct sja1105_l2_forwarding_params_entry *l2_fwd_params; 4448c2ecf20Sopenharmony_ci struct sja1105_vl_forwarding_params_entry *vl_fwd_params; 4458c2ecf20Sopenharmony_ci struct sja1105_table *table; 4468c2ecf20Sopenharmony_ci int max_mem; 4478c2ecf20Sopenharmony_ci 4488c2ecf20Sopenharmony_ci /* VLAN retagging is implemented using a loopback port that consumes 4498c2ecf20Sopenharmony_ci * frame buffers. That leaves less for us. 4508c2ecf20Sopenharmony_ci */ 4518c2ecf20Sopenharmony_ci if (priv->vlan_state == SJA1105_VLAN_BEST_EFFORT) 4528c2ecf20Sopenharmony_ci max_mem = SJA1105_MAX_FRAME_MEMORY_RETAGGING; 4538c2ecf20Sopenharmony_ci else 4548c2ecf20Sopenharmony_ci max_mem = SJA1105_MAX_FRAME_MEMORY; 4558c2ecf20Sopenharmony_ci 4568c2ecf20Sopenharmony_ci table = &priv->static_config.tables[BLK_IDX_L2_FORWARDING_PARAMS]; 4578c2ecf20Sopenharmony_ci l2_fwd_params = table->entries; 4588c2ecf20Sopenharmony_ci l2_fwd_params->part_spc[0] = max_mem; 4598c2ecf20Sopenharmony_ci 4608c2ecf20Sopenharmony_ci /* If we have any critical-traffic virtual links, we need to reserve 4618c2ecf20Sopenharmony_ci * some frame buffer memory for them. At the moment, hardcode the value 4628c2ecf20Sopenharmony_ci * at 100 blocks of 128 bytes of memory each. This leaves 829 blocks 4638c2ecf20Sopenharmony_ci * remaining for best-effort traffic. TODO: figure out a more flexible 4648c2ecf20Sopenharmony_ci * way to perform the frame buffer partitioning. 4658c2ecf20Sopenharmony_ci */ 4668c2ecf20Sopenharmony_ci if (!priv->static_config.tables[BLK_IDX_VL_FORWARDING].entry_count) 4678c2ecf20Sopenharmony_ci return; 4688c2ecf20Sopenharmony_ci 4698c2ecf20Sopenharmony_ci table = &priv->static_config.tables[BLK_IDX_VL_FORWARDING_PARAMS]; 4708c2ecf20Sopenharmony_ci vl_fwd_params = table->entries; 4718c2ecf20Sopenharmony_ci 4728c2ecf20Sopenharmony_ci l2_fwd_params->part_spc[0] -= SJA1105_VL_FRAME_MEMORY; 4738c2ecf20Sopenharmony_ci vl_fwd_params->partspc[0] = SJA1105_VL_FRAME_MEMORY; 4748c2ecf20Sopenharmony_ci} 4758c2ecf20Sopenharmony_ci 4768c2ecf20Sopenharmony_cistatic int sja1105_init_general_params(struct sja1105_private *priv) 4778c2ecf20Sopenharmony_ci{ 4788c2ecf20Sopenharmony_ci struct sja1105_general_params_entry default_general_params = { 4798c2ecf20Sopenharmony_ci /* Allow dynamic changing of the mirror port */ 4808c2ecf20Sopenharmony_ci .mirr_ptacu = true, 4818c2ecf20Sopenharmony_ci .switchid = priv->ds->index, 4828c2ecf20Sopenharmony_ci /* Priority queue for link-local management frames 4838c2ecf20Sopenharmony_ci * (both ingress to and egress from CPU - PTP, STP etc) 4848c2ecf20Sopenharmony_ci */ 4858c2ecf20Sopenharmony_ci .hostprio = 7, 4868c2ecf20Sopenharmony_ci .mac_fltres1 = SJA1105_LINKLOCAL_FILTER_A, 4878c2ecf20Sopenharmony_ci .mac_flt1 = SJA1105_LINKLOCAL_FILTER_A_MASK, 4888c2ecf20Sopenharmony_ci .incl_srcpt1 = false, 4898c2ecf20Sopenharmony_ci .send_meta1 = false, 4908c2ecf20Sopenharmony_ci .mac_fltres0 = SJA1105_LINKLOCAL_FILTER_B, 4918c2ecf20Sopenharmony_ci .mac_flt0 = SJA1105_LINKLOCAL_FILTER_B_MASK, 4928c2ecf20Sopenharmony_ci .incl_srcpt0 = false, 4938c2ecf20Sopenharmony_ci .send_meta0 = false, 4948c2ecf20Sopenharmony_ci /* The destination for traffic matching mac_fltres1 and 4958c2ecf20Sopenharmony_ci * mac_fltres0 on all ports except host_port. Such traffic 4968c2ecf20Sopenharmony_ci * receieved on host_port itself would be dropped, except 4978c2ecf20Sopenharmony_ci * by installing a temporary 'management route' 4988c2ecf20Sopenharmony_ci */ 4998c2ecf20Sopenharmony_ci .host_port = dsa_upstream_port(priv->ds, 0), 5008c2ecf20Sopenharmony_ci /* Default to an invalid value */ 5018c2ecf20Sopenharmony_ci .mirr_port = SJA1105_NUM_PORTS, 5028c2ecf20Sopenharmony_ci /* Link-local traffic received on casc_port will be forwarded 5038c2ecf20Sopenharmony_ci * to host_port without embedding the source port and device ID 5048c2ecf20Sopenharmony_ci * info in the destination MAC address (presumably because it 5058c2ecf20Sopenharmony_ci * is a cascaded port and a downstream SJA switch already did 5068c2ecf20Sopenharmony_ci * that). Default to an invalid port (to disable the feature) 5078c2ecf20Sopenharmony_ci * and overwrite this if we find any DSA (cascaded) ports. 5088c2ecf20Sopenharmony_ci */ 5098c2ecf20Sopenharmony_ci .casc_port = SJA1105_NUM_PORTS, 5108c2ecf20Sopenharmony_ci /* No TTEthernet */ 5118c2ecf20Sopenharmony_ci .vllupformat = SJA1105_VL_FORMAT_PSFP, 5128c2ecf20Sopenharmony_ci .vlmarker = 0, 5138c2ecf20Sopenharmony_ci .vlmask = 0, 5148c2ecf20Sopenharmony_ci /* Only update correctionField for 1-step PTP (L2 transport) */ 5158c2ecf20Sopenharmony_ci .ignore2stf = 0, 5168c2ecf20Sopenharmony_ci /* Forcefully disable VLAN filtering by telling 5178c2ecf20Sopenharmony_ci * the switch that VLAN has a different EtherType. 5188c2ecf20Sopenharmony_ci */ 5198c2ecf20Sopenharmony_ci .tpid = ETH_P_SJA1105, 5208c2ecf20Sopenharmony_ci .tpid2 = ETH_P_SJA1105, 5218c2ecf20Sopenharmony_ci }; 5228c2ecf20Sopenharmony_ci struct sja1105_table *table; 5238c2ecf20Sopenharmony_ci 5248c2ecf20Sopenharmony_ci table = &priv->static_config.tables[BLK_IDX_GENERAL_PARAMS]; 5258c2ecf20Sopenharmony_ci 5268c2ecf20Sopenharmony_ci if (table->entry_count) { 5278c2ecf20Sopenharmony_ci kfree(table->entries); 5288c2ecf20Sopenharmony_ci table->entry_count = 0; 5298c2ecf20Sopenharmony_ci } 5308c2ecf20Sopenharmony_ci 5318c2ecf20Sopenharmony_ci table->entries = kcalloc(SJA1105_MAX_GENERAL_PARAMS_COUNT, 5328c2ecf20Sopenharmony_ci table->ops->unpacked_entry_size, GFP_KERNEL); 5338c2ecf20Sopenharmony_ci if (!table->entries) 5348c2ecf20Sopenharmony_ci return -ENOMEM; 5358c2ecf20Sopenharmony_ci 5368c2ecf20Sopenharmony_ci table->entry_count = SJA1105_MAX_GENERAL_PARAMS_COUNT; 5378c2ecf20Sopenharmony_ci 5388c2ecf20Sopenharmony_ci /* This table only has a single entry */ 5398c2ecf20Sopenharmony_ci ((struct sja1105_general_params_entry *)table->entries)[0] = 5408c2ecf20Sopenharmony_ci default_general_params; 5418c2ecf20Sopenharmony_ci 5428c2ecf20Sopenharmony_ci return 0; 5438c2ecf20Sopenharmony_ci} 5448c2ecf20Sopenharmony_ci 5458c2ecf20Sopenharmony_cistatic int sja1105_init_avb_params(struct sja1105_private *priv) 5468c2ecf20Sopenharmony_ci{ 5478c2ecf20Sopenharmony_ci struct sja1105_avb_params_entry *avb; 5488c2ecf20Sopenharmony_ci struct sja1105_table *table; 5498c2ecf20Sopenharmony_ci 5508c2ecf20Sopenharmony_ci table = &priv->static_config.tables[BLK_IDX_AVB_PARAMS]; 5518c2ecf20Sopenharmony_ci 5528c2ecf20Sopenharmony_ci /* Discard previous AVB Parameters Table */ 5538c2ecf20Sopenharmony_ci if (table->entry_count) { 5548c2ecf20Sopenharmony_ci kfree(table->entries); 5558c2ecf20Sopenharmony_ci table->entry_count = 0; 5568c2ecf20Sopenharmony_ci } 5578c2ecf20Sopenharmony_ci 5588c2ecf20Sopenharmony_ci table->entries = kcalloc(SJA1105_MAX_AVB_PARAMS_COUNT, 5598c2ecf20Sopenharmony_ci table->ops->unpacked_entry_size, GFP_KERNEL); 5608c2ecf20Sopenharmony_ci if (!table->entries) 5618c2ecf20Sopenharmony_ci return -ENOMEM; 5628c2ecf20Sopenharmony_ci 5638c2ecf20Sopenharmony_ci table->entry_count = SJA1105_MAX_AVB_PARAMS_COUNT; 5648c2ecf20Sopenharmony_ci 5658c2ecf20Sopenharmony_ci avb = table->entries; 5668c2ecf20Sopenharmony_ci 5678c2ecf20Sopenharmony_ci /* Configure the MAC addresses for meta frames */ 5688c2ecf20Sopenharmony_ci avb->destmeta = SJA1105_META_DMAC; 5698c2ecf20Sopenharmony_ci avb->srcmeta = SJA1105_META_SMAC; 5708c2ecf20Sopenharmony_ci /* On P/Q/R/S, configure the direction of the PTP_CLK pin as input by 5718c2ecf20Sopenharmony_ci * default. This is because there might be boards with a hardware 5728c2ecf20Sopenharmony_ci * layout where enabling the pin as output might cause an electrical 5738c2ecf20Sopenharmony_ci * clash. On E/T the pin is always an output, which the board designers 5748c2ecf20Sopenharmony_ci * probably already knew, so even if there are going to be electrical 5758c2ecf20Sopenharmony_ci * issues, there's nothing we can do. 5768c2ecf20Sopenharmony_ci */ 5778c2ecf20Sopenharmony_ci avb->cas_master = false; 5788c2ecf20Sopenharmony_ci 5798c2ecf20Sopenharmony_ci return 0; 5808c2ecf20Sopenharmony_ci} 5818c2ecf20Sopenharmony_ci 5828c2ecf20Sopenharmony_ci/* The L2 policing table is 2-stage. The table is looked up for each frame 5838c2ecf20Sopenharmony_ci * according to the ingress port, whether it was broadcast or not, and the 5848c2ecf20Sopenharmony_ci * classified traffic class (given by VLAN PCP). This portion of the lookup is 5858c2ecf20Sopenharmony_ci * fixed, and gives access to the SHARINDX, an indirection register pointing 5868c2ecf20Sopenharmony_ci * within the policing table itself, which is used to resolve the policer that 5878c2ecf20Sopenharmony_ci * will be used for this frame. 5888c2ecf20Sopenharmony_ci * 5898c2ecf20Sopenharmony_ci * Stage 1 Stage 2 5908c2ecf20Sopenharmony_ci * +------------+--------+ +---------------------------------+ 5918c2ecf20Sopenharmony_ci * |Port 0 TC 0 |SHARINDX| | Policer 0: Rate, Burst, MTU | 5928c2ecf20Sopenharmony_ci * +------------+--------+ +---------------------------------+ 5938c2ecf20Sopenharmony_ci * |Port 0 TC 1 |SHARINDX| | Policer 1: Rate, Burst, MTU | 5948c2ecf20Sopenharmony_ci * +------------+--------+ +---------------------------------+ 5958c2ecf20Sopenharmony_ci * ... | Policer 2: Rate, Burst, MTU | 5968c2ecf20Sopenharmony_ci * +------------+--------+ +---------------------------------+ 5978c2ecf20Sopenharmony_ci * |Port 0 TC 7 |SHARINDX| | Policer 3: Rate, Burst, MTU | 5988c2ecf20Sopenharmony_ci * +------------+--------+ +---------------------------------+ 5998c2ecf20Sopenharmony_ci * |Port 1 TC 0 |SHARINDX| | Policer 4: Rate, Burst, MTU | 6008c2ecf20Sopenharmony_ci * +------------+--------+ +---------------------------------+ 6018c2ecf20Sopenharmony_ci * ... | Policer 5: Rate, Burst, MTU | 6028c2ecf20Sopenharmony_ci * +------------+--------+ +---------------------------------+ 6038c2ecf20Sopenharmony_ci * |Port 1 TC 7 |SHARINDX| | Policer 6: Rate, Burst, MTU | 6048c2ecf20Sopenharmony_ci * +------------+--------+ +---------------------------------+ 6058c2ecf20Sopenharmony_ci * ... | Policer 7: Rate, Burst, MTU | 6068c2ecf20Sopenharmony_ci * +------------+--------+ +---------------------------------+ 6078c2ecf20Sopenharmony_ci * |Port 4 TC 7 |SHARINDX| ... 6088c2ecf20Sopenharmony_ci * +------------+--------+ 6098c2ecf20Sopenharmony_ci * |Port 0 BCAST|SHARINDX| ... 6108c2ecf20Sopenharmony_ci * +------------+--------+ 6118c2ecf20Sopenharmony_ci * |Port 1 BCAST|SHARINDX| ... 6128c2ecf20Sopenharmony_ci * +------------+--------+ 6138c2ecf20Sopenharmony_ci * ... ... 6148c2ecf20Sopenharmony_ci * +------------+--------+ +---------------------------------+ 6158c2ecf20Sopenharmony_ci * |Port 4 BCAST|SHARINDX| | Policer 44: Rate, Burst, MTU | 6168c2ecf20Sopenharmony_ci * +------------+--------+ +---------------------------------+ 6178c2ecf20Sopenharmony_ci * 6188c2ecf20Sopenharmony_ci * In this driver, we shall use policers 0-4 as statically alocated port 6198c2ecf20Sopenharmony_ci * (matchall) policers. So we need to make the SHARINDX for all lookups 6208c2ecf20Sopenharmony_ci * corresponding to this ingress port (8 VLAN PCP lookups and 1 broadcast 6218c2ecf20Sopenharmony_ci * lookup) equal. 6228c2ecf20Sopenharmony_ci * The remaining policers (40) shall be dynamically allocated for flower 6238c2ecf20Sopenharmony_ci * policers, where the key is either vlan_prio or dst_mac ff:ff:ff:ff:ff:ff. 6248c2ecf20Sopenharmony_ci */ 6258c2ecf20Sopenharmony_ci#define SJA1105_RATE_MBPS(speed) (((speed) * 64000) / 1000) 6268c2ecf20Sopenharmony_ci 6278c2ecf20Sopenharmony_cistatic int sja1105_init_l2_policing(struct sja1105_private *priv) 6288c2ecf20Sopenharmony_ci{ 6298c2ecf20Sopenharmony_ci struct sja1105_l2_policing_entry *policing; 6308c2ecf20Sopenharmony_ci struct sja1105_table *table; 6318c2ecf20Sopenharmony_ci int port, tc; 6328c2ecf20Sopenharmony_ci 6338c2ecf20Sopenharmony_ci table = &priv->static_config.tables[BLK_IDX_L2_POLICING]; 6348c2ecf20Sopenharmony_ci 6358c2ecf20Sopenharmony_ci /* Discard previous L2 Policing Table */ 6368c2ecf20Sopenharmony_ci if (table->entry_count) { 6378c2ecf20Sopenharmony_ci kfree(table->entries); 6388c2ecf20Sopenharmony_ci table->entry_count = 0; 6398c2ecf20Sopenharmony_ci } 6408c2ecf20Sopenharmony_ci 6418c2ecf20Sopenharmony_ci table->entries = kcalloc(SJA1105_MAX_L2_POLICING_COUNT, 6428c2ecf20Sopenharmony_ci table->ops->unpacked_entry_size, GFP_KERNEL); 6438c2ecf20Sopenharmony_ci if (!table->entries) 6448c2ecf20Sopenharmony_ci return -ENOMEM; 6458c2ecf20Sopenharmony_ci 6468c2ecf20Sopenharmony_ci table->entry_count = SJA1105_MAX_L2_POLICING_COUNT; 6478c2ecf20Sopenharmony_ci 6488c2ecf20Sopenharmony_ci policing = table->entries; 6498c2ecf20Sopenharmony_ci 6508c2ecf20Sopenharmony_ci /* Setup shared indices for the matchall policers */ 6518c2ecf20Sopenharmony_ci for (port = 0; port < SJA1105_NUM_PORTS; port++) { 6528c2ecf20Sopenharmony_ci int bcast = (SJA1105_NUM_PORTS * SJA1105_NUM_TC) + port; 6538c2ecf20Sopenharmony_ci 6548c2ecf20Sopenharmony_ci for (tc = 0; tc < SJA1105_NUM_TC; tc++) 6558c2ecf20Sopenharmony_ci policing[port * SJA1105_NUM_TC + tc].sharindx = port; 6568c2ecf20Sopenharmony_ci 6578c2ecf20Sopenharmony_ci policing[bcast].sharindx = port; 6588c2ecf20Sopenharmony_ci } 6598c2ecf20Sopenharmony_ci 6608c2ecf20Sopenharmony_ci /* Setup the matchall policer parameters */ 6618c2ecf20Sopenharmony_ci for (port = 0; port < SJA1105_NUM_PORTS; port++) { 6628c2ecf20Sopenharmony_ci int mtu = VLAN_ETH_FRAME_LEN + ETH_FCS_LEN; 6638c2ecf20Sopenharmony_ci 6648c2ecf20Sopenharmony_ci if (dsa_is_cpu_port(priv->ds, port)) 6658c2ecf20Sopenharmony_ci mtu += VLAN_HLEN; 6668c2ecf20Sopenharmony_ci 6678c2ecf20Sopenharmony_ci policing[port].smax = 65535; /* Burst size in bytes */ 6688c2ecf20Sopenharmony_ci policing[port].rate = SJA1105_RATE_MBPS(1000); 6698c2ecf20Sopenharmony_ci policing[port].maxlen = mtu; 6708c2ecf20Sopenharmony_ci policing[port].partition = 0; 6718c2ecf20Sopenharmony_ci } 6728c2ecf20Sopenharmony_ci 6738c2ecf20Sopenharmony_ci return 0; 6748c2ecf20Sopenharmony_ci} 6758c2ecf20Sopenharmony_ci 6768c2ecf20Sopenharmony_cistatic int sja1105_static_config_load(struct sja1105_private *priv, 6778c2ecf20Sopenharmony_ci struct sja1105_dt_port *ports) 6788c2ecf20Sopenharmony_ci{ 6798c2ecf20Sopenharmony_ci int rc; 6808c2ecf20Sopenharmony_ci 6818c2ecf20Sopenharmony_ci sja1105_static_config_free(&priv->static_config); 6828c2ecf20Sopenharmony_ci rc = sja1105_static_config_init(&priv->static_config, 6838c2ecf20Sopenharmony_ci priv->info->static_ops, 6848c2ecf20Sopenharmony_ci priv->info->device_id); 6858c2ecf20Sopenharmony_ci if (rc) 6868c2ecf20Sopenharmony_ci return rc; 6878c2ecf20Sopenharmony_ci 6888c2ecf20Sopenharmony_ci /* Build static configuration */ 6898c2ecf20Sopenharmony_ci rc = sja1105_init_mac_settings(priv); 6908c2ecf20Sopenharmony_ci if (rc < 0) 6918c2ecf20Sopenharmony_ci return rc; 6928c2ecf20Sopenharmony_ci rc = sja1105_init_mii_settings(priv, ports); 6938c2ecf20Sopenharmony_ci if (rc < 0) 6948c2ecf20Sopenharmony_ci return rc; 6958c2ecf20Sopenharmony_ci rc = sja1105_init_static_fdb(priv); 6968c2ecf20Sopenharmony_ci if (rc < 0) 6978c2ecf20Sopenharmony_ci return rc; 6988c2ecf20Sopenharmony_ci rc = sja1105_init_static_vlan(priv); 6998c2ecf20Sopenharmony_ci if (rc < 0) 7008c2ecf20Sopenharmony_ci return rc; 7018c2ecf20Sopenharmony_ci rc = sja1105_init_l2_lookup_params(priv); 7028c2ecf20Sopenharmony_ci if (rc < 0) 7038c2ecf20Sopenharmony_ci return rc; 7048c2ecf20Sopenharmony_ci rc = sja1105_init_l2_forwarding(priv); 7058c2ecf20Sopenharmony_ci if (rc < 0) 7068c2ecf20Sopenharmony_ci return rc; 7078c2ecf20Sopenharmony_ci rc = sja1105_init_l2_forwarding_params(priv); 7088c2ecf20Sopenharmony_ci if (rc < 0) 7098c2ecf20Sopenharmony_ci return rc; 7108c2ecf20Sopenharmony_ci rc = sja1105_init_l2_policing(priv); 7118c2ecf20Sopenharmony_ci if (rc < 0) 7128c2ecf20Sopenharmony_ci return rc; 7138c2ecf20Sopenharmony_ci rc = sja1105_init_general_params(priv); 7148c2ecf20Sopenharmony_ci if (rc < 0) 7158c2ecf20Sopenharmony_ci return rc; 7168c2ecf20Sopenharmony_ci rc = sja1105_init_avb_params(priv); 7178c2ecf20Sopenharmony_ci if (rc < 0) 7188c2ecf20Sopenharmony_ci return rc; 7198c2ecf20Sopenharmony_ci 7208c2ecf20Sopenharmony_ci /* Send initial configuration to hardware via SPI */ 7218c2ecf20Sopenharmony_ci return sja1105_static_config_upload(priv); 7228c2ecf20Sopenharmony_ci} 7238c2ecf20Sopenharmony_ci 7248c2ecf20Sopenharmony_cistatic int sja1105_parse_rgmii_delays(struct sja1105_private *priv, 7258c2ecf20Sopenharmony_ci const struct sja1105_dt_port *ports) 7268c2ecf20Sopenharmony_ci{ 7278c2ecf20Sopenharmony_ci int i; 7288c2ecf20Sopenharmony_ci 7298c2ecf20Sopenharmony_ci for (i = 0; i < SJA1105_NUM_PORTS; i++) { 7308c2ecf20Sopenharmony_ci if (ports[i].role == XMII_MAC) 7318c2ecf20Sopenharmony_ci continue; 7328c2ecf20Sopenharmony_ci 7338c2ecf20Sopenharmony_ci if (ports[i].phy_mode == PHY_INTERFACE_MODE_RGMII_RXID || 7348c2ecf20Sopenharmony_ci ports[i].phy_mode == PHY_INTERFACE_MODE_RGMII_ID) 7358c2ecf20Sopenharmony_ci priv->rgmii_rx_delay[i] = true; 7368c2ecf20Sopenharmony_ci 7378c2ecf20Sopenharmony_ci if (ports[i].phy_mode == PHY_INTERFACE_MODE_RGMII_TXID || 7388c2ecf20Sopenharmony_ci ports[i].phy_mode == PHY_INTERFACE_MODE_RGMII_ID) 7398c2ecf20Sopenharmony_ci priv->rgmii_tx_delay[i] = true; 7408c2ecf20Sopenharmony_ci 7418c2ecf20Sopenharmony_ci if ((priv->rgmii_rx_delay[i] || priv->rgmii_tx_delay[i]) && 7428c2ecf20Sopenharmony_ci !priv->info->setup_rgmii_delay) 7438c2ecf20Sopenharmony_ci return -EINVAL; 7448c2ecf20Sopenharmony_ci } 7458c2ecf20Sopenharmony_ci return 0; 7468c2ecf20Sopenharmony_ci} 7478c2ecf20Sopenharmony_ci 7488c2ecf20Sopenharmony_cistatic int sja1105_parse_ports_node(struct sja1105_private *priv, 7498c2ecf20Sopenharmony_ci struct sja1105_dt_port *ports, 7508c2ecf20Sopenharmony_ci struct device_node *ports_node) 7518c2ecf20Sopenharmony_ci{ 7528c2ecf20Sopenharmony_ci struct device *dev = &priv->spidev->dev; 7538c2ecf20Sopenharmony_ci struct device_node *child; 7548c2ecf20Sopenharmony_ci 7558c2ecf20Sopenharmony_ci for_each_available_child_of_node(ports_node, child) { 7568c2ecf20Sopenharmony_ci struct device_node *phy_node; 7578c2ecf20Sopenharmony_ci phy_interface_t phy_mode; 7588c2ecf20Sopenharmony_ci u32 index; 7598c2ecf20Sopenharmony_ci int err; 7608c2ecf20Sopenharmony_ci 7618c2ecf20Sopenharmony_ci /* Get switch port number from DT */ 7628c2ecf20Sopenharmony_ci if (of_property_read_u32(child, "reg", &index) < 0) { 7638c2ecf20Sopenharmony_ci dev_err(dev, "Port number not defined in device tree " 7648c2ecf20Sopenharmony_ci "(property \"reg\")\n"); 7658c2ecf20Sopenharmony_ci of_node_put(child); 7668c2ecf20Sopenharmony_ci return -ENODEV; 7678c2ecf20Sopenharmony_ci } 7688c2ecf20Sopenharmony_ci 7698c2ecf20Sopenharmony_ci /* Get PHY mode from DT */ 7708c2ecf20Sopenharmony_ci err = of_get_phy_mode(child, &phy_mode); 7718c2ecf20Sopenharmony_ci if (err) { 7728c2ecf20Sopenharmony_ci dev_err(dev, "Failed to read phy-mode or " 7738c2ecf20Sopenharmony_ci "phy-interface-type property for port %d\n", 7748c2ecf20Sopenharmony_ci index); 7758c2ecf20Sopenharmony_ci of_node_put(child); 7768c2ecf20Sopenharmony_ci return -ENODEV; 7778c2ecf20Sopenharmony_ci } 7788c2ecf20Sopenharmony_ci ports[index].phy_mode = phy_mode; 7798c2ecf20Sopenharmony_ci 7808c2ecf20Sopenharmony_ci phy_node = of_parse_phandle(child, "phy-handle", 0); 7818c2ecf20Sopenharmony_ci if (!phy_node) { 7828c2ecf20Sopenharmony_ci if (!of_phy_is_fixed_link(child)) { 7838c2ecf20Sopenharmony_ci dev_err(dev, "phy-handle or fixed-link " 7848c2ecf20Sopenharmony_ci "properties missing!\n"); 7858c2ecf20Sopenharmony_ci of_node_put(child); 7868c2ecf20Sopenharmony_ci return -ENODEV; 7878c2ecf20Sopenharmony_ci } 7888c2ecf20Sopenharmony_ci /* phy-handle is missing, but fixed-link isn't. 7898c2ecf20Sopenharmony_ci * So it's a fixed link. Default to PHY role. 7908c2ecf20Sopenharmony_ci */ 7918c2ecf20Sopenharmony_ci ports[index].role = XMII_PHY; 7928c2ecf20Sopenharmony_ci } else { 7938c2ecf20Sopenharmony_ci /* phy-handle present => put port in MAC role */ 7948c2ecf20Sopenharmony_ci ports[index].role = XMII_MAC; 7958c2ecf20Sopenharmony_ci of_node_put(phy_node); 7968c2ecf20Sopenharmony_ci } 7978c2ecf20Sopenharmony_ci 7988c2ecf20Sopenharmony_ci /* The MAC/PHY role can be overridden with explicit bindings */ 7998c2ecf20Sopenharmony_ci if (of_property_read_bool(child, "sja1105,role-mac")) 8008c2ecf20Sopenharmony_ci ports[index].role = XMII_MAC; 8018c2ecf20Sopenharmony_ci else if (of_property_read_bool(child, "sja1105,role-phy")) 8028c2ecf20Sopenharmony_ci ports[index].role = XMII_PHY; 8038c2ecf20Sopenharmony_ci } 8048c2ecf20Sopenharmony_ci 8058c2ecf20Sopenharmony_ci return 0; 8068c2ecf20Sopenharmony_ci} 8078c2ecf20Sopenharmony_ci 8088c2ecf20Sopenharmony_cistatic int sja1105_parse_dt(struct sja1105_private *priv, 8098c2ecf20Sopenharmony_ci struct sja1105_dt_port *ports) 8108c2ecf20Sopenharmony_ci{ 8118c2ecf20Sopenharmony_ci struct device *dev = &priv->spidev->dev; 8128c2ecf20Sopenharmony_ci struct device_node *switch_node = dev->of_node; 8138c2ecf20Sopenharmony_ci struct device_node *ports_node; 8148c2ecf20Sopenharmony_ci int rc; 8158c2ecf20Sopenharmony_ci 8168c2ecf20Sopenharmony_ci ports_node = of_get_child_by_name(switch_node, "ports"); 8178c2ecf20Sopenharmony_ci if (!ports_node) { 8188c2ecf20Sopenharmony_ci dev_err(dev, "Incorrect bindings: absent \"ports\" node\n"); 8198c2ecf20Sopenharmony_ci return -ENODEV; 8208c2ecf20Sopenharmony_ci } 8218c2ecf20Sopenharmony_ci 8228c2ecf20Sopenharmony_ci rc = sja1105_parse_ports_node(priv, ports, ports_node); 8238c2ecf20Sopenharmony_ci of_node_put(ports_node); 8248c2ecf20Sopenharmony_ci 8258c2ecf20Sopenharmony_ci return rc; 8268c2ecf20Sopenharmony_ci} 8278c2ecf20Sopenharmony_ci 8288c2ecf20Sopenharmony_cistatic int sja1105_sgmii_read(struct sja1105_private *priv, int pcs_reg) 8298c2ecf20Sopenharmony_ci{ 8308c2ecf20Sopenharmony_ci const struct sja1105_regs *regs = priv->info->regs; 8318c2ecf20Sopenharmony_ci u32 val; 8328c2ecf20Sopenharmony_ci int rc; 8338c2ecf20Sopenharmony_ci 8348c2ecf20Sopenharmony_ci rc = sja1105_xfer_u32(priv, SPI_READ, regs->sgmii + pcs_reg, &val, 8358c2ecf20Sopenharmony_ci NULL); 8368c2ecf20Sopenharmony_ci if (rc < 0) 8378c2ecf20Sopenharmony_ci return rc; 8388c2ecf20Sopenharmony_ci 8398c2ecf20Sopenharmony_ci return val; 8408c2ecf20Sopenharmony_ci} 8418c2ecf20Sopenharmony_ci 8428c2ecf20Sopenharmony_cistatic int sja1105_sgmii_write(struct sja1105_private *priv, int pcs_reg, 8438c2ecf20Sopenharmony_ci u16 pcs_val) 8448c2ecf20Sopenharmony_ci{ 8458c2ecf20Sopenharmony_ci const struct sja1105_regs *regs = priv->info->regs; 8468c2ecf20Sopenharmony_ci u32 val = pcs_val; 8478c2ecf20Sopenharmony_ci int rc; 8488c2ecf20Sopenharmony_ci 8498c2ecf20Sopenharmony_ci rc = sja1105_xfer_u32(priv, SPI_WRITE, regs->sgmii + pcs_reg, &val, 8508c2ecf20Sopenharmony_ci NULL); 8518c2ecf20Sopenharmony_ci if (rc < 0) 8528c2ecf20Sopenharmony_ci return rc; 8538c2ecf20Sopenharmony_ci 8548c2ecf20Sopenharmony_ci return val; 8558c2ecf20Sopenharmony_ci} 8568c2ecf20Sopenharmony_ci 8578c2ecf20Sopenharmony_cistatic void sja1105_sgmii_pcs_config(struct sja1105_private *priv, 8588c2ecf20Sopenharmony_ci bool an_enabled, bool an_master) 8598c2ecf20Sopenharmony_ci{ 8608c2ecf20Sopenharmony_ci u16 ac = SJA1105_AC_AUTONEG_MODE_SGMII; 8618c2ecf20Sopenharmony_ci 8628c2ecf20Sopenharmony_ci /* DIGITAL_CONTROL_1: Enable vendor-specific MMD1, allow the PHY to 8638c2ecf20Sopenharmony_ci * stop the clock during LPI mode, make the MAC reconfigure 8648c2ecf20Sopenharmony_ci * autonomously after PCS autoneg is done, flush the internal FIFOs. 8658c2ecf20Sopenharmony_ci */ 8668c2ecf20Sopenharmony_ci sja1105_sgmii_write(priv, SJA1105_DC1, SJA1105_DC1_EN_VSMMD1 | 8678c2ecf20Sopenharmony_ci SJA1105_DC1_CLOCK_STOP_EN | 8688c2ecf20Sopenharmony_ci SJA1105_DC1_MAC_AUTO_SW | 8698c2ecf20Sopenharmony_ci SJA1105_DC1_INIT); 8708c2ecf20Sopenharmony_ci /* DIGITAL_CONTROL_2: No polarity inversion for TX and RX lanes */ 8718c2ecf20Sopenharmony_ci sja1105_sgmii_write(priv, SJA1105_DC2, SJA1105_DC2_TX_POL_INV_DISABLE); 8728c2ecf20Sopenharmony_ci /* AUTONEG_CONTROL: Use SGMII autoneg */ 8738c2ecf20Sopenharmony_ci if (an_master) 8748c2ecf20Sopenharmony_ci ac |= SJA1105_AC_PHY_MODE | SJA1105_AC_SGMII_LINK; 8758c2ecf20Sopenharmony_ci sja1105_sgmii_write(priv, SJA1105_AC, ac); 8768c2ecf20Sopenharmony_ci /* BASIC_CONTROL: enable in-band AN now, if requested. Otherwise, 8778c2ecf20Sopenharmony_ci * sja1105_sgmii_pcs_force_speed must be called later for the link 8788c2ecf20Sopenharmony_ci * to become operational. 8798c2ecf20Sopenharmony_ci */ 8808c2ecf20Sopenharmony_ci if (an_enabled) 8818c2ecf20Sopenharmony_ci sja1105_sgmii_write(priv, MII_BMCR, 8828c2ecf20Sopenharmony_ci BMCR_ANENABLE | BMCR_ANRESTART); 8838c2ecf20Sopenharmony_ci} 8848c2ecf20Sopenharmony_ci 8858c2ecf20Sopenharmony_cistatic void sja1105_sgmii_pcs_force_speed(struct sja1105_private *priv, 8868c2ecf20Sopenharmony_ci int speed) 8878c2ecf20Sopenharmony_ci{ 8888c2ecf20Sopenharmony_ci int pcs_speed; 8898c2ecf20Sopenharmony_ci 8908c2ecf20Sopenharmony_ci switch (speed) { 8918c2ecf20Sopenharmony_ci case SPEED_1000: 8928c2ecf20Sopenharmony_ci pcs_speed = BMCR_SPEED1000; 8938c2ecf20Sopenharmony_ci break; 8948c2ecf20Sopenharmony_ci case SPEED_100: 8958c2ecf20Sopenharmony_ci pcs_speed = BMCR_SPEED100; 8968c2ecf20Sopenharmony_ci break; 8978c2ecf20Sopenharmony_ci case SPEED_10: 8988c2ecf20Sopenharmony_ci pcs_speed = BMCR_SPEED10; 8998c2ecf20Sopenharmony_ci break; 9008c2ecf20Sopenharmony_ci default: 9018c2ecf20Sopenharmony_ci dev_err(priv->ds->dev, "Invalid speed %d\n", speed); 9028c2ecf20Sopenharmony_ci return; 9038c2ecf20Sopenharmony_ci } 9048c2ecf20Sopenharmony_ci sja1105_sgmii_write(priv, MII_BMCR, pcs_speed | BMCR_FULLDPLX); 9058c2ecf20Sopenharmony_ci} 9068c2ecf20Sopenharmony_ci 9078c2ecf20Sopenharmony_ci/* Convert link speed from SJA1105 to ethtool encoding */ 9088c2ecf20Sopenharmony_cistatic int sja1105_speed[] = { 9098c2ecf20Sopenharmony_ci [SJA1105_SPEED_AUTO] = SPEED_UNKNOWN, 9108c2ecf20Sopenharmony_ci [SJA1105_SPEED_10MBPS] = SPEED_10, 9118c2ecf20Sopenharmony_ci [SJA1105_SPEED_100MBPS] = SPEED_100, 9128c2ecf20Sopenharmony_ci [SJA1105_SPEED_1000MBPS] = SPEED_1000, 9138c2ecf20Sopenharmony_ci}; 9148c2ecf20Sopenharmony_ci 9158c2ecf20Sopenharmony_ci/* Set link speed in the MAC configuration for a specific port. */ 9168c2ecf20Sopenharmony_cistatic int sja1105_adjust_port_config(struct sja1105_private *priv, int port, 9178c2ecf20Sopenharmony_ci int speed_mbps) 9188c2ecf20Sopenharmony_ci{ 9198c2ecf20Sopenharmony_ci struct sja1105_xmii_params_entry *mii; 9208c2ecf20Sopenharmony_ci struct sja1105_mac_config_entry *mac; 9218c2ecf20Sopenharmony_ci struct device *dev = priv->ds->dev; 9228c2ecf20Sopenharmony_ci sja1105_phy_interface_t phy_mode; 9238c2ecf20Sopenharmony_ci sja1105_speed_t speed; 9248c2ecf20Sopenharmony_ci int rc; 9258c2ecf20Sopenharmony_ci 9268c2ecf20Sopenharmony_ci /* On P/Q/R/S, one can read from the device via the MAC reconfiguration 9278c2ecf20Sopenharmony_ci * tables. On E/T, MAC reconfig tables are not readable, only writable. 9288c2ecf20Sopenharmony_ci * We have to *know* what the MAC looks like. For the sake of keeping 9298c2ecf20Sopenharmony_ci * the code common, we'll use the static configuration tables as a 9308c2ecf20Sopenharmony_ci * reasonable approximation for both E/T and P/Q/R/S. 9318c2ecf20Sopenharmony_ci */ 9328c2ecf20Sopenharmony_ci mac = priv->static_config.tables[BLK_IDX_MAC_CONFIG].entries; 9338c2ecf20Sopenharmony_ci mii = priv->static_config.tables[BLK_IDX_XMII_PARAMS].entries; 9348c2ecf20Sopenharmony_ci 9358c2ecf20Sopenharmony_ci switch (speed_mbps) { 9368c2ecf20Sopenharmony_ci case SPEED_UNKNOWN: 9378c2ecf20Sopenharmony_ci /* PHYLINK called sja1105_mac_config() to inform us about 9388c2ecf20Sopenharmony_ci * the state->interface, but AN has not completed and the 9398c2ecf20Sopenharmony_ci * speed is not yet valid. UM10944.pdf says that setting 9408c2ecf20Sopenharmony_ci * SJA1105_SPEED_AUTO at runtime disables the port, so that is 9418c2ecf20Sopenharmony_ci * ok for power consumption in case AN will never complete - 9428c2ecf20Sopenharmony_ci * otherwise PHYLINK should come back with a new update. 9438c2ecf20Sopenharmony_ci */ 9448c2ecf20Sopenharmony_ci speed = SJA1105_SPEED_AUTO; 9458c2ecf20Sopenharmony_ci break; 9468c2ecf20Sopenharmony_ci case SPEED_10: 9478c2ecf20Sopenharmony_ci speed = SJA1105_SPEED_10MBPS; 9488c2ecf20Sopenharmony_ci break; 9498c2ecf20Sopenharmony_ci case SPEED_100: 9508c2ecf20Sopenharmony_ci speed = SJA1105_SPEED_100MBPS; 9518c2ecf20Sopenharmony_ci break; 9528c2ecf20Sopenharmony_ci case SPEED_1000: 9538c2ecf20Sopenharmony_ci speed = SJA1105_SPEED_1000MBPS; 9548c2ecf20Sopenharmony_ci break; 9558c2ecf20Sopenharmony_ci default: 9568c2ecf20Sopenharmony_ci dev_err(dev, "Invalid speed %iMbps\n", speed_mbps); 9578c2ecf20Sopenharmony_ci return -EINVAL; 9588c2ecf20Sopenharmony_ci } 9598c2ecf20Sopenharmony_ci 9608c2ecf20Sopenharmony_ci /* Overwrite SJA1105_SPEED_AUTO from the static MAC configuration 9618c2ecf20Sopenharmony_ci * table, since this will be used for the clocking setup, and we no 9628c2ecf20Sopenharmony_ci * longer need to store it in the static config (already told hardware 9638c2ecf20Sopenharmony_ci * we want auto during upload phase). 9648c2ecf20Sopenharmony_ci * Actually for the SGMII port, the MAC is fixed at 1 Gbps and 9658c2ecf20Sopenharmony_ci * we need to configure the PCS only (if even that). 9668c2ecf20Sopenharmony_ci */ 9678c2ecf20Sopenharmony_ci if (sja1105_supports_sgmii(priv, port)) 9688c2ecf20Sopenharmony_ci mac[port].speed = SJA1105_SPEED_1000MBPS; 9698c2ecf20Sopenharmony_ci else 9708c2ecf20Sopenharmony_ci mac[port].speed = speed; 9718c2ecf20Sopenharmony_ci 9728c2ecf20Sopenharmony_ci /* Write to the dynamic reconfiguration tables */ 9738c2ecf20Sopenharmony_ci rc = sja1105_dynamic_config_write(priv, BLK_IDX_MAC_CONFIG, port, 9748c2ecf20Sopenharmony_ci &mac[port], true); 9758c2ecf20Sopenharmony_ci if (rc < 0) { 9768c2ecf20Sopenharmony_ci dev_err(dev, "Failed to write MAC config: %d\n", rc); 9778c2ecf20Sopenharmony_ci return rc; 9788c2ecf20Sopenharmony_ci } 9798c2ecf20Sopenharmony_ci 9808c2ecf20Sopenharmony_ci /* Reconfigure the PLLs for the RGMII interfaces (required 125 MHz at 9818c2ecf20Sopenharmony_ci * gigabit, 25 MHz at 100 Mbps and 2.5 MHz at 10 Mbps). For MII and 9828c2ecf20Sopenharmony_ci * RMII no change of the clock setup is required. Actually, changing 9838c2ecf20Sopenharmony_ci * the clock setup does interrupt the clock signal for a certain time 9848c2ecf20Sopenharmony_ci * which causes trouble for all PHYs relying on this signal. 9858c2ecf20Sopenharmony_ci */ 9868c2ecf20Sopenharmony_ci phy_mode = mii->xmii_mode[port]; 9878c2ecf20Sopenharmony_ci if (phy_mode != XMII_MODE_RGMII) 9888c2ecf20Sopenharmony_ci return 0; 9898c2ecf20Sopenharmony_ci 9908c2ecf20Sopenharmony_ci return sja1105_clocking_setup_port(priv, port); 9918c2ecf20Sopenharmony_ci} 9928c2ecf20Sopenharmony_ci 9938c2ecf20Sopenharmony_ci/* The SJA1105 MAC programming model is through the static config (the xMII 9948c2ecf20Sopenharmony_ci * Mode table cannot be dynamically reconfigured), and we have to program 9958c2ecf20Sopenharmony_ci * that early (earlier than PHYLINK calls us, anyway). 9968c2ecf20Sopenharmony_ci * So just error out in case the connected PHY attempts to change the initial 9978c2ecf20Sopenharmony_ci * system interface MII protocol from what is defined in the DT, at least for 9988c2ecf20Sopenharmony_ci * now. 9998c2ecf20Sopenharmony_ci */ 10008c2ecf20Sopenharmony_cistatic bool sja1105_phy_mode_mismatch(struct sja1105_private *priv, int port, 10018c2ecf20Sopenharmony_ci phy_interface_t interface) 10028c2ecf20Sopenharmony_ci{ 10038c2ecf20Sopenharmony_ci struct sja1105_xmii_params_entry *mii; 10048c2ecf20Sopenharmony_ci sja1105_phy_interface_t phy_mode; 10058c2ecf20Sopenharmony_ci 10068c2ecf20Sopenharmony_ci mii = priv->static_config.tables[BLK_IDX_XMII_PARAMS].entries; 10078c2ecf20Sopenharmony_ci phy_mode = mii->xmii_mode[port]; 10088c2ecf20Sopenharmony_ci 10098c2ecf20Sopenharmony_ci switch (interface) { 10108c2ecf20Sopenharmony_ci case PHY_INTERFACE_MODE_MII: 10118c2ecf20Sopenharmony_ci return (phy_mode != XMII_MODE_MII); 10128c2ecf20Sopenharmony_ci case PHY_INTERFACE_MODE_RMII: 10138c2ecf20Sopenharmony_ci return (phy_mode != XMII_MODE_RMII); 10148c2ecf20Sopenharmony_ci case PHY_INTERFACE_MODE_RGMII: 10158c2ecf20Sopenharmony_ci case PHY_INTERFACE_MODE_RGMII_ID: 10168c2ecf20Sopenharmony_ci case PHY_INTERFACE_MODE_RGMII_RXID: 10178c2ecf20Sopenharmony_ci case PHY_INTERFACE_MODE_RGMII_TXID: 10188c2ecf20Sopenharmony_ci return (phy_mode != XMII_MODE_RGMII); 10198c2ecf20Sopenharmony_ci case PHY_INTERFACE_MODE_SGMII: 10208c2ecf20Sopenharmony_ci return (phy_mode != XMII_MODE_SGMII); 10218c2ecf20Sopenharmony_ci default: 10228c2ecf20Sopenharmony_ci return true; 10238c2ecf20Sopenharmony_ci } 10248c2ecf20Sopenharmony_ci} 10258c2ecf20Sopenharmony_ci 10268c2ecf20Sopenharmony_cistatic void sja1105_mac_config(struct dsa_switch *ds, int port, 10278c2ecf20Sopenharmony_ci unsigned int mode, 10288c2ecf20Sopenharmony_ci const struct phylink_link_state *state) 10298c2ecf20Sopenharmony_ci{ 10308c2ecf20Sopenharmony_ci struct sja1105_private *priv = ds->priv; 10318c2ecf20Sopenharmony_ci bool is_sgmii = sja1105_supports_sgmii(priv, port); 10328c2ecf20Sopenharmony_ci 10338c2ecf20Sopenharmony_ci if (sja1105_phy_mode_mismatch(priv, port, state->interface)) { 10348c2ecf20Sopenharmony_ci dev_err(ds->dev, "Changing PHY mode to %s not supported!\n", 10358c2ecf20Sopenharmony_ci phy_modes(state->interface)); 10368c2ecf20Sopenharmony_ci return; 10378c2ecf20Sopenharmony_ci } 10388c2ecf20Sopenharmony_ci 10398c2ecf20Sopenharmony_ci if (phylink_autoneg_inband(mode) && !is_sgmii) { 10408c2ecf20Sopenharmony_ci dev_err(ds->dev, "In-band AN not supported!\n"); 10418c2ecf20Sopenharmony_ci return; 10428c2ecf20Sopenharmony_ci } 10438c2ecf20Sopenharmony_ci 10448c2ecf20Sopenharmony_ci if (is_sgmii) 10458c2ecf20Sopenharmony_ci sja1105_sgmii_pcs_config(priv, phylink_autoneg_inband(mode), 10468c2ecf20Sopenharmony_ci false); 10478c2ecf20Sopenharmony_ci} 10488c2ecf20Sopenharmony_ci 10498c2ecf20Sopenharmony_cistatic void sja1105_mac_link_down(struct dsa_switch *ds, int port, 10508c2ecf20Sopenharmony_ci unsigned int mode, 10518c2ecf20Sopenharmony_ci phy_interface_t interface) 10528c2ecf20Sopenharmony_ci{ 10538c2ecf20Sopenharmony_ci sja1105_inhibit_tx(ds->priv, BIT(port), true); 10548c2ecf20Sopenharmony_ci} 10558c2ecf20Sopenharmony_ci 10568c2ecf20Sopenharmony_cistatic void sja1105_mac_link_up(struct dsa_switch *ds, int port, 10578c2ecf20Sopenharmony_ci unsigned int mode, 10588c2ecf20Sopenharmony_ci phy_interface_t interface, 10598c2ecf20Sopenharmony_ci struct phy_device *phydev, 10608c2ecf20Sopenharmony_ci int speed, int duplex, 10618c2ecf20Sopenharmony_ci bool tx_pause, bool rx_pause) 10628c2ecf20Sopenharmony_ci{ 10638c2ecf20Sopenharmony_ci struct sja1105_private *priv = ds->priv; 10648c2ecf20Sopenharmony_ci 10658c2ecf20Sopenharmony_ci sja1105_adjust_port_config(priv, port, speed); 10668c2ecf20Sopenharmony_ci 10678c2ecf20Sopenharmony_ci if (sja1105_supports_sgmii(priv, port) && !phylink_autoneg_inband(mode)) 10688c2ecf20Sopenharmony_ci sja1105_sgmii_pcs_force_speed(priv, speed); 10698c2ecf20Sopenharmony_ci 10708c2ecf20Sopenharmony_ci sja1105_inhibit_tx(priv, BIT(port), false); 10718c2ecf20Sopenharmony_ci} 10728c2ecf20Sopenharmony_ci 10738c2ecf20Sopenharmony_cistatic void sja1105_phylink_validate(struct dsa_switch *ds, int port, 10748c2ecf20Sopenharmony_ci unsigned long *supported, 10758c2ecf20Sopenharmony_ci struct phylink_link_state *state) 10768c2ecf20Sopenharmony_ci{ 10778c2ecf20Sopenharmony_ci /* Construct a new mask which exhaustively contains all link features 10788c2ecf20Sopenharmony_ci * supported by the MAC, and then apply that (logical AND) to what will 10798c2ecf20Sopenharmony_ci * be sent to the PHY for "marketing". 10808c2ecf20Sopenharmony_ci */ 10818c2ecf20Sopenharmony_ci __ETHTOOL_DECLARE_LINK_MODE_MASK(mask) = { 0, }; 10828c2ecf20Sopenharmony_ci struct sja1105_private *priv = ds->priv; 10838c2ecf20Sopenharmony_ci struct sja1105_xmii_params_entry *mii; 10848c2ecf20Sopenharmony_ci 10858c2ecf20Sopenharmony_ci mii = priv->static_config.tables[BLK_IDX_XMII_PARAMS].entries; 10868c2ecf20Sopenharmony_ci 10878c2ecf20Sopenharmony_ci /* include/linux/phylink.h says: 10888c2ecf20Sopenharmony_ci * When @state->interface is %PHY_INTERFACE_MODE_NA, phylink 10898c2ecf20Sopenharmony_ci * expects the MAC driver to return all supported link modes. 10908c2ecf20Sopenharmony_ci */ 10918c2ecf20Sopenharmony_ci if (state->interface != PHY_INTERFACE_MODE_NA && 10928c2ecf20Sopenharmony_ci sja1105_phy_mode_mismatch(priv, port, state->interface)) { 10938c2ecf20Sopenharmony_ci bitmap_zero(supported, __ETHTOOL_LINK_MODE_MASK_NBITS); 10948c2ecf20Sopenharmony_ci return; 10958c2ecf20Sopenharmony_ci } 10968c2ecf20Sopenharmony_ci 10978c2ecf20Sopenharmony_ci /* The MAC does not support pause frames, and also doesn't 10988c2ecf20Sopenharmony_ci * support half-duplex traffic modes. 10998c2ecf20Sopenharmony_ci */ 11008c2ecf20Sopenharmony_ci phylink_set(mask, Autoneg); 11018c2ecf20Sopenharmony_ci phylink_set(mask, MII); 11028c2ecf20Sopenharmony_ci phylink_set(mask, 10baseT_Full); 11038c2ecf20Sopenharmony_ci phylink_set(mask, 100baseT_Full); 11048c2ecf20Sopenharmony_ci phylink_set(mask, 100baseT1_Full); 11058c2ecf20Sopenharmony_ci if (mii->xmii_mode[port] == XMII_MODE_RGMII || 11068c2ecf20Sopenharmony_ci mii->xmii_mode[port] == XMII_MODE_SGMII) 11078c2ecf20Sopenharmony_ci phylink_set(mask, 1000baseT_Full); 11088c2ecf20Sopenharmony_ci 11098c2ecf20Sopenharmony_ci bitmap_and(supported, supported, mask, __ETHTOOL_LINK_MODE_MASK_NBITS); 11108c2ecf20Sopenharmony_ci bitmap_and(state->advertising, state->advertising, mask, 11118c2ecf20Sopenharmony_ci __ETHTOOL_LINK_MODE_MASK_NBITS); 11128c2ecf20Sopenharmony_ci} 11138c2ecf20Sopenharmony_ci 11148c2ecf20Sopenharmony_cistatic int sja1105_mac_pcs_get_state(struct dsa_switch *ds, int port, 11158c2ecf20Sopenharmony_ci struct phylink_link_state *state) 11168c2ecf20Sopenharmony_ci{ 11178c2ecf20Sopenharmony_ci struct sja1105_private *priv = ds->priv; 11188c2ecf20Sopenharmony_ci int ais; 11198c2ecf20Sopenharmony_ci 11208c2ecf20Sopenharmony_ci /* Read the vendor-specific AUTONEG_INTR_STATUS register */ 11218c2ecf20Sopenharmony_ci ais = sja1105_sgmii_read(priv, SJA1105_AIS); 11228c2ecf20Sopenharmony_ci if (ais < 0) 11238c2ecf20Sopenharmony_ci return ais; 11248c2ecf20Sopenharmony_ci 11258c2ecf20Sopenharmony_ci switch (SJA1105_AIS_SPEED(ais)) { 11268c2ecf20Sopenharmony_ci case 0: 11278c2ecf20Sopenharmony_ci state->speed = SPEED_10; 11288c2ecf20Sopenharmony_ci break; 11298c2ecf20Sopenharmony_ci case 1: 11308c2ecf20Sopenharmony_ci state->speed = SPEED_100; 11318c2ecf20Sopenharmony_ci break; 11328c2ecf20Sopenharmony_ci case 2: 11338c2ecf20Sopenharmony_ci state->speed = SPEED_1000; 11348c2ecf20Sopenharmony_ci break; 11358c2ecf20Sopenharmony_ci default: 11368c2ecf20Sopenharmony_ci dev_err(ds->dev, "Invalid SGMII PCS speed %lu\n", 11378c2ecf20Sopenharmony_ci SJA1105_AIS_SPEED(ais)); 11388c2ecf20Sopenharmony_ci } 11398c2ecf20Sopenharmony_ci state->duplex = SJA1105_AIS_DUPLEX_MODE(ais); 11408c2ecf20Sopenharmony_ci state->an_complete = SJA1105_AIS_COMPLETE(ais); 11418c2ecf20Sopenharmony_ci state->link = SJA1105_AIS_LINK_STATUS(ais); 11428c2ecf20Sopenharmony_ci 11438c2ecf20Sopenharmony_ci return 0; 11448c2ecf20Sopenharmony_ci} 11458c2ecf20Sopenharmony_ci 11468c2ecf20Sopenharmony_cistatic int 11478c2ecf20Sopenharmony_cisja1105_find_static_fdb_entry(struct sja1105_private *priv, int port, 11488c2ecf20Sopenharmony_ci const struct sja1105_l2_lookup_entry *requested) 11498c2ecf20Sopenharmony_ci{ 11508c2ecf20Sopenharmony_ci struct sja1105_l2_lookup_entry *l2_lookup; 11518c2ecf20Sopenharmony_ci struct sja1105_table *table; 11528c2ecf20Sopenharmony_ci int i; 11538c2ecf20Sopenharmony_ci 11548c2ecf20Sopenharmony_ci table = &priv->static_config.tables[BLK_IDX_L2_LOOKUP]; 11558c2ecf20Sopenharmony_ci l2_lookup = table->entries; 11568c2ecf20Sopenharmony_ci 11578c2ecf20Sopenharmony_ci for (i = 0; i < table->entry_count; i++) 11588c2ecf20Sopenharmony_ci if (l2_lookup[i].macaddr == requested->macaddr && 11598c2ecf20Sopenharmony_ci l2_lookup[i].vlanid == requested->vlanid && 11608c2ecf20Sopenharmony_ci l2_lookup[i].destports & BIT(port)) 11618c2ecf20Sopenharmony_ci return i; 11628c2ecf20Sopenharmony_ci 11638c2ecf20Sopenharmony_ci return -1; 11648c2ecf20Sopenharmony_ci} 11658c2ecf20Sopenharmony_ci 11668c2ecf20Sopenharmony_ci/* We want FDB entries added statically through the bridge command to persist 11678c2ecf20Sopenharmony_ci * across switch resets, which are a common thing during normal SJA1105 11688c2ecf20Sopenharmony_ci * operation. So we have to back them up in the static configuration tables 11698c2ecf20Sopenharmony_ci * and hence apply them on next static config upload... yay! 11708c2ecf20Sopenharmony_ci */ 11718c2ecf20Sopenharmony_cistatic int 11728c2ecf20Sopenharmony_cisja1105_static_fdb_change(struct sja1105_private *priv, int port, 11738c2ecf20Sopenharmony_ci const struct sja1105_l2_lookup_entry *requested, 11748c2ecf20Sopenharmony_ci bool keep) 11758c2ecf20Sopenharmony_ci{ 11768c2ecf20Sopenharmony_ci struct sja1105_l2_lookup_entry *l2_lookup; 11778c2ecf20Sopenharmony_ci struct sja1105_table *table; 11788c2ecf20Sopenharmony_ci int rc, match; 11798c2ecf20Sopenharmony_ci 11808c2ecf20Sopenharmony_ci table = &priv->static_config.tables[BLK_IDX_L2_LOOKUP]; 11818c2ecf20Sopenharmony_ci 11828c2ecf20Sopenharmony_ci match = sja1105_find_static_fdb_entry(priv, port, requested); 11838c2ecf20Sopenharmony_ci if (match < 0) { 11848c2ecf20Sopenharmony_ci /* Can't delete a missing entry. */ 11858c2ecf20Sopenharmony_ci if (!keep) 11868c2ecf20Sopenharmony_ci return 0; 11878c2ecf20Sopenharmony_ci 11888c2ecf20Sopenharmony_ci /* No match => new entry */ 11898c2ecf20Sopenharmony_ci rc = sja1105_table_resize(table, table->entry_count + 1); 11908c2ecf20Sopenharmony_ci if (rc) 11918c2ecf20Sopenharmony_ci return rc; 11928c2ecf20Sopenharmony_ci 11938c2ecf20Sopenharmony_ci match = table->entry_count - 1; 11948c2ecf20Sopenharmony_ci } 11958c2ecf20Sopenharmony_ci 11968c2ecf20Sopenharmony_ci /* Assign pointer after the resize (it may be new memory) */ 11978c2ecf20Sopenharmony_ci l2_lookup = table->entries; 11988c2ecf20Sopenharmony_ci 11998c2ecf20Sopenharmony_ci /* We have a match. 12008c2ecf20Sopenharmony_ci * If the job was to add this FDB entry, it's already done (mostly 12018c2ecf20Sopenharmony_ci * anyway, since the port forwarding mask may have changed, case in 12028c2ecf20Sopenharmony_ci * which we update it). 12038c2ecf20Sopenharmony_ci * Otherwise we have to delete it. 12048c2ecf20Sopenharmony_ci */ 12058c2ecf20Sopenharmony_ci if (keep) { 12068c2ecf20Sopenharmony_ci l2_lookup[match] = *requested; 12078c2ecf20Sopenharmony_ci return 0; 12088c2ecf20Sopenharmony_ci } 12098c2ecf20Sopenharmony_ci 12108c2ecf20Sopenharmony_ci /* To remove, the strategy is to overwrite the element with 12118c2ecf20Sopenharmony_ci * the last one, and then reduce the array size by 1 12128c2ecf20Sopenharmony_ci */ 12138c2ecf20Sopenharmony_ci l2_lookup[match] = l2_lookup[table->entry_count - 1]; 12148c2ecf20Sopenharmony_ci return sja1105_table_resize(table, table->entry_count - 1); 12158c2ecf20Sopenharmony_ci} 12168c2ecf20Sopenharmony_ci 12178c2ecf20Sopenharmony_ci/* First-generation switches have a 4-way set associative TCAM that 12188c2ecf20Sopenharmony_ci * holds the FDB entries. An FDB index spans from 0 to 1023 and is comprised of 12198c2ecf20Sopenharmony_ci * a "bin" (grouping of 4 entries) and a "way" (an entry within a bin). 12208c2ecf20Sopenharmony_ci * For the placement of a newly learnt FDB entry, the switch selects the bin 12218c2ecf20Sopenharmony_ci * based on a hash function, and the way within that bin incrementally. 12228c2ecf20Sopenharmony_ci */ 12238c2ecf20Sopenharmony_cistatic int sja1105et_fdb_index(int bin, int way) 12248c2ecf20Sopenharmony_ci{ 12258c2ecf20Sopenharmony_ci return bin * SJA1105ET_FDB_BIN_SIZE + way; 12268c2ecf20Sopenharmony_ci} 12278c2ecf20Sopenharmony_ci 12288c2ecf20Sopenharmony_cistatic int sja1105et_is_fdb_entry_in_bin(struct sja1105_private *priv, int bin, 12298c2ecf20Sopenharmony_ci const u8 *addr, u16 vid, 12308c2ecf20Sopenharmony_ci struct sja1105_l2_lookup_entry *match, 12318c2ecf20Sopenharmony_ci int *last_unused) 12328c2ecf20Sopenharmony_ci{ 12338c2ecf20Sopenharmony_ci int way; 12348c2ecf20Sopenharmony_ci 12358c2ecf20Sopenharmony_ci for (way = 0; way < SJA1105ET_FDB_BIN_SIZE; way++) { 12368c2ecf20Sopenharmony_ci struct sja1105_l2_lookup_entry l2_lookup = {0}; 12378c2ecf20Sopenharmony_ci int index = sja1105et_fdb_index(bin, way); 12388c2ecf20Sopenharmony_ci 12398c2ecf20Sopenharmony_ci /* Skip unused entries, optionally marking them 12408c2ecf20Sopenharmony_ci * into the return value 12418c2ecf20Sopenharmony_ci */ 12428c2ecf20Sopenharmony_ci if (sja1105_dynamic_config_read(priv, BLK_IDX_L2_LOOKUP, 12438c2ecf20Sopenharmony_ci index, &l2_lookup)) { 12448c2ecf20Sopenharmony_ci if (last_unused) 12458c2ecf20Sopenharmony_ci *last_unused = way; 12468c2ecf20Sopenharmony_ci continue; 12478c2ecf20Sopenharmony_ci } 12488c2ecf20Sopenharmony_ci 12498c2ecf20Sopenharmony_ci if (l2_lookup.macaddr == ether_addr_to_u64(addr) && 12508c2ecf20Sopenharmony_ci l2_lookup.vlanid == vid) { 12518c2ecf20Sopenharmony_ci if (match) 12528c2ecf20Sopenharmony_ci *match = l2_lookup; 12538c2ecf20Sopenharmony_ci return way; 12548c2ecf20Sopenharmony_ci } 12558c2ecf20Sopenharmony_ci } 12568c2ecf20Sopenharmony_ci /* Return an invalid entry index if not found */ 12578c2ecf20Sopenharmony_ci return -1; 12588c2ecf20Sopenharmony_ci} 12598c2ecf20Sopenharmony_ci 12608c2ecf20Sopenharmony_ciint sja1105et_fdb_add(struct dsa_switch *ds, int port, 12618c2ecf20Sopenharmony_ci const unsigned char *addr, u16 vid) 12628c2ecf20Sopenharmony_ci{ 12638c2ecf20Sopenharmony_ci struct sja1105_l2_lookup_entry l2_lookup = {0}, tmp; 12648c2ecf20Sopenharmony_ci struct sja1105_private *priv = ds->priv; 12658c2ecf20Sopenharmony_ci struct device *dev = ds->dev; 12668c2ecf20Sopenharmony_ci int last_unused = -1; 12678c2ecf20Sopenharmony_ci int start, end, i; 12688c2ecf20Sopenharmony_ci int bin, way, rc; 12698c2ecf20Sopenharmony_ci 12708c2ecf20Sopenharmony_ci bin = sja1105et_fdb_hash(priv, addr, vid); 12718c2ecf20Sopenharmony_ci 12728c2ecf20Sopenharmony_ci way = sja1105et_is_fdb_entry_in_bin(priv, bin, addr, vid, 12738c2ecf20Sopenharmony_ci &l2_lookup, &last_unused); 12748c2ecf20Sopenharmony_ci if (way >= 0) { 12758c2ecf20Sopenharmony_ci /* We have an FDB entry. Is our port in the destination 12768c2ecf20Sopenharmony_ci * mask? If yes, we need to do nothing. If not, we need 12778c2ecf20Sopenharmony_ci * to rewrite the entry by adding this port to it. 12788c2ecf20Sopenharmony_ci */ 12798c2ecf20Sopenharmony_ci if ((l2_lookup.destports & BIT(port)) && l2_lookup.lockeds) 12808c2ecf20Sopenharmony_ci return 0; 12818c2ecf20Sopenharmony_ci l2_lookup.destports |= BIT(port); 12828c2ecf20Sopenharmony_ci } else { 12838c2ecf20Sopenharmony_ci int index = sja1105et_fdb_index(bin, way); 12848c2ecf20Sopenharmony_ci 12858c2ecf20Sopenharmony_ci /* We don't have an FDB entry. We construct a new one and 12868c2ecf20Sopenharmony_ci * try to find a place for it within the FDB table. 12878c2ecf20Sopenharmony_ci */ 12888c2ecf20Sopenharmony_ci l2_lookup.macaddr = ether_addr_to_u64(addr); 12898c2ecf20Sopenharmony_ci l2_lookup.destports = BIT(port); 12908c2ecf20Sopenharmony_ci l2_lookup.vlanid = vid; 12918c2ecf20Sopenharmony_ci 12928c2ecf20Sopenharmony_ci if (last_unused >= 0) { 12938c2ecf20Sopenharmony_ci way = last_unused; 12948c2ecf20Sopenharmony_ci } else { 12958c2ecf20Sopenharmony_ci /* Bin is full, need to evict somebody. 12968c2ecf20Sopenharmony_ci * Choose victim at random. If you get these messages 12978c2ecf20Sopenharmony_ci * often, you may need to consider changing the 12988c2ecf20Sopenharmony_ci * distribution function: 12998c2ecf20Sopenharmony_ci * static_config[BLK_IDX_L2_LOOKUP_PARAMS].entries->poly 13008c2ecf20Sopenharmony_ci */ 13018c2ecf20Sopenharmony_ci get_random_bytes(&way, sizeof(u8)); 13028c2ecf20Sopenharmony_ci way %= SJA1105ET_FDB_BIN_SIZE; 13038c2ecf20Sopenharmony_ci dev_warn(dev, "Warning, FDB bin %d full while adding entry for %pM. Evicting entry %u.\n", 13048c2ecf20Sopenharmony_ci bin, addr, way); 13058c2ecf20Sopenharmony_ci /* Evict entry */ 13068c2ecf20Sopenharmony_ci sja1105_dynamic_config_write(priv, BLK_IDX_L2_LOOKUP, 13078c2ecf20Sopenharmony_ci index, NULL, false); 13088c2ecf20Sopenharmony_ci } 13098c2ecf20Sopenharmony_ci } 13108c2ecf20Sopenharmony_ci l2_lookup.lockeds = true; 13118c2ecf20Sopenharmony_ci l2_lookup.index = sja1105et_fdb_index(bin, way); 13128c2ecf20Sopenharmony_ci 13138c2ecf20Sopenharmony_ci rc = sja1105_dynamic_config_write(priv, BLK_IDX_L2_LOOKUP, 13148c2ecf20Sopenharmony_ci l2_lookup.index, &l2_lookup, 13158c2ecf20Sopenharmony_ci true); 13168c2ecf20Sopenharmony_ci if (rc < 0) 13178c2ecf20Sopenharmony_ci return rc; 13188c2ecf20Sopenharmony_ci 13198c2ecf20Sopenharmony_ci /* Invalidate a dynamically learned entry if that exists */ 13208c2ecf20Sopenharmony_ci start = sja1105et_fdb_index(bin, 0); 13218c2ecf20Sopenharmony_ci end = sja1105et_fdb_index(bin, way); 13228c2ecf20Sopenharmony_ci 13238c2ecf20Sopenharmony_ci for (i = start; i < end; i++) { 13248c2ecf20Sopenharmony_ci rc = sja1105_dynamic_config_read(priv, BLK_IDX_L2_LOOKUP, 13258c2ecf20Sopenharmony_ci i, &tmp); 13268c2ecf20Sopenharmony_ci if (rc == -ENOENT) 13278c2ecf20Sopenharmony_ci continue; 13288c2ecf20Sopenharmony_ci if (rc) 13298c2ecf20Sopenharmony_ci return rc; 13308c2ecf20Sopenharmony_ci 13318c2ecf20Sopenharmony_ci if (tmp.macaddr != ether_addr_to_u64(addr) || tmp.vlanid != vid) 13328c2ecf20Sopenharmony_ci continue; 13338c2ecf20Sopenharmony_ci 13348c2ecf20Sopenharmony_ci rc = sja1105_dynamic_config_write(priv, BLK_IDX_L2_LOOKUP, 13358c2ecf20Sopenharmony_ci i, NULL, false); 13368c2ecf20Sopenharmony_ci if (rc) 13378c2ecf20Sopenharmony_ci return rc; 13388c2ecf20Sopenharmony_ci 13398c2ecf20Sopenharmony_ci break; 13408c2ecf20Sopenharmony_ci } 13418c2ecf20Sopenharmony_ci 13428c2ecf20Sopenharmony_ci return sja1105_static_fdb_change(priv, port, &l2_lookup, true); 13438c2ecf20Sopenharmony_ci} 13448c2ecf20Sopenharmony_ci 13458c2ecf20Sopenharmony_ciint sja1105et_fdb_del(struct dsa_switch *ds, int port, 13468c2ecf20Sopenharmony_ci const unsigned char *addr, u16 vid) 13478c2ecf20Sopenharmony_ci{ 13488c2ecf20Sopenharmony_ci struct sja1105_l2_lookup_entry l2_lookup = {0}; 13498c2ecf20Sopenharmony_ci struct sja1105_private *priv = ds->priv; 13508c2ecf20Sopenharmony_ci int index, bin, way, rc; 13518c2ecf20Sopenharmony_ci bool keep; 13528c2ecf20Sopenharmony_ci 13538c2ecf20Sopenharmony_ci bin = sja1105et_fdb_hash(priv, addr, vid); 13548c2ecf20Sopenharmony_ci way = sja1105et_is_fdb_entry_in_bin(priv, bin, addr, vid, 13558c2ecf20Sopenharmony_ci &l2_lookup, NULL); 13568c2ecf20Sopenharmony_ci if (way < 0) 13578c2ecf20Sopenharmony_ci return 0; 13588c2ecf20Sopenharmony_ci index = sja1105et_fdb_index(bin, way); 13598c2ecf20Sopenharmony_ci 13608c2ecf20Sopenharmony_ci /* We have an FDB entry. Is our port in the destination mask? If yes, 13618c2ecf20Sopenharmony_ci * we need to remove it. If the resulting port mask becomes empty, we 13628c2ecf20Sopenharmony_ci * need to completely evict the FDB entry. 13638c2ecf20Sopenharmony_ci * Otherwise we just write it back. 13648c2ecf20Sopenharmony_ci */ 13658c2ecf20Sopenharmony_ci l2_lookup.destports &= ~BIT(port); 13668c2ecf20Sopenharmony_ci 13678c2ecf20Sopenharmony_ci if (l2_lookup.destports) 13688c2ecf20Sopenharmony_ci keep = true; 13698c2ecf20Sopenharmony_ci else 13708c2ecf20Sopenharmony_ci keep = false; 13718c2ecf20Sopenharmony_ci 13728c2ecf20Sopenharmony_ci rc = sja1105_dynamic_config_write(priv, BLK_IDX_L2_LOOKUP, 13738c2ecf20Sopenharmony_ci index, &l2_lookup, keep); 13748c2ecf20Sopenharmony_ci if (rc < 0) 13758c2ecf20Sopenharmony_ci return rc; 13768c2ecf20Sopenharmony_ci 13778c2ecf20Sopenharmony_ci return sja1105_static_fdb_change(priv, port, &l2_lookup, keep); 13788c2ecf20Sopenharmony_ci} 13798c2ecf20Sopenharmony_ci 13808c2ecf20Sopenharmony_ciint sja1105pqrs_fdb_add(struct dsa_switch *ds, int port, 13818c2ecf20Sopenharmony_ci const unsigned char *addr, u16 vid) 13828c2ecf20Sopenharmony_ci{ 13838c2ecf20Sopenharmony_ci struct sja1105_l2_lookup_entry l2_lookup = {0}, tmp; 13848c2ecf20Sopenharmony_ci struct sja1105_private *priv = ds->priv; 13858c2ecf20Sopenharmony_ci int rc, i; 13868c2ecf20Sopenharmony_ci 13878c2ecf20Sopenharmony_ci /* Search for an existing entry in the FDB table */ 13888c2ecf20Sopenharmony_ci l2_lookup.macaddr = ether_addr_to_u64(addr); 13898c2ecf20Sopenharmony_ci l2_lookup.vlanid = vid; 13908c2ecf20Sopenharmony_ci l2_lookup.mask_macaddr = GENMASK_ULL(ETH_ALEN * 8 - 1, 0); 13918c2ecf20Sopenharmony_ci l2_lookup.mask_vlanid = VLAN_VID_MASK; 13928c2ecf20Sopenharmony_ci l2_lookup.destports = BIT(port); 13938c2ecf20Sopenharmony_ci 13948c2ecf20Sopenharmony_ci rc = sja1105_dynamic_config_read(priv, BLK_IDX_L2_LOOKUP, 13958c2ecf20Sopenharmony_ci SJA1105_SEARCH, &l2_lookup); 13968c2ecf20Sopenharmony_ci if (rc == 0) { 13978c2ecf20Sopenharmony_ci /* Found a static entry and this port is already in the entry's 13988c2ecf20Sopenharmony_ci * port mask => job done 13998c2ecf20Sopenharmony_ci */ 14008c2ecf20Sopenharmony_ci if ((l2_lookup.destports & BIT(port)) && l2_lookup.lockeds) 14018c2ecf20Sopenharmony_ci return 0; 14028c2ecf20Sopenharmony_ci /* l2_lookup.index is populated by the switch in case it 14038c2ecf20Sopenharmony_ci * found something. 14048c2ecf20Sopenharmony_ci */ 14058c2ecf20Sopenharmony_ci l2_lookup.destports |= BIT(port); 14068c2ecf20Sopenharmony_ci goto skip_finding_an_index; 14078c2ecf20Sopenharmony_ci } 14088c2ecf20Sopenharmony_ci 14098c2ecf20Sopenharmony_ci /* Not found, so try to find an unused spot in the FDB. 14108c2ecf20Sopenharmony_ci * This is slightly inefficient because the strategy is knock-knock at 14118c2ecf20Sopenharmony_ci * every possible position from 0 to 1023. 14128c2ecf20Sopenharmony_ci */ 14138c2ecf20Sopenharmony_ci for (i = 0; i < SJA1105_MAX_L2_LOOKUP_COUNT; i++) { 14148c2ecf20Sopenharmony_ci rc = sja1105_dynamic_config_read(priv, BLK_IDX_L2_LOOKUP, 14158c2ecf20Sopenharmony_ci i, NULL); 14168c2ecf20Sopenharmony_ci if (rc < 0) 14178c2ecf20Sopenharmony_ci break; 14188c2ecf20Sopenharmony_ci } 14198c2ecf20Sopenharmony_ci if (i == SJA1105_MAX_L2_LOOKUP_COUNT) { 14208c2ecf20Sopenharmony_ci dev_err(ds->dev, "FDB is full, cannot add entry.\n"); 14218c2ecf20Sopenharmony_ci return -EINVAL; 14228c2ecf20Sopenharmony_ci } 14238c2ecf20Sopenharmony_ci l2_lookup.index = i; 14248c2ecf20Sopenharmony_ci 14258c2ecf20Sopenharmony_ciskip_finding_an_index: 14268c2ecf20Sopenharmony_ci l2_lookup.lockeds = true; 14278c2ecf20Sopenharmony_ci 14288c2ecf20Sopenharmony_ci rc = sja1105_dynamic_config_write(priv, BLK_IDX_L2_LOOKUP, 14298c2ecf20Sopenharmony_ci l2_lookup.index, &l2_lookup, 14308c2ecf20Sopenharmony_ci true); 14318c2ecf20Sopenharmony_ci if (rc < 0) 14328c2ecf20Sopenharmony_ci return rc; 14338c2ecf20Sopenharmony_ci 14348c2ecf20Sopenharmony_ci /* The switch learns dynamic entries and looks up the FDB left to 14358c2ecf20Sopenharmony_ci * right. It is possible that our addition was concurrent with the 14368c2ecf20Sopenharmony_ci * dynamic learning of the same address, so now that the static entry 14378c2ecf20Sopenharmony_ci * has been installed, we are certain that address learning for this 14388c2ecf20Sopenharmony_ci * particular address has been turned off, so the dynamic entry either 14398c2ecf20Sopenharmony_ci * is in the FDB at an index smaller than the static one, or isn't (it 14408c2ecf20Sopenharmony_ci * can also be at a larger index, but in that case it is inactive 14418c2ecf20Sopenharmony_ci * because the static FDB entry will match first, and the dynamic one 14428c2ecf20Sopenharmony_ci * will eventually age out). Search for a dynamically learned address 14438c2ecf20Sopenharmony_ci * prior to our static one and invalidate it. 14448c2ecf20Sopenharmony_ci */ 14458c2ecf20Sopenharmony_ci tmp = l2_lookup; 14468c2ecf20Sopenharmony_ci 14478c2ecf20Sopenharmony_ci rc = sja1105_dynamic_config_read(priv, BLK_IDX_L2_LOOKUP, 14488c2ecf20Sopenharmony_ci SJA1105_SEARCH, &tmp); 14498c2ecf20Sopenharmony_ci if (rc < 0) { 14508c2ecf20Sopenharmony_ci dev_err(ds->dev, 14518c2ecf20Sopenharmony_ci "port %d failed to read back entry for %pM vid %d: %pe\n", 14528c2ecf20Sopenharmony_ci port, addr, vid, ERR_PTR(rc)); 14538c2ecf20Sopenharmony_ci return rc; 14548c2ecf20Sopenharmony_ci } 14558c2ecf20Sopenharmony_ci 14568c2ecf20Sopenharmony_ci if (tmp.index < l2_lookup.index) { 14578c2ecf20Sopenharmony_ci rc = sja1105_dynamic_config_write(priv, BLK_IDX_L2_LOOKUP, 14588c2ecf20Sopenharmony_ci tmp.index, NULL, false); 14598c2ecf20Sopenharmony_ci if (rc < 0) 14608c2ecf20Sopenharmony_ci return rc; 14618c2ecf20Sopenharmony_ci } 14628c2ecf20Sopenharmony_ci 14638c2ecf20Sopenharmony_ci return sja1105_static_fdb_change(priv, port, &l2_lookup, true); 14648c2ecf20Sopenharmony_ci} 14658c2ecf20Sopenharmony_ci 14668c2ecf20Sopenharmony_ciint sja1105pqrs_fdb_del(struct dsa_switch *ds, int port, 14678c2ecf20Sopenharmony_ci const unsigned char *addr, u16 vid) 14688c2ecf20Sopenharmony_ci{ 14698c2ecf20Sopenharmony_ci struct sja1105_l2_lookup_entry l2_lookup = {0}; 14708c2ecf20Sopenharmony_ci struct sja1105_private *priv = ds->priv; 14718c2ecf20Sopenharmony_ci bool keep; 14728c2ecf20Sopenharmony_ci int rc; 14738c2ecf20Sopenharmony_ci 14748c2ecf20Sopenharmony_ci l2_lookup.macaddr = ether_addr_to_u64(addr); 14758c2ecf20Sopenharmony_ci l2_lookup.vlanid = vid; 14768c2ecf20Sopenharmony_ci l2_lookup.mask_macaddr = GENMASK_ULL(ETH_ALEN * 8 - 1, 0); 14778c2ecf20Sopenharmony_ci l2_lookup.mask_vlanid = VLAN_VID_MASK; 14788c2ecf20Sopenharmony_ci l2_lookup.destports = BIT(port); 14798c2ecf20Sopenharmony_ci 14808c2ecf20Sopenharmony_ci rc = sja1105_dynamic_config_read(priv, BLK_IDX_L2_LOOKUP, 14818c2ecf20Sopenharmony_ci SJA1105_SEARCH, &l2_lookup); 14828c2ecf20Sopenharmony_ci if (rc < 0) 14838c2ecf20Sopenharmony_ci return 0; 14848c2ecf20Sopenharmony_ci 14858c2ecf20Sopenharmony_ci l2_lookup.destports &= ~BIT(port); 14868c2ecf20Sopenharmony_ci 14878c2ecf20Sopenharmony_ci /* Decide whether we remove just this port from the FDB entry, 14888c2ecf20Sopenharmony_ci * or if we remove it completely. 14898c2ecf20Sopenharmony_ci */ 14908c2ecf20Sopenharmony_ci if (l2_lookup.destports) 14918c2ecf20Sopenharmony_ci keep = true; 14928c2ecf20Sopenharmony_ci else 14938c2ecf20Sopenharmony_ci keep = false; 14948c2ecf20Sopenharmony_ci 14958c2ecf20Sopenharmony_ci rc = sja1105_dynamic_config_write(priv, BLK_IDX_L2_LOOKUP, 14968c2ecf20Sopenharmony_ci l2_lookup.index, &l2_lookup, keep); 14978c2ecf20Sopenharmony_ci if (rc < 0) 14988c2ecf20Sopenharmony_ci return rc; 14998c2ecf20Sopenharmony_ci 15008c2ecf20Sopenharmony_ci return sja1105_static_fdb_change(priv, port, &l2_lookup, keep); 15018c2ecf20Sopenharmony_ci} 15028c2ecf20Sopenharmony_ci 15038c2ecf20Sopenharmony_cistatic int sja1105_fdb_add(struct dsa_switch *ds, int port, 15048c2ecf20Sopenharmony_ci const unsigned char *addr, u16 vid) 15058c2ecf20Sopenharmony_ci{ 15068c2ecf20Sopenharmony_ci struct sja1105_private *priv = ds->priv; 15078c2ecf20Sopenharmony_ci 15088c2ecf20Sopenharmony_ci /* dsa_8021q is in effect when the bridge's vlan_filtering isn't, 15098c2ecf20Sopenharmony_ci * so the switch still does some VLAN processing internally. 15108c2ecf20Sopenharmony_ci * But Shared VLAN Learning (SVL) is also active, and it will take 15118c2ecf20Sopenharmony_ci * care of autonomous forwarding between the unique pvid's of each 15128c2ecf20Sopenharmony_ci * port. Here we just make sure that users can't add duplicate FDB 15138c2ecf20Sopenharmony_ci * entries when in this mode - the actual VID doesn't matter except 15148c2ecf20Sopenharmony_ci * for what gets printed in 'bridge fdb show'. In the case of zero, 15158c2ecf20Sopenharmony_ci * no VID gets printed at all. 15168c2ecf20Sopenharmony_ci */ 15178c2ecf20Sopenharmony_ci if (priv->vlan_state != SJA1105_VLAN_FILTERING_FULL) 15188c2ecf20Sopenharmony_ci vid = 0; 15198c2ecf20Sopenharmony_ci 15208c2ecf20Sopenharmony_ci return priv->info->fdb_add_cmd(ds, port, addr, vid); 15218c2ecf20Sopenharmony_ci} 15228c2ecf20Sopenharmony_ci 15238c2ecf20Sopenharmony_cistatic int sja1105_fdb_del(struct dsa_switch *ds, int port, 15248c2ecf20Sopenharmony_ci const unsigned char *addr, u16 vid) 15258c2ecf20Sopenharmony_ci{ 15268c2ecf20Sopenharmony_ci struct sja1105_private *priv = ds->priv; 15278c2ecf20Sopenharmony_ci 15288c2ecf20Sopenharmony_ci if (priv->vlan_state != SJA1105_VLAN_FILTERING_FULL) 15298c2ecf20Sopenharmony_ci vid = 0; 15308c2ecf20Sopenharmony_ci 15318c2ecf20Sopenharmony_ci return priv->info->fdb_del_cmd(ds, port, addr, vid); 15328c2ecf20Sopenharmony_ci} 15338c2ecf20Sopenharmony_ci 15348c2ecf20Sopenharmony_cistatic int sja1105_fdb_dump(struct dsa_switch *ds, int port, 15358c2ecf20Sopenharmony_ci dsa_fdb_dump_cb_t *cb, void *data) 15368c2ecf20Sopenharmony_ci{ 15378c2ecf20Sopenharmony_ci struct sja1105_private *priv = ds->priv; 15388c2ecf20Sopenharmony_ci struct device *dev = ds->dev; 15398c2ecf20Sopenharmony_ci int i; 15408c2ecf20Sopenharmony_ci 15418c2ecf20Sopenharmony_ci for (i = 0; i < SJA1105_MAX_L2_LOOKUP_COUNT; i++) { 15428c2ecf20Sopenharmony_ci struct sja1105_l2_lookup_entry l2_lookup = {0}; 15438c2ecf20Sopenharmony_ci u8 macaddr[ETH_ALEN]; 15448c2ecf20Sopenharmony_ci int rc; 15458c2ecf20Sopenharmony_ci 15468c2ecf20Sopenharmony_ci rc = sja1105_dynamic_config_read(priv, BLK_IDX_L2_LOOKUP, 15478c2ecf20Sopenharmony_ci i, &l2_lookup); 15488c2ecf20Sopenharmony_ci /* No fdb entry at i, not an issue */ 15498c2ecf20Sopenharmony_ci if (rc == -ENOENT) 15508c2ecf20Sopenharmony_ci continue; 15518c2ecf20Sopenharmony_ci if (rc) { 15528c2ecf20Sopenharmony_ci dev_err(dev, "Failed to dump FDB: %d\n", rc); 15538c2ecf20Sopenharmony_ci return rc; 15548c2ecf20Sopenharmony_ci } 15558c2ecf20Sopenharmony_ci 15568c2ecf20Sopenharmony_ci /* FDB dump callback is per port. This means we have to 15578c2ecf20Sopenharmony_ci * disregard a valid entry if it's not for this port, even if 15588c2ecf20Sopenharmony_ci * only to revisit it later. This is inefficient because the 15598c2ecf20Sopenharmony_ci * 1024-sized FDB table needs to be traversed 4 times through 15608c2ecf20Sopenharmony_ci * SPI during a 'bridge fdb show' command. 15618c2ecf20Sopenharmony_ci */ 15628c2ecf20Sopenharmony_ci if (!(l2_lookup.destports & BIT(port))) 15638c2ecf20Sopenharmony_ci continue; 15648c2ecf20Sopenharmony_ci u64_to_ether_addr(l2_lookup.macaddr, macaddr); 15658c2ecf20Sopenharmony_ci 15668c2ecf20Sopenharmony_ci /* We need to hide the dsa_8021q VLANs from the user. */ 15678c2ecf20Sopenharmony_ci if (priv->vlan_state == SJA1105_VLAN_UNAWARE) 15688c2ecf20Sopenharmony_ci l2_lookup.vlanid = 0; 15698c2ecf20Sopenharmony_ci rc = cb(macaddr, l2_lookup.vlanid, l2_lookup.lockeds, data); 15708c2ecf20Sopenharmony_ci if (rc) 15718c2ecf20Sopenharmony_ci return rc; 15728c2ecf20Sopenharmony_ci } 15738c2ecf20Sopenharmony_ci return 0; 15748c2ecf20Sopenharmony_ci} 15758c2ecf20Sopenharmony_ci 15768c2ecf20Sopenharmony_ci/* This callback needs to be present */ 15778c2ecf20Sopenharmony_cistatic int sja1105_mdb_prepare(struct dsa_switch *ds, int port, 15788c2ecf20Sopenharmony_ci const struct switchdev_obj_port_mdb *mdb) 15798c2ecf20Sopenharmony_ci{ 15808c2ecf20Sopenharmony_ci return 0; 15818c2ecf20Sopenharmony_ci} 15828c2ecf20Sopenharmony_ci 15838c2ecf20Sopenharmony_cistatic void sja1105_mdb_add(struct dsa_switch *ds, int port, 15848c2ecf20Sopenharmony_ci const struct switchdev_obj_port_mdb *mdb) 15858c2ecf20Sopenharmony_ci{ 15868c2ecf20Sopenharmony_ci sja1105_fdb_add(ds, port, mdb->addr, mdb->vid); 15878c2ecf20Sopenharmony_ci} 15888c2ecf20Sopenharmony_ci 15898c2ecf20Sopenharmony_cistatic int sja1105_mdb_del(struct dsa_switch *ds, int port, 15908c2ecf20Sopenharmony_ci const struct switchdev_obj_port_mdb *mdb) 15918c2ecf20Sopenharmony_ci{ 15928c2ecf20Sopenharmony_ci return sja1105_fdb_del(ds, port, mdb->addr, mdb->vid); 15938c2ecf20Sopenharmony_ci} 15948c2ecf20Sopenharmony_ci 15958c2ecf20Sopenharmony_cistatic int sja1105_bridge_member(struct dsa_switch *ds, int port, 15968c2ecf20Sopenharmony_ci struct net_device *br, bool member) 15978c2ecf20Sopenharmony_ci{ 15988c2ecf20Sopenharmony_ci struct sja1105_l2_forwarding_entry *l2_fwd; 15998c2ecf20Sopenharmony_ci struct sja1105_private *priv = ds->priv; 16008c2ecf20Sopenharmony_ci int i, rc; 16018c2ecf20Sopenharmony_ci 16028c2ecf20Sopenharmony_ci l2_fwd = priv->static_config.tables[BLK_IDX_L2_FORWARDING].entries; 16038c2ecf20Sopenharmony_ci 16048c2ecf20Sopenharmony_ci for (i = 0; i < SJA1105_NUM_PORTS; i++) { 16058c2ecf20Sopenharmony_ci /* Add this port to the forwarding matrix of the 16068c2ecf20Sopenharmony_ci * other ports in the same bridge, and viceversa. 16078c2ecf20Sopenharmony_ci */ 16088c2ecf20Sopenharmony_ci if (!dsa_is_user_port(ds, i)) 16098c2ecf20Sopenharmony_ci continue; 16108c2ecf20Sopenharmony_ci /* For the ports already under the bridge, only one thing needs 16118c2ecf20Sopenharmony_ci * to be done, and that is to add this port to their 16128c2ecf20Sopenharmony_ci * reachability domain. So we can perform the SPI write for 16138c2ecf20Sopenharmony_ci * them immediately. However, for this port itself (the one 16148c2ecf20Sopenharmony_ci * that is new to the bridge), we need to add all other ports 16158c2ecf20Sopenharmony_ci * to its reachability domain. So we do that incrementally in 16168c2ecf20Sopenharmony_ci * this loop, and perform the SPI write only at the end, once 16178c2ecf20Sopenharmony_ci * the domain contains all other bridge ports. 16188c2ecf20Sopenharmony_ci */ 16198c2ecf20Sopenharmony_ci if (i == port) 16208c2ecf20Sopenharmony_ci continue; 16218c2ecf20Sopenharmony_ci if (dsa_to_port(ds, i)->bridge_dev != br) 16228c2ecf20Sopenharmony_ci continue; 16238c2ecf20Sopenharmony_ci sja1105_port_allow_traffic(l2_fwd, i, port, member); 16248c2ecf20Sopenharmony_ci sja1105_port_allow_traffic(l2_fwd, port, i, member); 16258c2ecf20Sopenharmony_ci 16268c2ecf20Sopenharmony_ci rc = sja1105_dynamic_config_write(priv, BLK_IDX_L2_FORWARDING, 16278c2ecf20Sopenharmony_ci i, &l2_fwd[i], true); 16288c2ecf20Sopenharmony_ci if (rc < 0) 16298c2ecf20Sopenharmony_ci return rc; 16308c2ecf20Sopenharmony_ci } 16318c2ecf20Sopenharmony_ci 16328c2ecf20Sopenharmony_ci return sja1105_dynamic_config_write(priv, BLK_IDX_L2_FORWARDING, 16338c2ecf20Sopenharmony_ci port, &l2_fwd[port], true); 16348c2ecf20Sopenharmony_ci} 16358c2ecf20Sopenharmony_ci 16368c2ecf20Sopenharmony_cistatic void sja1105_bridge_stp_state_set(struct dsa_switch *ds, int port, 16378c2ecf20Sopenharmony_ci u8 state) 16388c2ecf20Sopenharmony_ci{ 16398c2ecf20Sopenharmony_ci struct sja1105_private *priv = ds->priv; 16408c2ecf20Sopenharmony_ci struct sja1105_mac_config_entry *mac; 16418c2ecf20Sopenharmony_ci 16428c2ecf20Sopenharmony_ci mac = priv->static_config.tables[BLK_IDX_MAC_CONFIG].entries; 16438c2ecf20Sopenharmony_ci 16448c2ecf20Sopenharmony_ci switch (state) { 16458c2ecf20Sopenharmony_ci case BR_STATE_DISABLED: 16468c2ecf20Sopenharmony_ci case BR_STATE_BLOCKING: 16478c2ecf20Sopenharmony_ci /* From UM10944 description of DRPDTAG (why put this there?): 16488c2ecf20Sopenharmony_ci * "Management traffic flows to the port regardless of the state 16498c2ecf20Sopenharmony_ci * of the INGRESS flag". So BPDUs are still be allowed to pass. 16508c2ecf20Sopenharmony_ci * At the moment no difference between DISABLED and BLOCKING. 16518c2ecf20Sopenharmony_ci */ 16528c2ecf20Sopenharmony_ci mac[port].ingress = false; 16538c2ecf20Sopenharmony_ci mac[port].egress = false; 16548c2ecf20Sopenharmony_ci mac[port].dyn_learn = false; 16558c2ecf20Sopenharmony_ci break; 16568c2ecf20Sopenharmony_ci case BR_STATE_LISTENING: 16578c2ecf20Sopenharmony_ci mac[port].ingress = true; 16588c2ecf20Sopenharmony_ci mac[port].egress = false; 16598c2ecf20Sopenharmony_ci mac[port].dyn_learn = false; 16608c2ecf20Sopenharmony_ci break; 16618c2ecf20Sopenharmony_ci case BR_STATE_LEARNING: 16628c2ecf20Sopenharmony_ci mac[port].ingress = true; 16638c2ecf20Sopenharmony_ci mac[port].egress = false; 16648c2ecf20Sopenharmony_ci mac[port].dyn_learn = true; 16658c2ecf20Sopenharmony_ci break; 16668c2ecf20Sopenharmony_ci case BR_STATE_FORWARDING: 16678c2ecf20Sopenharmony_ci mac[port].ingress = true; 16688c2ecf20Sopenharmony_ci mac[port].egress = true; 16698c2ecf20Sopenharmony_ci mac[port].dyn_learn = true; 16708c2ecf20Sopenharmony_ci break; 16718c2ecf20Sopenharmony_ci default: 16728c2ecf20Sopenharmony_ci dev_err(ds->dev, "invalid STP state: %d\n", state); 16738c2ecf20Sopenharmony_ci return; 16748c2ecf20Sopenharmony_ci } 16758c2ecf20Sopenharmony_ci 16768c2ecf20Sopenharmony_ci sja1105_dynamic_config_write(priv, BLK_IDX_MAC_CONFIG, port, 16778c2ecf20Sopenharmony_ci &mac[port], true); 16788c2ecf20Sopenharmony_ci} 16798c2ecf20Sopenharmony_ci 16808c2ecf20Sopenharmony_cistatic int sja1105_bridge_join(struct dsa_switch *ds, int port, 16818c2ecf20Sopenharmony_ci struct net_device *br) 16828c2ecf20Sopenharmony_ci{ 16838c2ecf20Sopenharmony_ci return sja1105_bridge_member(ds, port, br, true); 16848c2ecf20Sopenharmony_ci} 16858c2ecf20Sopenharmony_ci 16868c2ecf20Sopenharmony_cistatic void sja1105_bridge_leave(struct dsa_switch *ds, int port, 16878c2ecf20Sopenharmony_ci struct net_device *br) 16888c2ecf20Sopenharmony_ci{ 16898c2ecf20Sopenharmony_ci sja1105_bridge_member(ds, port, br, false); 16908c2ecf20Sopenharmony_ci} 16918c2ecf20Sopenharmony_ci 16928c2ecf20Sopenharmony_ci#define BYTES_PER_KBIT (1000LL / 8) 16938c2ecf20Sopenharmony_ci 16948c2ecf20Sopenharmony_cistatic int sja1105_find_cbs_shaper(struct sja1105_private *priv, 16958c2ecf20Sopenharmony_ci int port, int prio) 16968c2ecf20Sopenharmony_ci{ 16978c2ecf20Sopenharmony_ci int i; 16988c2ecf20Sopenharmony_ci 16998c2ecf20Sopenharmony_ci for (i = 0; i < priv->info->num_cbs_shapers; i++) 17008c2ecf20Sopenharmony_ci if (priv->cbs[i].port == port && priv->cbs[i].prio == prio) 17018c2ecf20Sopenharmony_ci return i; 17028c2ecf20Sopenharmony_ci 17038c2ecf20Sopenharmony_ci return -1; 17048c2ecf20Sopenharmony_ci} 17058c2ecf20Sopenharmony_ci 17068c2ecf20Sopenharmony_cistatic int sja1105_find_unused_cbs_shaper(struct sja1105_private *priv) 17078c2ecf20Sopenharmony_ci{ 17088c2ecf20Sopenharmony_ci int i; 17098c2ecf20Sopenharmony_ci 17108c2ecf20Sopenharmony_ci for (i = 0; i < priv->info->num_cbs_shapers; i++) 17118c2ecf20Sopenharmony_ci if (!priv->cbs[i].idle_slope && !priv->cbs[i].send_slope) 17128c2ecf20Sopenharmony_ci return i; 17138c2ecf20Sopenharmony_ci 17148c2ecf20Sopenharmony_ci return -1; 17158c2ecf20Sopenharmony_ci} 17168c2ecf20Sopenharmony_ci 17178c2ecf20Sopenharmony_cistatic int sja1105_delete_cbs_shaper(struct sja1105_private *priv, int port, 17188c2ecf20Sopenharmony_ci int prio) 17198c2ecf20Sopenharmony_ci{ 17208c2ecf20Sopenharmony_ci int i; 17218c2ecf20Sopenharmony_ci 17228c2ecf20Sopenharmony_ci for (i = 0; i < priv->info->num_cbs_shapers; i++) { 17238c2ecf20Sopenharmony_ci struct sja1105_cbs_entry *cbs = &priv->cbs[i]; 17248c2ecf20Sopenharmony_ci 17258c2ecf20Sopenharmony_ci if (cbs->port == port && cbs->prio == prio) { 17268c2ecf20Sopenharmony_ci memset(cbs, 0, sizeof(*cbs)); 17278c2ecf20Sopenharmony_ci return sja1105_dynamic_config_write(priv, BLK_IDX_CBS, 17288c2ecf20Sopenharmony_ci i, cbs, true); 17298c2ecf20Sopenharmony_ci } 17308c2ecf20Sopenharmony_ci } 17318c2ecf20Sopenharmony_ci 17328c2ecf20Sopenharmony_ci return 0; 17338c2ecf20Sopenharmony_ci} 17348c2ecf20Sopenharmony_ci 17358c2ecf20Sopenharmony_cistatic int sja1105_setup_tc_cbs(struct dsa_switch *ds, int port, 17368c2ecf20Sopenharmony_ci struct tc_cbs_qopt_offload *offload) 17378c2ecf20Sopenharmony_ci{ 17388c2ecf20Sopenharmony_ci struct sja1105_private *priv = ds->priv; 17398c2ecf20Sopenharmony_ci struct sja1105_cbs_entry *cbs; 17408c2ecf20Sopenharmony_ci s64 port_transmit_rate_kbps; 17418c2ecf20Sopenharmony_ci int index; 17428c2ecf20Sopenharmony_ci 17438c2ecf20Sopenharmony_ci if (!offload->enable) 17448c2ecf20Sopenharmony_ci return sja1105_delete_cbs_shaper(priv, port, offload->queue); 17458c2ecf20Sopenharmony_ci 17468c2ecf20Sopenharmony_ci /* The user may be replacing an existing shaper */ 17478c2ecf20Sopenharmony_ci index = sja1105_find_cbs_shaper(priv, port, offload->queue); 17488c2ecf20Sopenharmony_ci if (index < 0) { 17498c2ecf20Sopenharmony_ci /* That isn't the case - see if we can allocate a new one */ 17508c2ecf20Sopenharmony_ci index = sja1105_find_unused_cbs_shaper(priv); 17518c2ecf20Sopenharmony_ci if (index < 0) 17528c2ecf20Sopenharmony_ci return -ENOSPC; 17538c2ecf20Sopenharmony_ci } 17548c2ecf20Sopenharmony_ci 17558c2ecf20Sopenharmony_ci cbs = &priv->cbs[index]; 17568c2ecf20Sopenharmony_ci cbs->port = port; 17578c2ecf20Sopenharmony_ci cbs->prio = offload->queue; 17588c2ecf20Sopenharmony_ci /* locredit and sendslope are negative by definition. In hardware, 17598c2ecf20Sopenharmony_ci * positive values must be provided, and the negative sign is implicit. 17608c2ecf20Sopenharmony_ci */ 17618c2ecf20Sopenharmony_ci cbs->credit_hi = offload->hicredit; 17628c2ecf20Sopenharmony_ci cbs->credit_lo = abs(offload->locredit); 17638c2ecf20Sopenharmony_ci /* User space is in kbits/sec, while the hardware in bytes/sec times 17648c2ecf20Sopenharmony_ci * link speed. Since the given offload->sendslope is good only for the 17658c2ecf20Sopenharmony_ci * current link speed anyway, and user space is likely to reprogram it 17668c2ecf20Sopenharmony_ci * when that changes, don't even bother to track the port's link speed, 17678c2ecf20Sopenharmony_ci * but deduce the port transmit rate from idleslope - sendslope. 17688c2ecf20Sopenharmony_ci */ 17698c2ecf20Sopenharmony_ci port_transmit_rate_kbps = offload->idleslope - offload->sendslope; 17708c2ecf20Sopenharmony_ci cbs->idle_slope = div_s64(offload->idleslope * BYTES_PER_KBIT, 17718c2ecf20Sopenharmony_ci port_transmit_rate_kbps); 17728c2ecf20Sopenharmony_ci cbs->send_slope = div_s64(abs(offload->sendslope * BYTES_PER_KBIT), 17738c2ecf20Sopenharmony_ci port_transmit_rate_kbps); 17748c2ecf20Sopenharmony_ci /* Convert the negative values from 64-bit 2's complement 17758c2ecf20Sopenharmony_ci * to 32-bit 2's complement (for the case of 0x80000000 whose 17768c2ecf20Sopenharmony_ci * negative is still negative). 17778c2ecf20Sopenharmony_ci */ 17788c2ecf20Sopenharmony_ci cbs->credit_lo &= GENMASK_ULL(31, 0); 17798c2ecf20Sopenharmony_ci cbs->send_slope &= GENMASK_ULL(31, 0); 17808c2ecf20Sopenharmony_ci 17818c2ecf20Sopenharmony_ci return sja1105_dynamic_config_write(priv, BLK_IDX_CBS, index, cbs, 17828c2ecf20Sopenharmony_ci true); 17838c2ecf20Sopenharmony_ci} 17848c2ecf20Sopenharmony_ci 17858c2ecf20Sopenharmony_cistatic int sja1105_reload_cbs(struct sja1105_private *priv) 17868c2ecf20Sopenharmony_ci{ 17878c2ecf20Sopenharmony_ci int rc = 0, i; 17888c2ecf20Sopenharmony_ci 17898c2ecf20Sopenharmony_ci /* The credit based shapers are only allocated if 17908c2ecf20Sopenharmony_ci * CONFIG_NET_SCH_CBS is enabled. 17918c2ecf20Sopenharmony_ci */ 17928c2ecf20Sopenharmony_ci if (!priv->cbs) 17938c2ecf20Sopenharmony_ci return 0; 17948c2ecf20Sopenharmony_ci 17958c2ecf20Sopenharmony_ci for (i = 0; i < priv->info->num_cbs_shapers; i++) { 17968c2ecf20Sopenharmony_ci struct sja1105_cbs_entry *cbs = &priv->cbs[i]; 17978c2ecf20Sopenharmony_ci 17988c2ecf20Sopenharmony_ci if (!cbs->idle_slope && !cbs->send_slope) 17998c2ecf20Sopenharmony_ci continue; 18008c2ecf20Sopenharmony_ci 18018c2ecf20Sopenharmony_ci rc = sja1105_dynamic_config_write(priv, BLK_IDX_CBS, i, cbs, 18028c2ecf20Sopenharmony_ci true); 18038c2ecf20Sopenharmony_ci if (rc) 18048c2ecf20Sopenharmony_ci break; 18058c2ecf20Sopenharmony_ci } 18068c2ecf20Sopenharmony_ci 18078c2ecf20Sopenharmony_ci return rc; 18088c2ecf20Sopenharmony_ci} 18098c2ecf20Sopenharmony_ci 18108c2ecf20Sopenharmony_cistatic const char * const sja1105_reset_reasons[] = { 18118c2ecf20Sopenharmony_ci [SJA1105_VLAN_FILTERING] = "VLAN filtering", 18128c2ecf20Sopenharmony_ci [SJA1105_RX_HWTSTAMPING] = "RX timestamping", 18138c2ecf20Sopenharmony_ci [SJA1105_AGEING_TIME] = "Ageing time", 18148c2ecf20Sopenharmony_ci [SJA1105_SCHEDULING] = "Time-aware scheduling", 18158c2ecf20Sopenharmony_ci [SJA1105_BEST_EFFORT_POLICING] = "Best-effort policing", 18168c2ecf20Sopenharmony_ci [SJA1105_VIRTUAL_LINKS] = "Virtual links", 18178c2ecf20Sopenharmony_ci}; 18188c2ecf20Sopenharmony_ci 18198c2ecf20Sopenharmony_ci/* For situations where we need to change a setting at runtime that is only 18208c2ecf20Sopenharmony_ci * available through the static configuration, resetting the switch in order 18218c2ecf20Sopenharmony_ci * to upload the new static config is unavoidable. Back up the settings we 18228c2ecf20Sopenharmony_ci * modify at runtime (currently only MAC) and restore them after uploading, 18238c2ecf20Sopenharmony_ci * such that this operation is relatively seamless. 18248c2ecf20Sopenharmony_ci */ 18258c2ecf20Sopenharmony_ciint sja1105_static_config_reload(struct sja1105_private *priv, 18268c2ecf20Sopenharmony_ci enum sja1105_reset_reason reason) 18278c2ecf20Sopenharmony_ci{ 18288c2ecf20Sopenharmony_ci struct ptp_system_timestamp ptp_sts_before; 18298c2ecf20Sopenharmony_ci struct ptp_system_timestamp ptp_sts_after; 18308c2ecf20Sopenharmony_ci struct sja1105_mac_config_entry *mac; 18318c2ecf20Sopenharmony_ci int speed_mbps[SJA1105_NUM_PORTS]; 18328c2ecf20Sopenharmony_ci struct dsa_switch *ds = priv->ds; 18338c2ecf20Sopenharmony_ci s64 t1, t2, t3, t4; 18348c2ecf20Sopenharmony_ci s64 t12, t34; 18358c2ecf20Sopenharmony_ci u16 bmcr = 0; 18368c2ecf20Sopenharmony_ci int rc, i; 18378c2ecf20Sopenharmony_ci s64 now; 18388c2ecf20Sopenharmony_ci 18398c2ecf20Sopenharmony_ci mutex_lock(&priv->mgmt_lock); 18408c2ecf20Sopenharmony_ci 18418c2ecf20Sopenharmony_ci mac = priv->static_config.tables[BLK_IDX_MAC_CONFIG].entries; 18428c2ecf20Sopenharmony_ci 18438c2ecf20Sopenharmony_ci /* Back up the dynamic link speed changed by sja1105_adjust_port_config 18448c2ecf20Sopenharmony_ci * in order to temporarily restore it to SJA1105_SPEED_AUTO - which the 18458c2ecf20Sopenharmony_ci * switch wants to see in the static config in order to allow us to 18468c2ecf20Sopenharmony_ci * change it through the dynamic interface later. 18478c2ecf20Sopenharmony_ci */ 18488c2ecf20Sopenharmony_ci for (i = 0; i < SJA1105_NUM_PORTS; i++) { 18498c2ecf20Sopenharmony_ci speed_mbps[i] = sja1105_speed[mac[i].speed]; 18508c2ecf20Sopenharmony_ci mac[i].speed = SJA1105_SPEED_AUTO; 18518c2ecf20Sopenharmony_ci } 18528c2ecf20Sopenharmony_ci 18538c2ecf20Sopenharmony_ci if (sja1105_supports_sgmii(priv, SJA1105_SGMII_PORT)) 18548c2ecf20Sopenharmony_ci bmcr = sja1105_sgmii_read(priv, MII_BMCR); 18558c2ecf20Sopenharmony_ci 18568c2ecf20Sopenharmony_ci /* No PTP operations can run right now */ 18578c2ecf20Sopenharmony_ci mutex_lock(&priv->ptp_data.lock); 18588c2ecf20Sopenharmony_ci 18598c2ecf20Sopenharmony_ci rc = __sja1105_ptp_gettimex(ds, &now, &ptp_sts_before); 18608c2ecf20Sopenharmony_ci if (rc < 0) 18618c2ecf20Sopenharmony_ci goto out_unlock_ptp; 18628c2ecf20Sopenharmony_ci 18638c2ecf20Sopenharmony_ci /* Reset switch and send updated static configuration */ 18648c2ecf20Sopenharmony_ci rc = sja1105_static_config_upload(priv); 18658c2ecf20Sopenharmony_ci if (rc < 0) 18668c2ecf20Sopenharmony_ci goto out_unlock_ptp; 18678c2ecf20Sopenharmony_ci 18688c2ecf20Sopenharmony_ci rc = __sja1105_ptp_settime(ds, 0, &ptp_sts_after); 18698c2ecf20Sopenharmony_ci if (rc < 0) 18708c2ecf20Sopenharmony_ci goto out_unlock_ptp; 18718c2ecf20Sopenharmony_ci 18728c2ecf20Sopenharmony_ci t1 = timespec64_to_ns(&ptp_sts_before.pre_ts); 18738c2ecf20Sopenharmony_ci t2 = timespec64_to_ns(&ptp_sts_before.post_ts); 18748c2ecf20Sopenharmony_ci t3 = timespec64_to_ns(&ptp_sts_after.pre_ts); 18758c2ecf20Sopenharmony_ci t4 = timespec64_to_ns(&ptp_sts_after.post_ts); 18768c2ecf20Sopenharmony_ci /* Mid point, corresponds to pre-reset PTPCLKVAL */ 18778c2ecf20Sopenharmony_ci t12 = t1 + (t2 - t1) / 2; 18788c2ecf20Sopenharmony_ci /* Mid point, corresponds to post-reset PTPCLKVAL, aka 0 */ 18798c2ecf20Sopenharmony_ci t34 = t3 + (t4 - t3) / 2; 18808c2ecf20Sopenharmony_ci /* Advance PTPCLKVAL by the time it took since its readout */ 18818c2ecf20Sopenharmony_ci now += (t34 - t12); 18828c2ecf20Sopenharmony_ci 18838c2ecf20Sopenharmony_ci __sja1105_ptp_adjtime(ds, now); 18848c2ecf20Sopenharmony_ci 18858c2ecf20Sopenharmony_ciout_unlock_ptp: 18868c2ecf20Sopenharmony_ci mutex_unlock(&priv->ptp_data.lock); 18878c2ecf20Sopenharmony_ci 18888c2ecf20Sopenharmony_ci dev_info(priv->ds->dev, 18898c2ecf20Sopenharmony_ci "Reset switch and programmed static config. Reason: %s\n", 18908c2ecf20Sopenharmony_ci sja1105_reset_reasons[reason]); 18918c2ecf20Sopenharmony_ci 18928c2ecf20Sopenharmony_ci /* Configure the CGU (PLLs) for MII and RMII PHYs. 18938c2ecf20Sopenharmony_ci * For these interfaces there is no dynamic configuration 18948c2ecf20Sopenharmony_ci * needed, since PLLs have same settings at all speeds. 18958c2ecf20Sopenharmony_ci */ 18968c2ecf20Sopenharmony_ci rc = sja1105_clocking_setup(priv); 18978c2ecf20Sopenharmony_ci if (rc < 0) 18988c2ecf20Sopenharmony_ci goto out; 18998c2ecf20Sopenharmony_ci 19008c2ecf20Sopenharmony_ci for (i = 0; i < SJA1105_NUM_PORTS; i++) { 19018c2ecf20Sopenharmony_ci rc = sja1105_adjust_port_config(priv, i, speed_mbps[i]); 19028c2ecf20Sopenharmony_ci if (rc < 0) 19038c2ecf20Sopenharmony_ci goto out; 19048c2ecf20Sopenharmony_ci } 19058c2ecf20Sopenharmony_ci 19068c2ecf20Sopenharmony_ci if (sja1105_supports_sgmii(priv, SJA1105_SGMII_PORT)) { 19078c2ecf20Sopenharmony_ci bool an_enabled = !!(bmcr & BMCR_ANENABLE); 19088c2ecf20Sopenharmony_ci 19098c2ecf20Sopenharmony_ci sja1105_sgmii_pcs_config(priv, an_enabled, false); 19108c2ecf20Sopenharmony_ci 19118c2ecf20Sopenharmony_ci if (!an_enabled) { 19128c2ecf20Sopenharmony_ci int speed = SPEED_UNKNOWN; 19138c2ecf20Sopenharmony_ci 19148c2ecf20Sopenharmony_ci if (bmcr & BMCR_SPEED1000) 19158c2ecf20Sopenharmony_ci speed = SPEED_1000; 19168c2ecf20Sopenharmony_ci else if (bmcr & BMCR_SPEED100) 19178c2ecf20Sopenharmony_ci speed = SPEED_100; 19188c2ecf20Sopenharmony_ci else 19198c2ecf20Sopenharmony_ci speed = SPEED_10; 19208c2ecf20Sopenharmony_ci 19218c2ecf20Sopenharmony_ci sja1105_sgmii_pcs_force_speed(priv, speed); 19228c2ecf20Sopenharmony_ci } 19238c2ecf20Sopenharmony_ci } 19248c2ecf20Sopenharmony_ci 19258c2ecf20Sopenharmony_ci rc = sja1105_reload_cbs(priv); 19268c2ecf20Sopenharmony_ci if (rc < 0) 19278c2ecf20Sopenharmony_ci goto out; 19288c2ecf20Sopenharmony_ciout: 19298c2ecf20Sopenharmony_ci mutex_unlock(&priv->mgmt_lock); 19308c2ecf20Sopenharmony_ci 19318c2ecf20Sopenharmony_ci return rc; 19328c2ecf20Sopenharmony_ci} 19338c2ecf20Sopenharmony_ci 19348c2ecf20Sopenharmony_cistatic int sja1105_pvid_apply(struct sja1105_private *priv, int port, u16 pvid) 19358c2ecf20Sopenharmony_ci{ 19368c2ecf20Sopenharmony_ci struct sja1105_mac_config_entry *mac; 19378c2ecf20Sopenharmony_ci 19388c2ecf20Sopenharmony_ci mac = priv->static_config.tables[BLK_IDX_MAC_CONFIG].entries; 19398c2ecf20Sopenharmony_ci 19408c2ecf20Sopenharmony_ci mac[port].vlanid = pvid; 19418c2ecf20Sopenharmony_ci 19428c2ecf20Sopenharmony_ci return sja1105_dynamic_config_write(priv, BLK_IDX_MAC_CONFIG, port, 19438c2ecf20Sopenharmony_ci &mac[port], true); 19448c2ecf20Sopenharmony_ci} 19458c2ecf20Sopenharmony_ci 19468c2ecf20Sopenharmony_cistatic int sja1105_crosschip_bridge_join(struct dsa_switch *ds, 19478c2ecf20Sopenharmony_ci int tree_index, int sw_index, 19488c2ecf20Sopenharmony_ci int other_port, struct net_device *br) 19498c2ecf20Sopenharmony_ci{ 19508c2ecf20Sopenharmony_ci struct dsa_switch *other_ds = dsa_switch_find(tree_index, sw_index); 19518c2ecf20Sopenharmony_ci struct sja1105_private *other_priv = other_ds->priv; 19528c2ecf20Sopenharmony_ci struct sja1105_private *priv = ds->priv; 19538c2ecf20Sopenharmony_ci int port, rc; 19548c2ecf20Sopenharmony_ci 19558c2ecf20Sopenharmony_ci if (other_ds->ops != &sja1105_switch_ops) 19568c2ecf20Sopenharmony_ci return 0; 19578c2ecf20Sopenharmony_ci 19588c2ecf20Sopenharmony_ci for (port = 0; port < ds->num_ports; port++) { 19598c2ecf20Sopenharmony_ci if (!dsa_is_user_port(ds, port)) 19608c2ecf20Sopenharmony_ci continue; 19618c2ecf20Sopenharmony_ci if (dsa_to_port(ds, port)->bridge_dev != br) 19628c2ecf20Sopenharmony_ci continue; 19638c2ecf20Sopenharmony_ci 19648c2ecf20Sopenharmony_ci rc = dsa_8021q_crosschip_bridge_join(priv->dsa_8021q_ctx, 19658c2ecf20Sopenharmony_ci port, 19668c2ecf20Sopenharmony_ci other_priv->dsa_8021q_ctx, 19678c2ecf20Sopenharmony_ci other_port); 19688c2ecf20Sopenharmony_ci if (rc) 19698c2ecf20Sopenharmony_ci return rc; 19708c2ecf20Sopenharmony_ci 19718c2ecf20Sopenharmony_ci rc = dsa_8021q_crosschip_bridge_join(other_priv->dsa_8021q_ctx, 19728c2ecf20Sopenharmony_ci other_port, 19738c2ecf20Sopenharmony_ci priv->dsa_8021q_ctx, 19748c2ecf20Sopenharmony_ci port); 19758c2ecf20Sopenharmony_ci if (rc) 19768c2ecf20Sopenharmony_ci return rc; 19778c2ecf20Sopenharmony_ci } 19788c2ecf20Sopenharmony_ci 19798c2ecf20Sopenharmony_ci return 0; 19808c2ecf20Sopenharmony_ci} 19818c2ecf20Sopenharmony_ci 19828c2ecf20Sopenharmony_cistatic void sja1105_crosschip_bridge_leave(struct dsa_switch *ds, 19838c2ecf20Sopenharmony_ci int tree_index, int sw_index, 19848c2ecf20Sopenharmony_ci int other_port, 19858c2ecf20Sopenharmony_ci struct net_device *br) 19868c2ecf20Sopenharmony_ci{ 19878c2ecf20Sopenharmony_ci struct dsa_switch *other_ds = dsa_switch_find(tree_index, sw_index); 19888c2ecf20Sopenharmony_ci struct sja1105_private *other_priv = other_ds->priv; 19898c2ecf20Sopenharmony_ci struct sja1105_private *priv = ds->priv; 19908c2ecf20Sopenharmony_ci int port; 19918c2ecf20Sopenharmony_ci 19928c2ecf20Sopenharmony_ci if (other_ds->ops != &sja1105_switch_ops) 19938c2ecf20Sopenharmony_ci return; 19948c2ecf20Sopenharmony_ci 19958c2ecf20Sopenharmony_ci for (port = 0; port < ds->num_ports; port++) { 19968c2ecf20Sopenharmony_ci if (!dsa_is_user_port(ds, port)) 19978c2ecf20Sopenharmony_ci continue; 19988c2ecf20Sopenharmony_ci if (dsa_to_port(ds, port)->bridge_dev != br) 19998c2ecf20Sopenharmony_ci continue; 20008c2ecf20Sopenharmony_ci 20018c2ecf20Sopenharmony_ci dsa_8021q_crosschip_bridge_leave(priv->dsa_8021q_ctx, port, 20028c2ecf20Sopenharmony_ci other_priv->dsa_8021q_ctx, 20038c2ecf20Sopenharmony_ci other_port); 20048c2ecf20Sopenharmony_ci 20058c2ecf20Sopenharmony_ci dsa_8021q_crosschip_bridge_leave(other_priv->dsa_8021q_ctx, 20068c2ecf20Sopenharmony_ci other_port, 20078c2ecf20Sopenharmony_ci priv->dsa_8021q_ctx, port); 20088c2ecf20Sopenharmony_ci } 20098c2ecf20Sopenharmony_ci} 20108c2ecf20Sopenharmony_ci 20118c2ecf20Sopenharmony_cistatic int sja1105_setup_8021q_tagging(struct dsa_switch *ds, bool enabled) 20128c2ecf20Sopenharmony_ci{ 20138c2ecf20Sopenharmony_ci struct sja1105_private *priv = ds->priv; 20148c2ecf20Sopenharmony_ci int rc; 20158c2ecf20Sopenharmony_ci 20168c2ecf20Sopenharmony_ci rc = dsa_8021q_setup(priv->dsa_8021q_ctx, enabled); 20178c2ecf20Sopenharmony_ci if (rc) 20188c2ecf20Sopenharmony_ci return rc; 20198c2ecf20Sopenharmony_ci 20208c2ecf20Sopenharmony_ci dev_info(ds->dev, "%s switch tagging\n", 20218c2ecf20Sopenharmony_ci enabled ? "Enabled" : "Disabled"); 20228c2ecf20Sopenharmony_ci return 0; 20238c2ecf20Sopenharmony_ci} 20248c2ecf20Sopenharmony_ci 20258c2ecf20Sopenharmony_cistatic enum dsa_tag_protocol 20268c2ecf20Sopenharmony_cisja1105_get_tag_protocol(struct dsa_switch *ds, int port, 20278c2ecf20Sopenharmony_ci enum dsa_tag_protocol mp) 20288c2ecf20Sopenharmony_ci{ 20298c2ecf20Sopenharmony_ci return DSA_TAG_PROTO_SJA1105; 20308c2ecf20Sopenharmony_ci} 20318c2ecf20Sopenharmony_ci 20328c2ecf20Sopenharmony_cistatic int sja1105_find_free_subvlan(u16 *subvlan_map, bool pvid) 20338c2ecf20Sopenharmony_ci{ 20348c2ecf20Sopenharmony_ci int subvlan; 20358c2ecf20Sopenharmony_ci 20368c2ecf20Sopenharmony_ci if (pvid) 20378c2ecf20Sopenharmony_ci return 0; 20388c2ecf20Sopenharmony_ci 20398c2ecf20Sopenharmony_ci for (subvlan = 1; subvlan < DSA_8021Q_N_SUBVLAN; subvlan++) 20408c2ecf20Sopenharmony_ci if (subvlan_map[subvlan] == VLAN_N_VID) 20418c2ecf20Sopenharmony_ci return subvlan; 20428c2ecf20Sopenharmony_ci 20438c2ecf20Sopenharmony_ci return -1; 20448c2ecf20Sopenharmony_ci} 20458c2ecf20Sopenharmony_ci 20468c2ecf20Sopenharmony_cistatic int sja1105_find_subvlan(u16 *subvlan_map, u16 vid) 20478c2ecf20Sopenharmony_ci{ 20488c2ecf20Sopenharmony_ci int subvlan; 20498c2ecf20Sopenharmony_ci 20508c2ecf20Sopenharmony_ci for (subvlan = 0; subvlan < DSA_8021Q_N_SUBVLAN; subvlan++) 20518c2ecf20Sopenharmony_ci if (subvlan_map[subvlan] == vid) 20528c2ecf20Sopenharmony_ci return subvlan; 20538c2ecf20Sopenharmony_ci 20548c2ecf20Sopenharmony_ci return -1; 20558c2ecf20Sopenharmony_ci} 20568c2ecf20Sopenharmony_ci 20578c2ecf20Sopenharmony_cistatic int sja1105_find_committed_subvlan(struct sja1105_private *priv, 20588c2ecf20Sopenharmony_ci int port, u16 vid) 20598c2ecf20Sopenharmony_ci{ 20608c2ecf20Sopenharmony_ci struct sja1105_port *sp = &priv->ports[port]; 20618c2ecf20Sopenharmony_ci 20628c2ecf20Sopenharmony_ci return sja1105_find_subvlan(sp->subvlan_map, vid); 20638c2ecf20Sopenharmony_ci} 20648c2ecf20Sopenharmony_ci 20658c2ecf20Sopenharmony_cistatic void sja1105_init_subvlan_map(u16 *subvlan_map) 20668c2ecf20Sopenharmony_ci{ 20678c2ecf20Sopenharmony_ci int subvlan; 20688c2ecf20Sopenharmony_ci 20698c2ecf20Sopenharmony_ci for (subvlan = 0; subvlan < DSA_8021Q_N_SUBVLAN; subvlan++) 20708c2ecf20Sopenharmony_ci subvlan_map[subvlan] = VLAN_N_VID; 20718c2ecf20Sopenharmony_ci} 20728c2ecf20Sopenharmony_ci 20738c2ecf20Sopenharmony_cistatic void sja1105_commit_subvlan_map(struct sja1105_private *priv, int port, 20748c2ecf20Sopenharmony_ci u16 *subvlan_map) 20758c2ecf20Sopenharmony_ci{ 20768c2ecf20Sopenharmony_ci struct sja1105_port *sp = &priv->ports[port]; 20778c2ecf20Sopenharmony_ci int subvlan; 20788c2ecf20Sopenharmony_ci 20798c2ecf20Sopenharmony_ci for (subvlan = 0; subvlan < DSA_8021Q_N_SUBVLAN; subvlan++) 20808c2ecf20Sopenharmony_ci sp->subvlan_map[subvlan] = subvlan_map[subvlan]; 20818c2ecf20Sopenharmony_ci} 20828c2ecf20Sopenharmony_ci 20838c2ecf20Sopenharmony_cistatic int sja1105_is_vlan_configured(struct sja1105_private *priv, u16 vid) 20848c2ecf20Sopenharmony_ci{ 20858c2ecf20Sopenharmony_ci struct sja1105_vlan_lookup_entry *vlan; 20868c2ecf20Sopenharmony_ci int count, i; 20878c2ecf20Sopenharmony_ci 20888c2ecf20Sopenharmony_ci vlan = priv->static_config.tables[BLK_IDX_VLAN_LOOKUP].entries; 20898c2ecf20Sopenharmony_ci count = priv->static_config.tables[BLK_IDX_VLAN_LOOKUP].entry_count; 20908c2ecf20Sopenharmony_ci 20918c2ecf20Sopenharmony_ci for (i = 0; i < count; i++) 20928c2ecf20Sopenharmony_ci if (vlan[i].vlanid == vid) 20938c2ecf20Sopenharmony_ci return i; 20948c2ecf20Sopenharmony_ci 20958c2ecf20Sopenharmony_ci /* Return an invalid entry index if not found */ 20968c2ecf20Sopenharmony_ci return -1; 20978c2ecf20Sopenharmony_ci} 20988c2ecf20Sopenharmony_ci 20998c2ecf20Sopenharmony_cistatic int 21008c2ecf20Sopenharmony_cisja1105_find_retagging_entry(struct sja1105_retagging_entry *retagging, 21018c2ecf20Sopenharmony_ci int count, int from_port, u16 from_vid, 21028c2ecf20Sopenharmony_ci u16 to_vid) 21038c2ecf20Sopenharmony_ci{ 21048c2ecf20Sopenharmony_ci int i; 21058c2ecf20Sopenharmony_ci 21068c2ecf20Sopenharmony_ci for (i = 0; i < count; i++) 21078c2ecf20Sopenharmony_ci if (retagging[i].ing_port == BIT(from_port) && 21088c2ecf20Sopenharmony_ci retagging[i].vlan_ing == from_vid && 21098c2ecf20Sopenharmony_ci retagging[i].vlan_egr == to_vid) 21108c2ecf20Sopenharmony_ci return i; 21118c2ecf20Sopenharmony_ci 21128c2ecf20Sopenharmony_ci /* Return an invalid entry index if not found */ 21138c2ecf20Sopenharmony_ci return -1; 21148c2ecf20Sopenharmony_ci} 21158c2ecf20Sopenharmony_ci 21168c2ecf20Sopenharmony_cistatic int sja1105_commit_vlans(struct sja1105_private *priv, 21178c2ecf20Sopenharmony_ci struct sja1105_vlan_lookup_entry *new_vlan, 21188c2ecf20Sopenharmony_ci struct sja1105_retagging_entry *new_retagging, 21198c2ecf20Sopenharmony_ci int num_retagging) 21208c2ecf20Sopenharmony_ci{ 21218c2ecf20Sopenharmony_ci struct sja1105_retagging_entry *retagging; 21228c2ecf20Sopenharmony_ci struct sja1105_vlan_lookup_entry *vlan; 21238c2ecf20Sopenharmony_ci struct sja1105_table *table; 21248c2ecf20Sopenharmony_ci int num_vlans = 0; 21258c2ecf20Sopenharmony_ci int rc, i, k = 0; 21268c2ecf20Sopenharmony_ci 21278c2ecf20Sopenharmony_ci /* VLAN table */ 21288c2ecf20Sopenharmony_ci table = &priv->static_config.tables[BLK_IDX_VLAN_LOOKUP]; 21298c2ecf20Sopenharmony_ci vlan = table->entries; 21308c2ecf20Sopenharmony_ci 21318c2ecf20Sopenharmony_ci for (i = 0; i < VLAN_N_VID; i++) { 21328c2ecf20Sopenharmony_ci int match = sja1105_is_vlan_configured(priv, i); 21338c2ecf20Sopenharmony_ci 21348c2ecf20Sopenharmony_ci if (new_vlan[i].vlanid != VLAN_N_VID) 21358c2ecf20Sopenharmony_ci num_vlans++; 21368c2ecf20Sopenharmony_ci 21378c2ecf20Sopenharmony_ci if (new_vlan[i].vlanid == VLAN_N_VID && match >= 0) { 21388c2ecf20Sopenharmony_ci /* Was there before, no longer is. Delete */ 21398c2ecf20Sopenharmony_ci dev_dbg(priv->ds->dev, "Deleting VLAN %d\n", i); 21408c2ecf20Sopenharmony_ci rc = sja1105_dynamic_config_write(priv, 21418c2ecf20Sopenharmony_ci BLK_IDX_VLAN_LOOKUP, 21428c2ecf20Sopenharmony_ci i, &vlan[match], false); 21438c2ecf20Sopenharmony_ci if (rc < 0) 21448c2ecf20Sopenharmony_ci return rc; 21458c2ecf20Sopenharmony_ci } else if (new_vlan[i].vlanid != VLAN_N_VID) { 21468c2ecf20Sopenharmony_ci /* Nothing changed, don't do anything */ 21478c2ecf20Sopenharmony_ci if (match >= 0 && 21488c2ecf20Sopenharmony_ci vlan[match].vlanid == new_vlan[i].vlanid && 21498c2ecf20Sopenharmony_ci vlan[match].tag_port == new_vlan[i].tag_port && 21508c2ecf20Sopenharmony_ci vlan[match].vlan_bc == new_vlan[i].vlan_bc && 21518c2ecf20Sopenharmony_ci vlan[match].vmemb_port == new_vlan[i].vmemb_port) 21528c2ecf20Sopenharmony_ci continue; 21538c2ecf20Sopenharmony_ci /* Update entry */ 21548c2ecf20Sopenharmony_ci dev_dbg(priv->ds->dev, "Updating VLAN %d\n", i); 21558c2ecf20Sopenharmony_ci rc = sja1105_dynamic_config_write(priv, 21568c2ecf20Sopenharmony_ci BLK_IDX_VLAN_LOOKUP, 21578c2ecf20Sopenharmony_ci i, &new_vlan[i], 21588c2ecf20Sopenharmony_ci true); 21598c2ecf20Sopenharmony_ci if (rc < 0) 21608c2ecf20Sopenharmony_ci return rc; 21618c2ecf20Sopenharmony_ci } 21628c2ecf20Sopenharmony_ci } 21638c2ecf20Sopenharmony_ci 21648c2ecf20Sopenharmony_ci if (table->entry_count) 21658c2ecf20Sopenharmony_ci kfree(table->entries); 21668c2ecf20Sopenharmony_ci 21678c2ecf20Sopenharmony_ci table->entries = kcalloc(num_vlans, table->ops->unpacked_entry_size, 21688c2ecf20Sopenharmony_ci GFP_KERNEL); 21698c2ecf20Sopenharmony_ci if (!table->entries) 21708c2ecf20Sopenharmony_ci return -ENOMEM; 21718c2ecf20Sopenharmony_ci 21728c2ecf20Sopenharmony_ci table->entry_count = num_vlans; 21738c2ecf20Sopenharmony_ci vlan = table->entries; 21748c2ecf20Sopenharmony_ci 21758c2ecf20Sopenharmony_ci for (i = 0; i < VLAN_N_VID; i++) { 21768c2ecf20Sopenharmony_ci if (new_vlan[i].vlanid == VLAN_N_VID) 21778c2ecf20Sopenharmony_ci continue; 21788c2ecf20Sopenharmony_ci vlan[k++] = new_vlan[i]; 21798c2ecf20Sopenharmony_ci } 21808c2ecf20Sopenharmony_ci 21818c2ecf20Sopenharmony_ci /* VLAN Retagging Table */ 21828c2ecf20Sopenharmony_ci table = &priv->static_config.tables[BLK_IDX_RETAGGING]; 21838c2ecf20Sopenharmony_ci retagging = table->entries; 21848c2ecf20Sopenharmony_ci 21858c2ecf20Sopenharmony_ci for (i = 0; i < table->entry_count; i++) { 21868c2ecf20Sopenharmony_ci rc = sja1105_dynamic_config_write(priv, BLK_IDX_RETAGGING, 21878c2ecf20Sopenharmony_ci i, &retagging[i], false); 21888c2ecf20Sopenharmony_ci if (rc) 21898c2ecf20Sopenharmony_ci return rc; 21908c2ecf20Sopenharmony_ci } 21918c2ecf20Sopenharmony_ci 21928c2ecf20Sopenharmony_ci if (table->entry_count) 21938c2ecf20Sopenharmony_ci kfree(table->entries); 21948c2ecf20Sopenharmony_ci 21958c2ecf20Sopenharmony_ci table->entries = kcalloc(num_retagging, table->ops->unpacked_entry_size, 21968c2ecf20Sopenharmony_ci GFP_KERNEL); 21978c2ecf20Sopenharmony_ci if (!table->entries) 21988c2ecf20Sopenharmony_ci return -ENOMEM; 21998c2ecf20Sopenharmony_ci 22008c2ecf20Sopenharmony_ci table->entry_count = num_retagging; 22018c2ecf20Sopenharmony_ci retagging = table->entries; 22028c2ecf20Sopenharmony_ci 22038c2ecf20Sopenharmony_ci for (i = 0; i < num_retagging; i++) { 22048c2ecf20Sopenharmony_ci retagging[i] = new_retagging[i]; 22058c2ecf20Sopenharmony_ci 22068c2ecf20Sopenharmony_ci /* Update entry */ 22078c2ecf20Sopenharmony_ci rc = sja1105_dynamic_config_write(priv, BLK_IDX_RETAGGING, 22088c2ecf20Sopenharmony_ci i, &retagging[i], true); 22098c2ecf20Sopenharmony_ci if (rc < 0) 22108c2ecf20Sopenharmony_ci return rc; 22118c2ecf20Sopenharmony_ci } 22128c2ecf20Sopenharmony_ci 22138c2ecf20Sopenharmony_ci return 0; 22148c2ecf20Sopenharmony_ci} 22158c2ecf20Sopenharmony_ci 22168c2ecf20Sopenharmony_cistruct sja1105_crosschip_vlan { 22178c2ecf20Sopenharmony_ci struct list_head list; 22188c2ecf20Sopenharmony_ci u16 vid; 22198c2ecf20Sopenharmony_ci bool untagged; 22208c2ecf20Sopenharmony_ci int port; 22218c2ecf20Sopenharmony_ci int other_port; 22228c2ecf20Sopenharmony_ci struct dsa_8021q_context *other_ctx; 22238c2ecf20Sopenharmony_ci}; 22248c2ecf20Sopenharmony_ci 22258c2ecf20Sopenharmony_cistruct sja1105_crosschip_switch { 22268c2ecf20Sopenharmony_ci struct list_head list; 22278c2ecf20Sopenharmony_ci struct dsa_8021q_context *other_ctx; 22288c2ecf20Sopenharmony_ci}; 22298c2ecf20Sopenharmony_ci 22308c2ecf20Sopenharmony_cistatic int sja1105_commit_pvid(struct sja1105_private *priv) 22318c2ecf20Sopenharmony_ci{ 22328c2ecf20Sopenharmony_ci struct sja1105_bridge_vlan *v; 22338c2ecf20Sopenharmony_ci struct list_head *vlan_list; 22348c2ecf20Sopenharmony_ci int rc = 0; 22358c2ecf20Sopenharmony_ci 22368c2ecf20Sopenharmony_ci if (priv->vlan_state == SJA1105_VLAN_FILTERING_FULL) 22378c2ecf20Sopenharmony_ci vlan_list = &priv->bridge_vlans; 22388c2ecf20Sopenharmony_ci else 22398c2ecf20Sopenharmony_ci vlan_list = &priv->dsa_8021q_vlans; 22408c2ecf20Sopenharmony_ci 22418c2ecf20Sopenharmony_ci list_for_each_entry(v, vlan_list, list) { 22428c2ecf20Sopenharmony_ci if (v->pvid) { 22438c2ecf20Sopenharmony_ci rc = sja1105_pvid_apply(priv, v->port, v->vid); 22448c2ecf20Sopenharmony_ci if (rc) 22458c2ecf20Sopenharmony_ci break; 22468c2ecf20Sopenharmony_ci } 22478c2ecf20Sopenharmony_ci } 22488c2ecf20Sopenharmony_ci 22498c2ecf20Sopenharmony_ci return rc; 22508c2ecf20Sopenharmony_ci} 22518c2ecf20Sopenharmony_ci 22528c2ecf20Sopenharmony_cistatic int 22538c2ecf20Sopenharmony_cisja1105_build_bridge_vlans(struct sja1105_private *priv, 22548c2ecf20Sopenharmony_ci struct sja1105_vlan_lookup_entry *new_vlan) 22558c2ecf20Sopenharmony_ci{ 22568c2ecf20Sopenharmony_ci struct sja1105_bridge_vlan *v; 22578c2ecf20Sopenharmony_ci 22588c2ecf20Sopenharmony_ci if (priv->vlan_state == SJA1105_VLAN_UNAWARE) 22598c2ecf20Sopenharmony_ci return 0; 22608c2ecf20Sopenharmony_ci 22618c2ecf20Sopenharmony_ci list_for_each_entry(v, &priv->bridge_vlans, list) { 22628c2ecf20Sopenharmony_ci int match = v->vid; 22638c2ecf20Sopenharmony_ci 22648c2ecf20Sopenharmony_ci new_vlan[match].vlanid = v->vid; 22658c2ecf20Sopenharmony_ci new_vlan[match].vmemb_port |= BIT(v->port); 22668c2ecf20Sopenharmony_ci new_vlan[match].vlan_bc |= BIT(v->port); 22678c2ecf20Sopenharmony_ci if (!v->untagged) 22688c2ecf20Sopenharmony_ci new_vlan[match].tag_port |= BIT(v->port); 22698c2ecf20Sopenharmony_ci } 22708c2ecf20Sopenharmony_ci 22718c2ecf20Sopenharmony_ci return 0; 22728c2ecf20Sopenharmony_ci} 22738c2ecf20Sopenharmony_ci 22748c2ecf20Sopenharmony_cistatic int 22758c2ecf20Sopenharmony_cisja1105_build_dsa_8021q_vlans(struct sja1105_private *priv, 22768c2ecf20Sopenharmony_ci struct sja1105_vlan_lookup_entry *new_vlan) 22778c2ecf20Sopenharmony_ci{ 22788c2ecf20Sopenharmony_ci struct sja1105_bridge_vlan *v; 22798c2ecf20Sopenharmony_ci 22808c2ecf20Sopenharmony_ci if (priv->vlan_state == SJA1105_VLAN_FILTERING_FULL) 22818c2ecf20Sopenharmony_ci return 0; 22828c2ecf20Sopenharmony_ci 22838c2ecf20Sopenharmony_ci list_for_each_entry(v, &priv->dsa_8021q_vlans, list) { 22848c2ecf20Sopenharmony_ci int match = v->vid; 22858c2ecf20Sopenharmony_ci 22868c2ecf20Sopenharmony_ci new_vlan[match].vlanid = v->vid; 22878c2ecf20Sopenharmony_ci new_vlan[match].vmemb_port |= BIT(v->port); 22888c2ecf20Sopenharmony_ci new_vlan[match].vlan_bc |= BIT(v->port); 22898c2ecf20Sopenharmony_ci if (!v->untagged) 22908c2ecf20Sopenharmony_ci new_vlan[match].tag_port |= BIT(v->port); 22918c2ecf20Sopenharmony_ci } 22928c2ecf20Sopenharmony_ci 22938c2ecf20Sopenharmony_ci return 0; 22948c2ecf20Sopenharmony_ci} 22958c2ecf20Sopenharmony_ci 22968c2ecf20Sopenharmony_cistatic int sja1105_build_subvlans(struct sja1105_private *priv, 22978c2ecf20Sopenharmony_ci u16 subvlan_map[][DSA_8021Q_N_SUBVLAN], 22988c2ecf20Sopenharmony_ci struct sja1105_vlan_lookup_entry *new_vlan, 22998c2ecf20Sopenharmony_ci struct sja1105_retagging_entry *new_retagging, 23008c2ecf20Sopenharmony_ci int *num_retagging) 23018c2ecf20Sopenharmony_ci{ 23028c2ecf20Sopenharmony_ci struct sja1105_bridge_vlan *v; 23038c2ecf20Sopenharmony_ci int k = *num_retagging; 23048c2ecf20Sopenharmony_ci 23058c2ecf20Sopenharmony_ci if (priv->vlan_state != SJA1105_VLAN_BEST_EFFORT) 23068c2ecf20Sopenharmony_ci return 0; 23078c2ecf20Sopenharmony_ci 23088c2ecf20Sopenharmony_ci list_for_each_entry(v, &priv->bridge_vlans, list) { 23098c2ecf20Sopenharmony_ci int upstream = dsa_upstream_port(priv->ds, v->port); 23108c2ecf20Sopenharmony_ci int match, subvlan; 23118c2ecf20Sopenharmony_ci u16 rx_vid; 23128c2ecf20Sopenharmony_ci 23138c2ecf20Sopenharmony_ci /* Only sub-VLANs on user ports need to be applied. 23148c2ecf20Sopenharmony_ci * Bridge VLANs also include VLANs added automatically 23158c2ecf20Sopenharmony_ci * by DSA on the CPU port. 23168c2ecf20Sopenharmony_ci */ 23178c2ecf20Sopenharmony_ci if (!dsa_is_user_port(priv->ds, v->port)) 23188c2ecf20Sopenharmony_ci continue; 23198c2ecf20Sopenharmony_ci 23208c2ecf20Sopenharmony_ci subvlan = sja1105_find_subvlan(subvlan_map[v->port], 23218c2ecf20Sopenharmony_ci v->vid); 23228c2ecf20Sopenharmony_ci if (subvlan < 0) { 23238c2ecf20Sopenharmony_ci subvlan = sja1105_find_free_subvlan(subvlan_map[v->port], 23248c2ecf20Sopenharmony_ci v->pvid); 23258c2ecf20Sopenharmony_ci if (subvlan < 0) { 23268c2ecf20Sopenharmony_ci dev_err(priv->ds->dev, "No more free subvlans\n"); 23278c2ecf20Sopenharmony_ci return -ENOSPC; 23288c2ecf20Sopenharmony_ci } 23298c2ecf20Sopenharmony_ci } 23308c2ecf20Sopenharmony_ci 23318c2ecf20Sopenharmony_ci rx_vid = dsa_8021q_rx_vid_subvlan(priv->ds, v->port, subvlan); 23328c2ecf20Sopenharmony_ci 23338c2ecf20Sopenharmony_ci /* @v->vid on @v->port needs to be retagged to @rx_vid 23348c2ecf20Sopenharmony_ci * on @upstream. Assume @v->vid on @v->port and on 23358c2ecf20Sopenharmony_ci * @upstream was already configured by the previous 23368c2ecf20Sopenharmony_ci * iteration over bridge_vlans. 23378c2ecf20Sopenharmony_ci */ 23388c2ecf20Sopenharmony_ci match = rx_vid; 23398c2ecf20Sopenharmony_ci new_vlan[match].vlanid = rx_vid; 23408c2ecf20Sopenharmony_ci new_vlan[match].vmemb_port |= BIT(v->port); 23418c2ecf20Sopenharmony_ci new_vlan[match].vmemb_port |= BIT(upstream); 23428c2ecf20Sopenharmony_ci new_vlan[match].vlan_bc |= BIT(v->port); 23438c2ecf20Sopenharmony_ci new_vlan[match].vlan_bc |= BIT(upstream); 23448c2ecf20Sopenharmony_ci /* The "untagged" flag is set the same as for the 23458c2ecf20Sopenharmony_ci * original VLAN 23468c2ecf20Sopenharmony_ci */ 23478c2ecf20Sopenharmony_ci if (!v->untagged) 23488c2ecf20Sopenharmony_ci new_vlan[match].tag_port |= BIT(v->port); 23498c2ecf20Sopenharmony_ci /* But it's always tagged towards the CPU */ 23508c2ecf20Sopenharmony_ci new_vlan[match].tag_port |= BIT(upstream); 23518c2ecf20Sopenharmony_ci 23528c2ecf20Sopenharmony_ci /* The Retagging Table generates packet *clones* with 23538c2ecf20Sopenharmony_ci * the new VLAN. This is a very odd hardware quirk 23548c2ecf20Sopenharmony_ci * which we need to suppress by dropping the original 23558c2ecf20Sopenharmony_ci * packet. 23568c2ecf20Sopenharmony_ci * Deny egress of the original VLAN towards the CPU 23578c2ecf20Sopenharmony_ci * port. This will force the switch to drop it, and 23588c2ecf20Sopenharmony_ci * we'll see only the retagged packets. 23598c2ecf20Sopenharmony_ci */ 23608c2ecf20Sopenharmony_ci match = v->vid; 23618c2ecf20Sopenharmony_ci new_vlan[match].vlan_bc &= ~BIT(upstream); 23628c2ecf20Sopenharmony_ci 23638c2ecf20Sopenharmony_ci /* And the retagging itself */ 23648c2ecf20Sopenharmony_ci new_retagging[k].vlan_ing = v->vid; 23658c2ecf20Sopenharmony_ci new_retagging[k].vlan_egr = rx_vid; 23668c2ecf20Sopenharmony_ci new_retagging[k].ing_port = BIT(v->port); 23678c2ecf20Sopenharmony_ci new_retagging[k].egr_port = BIT(upstream); 23688c2ecf20Sopenharmony_ci if (k++ == SJA1105_MAX_RETAGGING_COUNT) { 23698c2ecf20Sopenharmony_ci dev_err(priv->ds->dev, "No more retagging rules\n"); 23708c2ecf20Sopenharmony_ci return -ENOSPC; 23718c2ecf20Sopenharmony_ci } 23728c2ecf20Sopenharmony_ci 23738c2ecf20Sopenharmony_ci subvlan_map[v->port][subvlan] = v->vid; 23748c2ecf20Sopenharmony_ci } 23758c2ecf20Sopenharmony_ci 23768c2ecf20Sopenharmony_ci *num_retagging = k; 23778c2ecf20Sopenharmony_ci 23788c2ecf20Sopenharmony_ci return 0; 23798c2ecf20Sopenharmony_ci} 23808c2ecf20Sopenharmony_ci 23818c2ecf20Sopenharmony_ci/* Sadly, in crosschip scenarios where the CPU port is also the link to another 23828c2ecf20Sopenharmony_ci * switch, we should retag backwards (the dsa_8021q vid to the original vid) on 23838c2ecf20Sopenharmony_ci * the CPU port of neighbour switches. 23848c2ecf20Sopenharmony_ci */ 23858c2ecf20Sopenharmony_cistatic int 23868c2ecf20Sopenharmony_cisja1105_build_crosschip_subvlans(struct sja1105_private *priv, 23878c2ecf20Sopenharmony_ci struct sja1105_vlan_lookup_entry *new_vlan, 23888c2ecf20Sopenharmony_ci struct sja1105_retagging_entry *new_retagging, 23898c2ecf20Sopenharmony_ci int *num_retagging) 23908c2ecf20Sopenharmony_ci{ 23918c2ecf20Sopenharmony_ci struct sja1105_crosschip_vlan *tmp, *pos; 23928c2ecf20Sopenharmony_ci struct dsa_8021q_crosschip_link *c; 23938c2ecf20Sopenharmony_ci struct sja1105_bridge_vlan *v, *w; 23948c2ecf20Sopenharmony_ci struct list_head crosschip_vlans; 23958c2ecf20Sopenharmony_ci int k = *num_retagging; 23968c2ecf20Sopenharmony_ci int rc = 0; 23978c2ecf20Sopenharmony_ci 23988c2ecf20Sopenharmony_ci if (priv->vlan_state != SJA1105_VLAN_BEST_EFFORT) 23998c2ecf20Sopenharmony_ci return 0; 24008c2ecf20Sopenharmony_ci 24018c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&crosschip_vlans); 24028c2ecf20Sopenharmony_ci 24038c2ecf20Sopenharmony_ci list_for_each_entry(c, &priv->dsa_8021q_ctx->crosschip_links, list) { 24048c2ecf20Sopenharmony_ci struct sja1105_private *other_priv = c->other_ctx->ds->priv; 24058c2ecf20Sopenharmony_ci 24068c2ecf20Sopenharmony_ci if (other_priv->vlan_state == SJA1105_VLAN_FILTERING_FULL) 24078c2ecf20Sopenharmony_ci continue; 24088c2ecf20Sopenharmony_ci 24098c2ecf20Sopenharmony_ci /* Crosschip links are also added to the CPU ports. 24108c2ecf20Sopenharmony_ci * Ignore those. 24118c2ecf20Sopenharmony_ci */ 24128c2ecf20Sopenharmony_ci if (!dsa_is_user_port(priv->ds, c->port)) 24138c2ecf20Sopenharmony_ci continue; 24148c2ecf20Sopenharmony_ci if (!dsa_is_user_port(c->other_ctx->ds, c->other_port)) 24158c2ecf20Sopenharmony_ci continue; 24168c2ecf20Sopenharmony_ci 24178c2ecf20Sopenharmony_ci /* Search for VLANs on the remote port */ 24188c2ecf20Sopenharmony_ci list_for_each_entry(v, &other_priv->bridge_vlans, list) { 24198c2ecf20Sopenharmony_ci bool already_added = false; 24208c2ecf20Sopenharmony_ci bool we_have_it = false; 24218c2ecf20Sopenharmony_ci 24228c2ecf20Sopenharmony_ci if (v->port != c->other_port) 24238c2ecf20Sopenharmony_ci continue; 24248c2ecf20Sopenharmony_ci 24258c2ecf20Sopenharmony_ci /* If @v is a pvid on @other_ds, it does not need 24268c2ecf20Sopenharmony_ci * re-retagging, because its SVL field is 0 and we 24278c2ecf20Sopenharmony_ci * already allow that, via the dsa_8021q crosschip 24288c2ecf20Sopenharmony_ci * links. 24298c2ecf20Sopenharmony_ci */ 24308c2ecf20Sopenharmony_ci if (v->pvid) 24318c2ecf20Sopenharmony_ci continue; 24328c2ecf20Sopenharmony_ci 24338c2ecf20Sopenharmony_ci /* Search for the VLAN on our local port */ 24348c2ecf20Sopenharmony_ci list_for_each_entry(w, &priv->bridge_vlans, list) { 24358c2ecf20Sopenharmony_ci if (w->port == c->port && w->vid == v->vid) { 24368c2ecf20Sopenharmony_ci we_have_it = true; 24378c2ecf20Sopenharmony_ci break; 24388c2ecf20Sopenharmony_ci } 24398c2ecf20Sopenharmony_ci } 24408c2ecf20Sopenharmony_ci 24418c2ecf20Sopenharmony_ci if (!we_have_it) 24428c2ecf20Sopenharmony_ci continue; 24438c2ecf20Sopenharmony_ci 24448c2ecf20Sopenharmony_ci list_for_each_entry(tmp, &crosschip_vlans, list) { 24458c2ecf20Sopenharmony_ci if (tmp->vid == v->vid && 24468c2ecf20Sopenharmony_ci tmp->untagged == v->untagged && 24478c2ecf20Sopenharmony_ci tmp->port == c->port && 24488c2ecf20Sopenharmony_ci tmp->other_port == v->port && 24498c2ecf20Sopenharmony_ci tmp->other_ctx == c->other_ctx) { 24508c2ecf20Sopenharmony_ci already_added = true; 24518c2ecf20Sopenharmony_ci break; 24528c2ecf20Sopenharmony_ci } 24538c2ecf20Sopenharmony_ci } 24548c2ecf20Sopenharmony_ci 24558c2ecf20Sopenharmony_ci if (already_added) 24568c2ecf20Sopenharmony_ci continue; 24578c2ecf20Sopenharmony_ci 24588c2ecf20Sopenharmony_ci tmp = kzalloc(sizeof(*tmp), GFP_KERNEL); 24598c2ecf20Sopenharmony_ci if (!tmp) { 24608c2ecf20Sopenharmony_ci dev_err(priv->ds->dev, "Failed to allocate memory\n"); 24618c2ecf20Sopenharmony_ci rc = -ENOMEM; 24628c2ecf20Sopenharmony_ci goto out; 24638c2ecf20Sopenharmony_ci } 24648c2ecf20Sopenharmony_ci tmp->vid = v->vid; 24658c2ecf20Sopenharmony_ci tmp->port = c->port; 24668c2ecf20Sopenharmony_ci tmp->other_port = v->port; 24678c2ecf20Sopenharmony_ci tmp->other_ctx = c->other_ctx; 24688c2ecf20Sopenharmony_ci tmp->untagged = v->untagged; 24698c2ecf20Sopenharmony_ci list_add(&tmp->list, &crosschip_vlans); 24708c2ecf20Sopenharmony_ci } 24718c2ecf20Sopenharmony_ci } 24728c2ecf20Sopenharmony_ci 24738c2ecf20Sopenharmony_ci list_for_each_entry(tmp, &crosschip_vlans, list) { 24748c2ecf20Sopenharmony_ci struct sja1105_private *other_priv = tmp->other_ctx->ds->priv; 24758c2ecf20Sopenharmony_ci int upstream = dsa_upstream_port(priv->ds, tmp->port); 24768c2ecf20Sopenharmony_ci int match, subvlan; 24778c2ecf20Sopenharmony_ci u16 rx_vid; 24788c2ecf20Sopenharmony_ci 24798c2ecf20Sopenharmony_ci subvlan = sja1105_find_committed_subvlan(other_priv, 24808c2ecf20Sopenharmony_ci tmp->other_port, 24818c2ecf20Sopenharmony_ci tmp->vid); 24828c2ecf20Sopenharmony_ci /* If this happens, it's a bug. The neighbour switch does not 24838c2ecf20Sopenharmony_ci * have a subvlan for tmp->vid on tmp->other_port, but it 24848c2ecf20Sopenharmony_ci * should, since we already checked for its vlan_state. 24858c2ecf20Sopenharmony_ci */ 24868c2ecf20Sopenharmony_ci if (WARN_ON(subvlan < 0)) { 24878c2ecf20Sopenharmony_ci rc = -EINVAL; 24888c2ecf20Sopenharmony_ci goto out; 24898c2ecf20Sopenharmony_ci } 24908c2ecf20Sopenharmony_ci 24918c2ecf20Sopenharmony_ci rx_vid = dsa_8021q_rx_vid_subvlan(tmp->other_ctx->ds, 24928c2ecf20Sopenharmony_ci tmp->other_port, 24938c2ecf20Sopenharmony_ci subvlan); 24948c2ecf20Sopenharmony_ci 24958c2ecf20Sopenharmony_ci /* The @rx_vid retagged from @tmp->vid on 24968c2ecf20Sopenharmony_ci * {@tmp->other_ds, @tmp->other_port} needs to be 24978c2ecf20Sopenharmony_ci * re-retagged to @tmp->vid on the way back to us. 24988c2ecf20Sopenharmony_ci * 24998c2ecf20Sopenharmony_ci * Assume the original @tmp->vid is already configured 25008c2ecf20Sopenharmony_ci * on this local switch, otherwise we wouldn't be 25018c2ecf20Sopenharmony_ci * retagging its subvlan on the other switch in the 25028c2ecf20Sopenharmony_ci * first place. We just need to add a reverse retagging 25038c2ecf20Sopenharmony_ci * rule for @rx_vid and install @rx_vid on our ports. 25048c2ecf20Sopenharmony_ci */ 25058c2ecf20Sopenharmony_ci match = rx_vid; 25068c2ecf20Sopenharmony_ci new_vlan[match].vlanid = rx_vid; 25078c2ecf20Sopenharmony_ci new_vlan[match].vmemb_port |= BIT(tmp->port); 25088c2ecf20Sopenharmony_ci new_vlan[match].vmemb_port |= BIT(upstream); 25098c2ecf20Sopenharmony_ci /* The "untagged" flag is set the same as for the 25108c2ecf20Sopenharmony_ci * original VLAN. And towards the CPU, it doesn't 25118c2ecf20Sopenharmony_ci * really matter, because @rx_vid will only receive 25128c2ecf20Sopenharmony_ci * traffic on that port. For consistency with other dsa_8021q 25138c2ecf20Sopenharmony_ci * VLANs, we'll keep the CPU port tagged. 25148c2ecf20Sopenharmony_ci */ 25158c2ecf20Sopenharmony_ci if (!tmp->untagged) 25168c2ecf20Sopenharmony_ci new_vlan[match].tag_port |= BIT(tmp->port); 25178c2ecf20Sopenharmony_ci new_vlan[match].tag_port |= BIT(upstream); 25188c2ecf20Sopenharmony_ci /* Deny egress of @rx_vid towards our front-panel port. 25198c2ecf20Sopenharmony_ci * This will force the switch to drop it, and we'll see 25208c2ecf20Sopenharmony_ci * only the re-retagged packets (having the original, 25218c2ecf20Sopenharmony_ci * pre-initial-retagging, VLAN @tmp->vid). 25228c2ecf20Sopenharmony_ci */ 25238c2ecf20Sopenharmony_ci new_vlan[match].vlan_bc &= ~BIT(tmp->port); 25248c2ecf20Sopenharmony_ci 25258c2ecf20Sopenharmony_ci /* On reverse retagging, the same ingress VLAN goes to multiple 25268c2ecf20Sopenharmony_ci * ports. So we have an opportunity to create composite rules 25278c2ecf20Sopenharmony_ci * to not waste the limited space in the retagging table. 25288c2ecf20Sopenharmony_ci */ 25298c2ecf20Sopenharmony_ci k = sja1105_find_retagging_entry(new_retagging, *num_retagging, 25308c2ecf20Sopenharmony_ci upstream, rx_vid, tmp->vid); 25318c2ecf20Sopenharmony_ci if (k < 0) { 25328c2ecf20Sopenharmony_ci if (*num_retagging == SJA1105_MAX_RETAGGING_COUNT) { 25338c2ecf20Sopenharmony_ci dev_err(priv->ds->dev, "No more retagging rules\n"); 25348c2ecf20Sopenharmony_ci rc = -ENOSPC; 25358c2ecf20Sopenharmony_ci goto out; 25368c2ecf20Sopenharmony_ci } 25378c2ecf20Sopenharmony_ci k = (*num_retagging)++; 25388c2ecf20Sopenharmony_ci } 25398c2ecf20Sopenharmony_ci /* And the retagging itself */ 25408c2ecf20Sopenharmony_ci new_retagging[k].vlan_ing = rx_vid; 25418c2ecf20Sopenharmony_ci new_retagging[k].vlan_egr = tmp->vid; 25428c2ecf20Sopenharmony_ci new_retagging[k].ing_port = BIT(upstream); 25438c2ecf20Sopenharmony_ci new_retagging[k].egr_port |= BIT(tmp->port); 25448c2ecf20Sopenharmony_ci } 25458c2ecf20Sopenharmony_ci 25468c2ecf20Sopenharmony_ciout: 25478c2ecf20Sopenharmony_ci list_for_each_entry_safe(tmp, pos, &crosschip_vlans, list) { 25488c2ecf20Sopenharmony_ci list_del(&tmp->list); 25498c2ecf20Sopenharmony_ci kfree(tmp); 25508c2ecf20Sopenharmony_ci } 25518c2ecf20Sopenharmony_ci 25528c2ecf20Sopenharmony_ci return rc; 25538c2ecf20Sopenharmony_ci} 25548c2ecf20Sopenharmony_ci 25558c2ecf20Sopenharmony_cistatic int sja1105_build_vlan_table(struct sja1105_private *priv, bool notify); 25568c2ecf20Sopenharmony_ci 25578c2ecf20Sopenharmony_cistatic int sja1105_notify_crosschip_switches(struct sja1105_private *priv) 25588c2ecf20Sopenharmony_ci{ 25598c2ecf20Sopenharmony_ci struct sja1105_crosschip_switch *s, *pos; 25608c2ecf20Sopenharmony_ci struct list_head crosschip_switches; 25618c2ecf20Sopenharmony_ci struct dsa_8021q_crosschip_link *c; 25628c2ecf20Sopenharmony_ci int rc = 0; 25638c2ecf20Sopenharmony_ci 25648c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&crosschip_switches); 25658c2ecf20Sopenharmony_ci 25668c2ecf20Sopenharmony_ci list_for_each_entry(c, &priv->dsa_8021q_ctx->crosschip_links, list) { 25678c2ecf20Sopenharmony_ci bool already_added = false; 25688c2ecf20Sopenharmony_ci 25698c2ecf20Sopenharmony_ci list_for_each_entry(s, &crosschip_switches, list) { 25708c2ecf20Sopenharmony_ci if (s->other_ctx == c->other_ctx) { 25718c2ecf20Sopenharmony_ci already_added = true; 25728c2ecf20Sopenharmony_ci break; 25738c2ecf20Sopenharmony_ci } 25748c2ecf20Sopenharmony_ci } 25758c2ecf20Sopenharmony_ci 25768c2ecf20Sopenharmony_ci if (already_added) 25778c2ecf20Sopenharmony_ci continue; 25788c2ecf20Sopenharmony_ci 25798c2ecf20Sopenharmony_ci s = kzalloc(sizeof(*s), GFP_KERNEL); 25808c2ecf20Sopenharmony_ci if (!s) { 25818c2ecf20Sopenharmony_ci dev_err(priv->ds->dev, "Failed to allocate memory\n"); 25828c2ecf20Sopenharmony_ci rc = -ENOMEM; 25838c2ecf20Sopenharmony_ci goto out; 25848c2ecf20Sopenharmony_ci } 25858c2ecf20Sopenharmony_ci s->other_ctx = c->other_ctx; 25868c2ecf20Sopenharmony_ci list_add(&s->list, &crosschip_switches); 25878c2ecf20Sopenharmony_ci } 25888c2ecf20Sopenharmony_ci 25898c2ecf20Sopenharmony_ci list_for_each_entry(s, &crosschip_switches, list) { 25908c2ecf20Sopenharmony_ci struct sja1105_private *other_priv = s->other_ctx->ds->priv; 25918c2ecf20Sopenharmony_ci 25928c2ecf20Sopenharmony_ci rc = sja1105_build_vlan_table(other_priv, false); 25938c2ecf20Sopenharmony_ci if (rc) 25948c2ecf20Sopenharmony_ci goto out; 25958c2ecf20Sopenharmony_ci } 25968c2ecf20Sopenharmony_ci 25978c2ecf20Sopenharmony_ciout: 25988c2ecf20Sopenharmony_ci list_for_each_entry_safe(s, pos, &crosschip_switches, list) { 25998c2ecf20Sopenharmony_ci list_del(&s->list); 26008c2ecf20Sopenharmony_ci kfree(s); 26018c2ecf20Sopenharmony_ci } 26028c2ecf20Sopenharmony_ci 26038c2ecf20Sopenharmony_ci return rc; 26048c2ecf20Sopenharmony_ci} 26058c2ecf20Sopenharmony_ci 26068c2ecf20Sopenharmony_cistatic int sja1105_build_vlan_table(struct sja1105_private *priv, bool notify) 26078c2ecf20Sopenharmony_ci{ 26088c2ecf20Sopenharmony_ci u16 subvlan_map[SJA1105_NUM_PORTS][DSA_8021Q_N_SUBVLAN]; 26098c2ecf20Sopenharmony_ci struct sja1105_retagging_entry *new_retagging; 26108c2ecf20Sopenharmony_ci struct sja1105_vlan_lookup_entry *new_vlan; 26118c2ecf20Sopenharmony_ci struct sja1105_table *table; 26128c2ecf20Sopenharmony_ci int i, num_retagging = 0; 26138c2ecf20Sopenharmony_ci int rc; 26148c2ecf20Sopenharmony_ci 26158c2ecf20Sopenharmony_ci table = &priv->static_config.tables[BLK_IDX_VLAN_LOOKUP]; 26168c2ecf20Sopenharmony_ci new_vlan = kcalloc(VLAN_N_VID, 26178c2ecf20Sopenharmony_ci table->ops->unpacked_entry_size, GFP_KERNEL); 26188c2ecf20Sopenharmony_ci if (!new_vlan) 26198c2ecf20Sopenharmony_ci return -ENOMEM; 26208c2ecf20Sopenharmony_ci 26218c2ecf20Sopenharmony_ci table = &priv->static_config.tables[BLK_IDX_VLAN_LOOKUP]; 26228c2ecf20Sopenharmony_ci new_retagging = kcalloc(SJA1105_MAX_RETAGGING_COUNT, 26238c2ecf20Sopenharmony_ci table->ops->unpacked_entry_size, GFP_KERNEL); 26248c2ecf20Sopenharmony_ci if (!new_retagging) { 26258c2ecf20Sopenharmony_ci kfree(new_vlan); 26268c2ecf20Sopenharmony_ci return -ENOMEM; 26278c2ecf20Sopenharmony_ci } 26288c2ecf20Sopenharmony_ci 26298c2ecf20Sopenharmony_ci for (i = 0; i < VLAN_N_VID; i++) 26308c2ecf20Sopenharmony_ci new_vlan[i].vlanid = VLAN_N_VID; 26318c2ecf20Sopenharmony_ci 26328c2ecf20Sopenharmony_ci for (i = 0; i < SJA1105_MAX_RETAGGING_COUNT; i++) 26338c2ecf20Sopenharmony_ci new_retagging[i].vlan_ing = VLAN_N_VID; 26348c2ecf20Sopenharmony_ci 26358c2ecf20Sopenharmony_ci for (i = 0; i < priv->ds->num_ports; i++) 26368c2ecf20Sopenharmony_ci sja1105_init_subvlan_map(subvlan_map[i]); 26378c2ecf20Sopenharmony_ci 26388c2ecf20Sopenharmony_ci /* Bridge VLANs */ 26398c2ecf20Sopenharmony_ci rc = sja1105_build_bridge_vlans(priv, new_vlan); 26408c2ecf20Sopenharmony_ci if (rc) 26418c2ecf20Sopenharmony_ci goto out; 26428c2ecf20Sopenharmony_ci 26438c2ecf20Sopenharmony_ci /* VLANs necessary for dsa_8021q operation, given to us by tag_8021q.c: 26448c2ecf20Sopenharmony_ci * - RX VLANs 26458c2ecf20Sopenharmony_ci * - TX VLANs 26468c2ecf20Sopenharmony_ci * - Crosschip links 26478c2ecf20Sopenharmony_ci */ 26488c2ecf20Sopenharmony_ci rc = sja1105_build_dsa_8021q_vlans(priv, new_vlan); 26498c2ecf20Sopenharmony_ci if (rc) 26508c2ecf20Sopenharmony_ci goto out; 26518c2ecf20Sopenharmony_ci 26528c2ecf20Sopenharmony_ci /* Private VLANs necessary for dsa_8021q operation, which we need to 26538c2ecf20Sopenharmony_ci * determine on our own: 26548c2ecf20Sopenharmony_ci * - Sub-VLANs 26558c2ecf20Sopenharmony_ci * - Sub-VLANs of crosschip switches 26568c2ecf20Sopenharmony_ci */ 26578c2ecf20Sopenharmony_ci rc = sja1105_build_subvlans(priv, subvlan_map, new_vlan, new_retagging, 26588c2ecf20Sopenharmony_ci &num_retagging); 26598c2ecf20Sopenharmony_ci if (rc) 26608c2ecf20Sopenharmony_ci goto out; 26618c2ecf20Sopenharmony_ci 26628c2ecf20Sopenharmony_ci rc = sja1105_build_crosschip_subvlans(priv, new_vlan, new_retagging, 26638c2ecf20Sopenharmony_ci &num_retagging); 26648c2ecf20Sopenharmony_ci if (rc) 26658c2ecf20Sopenharmony_ci goto out; 26668c2ecf20Sopenharmony_ci 26678c2ecf20Sopenharmony_ci rc = sja1105_commit_vlans(priv, new_vlan, new_retagging, num_retagging); 26688c2ecf20Sopenharmony_ci if (rc) 26698c2ecf20Sopenharmony_ci goto out; 26708c2ecf20Sopenharmony_ci 26718c2ecf20Sopenharmony_ci rc = sja1105_commit_pvid(priv); 26728c2ecf20Sopenharmony_ci if (rc) 26738c2ecf20Sopenharmony_ci goto out; 26748c2ecf20Sopenharmony_ci 26758c2ecf20Sopenharmony_ci for (i = 0; i < priv->ds->num_ports; i++) 26768c2ecf20Sopenharmony_ci sja1105_commit_subvlan_map(priv, i, subvlan_map[i]); 26778c2ecf20Sopenharmony_ci 26788c2ecf20Sopenharmony_ci if (notify) { 26798c2ecf20Sopenharmony_ci rc = sja1105_notify_crosschip_switches(priv); 26808c2ecf20Sopenharmony_ci if (rc) 26818c2ecf20Sopenharmony_ci goto out; 26828c2ecf20Sopenharmony_ci } 26838c2ecf20Sopenharmony_ci 26848c2ecf20Sopenharmony_ciout: 26858c2ecf20Sopenharmony_ci kfree(new_vlan); 26868c2ecf20Sopenharmony_ci kfree(new_retagging); 26878c2ecf20Sopenharmony_ci 26888c2ecf20Sopenharmony_ci return rc; 26898c2ecf20Sopenharmony_ci} 26908c2ecf20Sopenharmony_ci 26918c2ecf20Sopenharmony_cistatic int sja1105_vlan_prepare(struct dsa_switch *ds, int port, 26928c2ecf20Sopenharmony_ci const struct switchdev_obj_port_vlan *vlan) 26938c2ecf20Sopenharmony_ci{ 26948c2ecf20Sopenharmony_ci struct sja1105_private *priv = ds->priv; 26958c2ecf20Sopenharmony_ci u16 vid; 26968c2ecf20Sopenharmony_ci 26978c2ecf20Sopenharmony_ci if (priv->vlan_state == SJA1105_VLAN_FILTERING_FULL) 26988c2ecf20Sopenharmony_ci return 0; 26998c2ecf20Sopenharmony_ci 27008c2ecf20Sopenharmony_ci /* If the user wants best-effort VLAN filtering (aka vlan_filtering 27018c2ecf20Sopenharmony_ci * bridge plus tagging), be sure to at least deny alterations to the 27028c2ecf20Sopenharmony_ci * configuration done by dsa_8021q. 27038c2ecf20Sopenharmony_ci */ 27048c2ecf20Sopenharmony_ci for (vid = vlan->vid_begin; vid <= vlan->vid_end; vid++) { 27058c2ecf20Sopenharmony_ci if (vid_is_dsa_8021q(vid)) { 27068c2ecf20Sopenharmony_ci dev_err(ds->dev, "Range 1024-3071 reserved for dsa_8021q operation\n"); 27078c2ecf20Sopenharmony_ci return -EBUSY; 27088c2ecf20Sopenharmony_ci } 27098c2ecf20Sopenharmony_ci } 27108c2ecf20Sopenharmony_ci 27118c2ecf20Sopenharmony_ci return 0; 27128c2ecf20Sopenharmony_ci} 27138c2ecf20Sopenharmony_ci 27148c2ecf20Sopenharmony_ci/* The TPID setting belongs to the General Parameters table, 27158c2ecf20Sopenharmony_ci * which can only be partially reconfigured at runtime (and not the TPID). 27168c2ecf20Sopenharmony_ci * So a switch reset is required. 27178c2ecf20Sopenharmony_ci */ 27188c2ecf20Sopenharmony_ciint sja1105_vlan_filtering(struct dsa_switch *ds, int port, bool enabled, 27198c2ecf20Sopenharmony_ci struct switchdev_trans *trans) 27208c2ecf20Sopenharmony_ci{ 27218c2ecf20Sopenharmony_ci struct sja1105_l2_lookup_params_entry *l2_lookup_params; 27228c2ecf20Sopenharmony_ci struct sja1105_general_params_entry *general_params; 27238c2ecf20Sopenharmony_ci struct sja1105_private *priv = ds->priv; 27248c2ecf20Sopenharmony_ci enum sja1105_vlan_state state; 27258c2ecf20Sopenharmony_ci struct sja1105_table *table; 27268c2ecf20Sopenharmony_ci struct sja1105_rule *rule; 27278c2ecf20Sopenharmony_ci bool want_tagging; 27288c2ecf20Sopenharmony_ci u16 tpid, tpid2; 27298c2ecf20Sopenharmony_ci int rc; 27308c2ecf20Sopenharmony_ci 27318c2ecf20Sopenharmony_ci if (switchdev_trans_ph_prepare(trans)) { 27328c2ecf20Sopenharmony_ci list_for_each_entry(rule, &priv->flow_block.rules, list) { 27338c2ecf20Sopenharmony_ci if (rule->type == SJA1105_RULE_VL) { 27348c2ecf20Sopenharmony_ci dev_err(ds->dev, 27358c2ecf20Sopenharmony_ci "Cannot change VLAN filtering with active VL rules\n"); 27368c2ecf20Sopenharmony_ci return -EBUSY; 27378c2ecf20Sopenharmony_ci } 27388c2ecf20Sopenharmony_ci } 27398c2ecf20Sopenharmony_ci 27408c2ecf20Sopenharmony_ci return 0; 27418c2ecf20Sopenharmony_ci } 27428c2ecf20Sopenharmony_ci 27438c2ecf20Sopenharmony_ci if (enabled) { 27448c2ecf20Sopenharmony_ci /* Enable VLAN filtering. */ 27458c2ecf20Sopenharmony_ci tpid = ETH_P_8021Q; 27468c2ecf20Sopenharmony_ci tpid2 = ETH_P_8021AD; 27478c2ecf20Sopenharmony_ci } else { 27488c2ecf20Sopenharmony_ci /* Disable VLAN filtering. */ 27498c2ecf20Sopenharmony_ci tpid = ETH_P_SJA1105; 27508c2ecf20Sopenharmony_ci tpid2 = ETH_P_SJA1105; 27518c2ecf20Sopenharmony_ci } 27528c2ecf20Sopenharmony_ci 27538c2ecf20Sopenharmony_ci for (port = 0; port < ds->num_ports; port++) { 27548c2ecf20Sopenharmony_ci struct sja1105_port *sp = &priv->ports[port]; 27558c2ecf20Sopenharmony_ci 27568c2ecf20Sopenharmony_ci if (enabled) 27578c2ecf20Sopenharmony_ci sp->xmit_tpid = priv->info->qinq_tpid; 27588c2ecf20Sopenharmony_ci else 27598c2ecf20Sopenharmony_ci sp->xmit_tpid = ETH_P_SJA1105; 27608c2ecf20Sopenharmony_ci } 27618c2ecf20Sopenharmony_ci 27628c2ecf20Sopenharmony_ci if (!enabled) 27638c2ecf20Sopenharmony_ci state = SJA1105_VLAN_UNAWARE; 27648c2ecf20Sopenharmony_ci else if (priv->best_effort_vlan_filtering) 27658c2ecf20Sopenharmony_ci state = SJA1105_VLAN_BEST_EFFORT; 27668c2ecf20Sopenharmony_ci else 27678c2ecf20Sopenharmony_ci state = SJA1105_VLAN_FILTERING_FULL; 27688c2ecf20Sopenharmony_ci 27698c2ecf20Sopenharmony_ci if (priv->vlan_state == state) 27708c2ecf20Sopenharmony_ci return 0; 27718c2ecf20Sopenharmony_ci 27728c2ecf20Sopenharmony_ci priv->vlan_state = state; 27738c2ecf20Sopenharmony_ci want_tagging = (state == SJA1105_VLAN_UNAWARE || 27748c2ecf20Sopenharmony_ci state == SJA1105_VLAN_BEST_EFFORT); 27758c2ecf20Sopenharmony_ci 27768c2ecf20Sopenharmony_ci table = &priv->static_config.tables[BLK_IDX_GENERAL_PARAMS]; 27778c2ecf20Sopenharmony_ci general_params = table->entries; 27788c2ecf20Sopenharmony_ci /* EtherType used to identify inner tagged (C-tag) VLAN traffic */ 27798c2ecf20Sopenharmony_ci general_params->tpid = tpid; 27808c2ecf20Sopenharmony_ci /* EtherType used to identify outer tagged (S-tag) VLAN traffic */ 27818c2ecf20Sopenharmony_ci general_params->tpid2 = tpid2; 27828c2ecf20Sopenharmony_ci /* When VLAN filtering is on, we need to at least be able to 27838c2ecf20Sopenharmony_ci * decode management traffic through the "backup plan". 27848c2ecf20Sopenharmony_ci */ 27858c2ecf20Sopenharmony_ci general_params->incl_srcpt1 = enabled; 27868c2ecf20Sopenharmony_ci general_params->incl_srcpt0 = enabled; 27878c2ecf20Sopenharmony_ci 27888c2ecf20Sopenharmony_ci want_tagging = priv->best_effort_vlan_filtering || !enabled; 27898c2ecf20Sopenharmony_ci 27908c2ecf20Sopenharmony_ci /* VLAN filtering => independent VLAN learning. 27918c2ecf20Sopenharmony_ci * No VLAN filtering (or best effort) => shared VLAN learning. 27928c2ecf20Sopenharmony_ci * 27938c2ecf20Sopenharmony_ci * In shared VLAN learning mode, untagged traffic still gets 27948c2ecf20Sopenharmony_ci * pvid-tagged, and the FDB table gets populated with entries 27958c2ecf20Sopenharmony_ci * containing the "real" (pvid or from VLAN tag) VLAN ID. 27968c2ecf20Sopenharmony_ci * However the switch performs a masked L2 lookup in the FDB, 27978c2ecf20Sopenharmony_ci * effectively only looking up a frame's DMAC (and not VID) for the 27988c2ecf20Sopenharmony_ci * forwarding decision. 27998c2ecf20Sopenharmony_ci * 28008c2ecf20Sopenharmony_ci * This is extremely convenient for us, because in modes with 28018c2ecf20Sopenharmony_ci * vlan_filtering=0, dsa_8021q actually installs unique pvid's into 28028c2ecf20Sopenharmony_ci * each front panel port. This is good for identification but breaks 28038c2ecf20Sopenharmony_ci * learning badly - the VID of the learnt FDB entry is unique, aka 28048c2ecf20Sopenharmony_ci * no frames coming from any other port are going to have it. So 28058c2ecf20Sopenharmony_ci * for forwarding purposes, this is as though learning was broken 28068c2ecf20Sopenharmony_ci * (all frames get flooded). 28078c2ecf20Sopenharmony_ci */ 28088c2ecf20Sopenharmony_ci table = &priv->static_config.tables[BLK_IDX_L2_LOOKUP_PARAMS]; 28098c2ecf20Sopenharmony_ci l2_lookup_params = table->entries; 28108c2ecf20Sopenharmony_ci l2_lookup_params->shared_learn = want_tagging; 28118c2ecf20Sopenharmony_ci 28128c2ecf20Sopenharmony_ci sja1105_frame_memory_partitioning(priv); 28138c2ecf20Sopenharmony_ci 28148c2ecf20Sopenharmony_ci rc = sja1105_build_vlan_table(priv, false); 28158c2ecf20Sopenharmony_ci if (rc) 28168c2ecf20Sopenharmony_ci return rc; 28178c2ecf20Sopenharmony_ci 28188c2ecf20Sopenharmony_ci rc = sja1105_static_config_reload(priv, SJA1105_VLAN_FILTERING); 28198c2ecf20Sopenharmony_ci if (rc) 28208c2ecf20Sopenharmony_ci dev_err(ds->dev, "Failed to change VLAN Ethertype\n"); 28218c2ecf20Sopenharmony_ci 28228c2ecf20Sopenharmony_ci /* Switch port identification based on 802.1Q is only passable 28238c2ecf20Sopenharmony_ci * if we are not under a vlan_filtering bridge. So make sure 28248c2ecf20Sopenharmony_ci * the two configurations are mutually exclusive (of course, the 28258c2ecf20Sopenharmony_ci * user may know better, i.e. best_effort_vlan_filtering). 28268c2ecf20Sopenharmony_ci */ 28278c2ecf20Sopenharmony_ci return sja1105_setup_8021q_tagging(ds, want_tagging); 28288c2ecf20Sopenharmony_ci} 28298c2ecf20Sopenharmony_ci 28308c2ecf20Sopenharmony_ci/* Returns number of VLANs added (0 or 1) on success, 28318c2ecf20Sopenharmony_ci * or a negative error code. 28328c2ecf20Sopenharmony_ci */ 28338c2ecf20Sopenharmony_cistatic int sja1105_vlan_add_one(struct dsa_switch *ds, int port, u16 vid, 28348c2ecf20Sopenharmony_ci u16 flags, struct list_head *vlan_list) 28358c2ecf20Sopenharmony_ci{ 28368c2ecf20Sopenharmony_ci bool untagged = flags & BRIDGE_VLAN_INFO_UNTAGGED; 28378c2ecf20Sopenharmony_ci bool pvid = flags & BRIDGE_VLAN_INFO_PVID; 28388c2ecf20Sopenharmony_ci struct sja1105_bridge_vlan *v; 28398c2ecf20Sopenharmony_ci 28408c2ecf20Sopenharmony_ci list_for_each_entry(v, vlan_list, list) { 28418c2ecf20Sopenharmony_ci if (v->port == port && v->vid == vid) { 28428c2ecf20Sopenharmony_ci /* Already added */ 28438c2ecf20Sopenharmony_ci if (v->untagged == untagged && v->pvid == pvid) 28448c2ecf20Sopenharmony_ci /* Nothing changed */ 28458c2ecf20Sopenharmony_ci return 0; 28468c2ecf20Sopenharmony_ci 28478c2ecf20Sopenharmony_ci /* It's the same VLAN, but some of the flags changed 28488c2ecf20Sopenharmony_ci * and the user did not bother to delete it first. 28498c2ecf20Sopenharmony_ci * Update it and trigger sja1105_build_vlan_table. 28508c2ecf20Sopenharmony_ci */ 28518c2ecf20Sopenharmony_ci v->untagged = untagged; 28528c2ecf20Sopenharmony_ci v->pvid = pvid; 28538c2ecf20Sopenharmony_ci return 1; 28548c2ecf20Sopenharmony_ci } 28558c2ecf20Sopenharmony_ci } 28568c2ecf20Sopenharmony_ci 28578c2ecf20Sopenharmony_ci v = kzalloc(sizeof(*v), GFP_KERNEL); 28588c2ecf20Sopenharmony_ci if (!v) { 28598c2ecf20Sopenharmony_ci dev_err(ds->dev, "Out of memory while storing VLAN\n"); 28608c2ecf20Sopenharmony_ci return -ENOMEM; 28618c2ecf20Sopenharmony_ci } 28628c2ecf20Sopenharmony_ci 28638c2ecf20Sopenharmony_ci v->port = port; 28648c2ecf20Sopenharmony_ci v->vid = vid; 28658c2ecf20Sopenharmony_ci v->untagged = untagged; 28668c2ecf20Sopenharmony_ci v->pvid = pvid; 28678c2ecf20Sopenharmony_ci list_add(&v->list, vlan_list); 28688c2ecf20Sopenharmony_ci 28698c2ecf20Sopenharmony_ci return 1; 28708c2ecf20Sopenharmony_ci} 28718c2ecf20Sopenharmony_ci 28728c2ecf20Sopenharmony_ci/* Returns number of VLANs deleted (0 or 1) */ 28738c2ecf20Sopenharmony_cistatic int sja1105_vlan_del_one(struct dsa_switch *ds, int port, u16 vid, 28748c2ecf20Sopenharmony_ci struct list_head *vlan_list) 28758c2ecf20Sopenharmony_ci{ 28768c2ecf20Sopenharmony_ci struct sja1105_bridge_vlan *v, *n; 28778c2ecf20Sopenharmony_ci 28788c2ecf20Sopenharmony_ci list_for_each_entry_safe(v, n, vlan_list, list) { 28798c2ecf20Sopenharmony_ci if (v->port == port && v->vid == vid) { 28808c2ecf20Sopenharmony_ci list_del(&v->list); 28818c2ecf20Sopenharmony_ci kfree(v); 28828c2ecf20Sopenharmony_ci return 1; 28838c2ecf20Sopenharmony_ci } 28848c2ecf20Sopenharmony_ci } 28858c2ecf20Sopenharmony_ci 28868c2ecf20Sopenharmony_ci return 0; 28878c2ecf20Sopenharmony_ci} 28888c2ecf20Sopenharmony_ci 28898c2ecf20Sopenharmony_cistatic void sja1105_vlan_add(struct dsa_switch *ds, int port, 28908c2ecf20Sopenharmony_ci const struct switchdev_obj_port_vlan *vlan) 28918c2ecf20Sopenharmony_ci{ 28928c2ecf20Sopenharmony_ci struct sja1105_private *priv = ds->priv; 28938c2ecf20Sopenharmony_ci bool vlan_table_changed = false; 28948c2ecf20Sopenharmony_ci u16 vid; 28958c2ecf20Sopenharmony_ci int rc; 28968c2ecf20Sopenharmony_ci 28978c2ecf20Sopenharmony_ci for (vid = vlan->vid_begin; vid <= vlan->vid_end; vid++) { 28988c2ecf20Sopenharmony_ci rc = sja1105_vlan_add_one(ds, port, vid, vlan->flags, 28998c2ecf20Sopenharmony_ci &priv->bridge_vlans); 29008c2ecf20Sopenharmony_ci if (rc < 0) 29018c2ecf20Sopenharmony_ci return; 29028c2ecf20Sopenharmony_ci if (rc > 0) 29038c2ecf20Sopenharmony_ci vlan_table_changed = true; 29048c2ecf20Sopenharmony_ci } 29058c2ecf20Sopenharmony_ci 29068c2ecf20Sopenharmony_ci if (!vlan_table_changed) 29078c2ecf20Sopenharmony_ci return; 29088c2ecf20Sopenharmony_ci 29098c2ecf20Sopenharmony_ci rc = sja1105_build_vlan_table(priv, true); 29108c2ecf20Sopenharmony_ci if (rc) 29118c2ecf20Sopenharmony_ci dev_err(ds->dev, "Failed to build VLAN table: %d\n", rc); 29128c2ecf20Sopenharmony_ci} 29138c2ecf20Sopenharmony_ci 29148c2ecf20Sopenharmony_cistatic int sja1105_vlan_del(struct dsa_switch *ds, int port, 29158c2ecf20Sopenharmony_ci const struct switchdev_obj_port_vlan *vlan) 29168c2ecf20Sopenharmony_ci{ 29178c2ecf20Sopenharmony_ci struct sja1105_private *priv = ds->priv; 29188c2ecf20Sopenharmony_ci bool vlan_table_changed = false; 29198c2ecf20Sopenharmony_ci u16 vid; 29208c2ecf20Sopenharmony_ci int rc; 29218c2ecf20Sopenharmony_ci 29228c2ecf20Sopenharmony_ci for (vid = vlan->vid_begin; vid <= vlan->vid_end; vid++) { 29238c2ecf20Sopenharmony_ci rc = sja1105_vlan_del_one(ds, port, vid, &priv->bridge_vlans); 29248c2ecf20Sopenharmony_ci if (rc > 0) 29258c2ecf20Sopenharmony_ci vlan_table_changed = true; 29268c2ecf20Sopenharmony_ci } 29278c2ecf20Sopenharmony_ci 29288c2ecf20Sopenharmony_ci if (!vlan_table_changed) 29298c2ecf20Sopenharmony_ci return 0; 29308c2ecf20Sopenharmony_ci 29318c2ecf20Sopenharmony_ci return sja1105_build_vlan_table(priv, true); 29328c2ecf20Sopenharmony_ci} 29338c2ecf20Sopenharmony_ci 29348c2ecf20Sopenharmony_cistatic int sja1105_dsa_8021q_vlan_add(struct dsa_switch *ds, int port, u16 vid, 29358c2ecf20Sopenharmony_ci u16 flags) 29368c2ecf20Sopenharmony_ci{ 29378c2ecf20Sopenharmony_ci struct sja1105_private *priv = ds->priv; 29388c2ecf20Sopenharmony_ci int rc; 29398c2ecf20Sopenharmony_ci 29408c2ecf20Sopenharmony_ci rc = sja1105_vlan_add_one(ds, port, vid, flags, &priv->dsa_8021q_vlans); 29418c2ecf20Sopenharmony_ci if (rc <= 0) 29428c2ecf20Sopenharmony_ci return rc; 29438c2ecf20Sopenharmony_ci 29448c2ecf20Sopenharmony_ci return sja1105_build_vlan_table(priv, true); 29458c2ecf20Sopenharmony_ci} 29468c2ecf20Sopenharmony_ci 29478c2ecf20Sopenharmony_cistatic int sja1105_dsa_8021q_vlan_del(struct dsa_switch *ds, int port, u16 vid) 29488c2ecf20Sopenharmony_ci{ 29498c2ecf20Sopenharmony_ci struct sja1105_private *priv = ds->priv; 29508c2ecf20Sopenharmony_ci int rc; 29518c2ecf20Sopenharmony_ci 29528c2ecf20Sopenharmony_ci rc = sja1105_vlan_del_one(ds, port, vid, &priv->dsa_8021q_vlans); 29538c2ecf20Sopenharmony_ci if (!rc) 29548c2ecf20Sopenharmony_ci return 0; 29558c2ecf20Sopenharmony_ci 29568c2ecf20Sopenharmony_ci return sja1105_build_vlan_table(priv, true); 29578c2ecf20Sopenharmony_ci} 29588c2ecf20Sopenharmony_ci 29598c2ecf20Sopenharmony_cistatic const struct dsa_8021q_ops sja1105_dsa_8021q_ops = { 29608c2ecf20Sopenharmony_ci .vlan_add = sja1105_dsa_8021q_vlan_add, 29618c2ecf20Sopenharmony_ci .vlan_del = sja1105_dsa_8021q_vlan_del, 29628c2ecf20Sopenharmony_ci}; 29638c2ecf20Sopenharmony_ci 29648c2ecf20Sopenharmony_ci/* The programming model for the SJA1105 switch is "all-at-once" via static 29658c2ecf20Sopenharmony_ci * configuration tables. Some of these can be dynamically modified at runtime, 29668c2ecf20Sopenharmony_ci * but not the xMII mode parameters table. 29678c2ecf20Sopenharmony_ci * Furthermode, some PHYs may not have crystals for generating their clocks 29688c2ecf20Sopenharmony_ci * (e.g. RMII). Instead, their 50MHz clock is supplied via the SJA1105 port's 29698c2ecf20Sopenharmony_ci * ref_clk pin. So port clocking needs to be initialized early, before 29708c2ecf20Sopenharmony_ci * connecting to PHYs is attempted, otherwise they won't respond through MDIO. 29718c2ecf20Sopenharmony_ci * Setting correct PHY link speed does not matter now. 29728c2ecf20Sopenharmony_ci * But dsa_slave_phy_setup is called later than sja1105_setup, so the PHY 29738c2ecf20Sopenharmony_ci * bindings are not yet parsed by DSA core. We need to parse early so that we 29748c2ecf20Sopenharmony_ci * can populate the xMII mode parameters table. 29758c2ecf20Sopenharmony_ci */ 29768c2ecf20Sopenharmony_cistatic int sja1105_setup(struct dsa_switch *ds) 29778c2ecf20Sopenharmony_ci{ 29788c2ecf20Sopenharmony_ci struct sja1105_dt_port ports[SJA1105_NUM_PORTS]; 29798c2ecf20Sopenharmony_ci struct sja1105_private *priv = ds->priv; 29808c2ecf20Sopenharmony_ci int rc; 29818c2ecf20Sopenharmony_ci 29828c2ecf20Sopenharmony_ci rc = sja1105_parse_dt(priv, ports); 29838c2ecf20Sopenharmony_ci if (rc < 0) { 29848c2ecf20Sopenharmony_ci dev_err(ds->dev, "Failed to parse DT: %d\n", rc); 29858c2ecf20Sopenharmony_ci return rc; 29868c2ecf20Sopenharmony_ci } 29878c2ecf20Sopenharmony_ci 29888c2ecf20Sopenharmony_ci /* Error out early if internal delays are required through DT 29898c2ecf20Sopenharmony_ci * and we can't apply them. 29908c2ecf20Sopenharmony_ci */ 29918c2ecf20Sopenharmony_ci rc = sja1105_parse_rgmii_delays(priv, ports); 29928c2ecf20Sopenharmony_ci if (rc < 0) { 29938c2ecf20Sopenharmony_ci dev_err(ds->dev, "RGMII delay not supported\n"); 29948c2ecf20Sopenharmony_ci return rc; 29958c2ecf20Sopenharmony_ci } 29968c2ecf20Sopenharmony_ci 29978c2ecf20Sopenharmony_ci rc = sja1105_ptp_clock_register(ds); 29988c2ecf20Sopenharmony_ci if (rc < 0) { 29998c2ecf20Sopenharmony_ci dev_err(ds->dev, "Failed to register PTP clock: %d\n", rc); 30008c2ecf20Sopenharmony_ci return rc; 30018c2ecf20Sopenharmony_ci } 30028c2ecf20Sopenharmony_ci /* Create and send configuration down to device */ 30038c2ecf20Sopenharmony_ci rc = sja1105_static_config_load(priv, ports); 30048c2ecf20Sopenharmony_ci if (rc < 0) { 30058c2ecf20Sopenharmony_ci dev_err(ds->dev, "Failed to load static config: %d\n", rc); 30068c2ecf20Sopenharmony_ci goto out_ptp_clock_unregister; 30078c2ecf20Sopenharmony_ci } 30088c2ecf20Sopenharmony_ci /* Configure the CGU (PHY link modes and speeds) */ 30098c2ecf20Sopenharmony_ci rc = sja1105_clocking_setup(priv); 30108c2ecf20Sopenharmony_ci if (rc < 0) { 30118c2ecf20Sopenharmony_ci dev_err(ds->dev, "Failed to configure MII clocking: %d\n", rc); 30128c2ecf20Sopenharmony_ci goto out_static_config_free; 30138c2ecf20Sopenharmony_ci } 30148c2ecf20Sopenharmony_ci /* On SJA1105, VLAN filtering per se is always enabled in hardware. 30158c2ecf20Sopenharmony_ci * The only thing we can do to disable it is lie about what the 802.1Q 30168c2ecf20Sopenharmony_ci * EtherType is. 30178c2ecf20Sopenharmony_ci * So it will still try to apply VLAN filtering, but all ingress 30188c2ecf20Sopenharmony_ci * traffic (except frames received with EtherType of ETH_P_SJA1105) 30198c2ecf20Sopenharmony_ci * will be internally tagged with a distorted VLAN header where the 30208c2ecf20Sopenharmony_ci * TPID is ETH_P_SJA1105, and the VLAN ID is the port pvid. 30218c2ecf20Sopenharmony_ci */ 30228c2ecf20Sopenharmony_ci ds->vlan_filtering_is_global = true; 30238c2ecf20Sopenharmony_ci 30248c2ecf20Sopenharmony_ci /* Advertise the 8 egress queues */ 30258c2ecf20Sopenharmony_ci ds->num_tx_queues = SJA1105_NUM_TC; 30268c2ecf20Sopenharmony_ci 30278c2ecf20Sopenharmony_ci ds->mtu_enforcement_ingress = true; 30288c2ecf20Sopenharmony_ci 30298c2ecf20Sopenharmony_ci ds->configure_vlan_while_not_filtering = true; 30308c2ecf20Sopenharmony_ci 30318c2ecf20Sopenharmony_ci rc = sja1105_devlink_setup(ds); 30328c2ecf20Sopenharmony_ci if (rc < 0) 30338c2ecf20Sopenharmony_ci goto out_static_config_free; 30348c2ecf20Sopenharmony_ci 30358c2ecf20Sopenharmony_ci /* The DSA/switchdev model brings up switch ports in standalone mode by 30368c2ecf20Sopenharmony_ci * default, and that means vlan_filtering is 0 since they're not under 30378c2ecf20Sopenharmony_ci * a bridge, so it's safe to set up switch tagging at this time. 30388c2ecf20Sopenharmony_ci */ 30398c2ecf20Sopenharmony_ci rtnl_lock(); 30408c2ecf20Sopenharmony_ci rc = sja1105_setup_8021q_tagging(ds, true); 30418c2ecf20Sopenharmony_ci rtnl_unlock(); 30428c2ecf20Sopenharmony_ci if (rc) 30438c2ecf20Sopenharmony_ci goto out_devlink_teardown; 30448c2ecf20Sopenharmony_ci 30458c2ecf20Sopenharmony_ci return 0; 30468c2ecf20Sopenharmony_ci 30478c2ecf20Sopenharmony_ciout_devlink_teardown: 30488c2ecf20Sopenharmony_ci sja1105_devlink_teardown(ds); 30498c2ecf20Sopenharmony_ciout_ptp_clock_unregister: 30508c2ecf20Sopenharmony_ci sja1105_ptp_clock_unregister(ds); 30518c2ecf20Sopenharmony_ciout_static_config_free: 30528c2ecf20Sopenharmony_ci sja1105_static_config_free(&priv->static_config); 30538c2ecf20Sopenharmony_ci 30548c2ecf20Sopenharmony_ci return rc; 30558c2ecf20Sopenharmony_ci} 30568c2ecf20Sopenharmony_ci 30578c2ecf20Sopenharmony_cistatic void sja1105_teardown(struct dsa_switch *ds) 30588c2ecf20Sopenharmony_ci{ 30598c2ecf20Sopenharmony_ci struct sja1105_private *priv = ds->priv; 30608c2ecf20Sopenharmony_ci struct sja1105_bridge_vlan *v, *n; 30618c2ecf20Sopenharmony_ci int port; 30628c2ecf20Sopenharmony_ci 30638c2ecf20Sopenharmony_ci for (port = 0; port < SJA1105_NUM_PORTS; port++) { 30648c2ecf20Sopenharmony_ci struct sja1105_port *sp = &priv->ports[port]; 30658c2ecf20Sopenharmony_ci 30668c2ecf20Sopenharmony_ci if (!dsa_is_user_port(ds, port)) 30678c2ecf20Sopenharmony_ci continue; 30688c2ecf20Sopenharmony_ci 30698c2ecf20Sopenharmony_ci if (sp->xmit_worker) 30708c2ecf20Sopenharmony_ci kthread_destroy_worker(sp->xmit_worker); 30718c2ecf20Sopenharmony_ci } 30728c2ecf20Sopenharmony_ci 30738c2ecf20Sopenharmony_ci sja1105_devlink_teardown(ds); 30748c2ecf20Sopenharmony_ci sja1105_flower_teardown(ds); 30758c2ecf20Sopenharmony_ci sja1105_tas_teardown(ds); 30768c2ecf20Sopenharmony_ci sja1105_ptp_clock_unregister(ds); 30778c2ecf20Sopenharmony_ci sja1105_static_config_free(&priv->static_config); 30788c2ecf20Sopenharmony_ci 30798c2ecf20Sopenharmony_ci list_for_each_entry_safe(v, n, &priv->dsa_8021q_vlans, list) { 30808c2ecf20Sopenharmony_ci list_del(&v->list); 30818c2ecf20Sopenharmony_ci kfree(v); 30828c2ecf20Sopenharmony_ci } 30838c2ecf20Sopenharmony_ci 30848c2ecf20Sopenharmony_ci list_for_each_entry_safe(v, n, &priv->bridge_vlans, list) { 30858c2ecf20Sopenharmony_ci list_del(&v->list); 30868c2ecf20Sopenharmony_ci kfree(v); 30878c2ecf20Sopenharmony_ci } 30888c2ecf20Sopenharmony_ci} 30898c2ecf20Sopenharmony_ci 30908c2ecf20Sopenharmony_cistatic int sja1105_port_enable(struct dsa_switch *ds, int port, 30918c2ecf20Sopenharmony_ci struct phy_device *phy) 30928c2ecf20Sopenharmony_ci{ 30938c2ecf20Sopenharmony_ci struct net_device *slave; 30948c2ecf20Sopenharmony_ci 30958c2ecf20Sopenharmony_ci if (!dsa_is_user_port(ds, port)) 30968c2ecf20Sopenharmony_ci return 0; 30978c2ecf20Sopenharmony_ci 30988c2ecf20Sopenharmony_ci slave = dsa_to_port(ds, port)->slave; 30998c2ecf20Sopenharmony_ci 31008c2ecf20Sopenharmony_ci slave->features &= ~NETIF_F_HW_VLAN_CTAG_FILTER; 31018c2ecf20Sopenharmony_ci 31028c2ecf20Sopenharmony_ci return 0; 31038c2ecf20Sopenharmony_ci} 31048c2ecf20Sopenharmony_ci 31058c2ecf20Sopenharmony_cistatic void sja1105_port_disable(struct dsa_switch *ds, int port) 31068c2ecf20Sopenharmony_ci{ 31078c2ecf20Sopenharmony_ci struct sja1105_private *priv = ds->priv; 31088c2ecf20Sopenharmony_ci struct sja1105_port *sp = &priv->ports[port]; 31098c2ecf20Sopenharmony_ci 31108c2ecf20Sopenharmony_ci if (!dsa_is_user_port(ds, port)) 31118c2ecf20Sopenharmony_ci return; 31128c2ecf20Sopenharmony_ci 31138c2ecf20Sopenharmony_ci kthread_cancel_work_sync(&sp->xmit_work); 31148c2ecf20Sopenharmony_ci skb_queue_purge(&sp->xmit_queue); 31158c2ecf20Sopenharmony_ci} 31168c2ecf20Sopenharmony_ci 31178c2ecf20Sopenharmony_cistatic int sja1105_mgmt_xmit(struct dsa_switch *ds, int port, int slot, 31188c2ecf20Sopenharmony_ci struct sk_buff *skb, bool takets) 31198c2ecf20Sopenharmony_ci{ 31208c2ecf20Sopenharmony_ci struct sja1105_mgmt_entry mgmt_route = {0}; 31218c2ecf20Sopenharmony_ci struct sja1105_private *priv = ds->priv; 31228c2ecf20Sopenharmony_ci struct ethhdr *hdr; 31238c2ecf20Sopenharmony_ci int timeout = 10; 31248c2ecf20Sopenharmony_ci int rc; 31258c2ecf20Sopenharmony_ci 31268c2ecf20Sopenharmony_ci hdr = eth_hdr(skb); 31278c2ecf20Sopenharmony_ci 31288c2ecf20Sopenharmony_ci mgmt_route.macaddr = ether_addr_to_u64(hdr->h_dest); 31298c2ecf20Sopenharmony_ci mgmt_route.destports = BIT(port); 31308c2ecf20Sopenharmony_ci mgmt_route.enfport = 1; 31318c2ecf20Sopenharmony_ci mgmt_route.tsreg = 0; 31328c2ecf20Sopenharmony_ci mgmt_route.takets = takets; 31338c2ecf20Sopenharmony_ci 31348c2ecf20Sopenharmony_ci rc = sja1105_dynamic_config_write(priv, BLK_IDX_MGMT_ROUTE, 31358c2ecf20Sopenharmony_ci slot, &mgmt_route, true); 31368c2ecf20Sopenharmony_ci if (rc < 0) { 31378c2ecf20Sopenharmony_ci kfree_skb(skb); 31388c2ecf20Sopenharmony_ci return rc; 31398c2ecf20Sopenharmony_ci } 31408c2ecf20Sopenharmony_ci 31418c2ecf20Sopenharmony_ci /* Transfer skb to the host port. */ 31428c2ecf20Sopenharmony_ci dsa_enqueue_skb(skb, dsa_to_port(ds, port)->slave); 31438c2ecf20Sopenharmony_ci 31448c2ecf20Sopenharmony_ci /* Wait until the switch has processed the frame */ 31458c2ecf20Sopenharmony_ci do { 31468c2ecf20Sopenharmony_ci rc = sja1105_dynamic_config_read(priv, BLK_IDX_MGMT_ROUTE, 31478c2ecf20Sopenharmony_ci slot, &mgmt_route); 31488c2ecf20Sopenharmony_ci if (rc < 0) { 31498c2ecf20Sopenharmony_ci dev_err_ratelimited(priv->ds->dev, 31508c2ecf20Sopenharmony_ci "failed to poll for mgmt route\n"); 31518c2ecf20Sopenharmony_ci continue; 31528c2ecf20Sopenharmony_ci } 31538c2ecf20Sopenharmony_ci 31548c2ecf20Sopenharmony_ci /* UM10944: The ENFPORT flag of the respective entry is 31558c2ecf20Sopenharmony_ci * cleared when a match is found. The host can use this 31568c2ecf20Sopenharmony_ci * flag as an acknowledgment. 31578c2ecf20Sopenharmony_ci */ 31588c2ecf20Sopenharmony_ci cpu_relax(); 31598c2ecf20Sopenharmony_ci } while (mgmt_route.enfport && --timeout); 31608c2ecf20Sopenharmony_ci 31618c2ecf20Sopenharmony_ci if (!timeout) { 31628c2ecf20Sopenharmony_ci /* Clean up the management route so that a follow-up 31638c2ecf20Sopenharmony_ci * frame may not match on it by mistake. 31648c2ecf20Sopenharmony_ci * This is only hardware supported on P/Q/R/S - on E/T it is 31658c2ecf20Sopenharmony_ci * a no-op and we are silently discarding the -EOPNOTSUPP. 31668c2ecf20Sopenharmony_ci */ 31678c2ecf20Sopenharmony_ci sja1105_dynamic_config_write(priv, BLK_IDX_MGMT_ROUTE, 31688c2ecf20Sopenharmony_ci slot, &mgmt_route, false); 31698c2ecf20Sopenharmony_ci dev_err_ratelimited(priv->ds->dev, "xmit timed out\n"); 31708c2ecf20Sopenharmony_ci } 31718c2ecf20Sopenharmony_ci 31728c2ecf20Sopenharmony_ci return NETDEV_TX_OK; 31738c2ecf20Sopenharmony_ci} 31748c2ecf20Sopenharmony_ci 31758c2ecf20Sopenharmony_ci#define work_to_port(work) \ 31768c2ecf20Sopenharmony_ci container_of((work), struct sja1105_port, xmit_work) 31778c2ecf20Sopenharmony_ci#define tagger_to_sja1105(t) \ 31788c2ecf20Sopenharmony_ci container_of((t), struct sja1105_private, tagger_data) 31798c2ecf20Sopenharmony_ci 31808c2ecf20Sopenharmony_ci/* Deferred work is unfortunately necessary because setting up the management 31818c2ecf20Sopenharmony_ci * route cannot be done from atomit context (SPI transfer takes a sleepable 31828c2ecf20Sopenharmony_ci * lock on the bus) 31838c2ecf20Sopenharmony_ci */ 31848c2ecf20Sopenharmony_cistatic void sja1105_port_deferred_xmit(struct kthread_work *work) 31858c2ecf20Sopenharmony_ci{ 31868c2ecf20Sopenharmony_ci struct sja1105_port *sp = work_to_port(work); 31878c2ecf20Sopenharmony_ci struct sja1105_tagger_data *tagger_data = sp->data; 31888c2ecf20Sopenharmony_ci struct sja1105_private *priv = tagger_to_sja1105(tagger_data); 31898c2ecf20Sopenharmony_ci int port = sp - priv->ports; 31908c2ecf20Sopenharmony_ci struct sk_buff *skb; 31918c2ecf20Sopenharmony_ci 31928c2ecf20Sopenharmony_ci while ((skb = skb_dequeue(&sp->xmit_queue)) != NULL) { 31938c2ecf20Sopenharmony_ci struct sk_buff *clone = DSA_SKB_CB(skb)->clone; 31948c2ecf20Sopenharmony_ci 31958c2ecf20Sopenharmony_ci mutex_lock(&priv->mgmt_lock); 31968c2ecf20Sopenharmony_ci 31978c2ecf20Sopenharmony_ci sja1105_mgmt_xmit(priv->ds, port, 0, skb, !!clone); 31988c2ecf20Sopenharmony_ci 31998c2ecf20Sopenharmony_ci /* The clone, if there, was made by dsa_skb_tx_timestamp */ 32008c2ecf20Sopenharmony_ci if (clone) 32018c2ecf20Sopenharmony_ci sja1105_ptp_txtstamp_skb(priv->ds, port, clone); 32028c2ecf20Sopenharmony_ci 32038c2ecf20Sopenharmony_ci mutex_unlock(&priv->mgmt_lock); 32048c2ecf20Sopenharmony_ci } 32058c2ecf20Sopenharmony_ci} 32068c2ecf20Sopenharmony_ci 32078c2ecf20Sopenharmony_ci/* The MAXAGE setting belongs to the L2 Forwarding Parameters table, 32088c2ecf20Sopenharmony_ci * which cannot be reconfigured at runtime. So a switch reset is required. 32098c2ecf20Sopenharmony_ci */ 32108c2ecf20Sopenharmony_cistatic int sja1105_set_ageing_time(struct dsa_switch *ds, 32118c2ecf20Sopenharmony_ci unsigned int ageing_time) 32128c2ecf20Sopenharmony_ci{ 32138c2ecf20Sopenharmony_ci struct sja1105_l2_lookup_params_entry *l2_lookup_params; 32148c2ecf20Sopenharmony_ci struct sja1105_private *priv = ds->priv; 32158c2ecf20Sopenharmony_ci struct sja1105_table *table; 32168c2ecf20Sopenharmony_ci unsigned int maxage; 32178c2ecf20Sopenharmony_ci 32188c2ecf20Sopenharmony_ci table = &priv->static_config.tables[BLK_IDX_L2_LOOKUP_PARAMS]; 32198c2ecf20Sopenharmony_ci l2_lookup_params = table->entries; 32208c2ecf20Sopenharmony_ci 32218c2ecf20Sopenharmony_ci maxage = SJA1105_AGEING_TIME_MS(ageing_time); 32228c2ecf20Sopenharmony_ci 32238c2ecf20Sopenharmony_ci if (l2_lookup_params->maxage == maxage) 32248c2ecf20Sopenharmony_ci return 0; 32258c2ecf20Sopenharmony_ci 32268c2ecf20Sopenharmony_ci l2_lookup_params->maxage = maxage; 32278c2ecf20Sopenharmony_ci 32288c2ecf20Sopenharmony_ci return sja1105_static_config_reload(priv, SJA1105_AGEING_TIME); 32298c2ecf20Sopenharmony_ci} 32308c2ecf20Sopenharmony_ci 32318c2ecf20Sopenharmony_cistatic int sja1105_change_mtu(struct dsa_switch *ds, int port, int new_mtu) 32328c2ecf20Sopenharmony_ci{ 32338c2ecf20Sopenharmony_ci struct sja1105_l2_policing_entry *policing; 32348c2ecf20Sopenharmony_ci struct sja1105_private *priv = ds->priv; 32358c2ecf20Sopenharmony_ci 32368c2ecf20Sopenharmony_ci new_mtu += VLAN_ETH_HLEN + ETH_FCS_LEN; 32378c2ecf20Sopenharmony_ci 32388c2ecf20Sopenharmony_ci if (dsa_is_cpu_port(ds, port)) 32398c2ecf20Sopenharmony_ci new_mtu += VLAN_HLEN; 32408c2ecf20Sopenharmony_ci 32418c2ecf20Sopenharmony_ci policing = priv->static_config.tables[BLK_IDX_L2_POLICING].entries; 32428c2ecf20Sopenharmony_ci 32438c2ecf20Sopenharmony_ci if (policing[port].maxlen == new_mtu) 32448c2ecf20Sopenharmony_ci return 0; 32458c2ecf20Sopenharmony_ci 32468c2ecf20Sopenharmony_ci policing[port].maxlen = new_mtu; 32478c2ecf20Sopenharmony_ci 32488c2ecf20Sopenharmony_ci return sja1105_static_config_reload(priv, SJA1105_BEST_EFFORT_POLICING); 32498c2ecf20Sopenharmony_ci} 32508c2ecf20Sopenharmony_ci 32518c2ecf20Sopenharmony_cistatic int sja1105_get_max_mtu(struct dsa_switch *ds, int port) 32528c2ecf20Sopenharmony_ci{ 32538c2ecf20Sopenharmony_ci return 2043 - VLAN_ETH_HLEN - ETH_FCS_LEN; 32548c2ecf20Sopenharmony_ci} 32558c2ecf20Sopenharmony_ci 32568c2ecf20Sopenharmony_cistatic int sja1105_port_setup_tc(struct dsa_switch *ds, int port, 32578c2ecf20Sopenharmony_ci enum tc_setup_type type, 32588c2ecf20Sopenharmony_ci void *type_data) 32598c2ecf20Sopenharmony_ci{ 32608c2ecf20Sopenharmony_ci switch (type) { 32618c2ecf20Sopenharmony_ci case TC_SETUP_QDISC_TAPRIO: 32628c2ecf20Sopenharmony_ci return sja1105_setup_tc_taprio(ds, port, type_data); 32638c2ecf20Sopenharmony_ci case TC_SETUP_QDISC_CBS: 32648c2ecf20Sopenharmony_ci return sja1105_setup_tc_cbs(ds, port, type_data); 32658c2ecf20Sopenharmony_ci default: 32668c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 32678c2ecf20Sopenharmony_ci } 32688c2ecf20Sopenharmony_ci} 32698c2ecf20Sopenharmony_ci 32708c2ecf20Sopenharmony_ci/* We have a single mirror (@to) port, but can configure ingress and egress 32718c2ecf20Sopenharmony_ci * mirroring on all other (@from) ports. 32728c2ecf20Sopenharmony_ci * We need to allow mirroring rules only as long as the @to port is always the 32738c2ecf20Sopenharmony_ci * same, and we need to unset the @to port from mirr_port only when there is no 32748c2ecf20Sopenharmony_ci * mirroring rule that references it. 32758c2ecf20Sopenharmony_ci */ 32768c2ecf20Sopenharmony_cistatic int sja1105_mirror_apply(struct sja1105_private *priv, int from, int to, 32778c2ecf20Sopenharmony_ci bool ingress, bool enabled) 32788c2ecf20Sopenharmony_ci{ 32798c2ecf20Sopenharmony_ci struct sja1105_general_params_entry *general_params; 32808c2ecf20Sopenharmony_ci struct sja1105_mac_config_entry *mac; 32818c2ecf20Sopenharmony_ci struct sja1105_table *table; 32828c2ecf20Sopenharmony_ci bool already_enabled; 32838c2ecf20Sopenharmony_ci u64 new_mirr_port; 32848c2ecf20Sopenharmony_ci int rc; 32858c2ecf20Sopenharmony_ci 32868c2ecf20Sopenharmony_ci table = &priv->static_config.tables[BLK_IDX_GENERAL_PARAMS]; 32878c2ecf20Sopenharmony_ci general_params = table->entries; 32888c2ecf20Sopenharmony_ci 32898c2ecf20Sopenharmony_ci mac = priv->static_config.tables[BLK_IDX_MAC_CONFIG].entries; 32908c2ecf20Sopenharmony_ci 32918c2ecf20Sopenharmony_ci already_enabled = (general_params->mirr_port != SJA1105_NUM_PORTS); 32928c2ecf20Sopenharmony_ci if (already_enabled && enabled && general_params->mirr_port != to) { 32938c2ecf20Sopenharmony_ci dev_err(priv->ds->dev, 32948c2ecf20Sopenharmony_ci "Delete mirroring rules towards port %llu first\n", 32958c2ecf20Sopenharmony_ci general_params->mirr_port); 32968c2ecf20Sopenharmony_ci return -EBUSY; 32978c2ecf20Sopenharmony_ci } 32988c2ecf20Sopenharmony_ci 32998c2ecf20Sopenharmony_ci new_mirr_port = to; 33008c2ecf20Sopenharmony_ci if (!enabled) { 33018c2ecf20Sopenharmony_ci bool keep = false; 33028c2ecf20Sopenharmony_ci int port; 33038c2ecf20Sopenharmony_ci 33048c2ecf20Sopenharmony_ci /* Anybody still referencing mirr_port? */ 33058c2ecf20Sopenharmony_ci for (port = 0; port < SJA1105_NUM_PORTS; port++) { 33068c2ecf20Sopenharmony_ci if (mac[port].ing_mirr || mac[port].egr_mirr) { 33078c2ecf20Sopenharmony_ci keep = true; 33088c2ecf20Sopenharmony_ci break; 33098c2ecf20Sopenharmony_ci } 33108c2ecf20Sopenharmony_ci } 33118c2ecf20Sopenharmony_ci /* Unset already_enabled for next time */ 33128c2ecf20Sopenharmony_ci if (!keep) 33138c2ecf20Sopenharmony_ci new_mirr_port = SJA1105_NUM_PORTS; 33148c2ecf20Sopenharmony_ci } 33158c2ecf20Sopenharmony_ci if (new_mirr_port != general_params->mirr_port) { 33168c2ecf20Sopenharmony_ci general_params->mirr_port = new_mirr_port; 33178c2ecf20Sopenharmony_ci 33188c2ecf20Sopenharmony_ci rc = sja1105_dynamic_config_write(priv, BLK_IDX_GENERAL_PARAMS, 33198c2ecf20Sopenharmony_ci 0, general_params, true); 33208c2ecf20Sopenharmony_ci if (rc < 0) 33218c2ecf20Sopenharmony_ci return rc; 33228c2ecf20Sopenharmony_ci } 33238c2ecf20Sopenharmony_ci 33248c2ecf20Sopenharmony_ci if (ingress) 33258c2ecf20Sopenharmony_ci mac[from].ing_mirr = enabled; 33268c2ecf20Sopenharmony_ci else 33278c2ecf20Sopenharmony_ci mac[from].egr_mirr = enabled; 33288c2ecf20Sopenharmony_ci 33298c2ecf20Sopenharmony_ci return sja1105_dynamic_config_write(priv, BLK_IDX_MAC_CONFIG, from, 33308c2ecf20Sopenharmony_ci &mac[from], true); 33318c2ecf20Sopenharmony_ci} 33328c2ecf20Sopenharmony_ci 33338c2ecf20Sopenharmony_cistatic int sja1105_mirror_add(struct dsa_switch *ds, int port, 33348c2ecf20Sopenharmony_ci struct dsa_mall_mirror_tc_entry *mirror, 33358c2ecf20Sopenharmony_ci bool ingress) 33368c2ecf20Sopenharmony_ci{ 33378c2ecf20Sopenharmony_ci return sja1105_mirror_apply(ds->priv, port, mirror->to_local_port, 33388c2ecf20Sopenharmony_ci ingress, true); 33398c2ecf20Sopenharmony_ci} 33408c2ecf20Sopenharmony_ci 33418c2ecf20Sopenharmony_cistatic void sja1105_mirror_del(struct dsa_switch *ds, int port, 33428c2ecf20Sopenharmony_ci struct dsa_mall_mirror_tc_entry *mirror) 33438c2ecf20Sopenharmony_ci{ 33448c2ecf20Sopenharmony_ci sja1105_mirror_apply(ds->priv, port, mirror->to_local_port, 33458c2ecf20Sopenharmony_ci mirror->ingress, false); 33468c2ecf20Sopenharmony_ci} 33478c2ecf20Sopenharmony_ci 33488c2ecf20Sopenharmony_cistatic int sja1105_port_policer_add(struct dsa_switch *ds, int port, 33498c2ecf20Sopenharmony_ci struct dsa_mall_policer_tc_entry *policer) 33508c2ecf20Sopenharmony_ci{ 33518c2ecf20Sopenharmony_ci struct sja1105_l2_policing_entry *policing; 33528c2ecf20Sopenharmony_ci struct sja1105_private *priv = ds->priv; 33538c2ecf20Sopenharmony_ci 33548c2ecf20Sopenharmony_ci policing = priv->static_config.tables[BLK_IDX_L2_POLICING].entries; 33558c2ecf20Sopenharmony_ci 33568c2ecf20Sopenharmony_ci /* In hardware, every 8 microseconds the credit level is incremented by 33578c2ecf20Sopenharmony_ci * the value of RATE bytes divided by 64, up to a maximum of SMAX 33588c2ecf20Sopenharmony_ci * bytes. 33598c2ecf20Sopenharmony_ci */ 33608c2ecf20Sopenharmony_ci policing[port].rate = div_u64(512 * policer->rate_bytes_per_sec, 33618c2ecf20Sopenharmony_ci 1000000); 33628c2ecf20Sopenharmony_ci policing[port].smax = policer->burst; 33638c2ecf20Sopenharmony_ci 33648c2ecf20Sopenharmony_ci return sja1105_static_config_reload(priv, SJA1105_BEST_EFFORT_POLICING); 33658c2ecf20Sopenharmony_ci} 33668c2ecf20Sopenharmony_ci 33678c2ecf20Sopenharmony_cistatic void sja1105_port_policer_del(struct dsa_switch *ds, int port) 33688c2ecf20Sopenharmony_ci{ 33698c2ecf20Sopenharmony_ci struct sja1105_l2_policing_entry *policing; 33708c2ecf20Sopenharmony_ci struct sja1105_private *priv = ds->priv; 33718c2ecf20Sopenharmony_ci 33728c2ecf20Sopenharmony_ci policing = priv->static_config.tables[BLK_IDX_L2_POLICING].entries; 33738c2ecf20Sopenharmony_ci 33748c2ecf20Sopenharmony_ci policing[port].rate = SJA1105_RATE_MBPS(1000); 33758c2ecf20Sopenharmony_ci policing[port].smax = 65535; 33768c2ecf20Sopenharmony_ci 33778c2ecf20Sopenharmony_ci sja1105_static_config_reload(priv, SJA1105_BEST_EFFORT_POLICING); 33788c2ecf20Sopenharmony_ci} 33798c2ecf20Sopenharmony_ci 33808c2ecf20Sopenharmony_cistatic const struct dsa_switch_ops sja1105_switch_ops = { 33818c2ecf20Sopenharmony_ci .get_tag_protocol = sja1105_get_tag_protocol, 33828c2ecf20Sopenharmony_ci .setup = sja1105_setup, 33838c2ecf20Sopenharmony_ci .teardown = sja1105_teardown, 33848c2ecf20Sopenharmony_ci .set_ageing_time = sja1105_set_ageing_time, 33858c2ecf20Sopenharmony_ci .port_change_mtu = sja1105_change_mtu, 33868c2ecf20Sopenharmony_ci .port_max_mtu = sja1105_get_max_mtu, 33878c2ecf20Sopenharmony_ci .phylink_validate = sja1105_phylink_validate, 33888c2ecf20Sopenharmony_ci .phylink_mac_link_state = sja1105_mac_pcs_get_state, 33898c2ecf20Sopenharmony_ci .phylink_mac_config = sja1105_mac_config, 33908c2ecf20Sopenharmony_ci .phylink_mac_link_up = sja1105_mac_link_up, 33918c2ecf20Sopenharmony_ci .phylink_mac_link_down = sja1105_mac_link_down, 33928c2ecf20Sopenharmony_ci .get_strings = sja1105_get_strings, 33938c2ecf20Sopenharmony_ci .get_ethtool_stats = sja1105_get_ethtool_stats, 33948c2ecf20Sopenharmony_ci .get_sset_count = sja1105_get_sset_count, 33958c2ecf20Sopenharmony_ci .get_ts_info = sja1105_get_ts_info, 33968c2ecf20Sopenharmony_ci .port_enable = sja1105_port_enable, 33978c2ecf20Sopenharmony_ci .port_disable = sja1105_port_disable, 33988c2ecf20Sopenharmony_ci .port_fdb_dump = sja1105_fdb_dump, 33998c2ecf20Sopenharmony_ci .port_fdb_add = sja1105_fdb_add, 34008c2ecf20Sopenharmony_ci .port_fdb_del = sja1105_fdb_del, 34018c2ecf20Sopenharmony_ci .port_bridge_join = sja1105_bridge_join, 34028c2ecf20Sopenharmony_ci .port_bridge_leave = sja1105_bridge_leave, 34038c2ecf20Sopenharmony_ci .port_stp_state_set = sja1105_bridge_stp_state_set, 34048c2ecf20Sopenharmony_ci .port_vlan_prepare = sja1105_vlan_prepare, 34058c2ecf20Sopenharmony_ci .port_vlan_filtering = sja1105_vlan_filtering, 34068c2ecf20Sopenharmony_ci .port_vlan_add = sja1105_vlan_add, 34078c2ecf20Sopenharmony_ci .port_vlan_del = sja1105_vlan_del, 34088c2ecf20Sopenharmony_ci .port_mdb_prepare = sja1105_mdb_prepare, 34098c2ecf20Sopenharmony_ci .port_mdb_add = sja1105_mdb_add, 34108c2ecf20Sopenharmony_ci .port_mdb_del = sja1105_mdb_del, 34118c2ecf20Sopenharmony_ci .port_hwtstamp_get = sja1105_hwtstamp_get, 34128c2ecf20Sopenharmony_ci .port_hwtstamp_set = sja1105_hwtstamp_set, 34138c2ecf20Sopenharmony_ci .port_rxtstamp = sja1105_port_rxtstamp, 34148c2ecf20Sopenharmony_ci .port_txtstamp = sja1105_port_txtstamp, 34158c2ecf20Sopenharmony_ci .port_setup_tc = sja1105_port_setup_tc, 34168c2ecf20Sopenharmony_ci .port_mirror_add = sja1105_mirror_add, 34178c2ecf20Sopenharmony_ci .port_mirror_del = sja1105_mirror_del, 34188c2ecf20Sopenharmony_ci .port_policer_add = sja1105_port_policer_add, 34198c2ecf20Sopenharmony_ci .port_policer_del = sja1105_port_policer_del, 34208c2ecf20Sopenharmony_ci .cls_flower_add = sja1105_cls_flower_add, 34218c2ecf20Sopenharmony_ci .cls_flower_del = sja1105_cls_flower_del, 34228c2ecf20Sopenharmony_ci .cls_flower_stats = sja1105_cls_flower_stats, 34238c2ecf20Sopenharmony_ci .crosschip_bridge_join = sja1105_crosschip_bridge_join, 34248c2ecf20Sopenharmony_ci .crosschip_bridge_leave = sja1105_crosschip_bridge_leave, 34258c2ecf20Sopenharmony_ci .devlink_param_get = sja1105_devlink_param_get, 34268c2ecf20Sopenharmony_ci .devlink_param_set = sja1105_devlink_param_set, 34278c2ecf20Sopenharmony_ci .devlink_info_get = sja1105_devlink_info_get, 34288c2ecf20Sopenharmony_ci}; 34298c2ecf20Sopenharmony_ci 34308c2ecf20Sopenharmony_cistatic const struct of_device_id sja1105_dt_ids[]; 34318c2ecf20Sopenharmony_ci 34328c2ecf20Sopenharmony_cistatic int sja1105_check_device_id(struct sja1105_private *priv) 34338c2ecf20Sopenharmony_ci{ 34348c2ecf20Sopenharmony_ci const struct sja1105_regs *regs = priv->info->regs; 34358c2ecf20Sopenharmony_ci u8 prod_id[SJA1105_SIZE_DEVICE_ID] = {0}; 34368c2ecf20Sopenharmony_ci struct device *dev = &priv->spidev->dev; 34378c2ecf20Sopenharmony_ci const struct of_device_id *match; 34388c2ecf20Sopenharmony_ci u32 device_id; 34398c2ecf20Sopenharmony_ci u64 part_no; 34408c2ecf20Sopenharmony_ci int rc; 34418c2ecf20Sopenharmony_ci 34428c2ecf20Sopenharmony_ci rc = sja1105_xfer_u32(priv, SPI_READ, regs->device_id, &device_id, 34438c2ecf20Sopenharmony_ci NULL); 34448c2ecf20Sopenharmony_ci if (rc < 0) 34458c2ecf20Sopenharmony_ci return rc; 34468c2ecf20Sopenharmony_ci 34478c2ecf20Sopenharmony_ci rc = sja1105_xfer_buf(priv, SPI_READ, regs->prod_id, prod_id, 34488c2ecf20Sopenharmony_ci SJA1105_SIZE_DEVICE_ID); 34498c2ecf20Sopenharmony_ci if (rc < 0) 34508c2ecf20Sopenharmony_ci return rc; 34518c2ecf20Sopenharmony_ci 34528c2ecf20Sopenharmony_ci sja1105_unpack(prod_id, &part_no, 19, 4, SJA1105_SIZE_DEVICE_ID); 34538c2ecf20Sopenharmony_ci 34548c2ecf20Sopenharmony_ci for (match = sja1105_dt_ids; match->compatible[0]; match++) { 34558c2ecf20Sopenharmony_ci const struct sja1105_info *info = match->data; 34568c2ecf20Sopenharmony_ci 34578c2ecf20Sopenharmony_ci /* Is what's been probed in our match table at all? */ 34588c2ecf20Sopenharmony_ci if (info->device_id != device_id || info->part_no != part_no) 34598c2ecf20Sopenharmony_ci continue; 34608c2ecf20Sopenharmony_ci 34618c2ecf20Sopenharmony_ci /* But is it what's in the device tree? */ 34628c2ecf20Sopenharmony_ci if (priv->info->device_id != device_id || 34638c2ecf20Sopenharmony_ci priv->info->part_no != part_no) { 34648c2ecf20Sopenharmony_ci dev_warn(dev, "Device tree specifies chip %s but found %s, please fix it!\n", 34658c2ecf20Sopenharmony_ci priv->info->name, info->name); 34668c2ecf20Sopenharmony_ci /* It isn't. No problem, pick that up. */ 34678c2ecf20Sopenharmony_ci priv->info = info; 34688c2ecf20Sopenharmony_ci } 34698c2ecf20Sopenharmony_ci 34708c2ecf20Sopenharmony_ci return 0; 34718c2ecf20Sopenharmony_ci } 34728c2ecf20Sopenharmony_ci 34738c2ecf20Sopenharmony_ci dev_err(dev, "Unexpected {device ID, part number}: 0x%x 0x%llx\n", 34748c2ecf20Sopenharmony_ci device_id, part_no); 34758c2ecf20Sopenharmony_ci 34768c2ecf20Sopenharmony_ci return -ENODEV; 34778c2ecf20Sopenharmony_ci} 34788c2ecf20Sopenharmony_ci 34798c2ecf20Sopenharmony_cistatic int sja1105_probe(struct spi_device *spi) 34808c2ecf20Sopenharmony_ci{ 34818c2ecf20Sopenharmony_ci struct sja1105_tagger_data *tagger_data; 34828c2ecf20Sopenharmony_ci struct device *dev = &spi->dev; 34838c2ecf20Sopenharmony_ci struct sja1105_private *priv; 34848c2ecf20Sopenharmony_ci struct dsa_switch *ds; 34858c2ecf20Sopenharmony_ci int rc, port; 34868c2ecf20Sopenharmony_ci 34878c2ecf20Sopenharmony_ci if (!dev->of_node) { 34888c2ecf20Sopenharmony_ci dev_err(dev, "No DTS bindings for SJA1105 driver\n"); 34898c2ecf20Sopenharmony_ci return -EINVAL; 34908c2ecf20Sopenharmony_ci } 34918c2ecf20Sopenharmony_ci 34928c2ecf20Sopenharmony_ci priv = devm_kzalloc(dev, sizeof(struct sja1105_private), GFP_KERNEL); 34938c2ecf20Sopenharmony_ci if (!priv) 34948c2ecf20Sopenharmony_ci return -ENOMEM; 34958c2ecf20Sopenharmony_ci 34968c2ecf20Sopenharmony_ci /* Configure the optional reset pin and bring up switch */ 34978c2ecf20Sopenharmony_ci priv->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH); 34988c2ecf20Sopenharmony_ci if (IS_ERR(priv->reset_gpio)) 34998c2ecf20Sopenharmony_ci dev_dbg(dev, "reset-gpios not defined, ignoring\n"); 35008c2ecf20Sopenharmony_ci else 35018c2ecf20Sopenharmony_ci sja1105_hw_reset(priv->reset_gpio, 1, 1); 35028c2ecf20Sopenharmony_ci 35038c2ecf20Sopenharmony_ci /* Populate our driver private structure (priv) based on 35048c2ecf20Sopenharmony_ci * the device tree node that was probed (spi) 35058c2ecf20Sopenharmony_ci */ 35068c2ecf20Sopenharmony_ci priv->spidev = spi; 35078c2ecf20Sopenharmony_ci spi_set_drvdata(spi, priv); 35088c2ecf20Sopenharmony_ci 35098c2ecf20Sopenharmony_ci /* Configure the SPI bus */ 35108c2ecf20Sopenharmony_ci spi->bits_per_word = 8; 35118c2ecf20Sopenharmony_ci rc = spi_setup(spi); 35128c2ecf20Sopenharmony_ci if (rc < 0) { 35138c2ecf20Sopenharmony_ci dev_err(dev, "Could not init SPI\n"); 35148c2ecf20Sopenharmony_ci return rc; 35158c2ecf20Sopenharmony_ci } 35168c2ecf20Sopenharmony_ci 35178c2ecf20Sopenharmony_ci priv->info = of_device_get_match_data(dev); 35188c2ecf20Sopenharmony_ci 35198c2ecf20Sopenharmony_ci /* Detect hardware device */ 35208c2ecf20Sopenharmony_ci rc = sja1105_check_device_id(priv); 35218c2ecf20Sopenharmony_ci if (rc < 0) { 35228c2ecf20Sopenharmony_ci dev_err(dev, "Device ID check failed: %d\n", rc); 35238c2ecf20Sopenharmony_ci return rc; 35248c2ecf20Sopenharmony_ci } 35258c2ecf20Sopenharmony_ci 35268c2ecf20Sopenharmony_ci dev_info(dev, "Probed switch chip: %s\n", priv->info->name); 35278c2ecf20Sopenharmony_ci 35288c2ecf20Sopenharmony_ci ds = devm_kzalloc(dev, sizeof(*ds), GFP_KERNEL); 35298c2ecf20Sopenharmony_ci if (!ds) 35308c2ecf20Sopenharmony_ci return -ENOMEM; 35318c2ecf20Sopenharmony_ci 35328c2ecf20Sopenharmony_ci ds->dev = dev; 35338c2ecf20Sopenharmony_ci ds->num_ports = SJA1105_NUM_PORTS; 35348c2ecf20Sopenharmony_ci ds->ops = &sja1105_switch_ops; 35358c2ecf20Sopenharmony_ci ds->priv = priv; 35368c2ecf20Sopenharmony_ci priv->ds = ds; 35378c2ecf20Sopenharmony_ci 35388c2ecf20Sopenharmony_ci tagger_data = &priv->tagger_data; 35398c2ecf20Sopenharmony_ci 35408c2ecf20Sopenharmony_ci mutex_init(&priv->ptp_data.lock); 35418c2ecf20Sopenharmony_ci mutex_init(&priv->mgmt_lock); 35428c2ecf20Sopenharmony_ci 35438c2ecf20Sopenharmony_ci priv->dsa_8021q_ctx = devm_kzalloc(dev, sizeof(*priv->dsa_8021q_ctx), 35448c2ecf20Sopenharmony_ci GFP_KERNEL); 35458c2ecf20Sopenharmony_ci if (!priv->dsa_8021q_ctx) 35468c2ecf20Sopenharmony_ci return -ENOMEM; 35478c2ecf20Sopenharmony_ci 35488c2ecf20Sopenharmony_ci priv->dsa_8021q_ctx->ops = &sja1105_dsa_8021q_ops; 35498c2ecf20Sopenharmony_ci priv->dsa_8021q_ctx->proto = htons(ETH_P_8021Q); 35508c2ecf20Sopenharmony_ci priv->dsa_8021q_ctx->ds = ds; 35518c2ecf20Sopenharmony_ci 35528c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&priv->dsa_8021q_ctx->crosschip_links); 35538c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&priv->bridge_vlans); 35548c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&priv->dsa_8021q_vlans); 35558c2ecf20Sopenharmony_ci 35568c2ecf20Sopenharmony_ci sja1105_tas_setup(ds); 35578c2ecf20Sopenharmony_ci sja1105_flower_setup(ds); 35588c2ecf20Sopenharmony_ci 35598c2ecf20Sopenharmony_ci rc = dsa_register_switch(priv->ds); 35608c2ecf20Sopenharmony_ci if (rc) 35618c2ecf20Sopenharmony_ci return rc; 35628c2ecf20Sopenharmony_ci 35638c2ecf20Sopenharmony_ci if (IS_ENABLED(CONFIG_NET_SCH_CBS)) { 35648c2ecf20Sopenharmony_ci priv->cbs = devm_kcalloc(dev, priv->info->num_cbs_shapers, 35658c2ecf20Sopenharmony_ci sizeof(struct sja1105_cbs_entry), 35668c2ecf20Sopenharmony_ci GFP_KERNEL); 35678c2ecf20Sopenharmony_ci if (!priv->cbs) { 35688c2ecf20Sopenharmony_ci rc = -ENOMEM; 35698c2ecf20Sopenharmony_ci goto out_unregister_switch; 35708c2ecf20Sopenharmony_ci } 35718c2ecf20Sopenharmony_ci } 35728c2ecf20Sopenharmony_ci 35738c2ecf20Sopenharmony_ci /* Connections between dsa_port and sja1105_port */ 35748c2ecf20Sopenharmony_ci for (port = 0; port < SJA1105_NUM_PORTS; port++) { 35758c2ecf20Sopenharmony_ci struct sja1105_port *sp = &priv->ports[port]; 35768c2ecf20Sopenharmony_ci struct dsa_port *dp = dsa_to_port(ds, port); 35778c2ecf20Sopenharmony_ci struct net_device *slave; 35788c2ecf20Sopenharmony_ci int subvlan; 35798c2ecf20Sopenharmony_ci 35808c2ecf20Sopenharmony_ci if (!dsa_is_user_port(ds, port)) 35818c2ecf20Sopenharmony_ci continue; 35828c2ecf20Sopenharmony_ci 35838c2ecf20Sopenharmony_ci dp->priv = sp; 35848c2ecf20Sopenharmony_ci sp->dp = dp; 35858c2ecf20Sopenharmony_ci sp->data = tagger_data; 35868c2ecf20Sopenharmony_ci slave = dp->slave; 35878c2ecf20Sopenharmony_ci kthread_init_work(&sp->xmit_work, sja1105_port_deferred_xmit); 35888c2ecf20Sopenharmony_ci sp->xmit_worker = kthread_create_worker(0, "%s_xmit", 35898c2ecf20Sopenharmony_ci slave->name); 35908c2ecf20Sopenharmony_ci if (IS_ERR(sp->xmit_worker)) { 35918c2ecf20Sopenharmony_ci rc = PTR_ERR(sp->xmit_worker); 35928c2ecf20Sopenharmony_ci dev_err(ds->dev, 35938c2ecf20Sopenharmony_ci "failed to create deferred xmit thread: %d\n", 35948c2ecf20Sopenharmony_ci rc); 35958c2ecf20Sopenharmony_ci goto out_destroy_workers; 35968c2ecf20Sopenharmony_ci } 35978c2ecf20Sopenharmony_ci skb_queue_head_init(&sp->xmit_queue); 35988c2ecf20Sopenharmony_ci sp->xmit_tpid = ETH_P_SJA1105; 35998c2ecf20Sopenharmony_ci 36008c2ecf20Sopenharmony_ci for (subvlan = 0; subvlan < DSA_8021Q_N_SUBVLAN; subvlan++) 36018c2ecf20Sopenharmony_ci sp->subvlan_map[subvlan] = VLAN_N_VID; 36028c2ecf20Sopenharmony_ci } 36038c2ecf20Sopenharmony_ci 36048c2ecf20Sopenharmony_ci return 0; 36058c2ecf20Sopenharmony_ci 36068c2ecf20Sopenharmony_ciout_destroy_workers: 36078c2ecf20Sopenharmony_ci while (port-- > 0) { 36088c2ecf20Sopenharmony_ci struct sja1105_port *sp = &priv->ports[port]; 36098c2ecf20Sopenharmony_ci 36108c2ecf20Sopenharmony_ci if (!dsa_is_user_port(ds, port)) 36118c2ecf20Sopenharmony_ci continue; 36128c2ecf20Sopenharmony_ci 36138c2ecf20Sopenharmony_ci kthread_destroy_worker(sp->xmit_worker); 36148c2ecf20Sopenharmony_ci } 36158c2ecf20Sopenharmony_ci 36168c2ecf20Sopenharmony_ciout_unregister_switch: 36178c2ecf20Sopenharmony_ci dsa_unregister_switch(ds); 36188c2ecf20Sopenharmony_ci 36198c2ecf20Sopenharmony_ci return rc; 36208c2ecf20Sopenharmony_ci} 36218c2ecf20Sopenharmony_ci 36228c2ecf20Sopenharmony_cistatic int sja1105_remove(struct spi_device *spi) 36238c2ecf20Sopenharmony_ci{ 36248c2ecf20Sopenharmony_ci struct sja1105_private *priv = spi_get_drvdata(spi); 36258c2ecf20Sopenharmony_ci 36268c2ecf20Sopenharmony_ci dsa_unregister_switch(priv->ds); 36278c2ecf20Sopenharmony_ci return 0; 36288c2ecf20Sopenharmony_ci} 36298c2ecf20Sopenharmony_ci 36308c2ecf20Sopenharmony_cistatic const struct of_device_id sja1105_dt_ids[] = { 36318c2ecf20Sopenharmony_ci { .compatible = "nxp,sja1105e", .data = &sja1105e_info }, 36328c2ecf20Sopenharmony_ci { .compatible = "nxp,sja1105t", .data = &sja1105t_info }, 36338c2ecf20Sopenharmony_ci { .compatible = "nxp,sja1105p", .data = &sja1105p_info }, 36348c2ecf20Sopenharmony_ci { .compatible = "nxp,sja1105q", .data = &sja1105q_info }, 36358c2ecf20Sopenharmony_ci { .compatible = "nxp,sja1105r", .data = &sja1105r_info }, 36368c2ecf20Sopenharmony_ci { .compatible = "nxp,sja1105s", .data = &sja1105s_info }, 36378c2ecf20Sopenharmony_ci { /* sentinel */ }, 36388c2ecf20Sopenharmony_ci}; 36398c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, sja1105_dt_ids); 36408c2ecf20Sopenharmony_ci 36418c2ecf20Sopenharmony_cistatic struct spi_driver sja1105_driver = { 36428c2ecf20Sopenharmony_ci .driver = { 36438c2ecf20Sopenharmony_ci .name = "sja1105", 36448c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 36458c2ecf20Sopenharmony_ci .of_match_table = of_match_ptr(sja1105_dt_ids), 36468c2ecf20Sopenharmony_ci }, 36478c2ecf20Sopenharmony_ci .probe = sja1105_probe, 36488c2ecf20Sopenharmony_ci .remove = sja1105_remove, 36498c2ecf20Sopenharmony_ci}; 36508c2ecf20Sopenharmony_ci 36518c2ecf20Sopenharmony_cimodule_spi_driver(sja1105_driver); 36528c2ecf20Sopenharmony_ci 36538c2ecf20Sopenharmony_ciMODULE_AUTHOR("Vladimir Oltean <olteanv@gmail.com>"); 36548c2ecf20Sopenharmony_ciMODULE_AUTHOR("Georg Waibel <georg.waibel@sensor-technik.de>"); 36558c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("SJA1105 Driver"); 36568c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 3657