diff options
Diffstat (limited to 'drivers/net/dsa/sja1105/sja1105_main.c')
-rw-r--r-- | drivers/net/dsa/sja1105/sja1105_main.c | 2561 |
1 files changed, 1888 insertions, 673 deletions
diff --git a/drivers/net/dsa/sja1105/sja1105_main.c b/drivers/net/dsa/sja1105/sja1105_main.c index 7edea5741a5f..412666111b0c 100644 --- a/drivers/net/dsa/sja1105/sja1105_main.c +++ b/drivers/net/dsa/sja1105/sja1105_main.c @@ -16,6 +16,7 @@ #include <linux/of_net.h> #include <linux/of_mdio.h> #include <linux/of_device.h> +#include <linux/pcs/pcs-xpcs.h> #include <linux/netdev_features.h> #include <linux/netdevice.h> #include <linux/if_bridge.h> @@ -24,39 +25,136 @@ #include "sja1105.h" #include "sja1105_tas.h" -static void sja1105_hw_reset(struct gpio_desc *gpio, unsigned int pulse_len, - unsigned int startup_delay) +#define SJA1105_UNKNOWN_MULTICAST 0x010000000000ull + +/* Configure the optional reset pin and bring up switch */ +static int sja1105_hw_reset(struct device *dev, unsigned int pulse_len, + unsigned int startup_delay) { + struct gpio_desc *gpio; + + gpio = gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH); + if (IS_ERR(gpio)) + return PTR_ERR(gpio); + + if (!gpio) + return 0; + gpiod_set_value_cansleep(gpio, 1); /* Wait for minimum reset pulse length */ msleep(pulse_len); gpiod_set_value_cansleep(gpio, 0); /* Wait until chip is ready after reset */ msleep(startup_delay); + + gpiod_put(gpio); + + return 0; } static void sja1105_port_allow_traffic(struct sja1105_l2_forwarding_entry *l2_fwd, int from, int to, bool allow) { - if (allow) { - l2_fwd[from].bc_domain |= BIT(to); + if (allow) l2_fwd[from].reach_port |= BIT(to); - l2_fwd[from].fl_domain |= BIT(to); - } else { - l2_fwd[from].bc_domain &= ~BIT(to); + else l2_fwd[from].reach_port &= ~BIT(to); - l2_fwd[from].fl_domain &= ~BIT(to); - } } -/* Structure used to temporarily transport device tree - * settings into sja1105_setup - */ -struct sja1105_dt_port { - phy_interface_t phy_mode; - sja1105_mii_role_t role; -}; +static bool sja1105_can_forward(struct sja1105_l2_forwarding_entry *l2_fwd, + int from, int to) +{ + return !!(l2_fwd[from].reach_port & BIT(to)); +} + +static int sja1105_is_vlan_configured(struct sja1105_private *priv, u16 vid) +{ + struct sja1105_vlan_lookup_entry *vlan; + int count, i; + + vlan = priv->static_config.tables[BLK_IDX_VLAN_LOOKUP].entries; + count = priv->static_config.tables[BLK_IDX_VLAN_LOOKUP].entry_count; + + for (i = 0; i < count; i++) + if (vlan[i].vlanid == vid) + return i; + + /* Return an invalid entry index if not found */ + return -1; +} + +static int sja1105_drop_untagged(struct dsa_switch *ds, int port, bool drop) +{ + struct sja1105_private *priv = ds->priv; + struct sja1105_mac_config_entry *mac; + + mac = priv->static_config.tables[BLK_IDX_MAC_CONFIG].entries; + + if (mac[port].drpuntag == drop) + return 0; + + mac[port].drpuntag = drop; + + return sja1105_dynamic_config_write(priv, BLK_IDX_MAC_CONFIG, port, + &mac[port], true); +} + +static int sja1105_pvid_apply(struct sja1105_private *priv, int port, u16 pvid) +{ + struct sja1105_mac_config_entry *mac; + + mac = priv->static_config.tables[BLK_IDX_MAC_CONFIG].entries; + + if (mac[port].vlanid == pvid) + return 0; + + mac[port].vlanid = pvid; + + return sja1105_dynamic_config_write(priv, BLK_IDX_MAC_CONFIG, port, + &mac[port], true); +} + +static int sja1105_commit_pvid(struct dsa_switch *ds, int port) +{ + struct dsa_port *dp = dsa_to_port(ds, port); + struct net_device *br = dsa_port_bridge_dev_get(dp); + struct sja1105_private *priv = ds->priv; + struct sja1105_vlan_lookup_entry *vlan; + bool drop_untagged = false; + int match, rc; + u16 pvid; + + if (br && br_vlan_enabled(br)) + pvid = priv->bridge_pvid[port]; + else + pvid = priv->tag_8021q_pvid[port]; + + rc = sja1105_pvid_apply(priv, port, pvid); + if (rc) + return rc; + + /* Only force dropping of untagged packets when the port is under a + * VLAN-aware bridge. When the tag_8021q pvid is used, we are + * deliberately removing the RX VLAN from the port's VMEMB_PORT list, + * to prevent DSA tag spoofing from the link partner. Untagged packets + * are the only ones that should be received with tag_8021q, so + * definitely don't drop them. + */ + if (pvid == priv->bridge_pvid[port]) { + vlan = priv->static_config.tables[BLK_IDX_VLAN_LOOKUP].entries; + + match = sja1105_is_vlan_configured(priv, pvid); + + if (match < 0 || !(vlan[match].vmemb_port & BIT(port))) + drop_untagged = true; + } + + if (dsa_is_cpu_port(ds, port) || dsa_is_dsa_port(ds, port)) + drop_untagged = true; + + return sja1105_drop_untagged(ds, port, drop_untagged); +} static int sja1105_init_mac_settings(struct sja1105_private *priv) { @@ -73,7 +171,7 @@ static int sja1105_init_mac_settings(struct sja1105_private *priv) /* Always put the MAC speed in automatic mode, where it can be * adjusted at runtime by PHYLINK. */ - .speed = SJA1105_SPEED_AUTO, + .speed = priv->info->port_speed[SJA1105_SPEED_AUTO], /* No static correction for 1-step 1588 events */ .tp_delin = 0, .tp_delout = 0, @@ -100,8 +198,9 @@ static int sja1105_init_mac_settings(struct sja1105_private *priv) .ingress = false, }; struct sja1105_mac_config_entry *mac; + struct dsa_switch *ds = priv->ds; struct sja1105_table *table; - int i; + struct dsa_port *dp; table = &priv->static_config.tables[BLK_IDX_MAC_CONFIG]; @@ -111,35 +210,46 @@ static int sja1105_init_mac_settings(struct sja1105_private *priv) table->entry_count = 0; } - table->entries = kcalloc(SJA1105_NUM_PORTS, + table->entries = kcalloc(table->ops->max_entry_count, table->ops->unpacked_entry_size, GFP_KERNEL); if (!table->entries) return -ENOMEM; - table->entry_count = SJA1105_NUM_PORTS; + table->entry_count = table->ops->max_entry_count; mac = table->entries; - for (i = 0; i < SJA1105_NUM_PORTS; i++) { - mac[i] = default_mac; - if (i == dsa_upstream_port(priv->ds, i)) { - /* STP doesn't get called for CPU port, so we need to - * set the I/O parameters statically. - */ - mac[i].dyn_learn = true; - mac[i].ingress = true; - mac[i].egress = true; - } + list_for_each_entry(dp, &ds->dst->ports, list) { + if (dp->ds != ds) + continue; + + mac[dp->index] = default_mac; + + /* Let sja1105_bridge_stp_state_set() keep address learning + * enabled for the DSA ports. CPU ports use software-assisted + * learning to ensure that only FDB entries belonging to the + * bridge are learned, and that they are learned towards all + * CPU ports in a cross-chip topology if multiple CPU ports + * exist. + */ + if (dsa_port_is_dsa(dp)) + dp->learning = true; + + /* Disallow untagged packets from being received on the + * CPU and DSA ports. + */ + if (dsa_port_is_cpu(dp) || dsa_port_is_dsa(dp)) + mac[dp->index].drpuntag = true; } return 0; } -static int sja1105_init_mii_settings(struct sja1105_private *priv, - struct sja1105_dt_port *ports) +static int sja1105_init_mii_settings(struct sja1105_private *priv) { struct device *dev = &priv->spidev->dev; struct sja1105_xmii_params_entry *mii; + struct dsa_switch *ds = priv->ds; struct sja1105_table *table; int i; @@ -151,60 +261,129 @@ static int sja1105_init_mii_settings(struct sja1105_private *priv, table->entry_count = 0; } - table->entries = kcalloc(SJA1105_MAX_XMII_PARAMS_COUNT, + table->entries = kcalloc(table->ops->max_entry_count, table->ops->unpacked_entry_size, GFP_KERNEL); if (!table->entries) return -ENOMEM; /* Override table based on PHYLINK DT bindings */ - table->entry_count = SJA1105_MAX_XMII_PARAMS_COUNT; + table->entry_count = table->ops->max_entry_count; mii = table->entries; - for (i = 0; i < SJA1105_NUM_PORTS; i++) { - switch (ports[i].phy_mode) { + for (i = 0; i < ds->num_ports; i++) { + sja1105_mii_role_t role = XMII_MAC; + + if (dsa_is_unused_port(priv->ds, i)) + continue; + + switch (priv->phy_mode[i]) { + case PHY_INTERFACE_MODE_INTERNAL: + if (priv->info->internal_phy[i] == SJA1105_NO_PHY) + goto unsupported; + + mii->xmii_mode[i] = XMII_MODE_MII; + if (priv->info->internal_phy[i] == SJA1105_PHY_BASE_TX) + mii->special[i] = true; + + break; + case PHY_INTERFACE_MODE_REVMII: + role = XMII_PHY; + fallthrough; case PHY_INTERFACE_MODE_MII: + if (!priv->info->supports_mii[i]) + goto unsupported; + mii->xmii_mode[i] = XMII_MODE_MII; break; + case PHY_INTERFACE_MODE_REVRMII: + role = XMII_PHY; + fallthrough; case PHY_INTERFACE_MODE_RMII: + if (!priv->info->supports_rmii[i]) + goto unsupported; + mii->xmii_mode[i] = XMII_MODE_RMII; break; case PHY_INTERFACE_MODE_RGMII: case PHY_INTERFACE_MODE_RGMII_ID: case PHY_INTERFACE_MODE_RGMII_RXID: case PHY_INTERFACE_MODE_RGMII_TXID: + if (!priv->info->supports_rgmii[i]) + goto unsupported; + mii->xmii_mode[i] = XMII_MODE_RGMII; break; + case PHY_INTERFACE_MODE_SGMII: + if (!priv->info->supports_sgmii[i]) + goto unsupported; + + mii->xmii_mode[i] = XMII_MODE_SGMII; + mii->special[i] = true; + break; + case PHY_INTERFACE_MODE_2500BASEX: + if (!priv->info->supports_2500basex[i]) + goto unsupported; + + mii->xmii_mode[i] = XMII_MODE_SGMII; + mii->special[i] = true; + break; +unsupported: default: - dev_err(dev, "Unsupported PHY mode %s!\n", - phy_modes(ports[i].phy_mode)); + dev_err(dev, "Unsupported PHY mode %s on port %d!\n", + phy_modes(priv->phy_mode[i]), i); + return -EINVAL; } - mii->phy_mac[i] = ports[i].role; + mii->phy_mac[i] = role; } return 0; } static int sja1105_init_static_fdb(struct sja1105_private *priv) { + struct sja1105_l2_lookup_entry *l2_lookup; struct sja1105_table *table; + int port; table = &priv->static_config.tables[BLK_IDX_L2_LOOKUP]; - /* We only populate the FDB table through dynamic - * L2 Address Lookup entries + /* We only populate the FDB table through dynamic L2 Address Lookup + * entries, except for a special entry at the end which is a catch-all + * for unknown multicast and will be used to control flooding domain. */ if (table->entry_count) { kfree(table->entries); table->entry_count = 0; } + + if (!priv->info->can_limit_mcast_flood) + return 0; + + table->entries = kcalloc(1, table->ops->unpacked_entry_size, + GFP_KERNEL); + if (!table->entries) + return -ENOMEM; + + table->entry_count = 1; + l2_lookup = table->entries; + + /* All L2 multicast addresses have an odd first octet */ + l2_lookup[0].macaddr = SJA1105_UNKNOWN_MULTICAST; + l2_lookup[0].mask_macaddr = SJA1105_UNKNOWN_MULTICAST; + l2_lookup[0].lockeds = true; + l2_lookup[0].index = SJA1105_MAX_L2_LOOKUP_COUNT - 1; + + /* Flood multicast to every port by default */ + for (port = 0; port < priv->ds->num_ports; port++) + if (!dsa_is_unused_port(priv->ds, port)) + l2_lookup[0].destports |= BIT(port); + return 0; } static int sja1105_init_l2_lookup_params(struct sja1105_private *priv) { - struct sja1105_table *table; - u64 max_fdb_entries = SJA1105_MAX_L2_LOOKUP_COUNT / SJA1105_NUM_PORTS; struct sja1105_l2_lookup_params_entry default_l2_lookup_params = { /* Learned FDB entries are forgotten after 300 seconds */ .maxage = SJA1105_AGEING_TIME_MS(300000), @@ -212,14 +391,10 @@ static int sja1105_init_l2_lookup_params(struct sja1105_private *priv) .dyn_tbsz = SJA1105ET_FDB_BIN_SIZE, /* And the P/Q/R/S equivalent setting: */ .start_dynspc = 0, - .maxaddrp = {max_fdb_entries, max_fdb_entries, max_fdb_entries, - max_fdb_entries, max_fdb_entries, }, /* 2^8 + 2^5 + 2^3 + 2^2 + 2^1 + 1 in Koopman notation */ .poly = 0x97, - /* This selects between Independent VLAN Learning (IVL) and - * Shared VLAN Learning (SVL) - */ - .shared_learn = true, + /* Always use Independent VLAN Learning (IVL) */ + .shared_learn = false, /* Don't discard management traffic based on ENFPORT - * we don't perform SMAC port enforcement anyway, so * what we are setting here doesn't matter. @@ -237,6 +412,23 @@ static int sja1105_init_l2_lookup_params(struct sja1105_private *priv) .owr_dyn = true, .drpnolearn = true, }; + struct dsa_switch *ds = priv->ds; + int port, num_used_ports = 0; + struct sja1105_table *table; + u64 max_fdb_entries; + + for (port = 0; port < ds->num_ports; port++) + if (!dsa_is_unused_port(ds, port)) + num_used_ports++; + + max_fdb_entries = SJA1105_MAX_L2_LOOKUP_COUNT / num_used_ports; + + for (port = 0; port < ds->num_ports; port++) { + if (dsa_is_unused_port(ds, port)) + continue; + + default_l2_lookup_params.maxaddrp[port] = max_fdb_entries; + } table = &priv->static_config.tables[BLK_IDX_L2_LOOKUP_PARAMS]; @@ -245,12 +437,12 @@ static int sja1105_init_l2_lookup_params(struct sja1105_private *priv) table->entry_count = 0; } - table->entries = kcalloc(SJA1105_MAX_L2_LOOKUP_PARAMS_COUNT, + table->entries = kcalloc(table->ops->max_entry_count, table->ops->unpacked_entry_size, GFP_KERNEL); if (!table->entries) return -ENOMEM; - table->entry_count = SJA1105_MAX_L2_LOOKUP_PARAMS_COUNT; + table->entry_count = table->ops->max_entry_count; /* This table only has a single entry */ ((struct sja1105_l2_lookup_params_entry *)table->entries)[0] = @@ -259,44 +451,54 @@ static int sja1105_init_l2_lookup_params(struct sja1105_private *priv) return 0; } +/* Set up a default VLAN for untagged traffic injected from the CPU + * using management routes (e.g. STP, PTP) as opposed to tag_8021q. + * All DT-defined ports are members of this VLAN, and there are no + * restrictions on forwarding (since the CPU selects the destination). + * Frames from this VLAN will always be transmitted as untagged, and + * neither the bridge nor the 8021q module cannot create this VLAN ID. + */ static int sja1105_init_static_vlan(struct sja1105_private *priv) { struct sja1105_table *table; struct sja1105_vlan_lookup_entry pvid = { + .type_entry = SJA1110_VLAN_D_TAG, .ving_mirr = 0, .vegr_mirr = 0, .vmemb_port = 0, .vlan_bc = 0, .tag_port = 0, - .vlanid = 1, + .vlanid = SJA1105_DEFAULT_VLAN, }; - int i; + struct dsa_switch *ds = priv->ds; + int port; table = &priv->static_config.tables[BLK_IDX_VLAN_LOOKUP]; - /* The static VLAN table will only contain the initial pvid of 1. - * All other VLANs are to be configured through dynamic entries, - * and kept in the static configuration table as backing memory. - */ if (table->entry_count) { kfree(table->entries); table->entry_count = 0; } - table->entries = kcalloc(1, table->ops->unpacked_entry_size, + table->entries = kzalloc(table->ops->unpacked_entry_size, GFP_KERNEL); if (!table->entries) return -ENOMEM; table->entry_count = 1; - /* VLAN 1: all DT-defined ports are members; no restrictions on - * forwarding; always transmit priority-tagged frames as untagged. - */ - for (i = 0; i < SJA1105_NUM_PORTS; i++) { - pvid.vmemb_port |= BIT(i); - pvid.vlan_bc |= BIT(i); - pvid.tag_port &= ~BIT(i); + for (port = 0; port < ds->num_ports; port++) { + if (dsa_is_unused_port(ds, port)) + continue; + + pvid.vmemb_port |= BIT(port); + pvid.vlan_bc |= BIT(port); + pvid.tag_port &= ~BIT(port); + + if (dsa_is_cpu_port(ds, port) || dsa_is_dsa_port(ds, port)) { + priv->tag_8021q_pvid[port] = SJA1105_DEFAULT_VLAN; + priv->bridge_pvid[port] = SJA1105_DEFAULT_VLAN; + } } ((struct sja1105_vlan_lookup_entry *)table->entries)[0] = pvid; @@ -306,8 +508,12 @@ static int sja1105_init_static_vlan(struct sja1105_private *priv) static int sja1105_init_l2_forwarding(struct sja1105_private *priv) { struct sja1105_l2_forwarding_entry *l2fwd; + struct dsa_switch *ds = priv->ds; + struct dsa_switch_tree *dst; struct sja1105_table *table; - int i, j; + struct dsa_link *dl; + int port, tc; + int from, to; table = &priv->static_config.tables[BLK_IDX_L2_FORWARDING]; @@ -316,46 +522,165 @@ static int sja1105_init_l2_forwarding(struct sja1105_private *priv) table->entry_count = 0; } - table->entries = kcalloc(SJA1105_MAX_L2_FORWARDING_COUNT, + table->entries = kcalloc(table->ops->max_entry_count, table->ops->unpacked_entry_size, GFP_KERNEL); if (!table->entries) return -ENOMEM; - table->entry_count = SJA1105_MAX_L2_FORWARDING_COUNT; + table->entry_count = table->ops->max_entry_count; l2fwd = table->entries; - /* First 5 entries define the forwarding rules */ - for (i = 0; i < SJA1105_NUM_PORTS; i++) { - unsigned int upstream = dsa_upstream_port(priv->ds, i); + /* First 5 entries in the L2 Forwarding Table define the forwarding + * rules and the VLAN PCP to ingress queue mapping. + * Set up the ingress queue mapping first. + */ + for (port = 0; port < ds->num_ports; port++) { + if (dsa_is_unused_port(ds, port)) + continue; - for (j = 0; j < SJA1105_NUM_TC; j++) - l2fwd[i].vlan_pmap[j] = j; + for (tc = 0; tc < SJA1105_NUM_TC; tc++) + l2fwd[port].vlan_pmap[tc] = tc; + } - if (i == upstream) + /* Then manage the forwarding domain for user ports. These can forward + * only to the always-on domain (CPU port and DSA links) + */ + for (from = 0; from < ds->num_ports; from++) { + if (!dsa_is_user_port(ds, from)) continue; - sja1105_port_allow_traffic(l2fwd, i, upstream, true); - sja1105_port_allow_traffic(l2fwd, upstream, i, true); + for (to = 0; to < ds->num_ports; to++) { + if (!dsa_is_cpu_port(ds, to) && + !dsa_is_dsa_port(ds, to)) + continue; + + l2fwd[from].bc_domain |= BIT(to); + l2fwd[from].fl_domain |= BIT(to); + + sja1105_port_allow_traffic(l2fwd, from, to, true); + } } + + /* Then manage the forwarding domain for DSA links and CPU ports (the + * always-on domain). These can send packets to any enabled port except + * themselves. + */ + for (from = 0; from < ds->num_ports; from++) { + if (!dsa_is_cpu_port(ds, from) && !dsa_is_dsa_port(ds, from)) + continue; + + for (to = 0; to < ds->num_ports; to++) { + if (dsa_is_unused_port(ds, to)) + continue; + + if (from == to) + continue; + + l2fwd[from].bc_domain |= BIT(to); + l2fwd[from].fl_domain |= BIT(to); + + sja1105_port_allow_traffic(l2fwd, from, to, true); + } + } + + /* In odd topologies ("H" connections where there is a DSA link to + * another switch which also has its own CPU port), TX packets can loop + * back into the system (they are flooded from CPU port 1 to the DSA + * link, and from there to CPU port 2). Prevent this from happening by + * cutting RX from DSA links towards our CPU port, if the remote switch + * has its own CPU port and therefore doesn't need ours for network + * stack termination. + */ + dst = ds->dst; + + list_for_each_entry(dl, &dst->rtable, list) { + if (dl->dp->ds != ds || dl->link_dp->cpu_dp == dl->dp->cpu_dp) + continue; + + from = dl->dp->index; + to = dsa_upstream_port(ds, from); + + dev_warn(ds->dev, + "H topology detected, cutting RX from DSA link %d to CPU port %d to prevent TX packet loops\n", + from, to); + + sja1105_port_allow_traffic(l2fwd, from, to, false); + + l2fwd[from].bc_domain &= ~BIT(to); + l2fwd[from].fl_domain &= ~BIT(to); + } + + /* Finally, manage the egress flooding domain. All ports start up with + * flooding enabled, including the CPU port and DSA links. + */ + for (port = 0; port < ds->num_ports; port++) { + if (dsa_is_unused_port(ds, port)) + continue; + + priv->ucast_egress_floods |= BIT(port); + priv->bcast_egress_floods |= BIT(port); + } + /* Next 8 entries define VLAN PCP mapping from ingress to egress. * Create a one-to-one mapping. */ - for (i = 0; i < SJA1105_NUM_TC; i++) - for (j = 0; j < SJA1105_NUM_PORTS; j++) - l2fwd[SJA1105_NUM_PORTS + i].vlan_pmap[j] = i; + for (tc = 0; tc < SJA1105_NUM_TC; tc++) { + for (port = 0; port < ds->num_ports; port++) { + if (dsa_is_unused_port(ds, port)) + continue; + + l2fwd[ds->num_ports + tc].vlan_pmap[port] = tc; + } + + l2fwd[ds->num_ports + tc].type_egrpcp2outputq = true; + } + + return 0; +} + +static int sja1110_init_pcp_remapping(struct sja1105_private *priv) +{ + struct sja1110_pcp_remapping_entry *pcp_remap; + struct dsa_switch *ds = priv->ds; + struct sja1105_table *table; + int port, tc; + + table = &priv->static_config.tables[BLK_IDX_PCP_REMAPPING]; + + /* Nothing to do for SJA1105 */ + if (!table->ops->max_entry_count) + return 0; + + if (table->entry_count) { + kfree(table->entries); + table->entry_count = 0; + } + + table->entries = kcalloc(table->ops->max_entry_count, + table->ops->unpacked_entry_size, GFP_KERNEL); + if (!table->entries) + return -ENOMEM; + + table->entry_count = table->ops->max_entry_count; + + pcp_remap = table->entries; + + /* Repeat the configuration done for vlan_pmap */ + for (port = 0; port < ds->num_ports; port++) { + if (dsa_is_unused_port(ds, port)) + continue; + + for (tc = 0; tc < SJA1105_NUM_TC; tc++) + pcp_remap[port].egrpcp[tc] = tc; + } return 0; } static int sja1105_init_l2_forwarding_params(struct sja1105_private *priv) { - struct sja1105_l2_forwarding_params_entry default_l2fwd_params = { - /* Disallow dynamic reconfiguration of vlan_pmap */ - .max_dynp = 0, - /* Use a single memory partition for all ingress queues */ - .part_spc = { SJA1105_MAX_FRAME_MEMORY, 0, 0, 0, 0, 0, 0, 0 }, - }; + struct sja1105_l2_forwarding_params_entry *l2fwd_params; struct sja1105_table *table; table = &priv->static_config.tables[BLK_IDX_L2_FORWARDING_PARAMS]; @@ -365,16 +690,166 @@ static int sja1105_init_l2_forwarding_params(struct sja1105_private *priv) table->entry_count = 0; } - table->entries = kcalloc(SJA1105_MAX_L2_FORWARDING_PARAMS_COUNT, + table->entries = kcalloc(table->ops->max_entry_count, table->ops->unpacked_entry_size, GFP_KERNEL); if (!table->entries) return -ENOMEM; - table->entry_count = SJA1105_MAX_L2_FORWARDING_PARAMS_COUNT; + table->entry_count = table->ops->max_entry_count; /* This table only has a single entry */ - ((struct sja1105_l2_forwarding_params_entry *)table->entries)[0] = - default_l2fwd_params; + l2fwd_params = table->entries; + + /* Disallow dynamic reconfiguration of vlan_pmap */ + l2fwd_params->max_dynp = 0; + /* Use a single memory partition for all ingress queues */ + l2fwd_params->part_spc[0] = priv->info->max_frame_mem; + + return 0; +} + +void sja1105_frame_memory_partitioning(struct sja1105_private *priv) +{ + struct sja1105_l2_forwarding_params_entry *l2_fwd_params; + struct sja1105_vl_forwarding_params_entry *vl_fwd_params; + struct sja1105_table *table; + + table = &priv->static_config.tables[BLK_IDX_L2_FORWARDING_PARAMS]; + l2_fwd_params = table->entries; + l2_fwd_params->part_spc[0] = SJA1105_MAX_FRAME_MEMORY; + + /* If we have any critical-traffic virtual links, we need to reserve + * some frame buffer memory for them. At the moment, hardcode the value + * at 100 blocks of 128 bytes of memory each. This leaves 829 blocks + * remaining for best-effort traffic. TODO: figure out a more flexible + * way to perform the frame buffer partitioning. + */ + if (!priv->static_config.tables[BLK_IDX_VL_FORWARDING].entry_count) + return; + + table = &priv->static_config.tables[BLK_IDX_VL_FORWARDING_PARAMS]; + vl_fwd_params = table->entries; + + l2_fwd_params->part_spc[0] -= SJA1105_VL_FRAME_MEMORY; + vl_fwd_params->partspc[0] = SJA1105_VL_FRAME_MEMORY; +} + +/* SJA1110 TDMACONFIGIDX values: + * + * | 100 Mbps ports | 1Gbps ports | 2.5Gbps ports | Disabled ports + * -----+----------------+---------------+---------------+--------------- + * 0 | 0, [5:10] | [1:2] | [3:4] | retag + * 1 |0, [5:10], retag| [1:2] | [3:4] | - + * 2 | 0, [5:10] | [1:3], retag | 4 | - + * 3 | 0, [5:10] |[1:2], 4, retag| 3 | - + * 4 | 0, 2, [5:10] | 1, retag | [3:4] | - + * 5 | 0, 1, [5:10] | 2, retag | [3:4] | - + * 14 | 0, [5:10] | [1:4], retag | - | - + * 15 | [5:10] | [0:4], retag | - | - + */ +static void sja1110_select_tdmaconfigidx(struct sja1105_private *priv) +{ + struct sja1105_general_params_entry *general_params; + struct sja1105_table *table; + bool port_1_is_base_tx; + bool port_3_is_2500; + bool port_4_is_2500; + u64 tdmaconfigidx; + + if (priv->info->device_id != SJA1110_DEVICE_ID) + return; + + table = &priv->static_config.tables[BLK_IDX_GENERAL_PARAMS]; + general_params = table->entries; + + /* All the settings below are "as opposed to SGMII", which is the + * other pinmuxing option. + */ + port_1_is_base_tx = priv->phy_mode[1] == PHY_INTERFACE_MODE_INTERNAL; + port_3_is_2500 = priv->phy_mode[3] == PHY_INTERFACE_MODE_2500BASEX; + port_4_is_2500 = priv->phy_mode[4] == PHY_INTERFACE_MODE_2500BASEX; + + if (port_1_is_base_tx) + /* Retagging port will operate at 1 Gbps */ + tdmaconfigidx = 5; + else if (port_3_is_2500 && port_4_is_2500) + /* Retagging port will operate at 100 Mbps */ + tdmaconfigidx = 1; + else if (port_3_is_2500) + /* Retagging port will operate at 1 Gbps */ + tdmaconfigidx = 3; + else if (port_4_is_2500) + /* Retagging port will operate at 1 Gbps */ + tdmaconfigidx = 2; + else + /* Retagging port will operate at 1 Gbps */ + tdmaconfigidx = 14; + + general_params->tdmaconfigidx = tdmaconfigidx; +} + +static int sja1105_init_topology(struct sja1105_private *priv, + struct sja1105_general_params_entry *general_params) +{ + struct dsa_switch *ds = priv->ds; + int port; + + /* The host port is the destination for traffic matching mac_fltres1 + * and mac_fltres0 on all ports except itself. Default to an invalid + * value. + */ + general_params->host_port = ds->num_ports; + + /* Link-local traffic received on casc_port will be forwarded + * to host_port without embedding the source port and device ID + * info in the destination MAC address, and no RX timestamps will be + * taken either (presumably because it is a cascaded port and a + * downstream SJA switch already did that). + * To disable the feature, we need to do different things depending on + * switch generation. On SJA1105 we need to set an invalid port, while + * on SJA1110 which support multiple cascaded ports, this field is a + * bitmask so it must be left zero. + */ + if (!priv->info->multiple_cascade_ports) + general_params->casc_port = ds->num_ports; + + for (port = 0; port < ds->num_ports; port++) { + bool is_upstream = dsa_is_upstream_port(ds, port); + bool is_dsa_link = dsa_is_dsa_port(ds, port); + + /* Upstream ports can be dedicated CPU ports or + * upstream-facing DSA links + */ + if (is_upstream) { + if (general_params->host_port == ds->num_ports) { + general_params->host_port = port; + } else { + dev_err(ds->dev, + "Port %llu is already a host port, configuring %d as one too is not supported\n", + general_params->host_port, port); + return -EINVAL; + } + } + + /* Cascade ports are downstream-facing DSA links */ + if (is_dsa_link && !is_upstream) { + if (priv->info->multiple_cascade_ports) { + general_params->casc_port |= BIT(port); + } else if (general_params->casc_port == ds->num_ports) { + general_params->casc_port = port; + } else { + dev_err(ds->dev, + "Port %llu is already a cascade port, configuring %d as one too is not supported\n", + general_params->casc_port, port); + return -EINVAL; + } + } + } + + if (general_params->host_port == ds->num_ports) { + dev_err(ds->dev, "No host port configured\n"); + return -EINVAL; + } return 0; } @@ -397,24 +872,10 @@ static int sja1105_init_general_params(struct sja1105_private *priv) .mac_flt0 = SJA1105_LINKLOCAL_FILTER_B_MASK, .incl_srcpt0 = false, .send_meta0 = false, - /* The destination for traffic matching mac_fltres1 and - * mac_fltres0 on all ports except host_port. Such traffic - * receieved on host_port itself would be dropped, except - * by installing a temporary 'management route' - */ - .host_port = dsa_upstream_port(priv->ds, 0), /* Default to an invalid value */ - .mirr_port = SJA1105_NUM_PORTS, - /* Link-local traffic received on casc_port will be forwarded - * to host_port without embedding the source port and device ID - * info in the destination MAC address (presumably because it - * is a cascaded port and a downstream SJA switch already did - * that). Default to an invalid port (to disable the feature) - * and overwrite this if we find any DSA (cascaded) ports. - */ - .casc_port = SJA1105_NUM_PORTS, + .mirr_port = priv->ds->num_ports, /* No TTEthernet */ - .vllupformat = 0, + .vllupformat = SJA1105_VL_FORMAT_PSFP, .vlmarker = 0, .vlmask = 0, /* Only update correctionField for 1-step PTP (L2 transport) */ @@ -424,8 +885,18 @@ static int sja1105_init_general_params(struct sja1105_private *priv) */ .tpid = ETH_P_SJA1105, .tpid2 = ETH_P_SJA1105, + /* Enable the TTEthernet engine on SJA1110 */ + .tte_en = true, + /* Set up the EtherType for control packets on SJA1110 */ + .header_type = ETH_P_SJA1110, }; + struct sja1105_general_params_entry *general_params; struct sja1105_table *table; + int rc; + + rc = sja1105_init_topology(priv, &default_general_params); + if (rc) + return rc; table = &priv->static_config.tables[BLK_IDX_GENERAL_PARAMS]; @@ -434,37 +905,111 @@ static int sja1105_init_general_params(struct sja1105_private *priv) table->entry_count = 0; } - table->entries = kcalloc(SJA1105_MAX_GENERAL_PARAMS_COUNT, + table->entries = kcalloc(table->ops->max_entry_count, table->ops->unpacked_entry_size, GFP_KERNEL); if (!table->entries) return -ENOMEM; - table->entry_count = SJA1105_MAX_GENERAL_PARAMS_COUNT; + table->entry_count = table->ops->max_entry_count; + + general_params = table->entries; /* This table only has a single entry */ - ((struct sja1105_general_params_entry *)table->entries)[0] = - default_general_params; + general_params[0] = default_general_params; + + sja1110_select_tdmaconfigidx(priv); return 0; } -#define SJA1105_RATE_MBPS(speed) (((speed) * 64000) / 1000) - -static void sja1105_setup_policer(struct sja1105_l2_policing_entry *policing, - int index) +static int sja1105_init_avb_params(struct sja1105_private *priv) { - policing[index].sharindx = index; - policing[index].smax = 65535; /* Burst size in bytes */ - policing[index].rate = SJA1105_RATE_MBPS(1000); - policing[index].maxlen = ETH_FRAME_LEN + VLAN_HLEN + ETH_FCS_LEN; - policing[index].partition = 0; + struct sja1105_avb_params_entry *avb; + struct sja1105_table *table; + + table = &priv->static_config.tables[BLK_IDX_AVB_PARAMS]; + + /* Discard previous AVB Parameters Table */ + if (table->entry_count) { + kfree(table->entries); + table->entry_count = 0; + } + + table->entries = kcalloc(table->ops->max_entry_count, + table->ops->unpacked_entry_size, GFP_KERNEL); + if (!table->entries) + return -ENOMEM; + + table->entry_count = table->ops->max_entry_count; + + avb = table->entries; + + /* Configure the MAC addresses for meta frames */ + avb->destmeta = SJA1105_META_DMAC; + avb->srcmeta = SJA1105_META_SMAC; + /* On P/Q/R/S, configure the direction of the PTP_CLK pin as input by + * default. This is because there might be boards with a hardware + * layout where enabling the pin as output might cause an electrical + * clash. On E/T the pin is always an output, which the board designers + * probably already knew, so even if there are going to be electrical + * issues, there's nothing we can do. + */ + avb->cas_master = false; + + return 0; } +/* The L2 policing table is 2-stage. The table is looked up for each frame + * according to the ingress port, whether it was broadcast or not, and the + * classified traffic class (given by VLAN PCP). This portion of the lookup is + * fixed, and gives access to the SHARINDX, an indirection register pointing + * within the policing table itself, which is used to resolve the policer that + * will be used for this frame. + * + * Stage 1 Stage 2 + * +------------+--------+ +---------------------------------+ + * |Port 0 TC 0 |SHARINDX| | Policer 0: Rate, Burst, MTU | + * +------------+--------+ +---------------------------------+ + * |Port 0 TC 1 |SHARINDX| | Policer 1: Rate, Burst, MTU | + * +------------+--------+ +---------------------------------+ + * ... | Policer 2: Rate, Burst, MTU | + * +------------+--------+ +---------------------------------+ + * |Port 0 TC 7 |SHARINDX| | Policer 3: Rate, Burst, MTU | + * +------------+--------+ +---------------------------------+ + * |Port 1 TC 0 |SHARINDX| | Policer 4: Rate, Burst, MTU | + * +------------+--------+ +---------------------------------+ + * ... | Policer 5: Rate, Burst, MTU | + * +------------+--------+ +---------------------------------+ + * |Port 1 TC 7 |SHARINDX| | Policer 6: Rate, Burst, MTU | + * +------------+--------+ +---------------------------------+ + * ... | Policer 7: Rate, Burst, MTU | + * +------------+--------+ +---------------------------------+ + * |Port 4 TC 7 |SHARINDX| ... + * +------------+--------+ + * |Port 0 BCAST|SHARINDX| ... + * +------------+--------+ + * |Port 1 BCAST|SHARINDX| ... + * +------------+--------+ + * ... ... + * +------------+--------+ +---------------------------------+ + * |Port 4 BCAST|SHARINDX| | Policer 44: Rate, Burst, MTU | + * +------------+--------+ +---------------------------------+ + * + * In this driver, we shall use policers 0-4 as statically alocated port + * (matchall) policers. So we need to make the SHARINDX for all lookups + * corresponding to this ingress port (8 VLAN PCP lookups and 1 broadcast + * lookup) equal. + * The remaining policers (40) shall be dynamically allocated for flower + * policers, where the key is either vlan_prio or dst_mac ff:ff:ff:ff:ff:ff. + */ +#define SJA1105_RATE_MBPS(speed) (((speed) * 64000) / 1000) + static int sja1105_init_l2_policing(struct sja1105_private *priv) { struct sja1105_l2_policing_entry *policing; + struct dsa_switch *ds = priv->ds; struct sja1105_table *table; - int i, j, k; + int port, tc; table = &priv->static_config.tables[BLK_IDX_L2_POLICING]; @@ -474,32 +1019,46 @@ static int sja1105_init_l2_policing(struct sja1105_private *priv) table->entry_count = 0; } - table->entries = kcalloc(SJA1105_MAX_L2_POLICING_COUNT, + table->entries = kcalloc(table->ops->max_entry_count, table->ops->unpacked_entry_size, GFP_KERNEL); if (!table->entries) return -ENOMEM; - table->entry_count = SJA1105_MAX_L2_POLICING_COUNT; + table->entry_count = table->ops->max_entry_count; policing = table->entries; - /* k sweeps through all unicast policers (0-39). - * bcast sweeps through policers 40-44. - */ - for (i = 0, k = 0; i < SJA1105_NUM_PORTS; i++) { - int bcast = (SJA1105_NUM_PORTS * SJA1105_NUM_TC) + i; + /* Setup shared indices for the matchall policers */ + for (port = 0; port < ds->num_ports; port++) { + int mcast = (ds->num_ports * (SJA1105_NUM_TC + 1)) + port; + int bcast = (ds->num_ports * SJA1105_NUM_TC) + port; + + for (tc = 0; tc < SJA1105_NUM_TC; tc++) + policing[port * SJA1105_NUM_TC + tc].sharindx = port; - for (j = 0; j < SJA1105_NUM_TC; j++, k++) - sja1105_setup_policer(policing, k); + policing[bcast].sharindx = port; + /* Only SJA1110 has multicast policers */ + if (mcast <= table->ops->max_entry_count) + policing[mcast].sharindx = port; + } + + /* Setup the matchall policer parameters */ + for (port = 0; port < ds->num_ports; port++) { + int mtu = VLAN_ETH_FRAME_LEN + ETH_FCS_LEN; - /* Set up this port's policer for broadcast traffic */ - sja1105_setup_policer(policing, bcast); + if (dsa_is_cpu_port(ds, port) || dsa_is_dsa_port(ds, port)) + mtu += VLAN_HLEN; + + policing[port].smax = 65535; /* Burst size in bytes */ + policing[port].rate = SJA1105_RATE_MBPS(1000); + policing[port].maxlen = mtu; + policing[port].partition = 0; } + return 0; } -static int sja1105_static_config_load(struct sja1105_private *priv, - struct sja1105_dt_port *ports) +static int sja1105_static_config_load(struct sja1105_private *priv) { int rc; @@ -514,7 +1073,7 @@ static int sja1105_static_config_load(struct sja1105_private *priv, rc = sja1105_init_mac_settings(priv); if (rc < 0) return rc; - rc = sja1105_init_mii_settings(priv, ports); + rc = sja1105_init_mii_settings(priv); if (rc < 0) return rc; rc = sja1105_init_static_fdb(priv); @@ -538,37 +1097,93 @@ static int sja1105_static_config_load(struct sja1105_private *priv, rc = sja1105_init_general_params(priv); if (rc < 0) return rc; + rc = sja1105_init_avb_params(priv); + if (rc < 0) + return rc; + rc = sja1110_init_pcp_remapping(priv); + if (rc < 0) + return rc; /* Send initial configuration to hardware via SPI */ return sja1105_static_config_upload(priv); } -static int sja1105_parse_rgmii_delays(struct sja1105_private *priv, - const struct sja1105_dt_port *ports) +/* This is the "new way" for a MAC driver to configure its RGMII delay lines, + * based on the explicit "rx-internal-delay-ps" and "tx-internal-delay-ps" + * properties. It has the advantage of working with fixed links and with PHYs + * that apply RGMII delays too, and the MAC driver needs not perform any + * special checks. + * + * Previously we were acting upon the "phy-mode" property when we were + * operating in fixed-link, basically acting as a PHY, but with a reversed + * interpretation: PHY_INTERFACE_MODE_RGMII_TXID means that the MAC should + * behave as if it is connected to a PHY which has applied RGMII delays in the + * TX direction. So if anything, RX delays should have been added by the MAC, + * but we were adding TX delays. + * + * If the "{rx,tx}-internal-delay-ps" properties are not specified, we fall + * back to the legacy behavior and apply delays on fixed-link ports based on + * the reverse interpretation of the phy-mode. This is a deviation from the + * expected default behavior which is to simply apply no delays. To achieve + * that behavior with the new bindings, it is mandatory to specify + * "{rx,tx}-internal-delay-ps" with a value of 0. + */ +static int sja1105_parse_rgmii_delays(struct sja1105_private *priv, int port, + struct device_node *port_dn) { - int i; + phy_interface_t phy_mode = priv->phy_mode[port]; + struct device *dev = &priv->spidev->dev; + int rx_delay = -1, tx_delay = -1; - for (i = 0; i < SJA1105_NUM_PORTS; i++) { - if (ports[i].role == XMII_MAC) - continue; + if (!phy_interface_mode_is_rgmii(phy_mode)) + return 0; - if (ports[i].phy_mode == PHY_INTERFACE_MODE_RGMII_RXID || - ports[i].phy_mode == PHY_INTERFACE_MODE_RGMII_ID) - priv->rgmii_rx_delay[i] = true; + of_property_read_u32(port_dn, "rx-internal-delay-ps", &rx_delay); + of_property_read_u32(port_dn, "tx-internal-delay-ps", &tx_delay); - if (ports[i].phy_mode == PHY_INTERFACE_MODE_RGMII_TXID || - ports[i].phy_mode == PHY_INTERFACE_MODE_RGMII_ID) - priv->rgmii_tx_delay[i] = true; + if (rx_delay == -1 && tx_delay == -1 && priv->fixed_link[port]) { + dev_warn(dev, + "Port %d interpreting RGMII delay settings based on \"phy-mode\" property, " + "please update device tree to specify \"rx-internal-delay-ps\" and " + "\"tx-internal-delay-ps\"", + port); - if ((priv->rgmii_rx_delay[i] || priv->rgmii_tx_delay[i]) && - !priv->info->setup_rgmii_delay) - return -EINVAL; + if (phy_mode == PHY_INTERFACE_MODE_RGMII_RXID || + phy_mode == PHY_INTERFACE_MODE_RGMII_ID) + rx_delay = 2000; + + if (phy_mode == PHY_INTERFACE_MODE_RGMII_TXID || + phy_mode == PHY_INTERFACE_MODE_RGMII_ID) + tx_delay = 2000; + } + + if (rx_delay < 0) + rx_delay = 0; + if (tx_delay < 0) + tx_delay = 0; + + if ((rx_delay || tx_delay) && !priv->info->setup_rgmii_delay) { + dev_err(dev, "Chip cannot apply RGMII delays\n"); + return -EINVAL; + } + + if ((rx_delay && rx_delay < SJA1105_RGMII_DELAY_MIN_PS) || + (tx_delay && tx_delay < SJA1105_RGMII_DELAY_MIN_PS) || + (rx_delay > SJA1105_RGMII_DELAY_MAX_PS) || + (tx_delay > SJA1105_RGMII_DELAY_MAX_PS)) { + dev_err(dev, + "port %d RGMII delay values out of range, must be between %d and %d ps\n", + port, SJA1105_RGMII_DELAY_MIN_PS, SJA1105_RGMII_DELAY_MAX_PS); + return -ERANGE; } + + priv->rgmii_rx_delay_ps[port] = rx_delay; + priv->rgmii_tx_delay_ps[port] = tx_delay; + return 0; } static int sja1105_parse_ports_node(struct sja1105_private *priv, - struct sja1105_dt_port *ports, struct device_node *ports_node) { struct device *dev = &priv->spidev->dev; @@ -597,7 +1212,6 @@ static int sja1105_parse_ports_node(struct sja1105_private *priv, of_node_put(child); return -ENODEV; } - ports[index].phy_mode = phy_mode; phy_node = of_parse_phandle(child, "phy-handle", 0); if (!phy_node) { @@ -610,25 +1224,24 @@ static int sja1105_parse_ports_node(struct sja1105_private *priv, /* phy-handle is missing, but fixed-link isn't. * So it's a fixed link. Default to PHY role. */ - ports[index].role = XMII_PHY; + priv->fixed_link[index] = true; } else { - /* phy-handle present => put port in MAC role */ - ports[index].role = XMII_MAC; of_node_put(phy_node); } - /* The MAC/PHY role can be overridden with explicit bindings */ - if (of_property_read_bool(child, "sja1105,role-mac")) - ports[index].role = XMII_MAC; - else if (of_property_read_bool(child, "sja1105,role-phy")) - ports[index].role = XMII_PHY; + priv->phy_mode[index] = phy_mode; + + err = sja1105_parse_rgmii_delays(priv, index, child); + if (err) { + of_node_put(child); + return err; + } } return 0; } -static int sja1105_parse_dt(struct sja1105_private *priv, - struct sja1105_dt_port *ports) +static int sja1105_parse_dt(struct sja1105_private *priv) { struct device *dev = &priv->spidev->dev; struct device_node *switch_node = dev->of_node; @@ -636,34 +1249,41 @@ static int sja1105_parse_dt(struct sja1105_private *priv, int rc; ports_node = of_get_child_by_name(switch_node, "ports"); + if (!ports_node) + ports_node = of_get_child_by_name(switch_node, "ethernet-ports"); if (!ports_node) { dev_err(dev, "Incorrect bindings: absent \"ports\" node\n"); return -ENODEV; } - rc = sja1105_parse_ports_node(priv, ports, ports_node); + rc = sja1105_parse_ports_node(priv, ports_node); of_node_put(ports_node); return rc; } /* Convert link speed from SJA1105 to ethtool encoding */ -static int sja1105_speed[] = { - [SJA1105_SPEED_AUTO] = SPEED_UNKNOWN, - [SJA1105_SPEED_10MBPS] = SPEED_10, - [SJA1105_SPEED_100MBPS] = SPEED_100, - [SJA1105_SPEED_1000MBPS] = SPEED_1000, -}; +static int sja1105_port_speed_to_ethtool(struct sja1105_private *priv, + u64 speed) +{ + if (speed == priv->info->port_speed[SJA1105_SPEED_10MBPS]) + return SPEED_10; + if (speed == priv->info->port_speed[SJA1105_SPEED_100MBPS]) + return SPEED_100; + if (speed == priv->info->port_speed[SJA1105_SPEED_1000MBPS]) + return SPEED_1000; + if (speed == priv->info->port_speed[SJA1105_SPEED_2500MBPS]) + return SPEED_2500; + return SPEED_UNKNOWN; +} /* Set link speed in the MAC configuration for a specific port. */ static int sja1105_adjust_port_config(struct sja1105_private *priv, int port, int speed_mbps) { - struct sja1105_xmii_params_entry *mii; struct sja1105_mac_config_entry *mac; struct device *dev = priv->ds->dev; - sja1105_phy_interface_t phy_mode; - sja1105_speed_t speed; + u64 speed; int rc; /* On P/Q/R/S, one can read from the device via the MAC reconfiguration @@ -673,7 +1293,6 @@ static int sja1105_adjust_port_config(struct sja1105_private *priv, int port, * reasonable approximation for both E/T and P/Q/R/S. */ mac = priv->static_config.tables[BLK_IDX_MAC_CONFIG].entries; - mii = priv->static_config.tables[BLK_IDX_XMII_PARAMS].entries; switch (speed_mbps) { case SPEED_UNKNOWN: @@ -684,16 +1303,19 @@ static int sja1105_adjust_port_config(struct sja1105_private *priv, int port, * ok for power consumption in case AN will never complete - * otherwise PHYLINK should come back with a new update. */ - speed = SJA1105_SPEED_AUTO; + speed = priv->info->port_speed[SJA1105_SPEED_AUTO]; break; case SPEED_10: - speed = SJA1105_SPEED_10MBPS; + speed = priv->info->port_speed[SJA1105_SPEED_10MBPS]; break; case SPEED_100: - speed = SJA1105_SPEED_100MBPS; + speed = priv->info->port_speed[SJA1105_SPEED_100MBPS]; break; case SPEED_1000: - speed = SJA1105_SPEED_1000MBPS; + speed = priv->info->port_speed[SJA1105_SPEED_1000MBPS]; + break; + case SPEED_2500: + speed = priv->info->port_speed[SJA1105_SPEED_2500MBPS]; break; default: dev_err(dev, "Invalid speed %iMbps\n", speed_mbps); @@ -704,8 +1326,15 @@ static int sja1105_adjust_port_config(struct sja1105_private *priv, int port, * table, since this will be used for the clocking setup, and we no * longer need to store it in the static config (already told hardware * we want auto during upload phase). + * Actually for the SGMII port, the MAC is fixed at 1 Gbps and + * we need to configure the PCS only (if even that). */ - mac[port].speed = speed; + if (priv->phy_mode[port] == PHY_INTERFACE_MODE_SGMII) + mac[port].speed = priv->info->port_speed[SJA1105_SPEED_1000MBPS]; + else if (priv->phy_mode[port] == PHY_INTERFACE_MODE_2500BASEX) + mac[port].speed = priv->info->port_speed[SJA1105_SPEED_2500MBPS]; + else + mac[port].speed = speed; /* Write to the dynamic reconfiguration tables */ rc = sja1105_dynamic_config_write(priv, BLK_IDX_MAC_CONFIG, port, @@ -721,59 +1350,22 @@ static int sja1105_adjust_port_config(struct sja1105_private *priv, int port, * the clock setup does interrupt the clock signal for a certain time * which causes trouble for all PHYs relying on this signal. */ - phy_mode = mii->xmii_mode[port]; - if (phy_mode != XMII_MODE_RGMII) + if (!phy_interface_mode_is_rgmii(priv->phy_mode[port])) return 0; return sja1105_clocking_setup_port(priv, port); } -/* The SJA1105 MAC programming model is through the static config (the xMII - * Mode table cannot be dynamically reconfigured), and we have to program - * that early (earlier than PHYLINK calls us, anyway). - * So just error out in case the connected PHY attempts to change the initial - * system interface MII protocol from what is defined in the DT, at least for - * now. - */ -static bool sja1105_phy_mode_mismatch(struct sja1105_private *priv, int port, - phy_interface_t interface) -{ - struct sja1105_xmii_params_entry *mii; - sja1105_phy_interface_t phy_mode; - - mii = priv->static_config.tables[BLK_IDX_XMII_PARAMS].entries; - phy_mode = mii->xmii_mode[port]; - - switch (interface) { - case PHY_INTERFACE_MODE_MII: - return (phy_mode != XMII_MODE_MII); - case PHY_INTERFACE_MODE_RMII: - return (phy_mode != XMII_MODE_RMII); - case PHY_INTERFACE_MODE_RGMII: - case PHY_INTERFACE_MODE_RGMII_ID: - case PHY_INTERFACE_MODE_RGMII_RXID: - case PHY_INTERFACE_MODE_RGMII_TXID: - return (phy_mode != XMII_MODE_RGMII); - default: - return true; - } -} - -static void sja1105_mac_config(struct dsa_switch *ds, int port, - unsigned int link_an_mode, - const struct phylink_link_state *state) +static struct phylink_pcs * +sja1105_mac_select_pcs(struct dsa_switch *ds, int port, phy_interface_t iface) { struct sja1105_private *priv = ds->priv; + struct dw_xpcs *xpcs = priv->xpcs[port]; - if (sja1105_phy_mode_mismatch(priv, port, state->interface)) - return; + if (xpcs) + return &xpcs->pcs; - if (link_an_mode == MLO_AN_INBAND) { - dev_err(ds->dev, "In-band AN not supported!\n"); - return; - } - - sja1105_adjust_port_config(priv, port, state->speed); + return NULL; } static void sja1105_mac_link_down(struct dsa_switch *ds, int port, @@ -786,48 +1378,64 @@ static void sja1105_mac_link_down(struct dsa_switch *ds, int port, static void sja1105_mac_link_up(struct dsa_switch *ds, int port, unsigned int mode, phy_interface_t interface, - struct phy_device *phydev) + struct phy_device *phydev, + int speed, int duplex, + bool tx_pause, bool rx_pause) { - sja1105_inhibit_tx(ds->priv, BIT(port), false); + struct sja1105_private *priv = ds->priv; + + sja1105_adjust_port_config(priv, port, speed); + + sja1105_inhibit_tx(priv, BIT(port), false); } -static void sja1105_phylink_validate(struct dsa_switch *ds, int port, - unsigned long *supported, - struct phylink_link_state *state) +static void sja1105_phylink_get_caps(struct dsa_switch *ds, int port, + struct phylink_config *config) { - /* Construct a new mask which exhaustively contains all link features - * supported by the MAC, and then apply that (logical AND) to what will - * be sent to the PHY for "marketing". - */ - __ETHTOOL_DECLARE_LINK_MODE_MASK(mask) = { 0, }; struct sja1105_private *priv = ds->priv; struct sja1105_xmii_params_entry *mii; + phy_interface_t phy_mode; - mii = priv->static_config.tables[BLK_IDX_XMII_PARAMS].entries; - - /* include/linux/phylink.h says: - * When @state->interface is %PHY_INTERFACE_MODE_NA, phylink - * expects the MAC driver to return all supported link modes. + /* This driver does not make use of the speed, duplex, pause or the + * advertisement in its mac_config, so it is safe to mark this driver + * as non-legacy. */ - if (state->interface != PHY_INTERFACE_MODE_NA && - sja1105_phy_mode_mismatch(priv, port, state->interface)) { - bitmap_zero(supported, __ETHTOOL_LINK_MODE_MASK_NBITS); - return; + config->legacy_pre_march2020 = false; + + phy_mode = priv->phy_mode[port]; + if (phy_mode == PHY_INTERFACE_MODE_SGMII || + phy_mode == PHY_INTERFACE_MODE_2500BASEX) { + /* Changing the PHY mode on SERDES ports is possible and makes + * sense, because that is done through the XPCS. We allow + * changes between SGMII and 2500base-X. + */ + if (priv->info->supports_sgmii[port]) + __set_bit(PHY_INTERFACE_MODE_SGMII, + config->supported_interfaces); + + if (priv->info->supports_2500basex[port]) + __set_bit(PHY_INTERFACE_MODE_2500BASEX, + config->supported_interfaces); + } else { + /* The SJA1105 MAC programming model is through the static + * config (the xMII Mode table cannot be dynamically + * reconfigured), and we have to program that early. + */ + __set_bit(phy_mode, config->supported_interfaces); } /* The MAC does not support pause frames, and also doesn't * support half-duplex traffic modes. */ - phylink_set(mask, Autoneg); - phylink_set(mask, MII); - phylink_set(mask, 10baseT_Full); - phylink_set(mask, 100baseT_Full); - if (mii->xmii_mode[port] == XMII_MODE_RGMII) - phylink_set(mask, 1000baseT_Full); + config->mac_capabilities = MAC_10FD | MAC_100FD; + + mii = priv->static_config.tables[BLK_IDX_XMII_PARAMS].entries; + if (mii->xmii_mode[port] == XMII_MODE_RGMII || + mii->xmii_mode[port] == XMII_MODE_SGMII) + config->mac_capabilities |= MAC_1000FD; - bitmap_and(supported, supported, mask, __ETHTOOL_LINK_MODE_MASK_NBITS); - bitmap_and(state->advertising, state->advertising, mask, - __ETHTOOL_LINK_MODE_MASK_NBITS); + if (priv->info->supports_2500basex[port]) + config->mac_capabilities |= MAC_2500FD; } static int @@ -947,10 +1555,11 @@ static int sja1105et_is_fdb_entry_in_bin(struct sja1105_private *priv, int bin, int sja1105et_fdb_add(struct dsa_switch *ds, int port, const unsigned char *addr, u16 vid) { - struct sja1105_l2_lookup_entry l2_lookup = {0}; + struct sja1105_l2_lookup_entry l2_lookup = {0}, tmp; struct sja1105_private *priv = ds->priv; struct device *dev = ds->dev; int last_unused = -1; + int start, end, i; int bin, way, rc; bin = sja1105et_fdb_hash(priv, addr, vid); @@ -962,7 +1571,7 @@ int sja1105et_fdb_add(struct dsa_switch *ds, int port, * mask? If yes, we need to do nothing. If not, we need * to rewrite the entry by adding this port to it. */ - if (l2_lookup.destports & BIT(port)) + if ((l2_lookup.destports & BIT(port)) && l2_lookup.lockeds) return 0; l2_lookup.destports |= BIT(port); } else { @@ -993,6 +1602,7 @@ int sja1105et_fdb_add(struct dsa_switch *ds, int port, index, NULL, false); } } + l2_lookup.lockeds = true; l2_lookup.index = sja1105et_fdb_index(bin, way); rc = sja1105_dynamic_config_write(priv, BLK_IDX_L2_LOOKUP, @@ -1001,6 +1611,29 @@ int sja1105et_fdb_add(struct dsa_switch *ds, int port, if (rc < 0) return rc; + /* Invalidate a dynamically learned entry if that exists */ + start = sja1105et_fdb_index(bin, 0); + end = sja1105et_fdb_index(bin, way); + + for (i = start; i < end; i++) { + rc = sja1105_dynamic_config_read(priv, BLK_IDX_L2_LOOKUP, + i, &tmp); + if (rc == -ENOENT) + continue; + if (rc) + return rc; + + if (tmp.macaddr != ether_addr_to_u64(addr) || tmp.vlanid != vid) + continue; + + rc = sja1105_dynamic_config_write(priv, BLK_IDX_L2_LOOKUP, + i, NULL, false); + if (rc) + return rc; + + break; + } + return sja1105_static_fdb_change(priv, port, &l2_lookup, true); } @@ -1042,32 +1675,30 @@ int sja1105et_fdb_del(struct dsa_switch *ds, int port, int sja1105pqrs_fdb_add(struct dsa_switch *ds, int port, const unsigned char *addr, u16 vid) { - struct sja1105_l2_lookup_entry l2_lookup = {0}; + struct sja1105_l2_lookup_entry l2_lookup = {0}, tmp; struct sja1105_private *priv = ds->priv; int rc, i; /* Search for an existing entry in the FDB table */ l2_lookup.macaddr = ether_addr_to_u64(addr); l2_lookup.vlanid = vid; - l2_lookup.iotag = SJA1105_S_TAG; l2_lookup.mask_macaddr = GENMASK_ULL(ETH_ALEN * 8 - 1, 0); - if (dsa_port_is_vlan_filtering(dsa_to_port(ds, port))) { - l2_lookup.mask_vlanid = VLAN_VID_MASK; - l2_lookup.mask_iotag = BIT(0); - } else { - l2_lookup.mask_vlanid = 0; - l2_lookup.mask_iotag = 0; - } + l2_lookup.mask_vlanid = VLAN_VID_MASK; l2_lookup.destports = BIT(port); + tmp = l2_lookup; + rc = sja1105_dynamic_config_read(priv, BLK_IDX_L2_LOOKUP, - SJA1105_SEARCH, &l2_lookup); - if (rc == 0) { - /* Found and this port is already in the entry's + SJA1105_SEARCH, &tmp); + if (rc == 0 && tmp.index != SJA1105_MAX_L2_LOOKUP_COUNT - 1) { + /* Found a static entry and this port is already in the entry's * port mask => job done */ - if (l2_lookup.destports & BIT(port)) + if ((tmp.destports & BIT(port)) && tmp.lockeds) return 0; + + l2_lookup = tmp; + /* l2_lookup.index is populated by the switch in case it * found something. */ @@ -1089,16 +1720,46 @@ int sja1105pqrs_fdb_add(struct dsa_switch *ds, int port, dev_err(ds->dev, "FDB is full, cannot add entry.\n"); return -EINVAL; } - l2_lookup.lockeds = true; l2_lookup.index = i; skip_finding_an_index: + l2_lookup.lockeds = true; + rc = sja1105_dynamic_config_write(priv, BLK_IDX_L2_LOOKUP, l2_lookup.index, &l2_lookup, true); if (rc < 0) return rc; + /* The switch learns dynamic entries and looks up the FDB left to + * right. It is possible that our addition was concurrent with the + * dynamic learning of the same address, so now that the static entry + * has been installed, we are certain that address learning for this + * particular address has been turned off, so the dynamic entry either + * is in the FDB at an index smaller than the static one, or isn't (it + * can also be at a larger index, but in that case it is inactive + * because the static FDB entry will match first, and the dynamic one + * will eventually age out). Search for a dynamically learned address + * prior to our static one and invalidate it. + */ + tmp = l2_lookup; + + rc = sja1105_dynamic_config_read(priv, BLK_IDX_L2_LOOKUP, + SJA1105_SEARCH, &tmp); + if (rc < 0) { + dev_err(ds->dev, + "port %d failed to read back entry for %pM vid %d: %pe\n", + port, addr, vid, ERR_PTR(rc)); + return rc; + } + + if (tmp.index < l2_lookup.index) { + rc = sja1105_dynamic_config_write(priv, BLK_IDX_L2_LOOKUP, + tmp.index, NULL, false); + if (rc < 0) + return rc; + } + return sja1105_static_fdb_change(priv, port, &l2_lookup, true); } @@ -1112,15 +1773,8 @@ int sja1105pqrs_fdb_del(struct dsa_switch *ds, int port, l2_lookup.macaddr = ether_addr_to_u64(addr); l2_lookup.vlanid = vid; - l2_lookup.iotag = SJA1105_S_TAG; l2_lookup.mask_macaddr = GENMASK_ULL(ETH_ALEN * 8 - 1, 0); - if (dsa_port_is_vlan_filtering(dsa_to_port(ds, port))) { - l2_lookup.mask_vlanid = VLAN_VID_MASK; - l2_lookup.mask_iotag = BIT(0); - } else { - l2_lookup.mask_vlanid = 0; - l2_lookup.mask_iotag = 0; - } + l2_lookup.mask_vlanid = VLAN_VID_MASK; l2_lookup.destports = BIT(port); rc = sja1105_dynamic_config_read(priv, BLK_IDX_L2_LOOKUP, @@ -1147,32 +1801,45 @@ int sja1105pqrs_fdb_del(struct dsa_switch *ds, int port, } static int sja1105_fdb_add(struct dsa_switch *ds, int port, - const unsigned char *addr, u16 vid) + const unsigned char *addr, u16 vid, + struct dsa_db db) { struct sja1105_private *priv = ds->priv; - /* dsa_8021q is in effect when the bridge's vlan_filtering isn't, - * so the switch still does some VLAN processing internally. - * But Shared VLAN Learning (SVL) is also active, and it will take - * care of autonomous forwarding between the unique pvid's of each - * port. Here we just make sure that users can't add duplicate FDB - * entries when in this mode - the actual VID doesn't matter except - * for what gets printed in 'bridge fdb show'. In the case of zero, - * no VID gets printed at all. - */ - if (!dsa_port_is_vlan_filtering(dsa_to_port(ds, port))) - vid = 0; + if (!vid) { + switch (db.type) { + case DSA_DB_PORT: + vid = dsa_tag_8021q_standalone_vid(db.dp); + break; + case DSA_DB_BRIDGE: + vid = dsa_tag_8021q_bridge_vid(db.bridge.num); + break; + default: + return -EOPNOTSUPP; + } + } return priv->info->fdb_add_cmd(ds, port, addr, vid); } static int sja1105_fdb_del(struct dsa_switch *ds, int port, - const unsigned char *addr, u16 vid) + const unsigned char *addr, u16 vid, + struct dsa_db db) { struct sja1105_private *priv = ds->priv; - if (!dsa_port_is_vlan_filtering(dsa_to_port(ds, port))) - vid = 0; + if (!vid) { + switch (db.type) { + case DSA_DB_PORT: + vid = dsa_tag_8021q_standalone_vid(db.dp); + break; + case DSA_DB_BRIDGE: + vid = dsa_tag_8021q_bridge_vid(db.bridge.num); + break; + default: + return -EOPNOTSUPP; + } + } return priv->info->fdb_del_cmd(ds, port, addr, vid); } @@ -1207,37 +1874,132 @@ static int sja1105_fdb_dump(struct dsa_switch *ds, int port, */ if (!(l2_lookup.destports & BIT(port))) continue; + + /* We need to hide the FDB entry for unknown multicast */ + if (l2_lookup.macaddr == SJA1105_UNKNOWN_MULTICAST && + l2_lookup.mask_macaddr == SJA1105_UNKNOWN_MULTICAST) + continue; + u64_to_ether_addr(l2_lookup.macaddr, macaddr); /* We need to hide the dsa_8021q VLANs from the user. */ - if (!dsa_port_is_vlan_filtering(dsa_to_port(ds, port))) + if (vid_is_dsa_8021q(l2_lookup.vlanid)) l2_lookup.vlanid = 0; - cb(macaddr, l2_lookup.vlanid, l2_lookup.lockeds, data); + rc = cb(macaddr, l2_lookup.vlanid, l2_lookup.lockeds, data); + if (rc) + return rc; } return 0; } -/* This callback needs to be present */ -static int sja1105_mdb_prepare(struct dsa_switch *ds, int port, - const struct switchdev_obj_port_mdb *mdb) +static void sja1105_fast_age(struct dsa_switch *ds, int port) { - return 0; + struct dsa_port *dp = dsa_to_port(ds, port); + struct sja1105_private *priv = ds->priv; + struct dsa_db db = { + .type = DSA_DB_BRIDGE, + .bridge = { + .dev = dsa_port_bridge_dev_get(dp), + .num = dsa_port_bridge_num_get(dp), + }, + }; + int i; + + for (i = 0; i < SJA1105_MAX_L2_LOOKUP_COUNT; i++) { + struct sja1105_l2_lookup_entry l2_lookup = {0}; + u8 macaddr[ETH_ALEN]; + int rc; + + rc = sja1105_dynamic_config_read(priv, BLK_IDX_L2_LOOKUP, + i, &l2_lookup); + /* No fdb entry at i, not an issue */ + if (rc == -ENOENT) + continue; + if (rc) { + dev_err(ds->dev, "Failed to read FDB: %pe\n", + ERR_PTR(rc)); + return; + } + + if (!(l2_lookup.destports & BIT(port))) + continue; + + /* Don't delete static FDB entries */ + if (l2_lookup.lockeds) + continue; + + u64_to_ether_addr(l2_lookup.macaddr, macaddr); + + rc = sja1105_fdb_del(ds, port, macaddr, l2_lookup.vlanid, db); + if (rc) { + dev_err(ds->dev, + "Failed to delete FDB entry %pM vid %lld: %pe\n", + macaddr, l2_lookup.vlanid, ERR_PTR(rc)); + return; + } + } } -static void sja1105_mdb_add(struct dsa_switch *ds, int port, - const struct switchdev_obj_port_mdb *mdb) +static int sja1105_mdb_add(struct dsa_switch *ds, int port, + const struct switchdev_obj_port_mdb *mdb, + struct dsa_db db) { - sja1105_fdb_add(ds, port, mdb->addr, mdb->vid); + return sja1105_fdb_add(ds, port, mdb->addr, mdb->vid, db); } static int sja1105_mdb_del(struct dsa_switch *ds, int port, - const struct switchdev_obj_port_mdb *mdb) + const struct switchdev_obj_port_mdb *mdb, + struct dsa_db db) +{ + return sja1105_fdb_del(ds, port, mdb->addr, mdb->vid, db); +} + +/* Common function for unicast and broadcast flood configuration. + * Flooding is configured between each {ingress, egress} port pair, and since + * the bridge's semantics are those of "egress flooding", it means we must + * enable flooding towards this port from all ingress ports that are in the + * same forwarding domain. + */ +static int sja1105_manage_flood_domains(struct sja1105_private *priv) { - return sja1105_fdb_del(ds, port, mdb->addr, mdb->vid); + struct sja1105_l2_forwarding_entry *l2_fwd; + struct dsa_switch *ds = priv->ds; + int from, to, rc; + + l2_fwd = priv->static_config.tables[BLK_IDX_L2_FORWARDING].entries; + + for (from = 0; from < ds->num_ports; from++) { + u64 fl_domain = 0, bc_domain = 0; + + for (to = 0; to < priv->ds->num_ports; to++) { + if (!sja1105_can_forward(l2_fwd, from, to)) + continue; + + if (priv->ucast_egress_floods & BIT(to)) + fl_domain |= BIT(to); + if (priv->bcast_egress_floods & BIT(to)) + bc_domain |= BIT(to); + } + + /* Nothing changed, nothing to do */ + if (l2_fwd[from].fl_domain == fl_domain && + l2_fwd[from].bc_domain == bc_domain) + continue; + + l2_fwd[from].fl_domain = fl_domain; + l2_fwd[from].bc_domain = bc_domain; + + rc = sja1105_dynamic_config_write(priv, BLK_IDX_L2_FORWARDING, + from, &l2_fwd[from], true); + if (rc < 0) + return rc; + } + + return 0; } static int sja1105_bridge_member(struct dsa_switch *ds, int port, - struct net_device *br, bool member) + struct dsa_bridge bridge, bool member) { struct sja1105_l2_forwarding_entry *l2_fwd; struct sja1105_private *priv = ds->priv; @@ -1245,7 +2007,7 @@ static int sja1105_bridge_member(struct dsa_switch *ds, int port, l2_fwd = priv->static_config.tables[BLK_IDX_L2_FORWARDING].entries; - for (i = 0; i < SJA1105_NUM_PORTS; i++) { + for (i = 0; i < ds->num_ports; i++) { /* Add this port to the forwarding matrix of the * other ports in the same bridge, and viceversa. */ @@ -1262,7 +2024,7 @@ static int sja1105_bridge_member(struct dsa_switch *ds, int port, */ if (i == port) continue; - if (dsa_to_port(ds, i)->bridge_dev != br) + if (!dsa_port_offloads_bridge(dsa_to_port(ds, i), &bridge)) continue; sja1105_port_allow_traffic(l2_fwd, i, port, member); sja1105_port_allow_traffic(l2_fwd, port, i, member); @@ -1273,13 +2035,22 @@ static int sja1105_bridge_member(struct dsa_switch *ds, int port, return rc; } - return sja1105_dynamic_config_write(priv, BLK_IDX_L2_FORWARDING, - port, &l2_fwd[port], true); + rc = sja1105_dynamic_config_write(priv, BLK_IDX_L2_FORWARDING, + port, &l2_fwd[port], true); + if (rc) + return rc; + + rc = sja1105_commit_pvid(ds, port); + if (rc) + return rc; + + return sja1105_manage_flood_domains(priv); } static void sja1105_bridge_stp_state_set(struct dsa_switch *ds, int port, u8 state) { + struct dsa_port *dp = dsa_to_port(ds, port); struct sja1105_private *priv = ds->priv; struct sja1105_mac_config_entry *mac; @@ -1305,12 +2076,12 @@ static void sja1105_bridge_stp_state_set(struct dsa_switch *ds, int port, case BR_STATE_LEARNING: mac[port].ingress = true; mac[port].egress = false; - mac[port].dyn_learn = true; + mac[port].dyn_learn = dp->learning; break; case BR_STATE_FORWARDING: mac[port].ingress = true; mac[port].egress = true; - mac[port].dyn_learn = true; + mac[port].dyn_learn = dp->learning; break; default: dev_err(ds->dev, "invalid STP state: %d\n", state); @@ -1322,15 +2093,124 @@ static void sja1105_bridge_stp_state_set(struct dsa_switch *ds, int port, } static int sja1105_bridge_join(struct dsa_switch *ds, int port, - struct net_device *br) + struct dsa_bridge bridge, + bool *tx_fwd_offload, + struct netlink_ext_ack *extack) { - return sja1105_bridge_member(ds, port, br, true); + int rc; + + rc = sja1105_bridge_member(ds, port, bridge, true); + if (rc) + return rc; + + rc = dsa_tag_8021q_bridge_join(ds, port, bridge); + if (rc) { + sja1105_bridge_member(ds, port, bridge, false); + return rc; + } + + *tx_fwd_offload = true; + + return 0; } static void sja1105_bridge_leave(struct dsa_switch *ds, int port, - struct net_device *br) + struct dsa_bridge bridge) +{ + dsa_tag_8021q_bridge_leave(ds, port, bridge); + sja1105_bridge_member(ds, port, bridge, false); +} + +#define BYTES_PER_KBIT (1000LL / 8) + +static int sja1105_find_unused_cbs_shaper(struct sja1105_private *priv) +{ + int i; + + for (i = 0; i < priv->info->num_cbs_shapers; i++) + if (!priv->cbs[i].idle_slope && !priv->cbs[i].send_slope) + return i; + + return -1; +} + +static int sja1105_delete_cbs_shaper(struct sja1105_private *priv, int port, + int prio) +{ + int i; + + for (i = 0; i < priv->info->num_cbs_shapers; i++) { + struct sja1105_cbs_entry *cbs = &priv->cbs[i]; + + if (cbs->port == port && cbs->prio == prio) { + memset(cbs, 0, sizeof(*cbs)); + return sja1105_dynamic_config_write(priv, BLK_IDX_CBS, + i, cbs, true); + } + } + + return 0; +} + +static int sja1105_setup_tc_cbs(struct dsa_switch *ds, int port, + struct tc_cbs_qopt_offload *offload) +{ + struct sja1105_private *priv = ds->priv; + struct sja1105_cbs_entry *cbs; + int index; + + if (!offload->enable) + return sja1105_delete_cbs_shaper(priv, port, offload->queue); + + index = sja1105_find_unused_cbs_shaper(priv); + if (index < 0) + return -ENOSPC; + + cbs = &priv->cbs[index]; + cbs->port = port; + cbs->prio = offload->queue; + /* locredit and sendslope are negative by definition. In hardware, + * positive values must be provided, and the negative sign is implicit. + */ + cbs->credit_hi = offload->hicredit; + cbs->credit_lo = abs(offload->locredit); + /* User space is in kbits/sec, hardware in bytes/sec */ + cbs->idle_slope = offload->idleslope * BYTES_PER_KBIT; + cbs->send_slope = abs(offload->sendslope * BYTES_PER_KBIT); + /* Convert the negative values from 64-bit 2's complement + * to 32-bit 2's complement (for the case of 0x80000000 whose + * negative is still negative). + */ + cbs->credit_lo &= GENMASK_ULL(31, 0); + cbs->send_slope &= GENMASK_ULL(31, 0); + + return sja1105_dynamic_config_write(priv, BLK_IDX_CBS, index, cbs, + true); +} + +static int sja1105_reload_cbs(struct sja1105_private *priv) { - sja1105_bridge_member(ds, port, br, false); + int rc = 0, i; + + /* The credit based shapers are only allocated if + * CONFIG_NET_SCH_CBS is enabled. + */ + if (!priv->cbs) + return 0; + + for (i = 0; i < priv->info->num_cbs_shapers; i++) { + struct sja1105_cbs_entry *cbs = &priv->cbs[i]; + + if (!cbs->idle_slope && !cbs->send_slope) + continue; + + rc = sja1105_dynamic_config_write(priv, BLK_IDX_CBS, i, cbs, + true); + if (rc) + break; + } + + return rc; } static const char * const sja1105_reset_reasons[] = { @@ -1338,6 +2218,8 @@ static const char * const sja1105_reset_reasons[] = { [SJA1105_RX_HWTSTAMPING] = "RX timestamping", [SJA1105_AGEING_TIME] = "Ageing time", [SJA1105_SCHEDULING] = "Time-aware scheduling", + [SJA1105_BEST_EFFORT_POLICING] = "Best-effort policing", + [SJA1105_VIRTUAL_LINKS] = "Virtual links", }; /* For situations where we need to change a setting at runtime that is only @@ -1351,8 +2233,9 @@ int sja1105_static_config_reload(struct sja1105_private *priv, { struct ptp_system_timestamp ptp_sts_before; struct ptp_system_timestamp ptp_sts_after; + int speed_mbps[SJA1105_MAX_NUM_PORTS]; + u16 bmcr[SJA1105_MAX_NUM_PORTS] = {0}; struct sja1105_mac_config_entry *mac; - int speed_mbps[SJA1105_NUM_PORTS]; struct dsa_switch *ds = priv->ds; s64 t1, t2, t3, t4; s64 t12, t34; @@ -1368,26 +2251,37 @@ int sja1105_static_config_reload(struct sja1105_private *priv, * switch wants to see in the static config in order to allow us to * change it through the dynamic interface later. */ - for (i = 0; i < SJA1105_NUM_PORTS; i++) { - speed_mbps[i] = sja1105_speed[mac[i].speed]; - mac[i].speed = SJA1105_SPEED_AUTO; + for (i = 0; i < ds->num_ports; i++) { + speed_mbps[i] = sja1105_port_speed_to_ethtool(priv, + mac[i].speed); + mac[i].speed = priv->info->port_speed[SJA1105_SPEED_AUTO]; + + if (priv->xpcs[i]) + bmcr[i] = mdiobus_c45_read(priv->mdio_pcs, i, + MDIO_MMD_VEND2, MDIO_CTRL1); } /* No PTP operations can run right now */ mutex_lock(&priv->ptp_data.lock); rc = __sja1105_ptp_gettimex(ds, &now, &ptp_sts_before); - if (rc < 0) - goto out_unlock_ptp; + if (rc < 0) { + mutex_unlock(&priv->ptp_data.lock); + goto out; + } /* Reset switch and send updated static configuration */ rc = sja1105_static_config_upload(priv); - if (rc < 0) - goto out_unlock_ptp; + if (rc < 0) { + mutex_unlock(&priv->ptp_data.lock); + goto out; + } rc = __sja1105_ptp_settime(ds, 0, &ptp_sts_after); - if (rc < 0) - goto out_unlock_ptp; + if (rc < 0) { + mutex_unlock(&priv->ptp_data.lock); + goto out; + } t1 = timespec64_to_ns(&ptp_sts_before.pre_ts); t2 = timespec64_to_ns(&ptp_sts_before.post_ts); @@ -1402,7 +2296,6 @@ int sja1105_static_config_reload(struct sja1105_private *priv, __sja1105_ptp_adjtime(ds, now); -out_unlock_ptp: mutex_unlock(&priv->ptp_data.lock); dev_info(priv->ds->dev, @@ -1413,153 +2306,91 @@ out_unlock_ptp: * For these interfaces there is no dynamic configuration * needed, since PLLs have same settings at all speeds. */ - rc = sja1105_clocking_setup(priv); - if (rc < 0) - goto out; - - for (i = 0; i < SJA1105_NUM_PORTS; i++) { - rc = sja1105_adjust_port_config(priv, i, speed_mbps[i]); + if (priv->info->clocking_setup) { + rc = priv->info->clocking_setup(priv); if (rc < 0) goto out; } -out: - mutex_unlock(&priv->mgmt_lock); - - return rc; -} -static int sja1105_pvid_apply(struct sja1105_private *priv, int port, u16 pvid) -{ - struct sja1105_mac_config_entry *mac; + for (i = 0; i < ds->num_ports; i++) { + struct dw_xpcs *xpcs = priv->xpcs[i]; + unsigned int mode; - mac = priv->static_config.tables[BLK_IDX_MAC_CONFIG].entries; - - mac[port].vlanid = pvid; - - return sja1105_dynamic_config_write(priv, BLK_IDX_MAC_CONFIG, port, - &mac[port], true); -} - -static int sja1105_is_vlan_configured(struct sja1105_private *priv, u16 vid) -{ - struct sja1105_vlan_lookup_entry *vlan; - int count, i; + rc = sja1105_adjust_port_config(priv, i, speed_mbps[i]); + if (rc < 0) + goto out; - vlan = priv->static_config.tables[BLK_IDX_VLAN_LOOKUP].entries; - count = priv->static_config.tables[BLK_IDX_VLAN_LOOKUP].entry_count; + if (!xpcs) + continue; - for (i = 0; i < count; i++) - if (vlan[i].vlanid == vid) - return i; + if (bmcr[i] & BMCR_ANENABLE) + mode = MLO_AN_INBAND; + else if (priv->fixed_link[i]) + mode = MLO_AN_FIXED; + else + mode = MLO_AN_PHY; - /* Return an invalid entry index if not found */ - return -1; -} + rc = xpcs_do_config(xpcs, priv->phy_mode[i], mode, NULL); + if (rc < 0) + goto out; -static int sja1105_vlan_apply(struct sja1105_private *priv, int port, u16 vid, - bool enabled, bool untagged) -{ - struct sja1105_vlan_lookup_entry *vlan; - struct sja1105_table *table; - bool keep = true; - int match, rc; + if (!phylink_autoneg_inband(mode)) { + int speed = SPEED_UNKNOWN; - table = &priv->static_config.tables[BLK_IDX_VLAN_LOOKUP]; + if (priv->phy_mode[i] == PHY_INTERFACE_MODE_2500BASEX) + speed = SPEED_2500; + else if (bmcr[i] & BMCR_SPEED1000) + speed = SPEED_1000; + else if (bmcr[i] & BMCR_SPEED100) + speed = SPEED_100; + else + speed = SPEED_10; - match = sja1105_is_vlan_configured(priv, vid); - if (match < 0) { - /* Can't delete a missing entry. */ - if (!enabled) - return 0; - rc = sja1105_table_resize(table, table->entry_count + 1); - if (rc) - return rc; - match = table->entry_count - 1; - } - /* Assign pointer after the resize (it's new memory) */ - vlan = table->entries; - vlan[match].vlanid = vid; - if (enabled) { - vlan[match].vlan_bc |= BIT(port); - vlan[match].vmemb_port |= BIT(port); - } else { - vlan[match].vlan_bc &= ~BIT(port); - vlan[match].vmemb_port &= ~BIT(port); + xpcs_link_up(&xpcs->pcs, mode, priv->phy_mode[i], + speed, DUPLEX_FULL); + } } - /* Also unset tag_port if removing this VLAN was requested, - * just so we don't have a confusing bitmap (no practical purpose). - */ - if (untagged || !enabled) - vlan[match].tag_port &= ~BIT(port); - else - vlan[match].tag_port |= BIT(port); - /* If there's no port left as member of this VLAN, - * it's time for it to go. - */ - if (!vlan[match].vmemb_port) - keep = false; - - dev_dbg(priv->ds->dev, - "%s: port %d, vid %llu, broadcast domain 0x%llx, " - "port members 0x%llx, tagged ports 0x%llx, keep %d\n", - __func__, port, vlan[match].vlanid, vlan[match].vlan_bc, - vlan[match].vmemb_port, vlan[match].tag_port, keep); - rc = sja1105_dynamic_config_write(priv, BLK_IDX_VLAN_LOOKUP, vid, - &vlan[match], keep); + rc = sja1105_reload_cbs(priv); if (rc < 0) - return rc; - - if (!keep) - return sja1105_table_delete_entry(table, match); - - return 0; -} - -static int sja1105_setup_8021q_tagging(struct dsa_switch *ds, bool enabled) -{ - int rc, i; + goto out; +out: + mutex_unlock(&priv->mgmt_lock); - for (i = 0; i < SJA1105_NUM_PORTS; i++) { - rc = dsa_port_setup_8021q_tagging(ds, i, enabled); - if (rc < 0) { - dev_err(ds->dev, "Failed to setup VLAN tagging for port %d: %d\n", - i, rc); - return rc; - } - } - dev_info(ds->dev, "%s switch tagging\n", - enabled ? "Enabled" : "Disabled"); - return 0; + return rc; } static enum dsa_tag_protocol sja1105_get_tag_protocol(struct dsa_switch *ds, int port, enum dsa_tag_protocol mp) { - return DSA_TAG_PROTO_SJA1105; -} + struct sja1105_private *priv = ds->priv; -/* This callback needs to be present */ -static int sja1105_vlan_prepare(struct dsa_switch *ds, int port, - const struct switchdev_obj_port_vlan *vlan) -{ - return 0; + return priv->info->tag_proto; } /* The TPID setting belongs to the General Parameters table, * which can only be partially reconfigured at runtime (and not the TPID). * So a switch reset is required. */ -static int sja1105_vlan_filtering(struct dsa_switch *ds, int port, bool enabled) +int sja1105_vlan_filtering(struct dsa_switch *ds, int port, bool enabled, + struct netlink_ext_ack *extack) { - struct sja1105_l2_lookup_params_entry *l2_lookup_params; struct sja1105_general_params_entry *general_params; struct sja1105_private *priv = ds->priv; struct sja1105_table *table; + struct sja1105_rule *rule; u16 tpid, tpid2; int rc; + list_for_each_entry(rule, &priv->flow_block.rules, list) { + if (rule->type == SJA1105_RULE_VL) { + NL_SET_ERR_MSG_MOD(extack, + "Cannot change VLAN filtering with active VL rules"); + return -EBUSY; + } + } + if (enabled) { /* Enable VLAN filtering. */ tpid = ETH_P_8021Q; @@ -1582,199 +2413,204 @@ static int sja1105_vlan_filtering(struct dsa_switch *ds, int port, bool enabled) general_params->incl_srcpt1 = enabled; general_params->incl_srcpt0 = enabled; - /* VLAN filtering => independent VLAN learning. - * No VLAN filtering => shared VLAN learning. - * - * In shared VLAN learning mode, untagged traffic still gets - * pvid-tagged, and the FDB table gets populated with entries - * containing the "real" (pvid or from VLAN tag) VLAN ID. - * However the switch performs a masked L2 lookup in the FDB, - * effectively only looking up a frame's DMAC (and not VID) for the - * forwarding decision. - * - * This is extremely convenient for us, because in modes with - * vlan_filtering=0, dsa_8021q actually installs unique pvid's into - * each front panel port. This is good for identification but breaks - * learning badly - the VID of the learnt FDB entry is unique, aka - * no frames coming from any other port are going to have it. So - * for forwarding purposes, this is as though learning was broken - * (all frames get flooded). - */ - table = &priv->static_config.tables[BLK_IDX_L2_LOOKUP_PARAMS]; - l2_lookup_params = table->entries; - l2_lookup_params->shared_learn = !enabled; + for (port = 0; port < ds->num_ports; port++) { + if (dsa_is_unused_port(ds, port)) + continue; + + rc = sja1105_commit_pvid(ds, port); + if (rc) + return rc; + } rc = sja1105_static_config_reload(priv, SJA1105_VLAN_FILTERING); if (rc) - dev_err(ds->dev, "Failed to change VLAN Ethertype\n"); + NL_SET_ERR_MSG_MOD(extack, "Failed to change VLAN Ethertype"); - /* Switch port identification based on 802.1Q is only passable - * if we are not under a vlan_filtering bridge. So make sure - * the two configurations are mutually exclusive. - */ - return sja1105_setup_8021q_tagging(ds, !enabled); + return rc; } -static void sja1105_vlan_add(struct dsa_switch *ds, int port, - const struct switchdev_obj_port_vlan *vlan) +static int sja1105_vlan_add(struct sja1105_private *priv, int port, u16 vid, + u16 flags, bool allowed_ingress) { - struct sja1105_private *priv = ds->priv; - u16 vid; - int rc; + struct sja1105_vlan_lookup_entry *vlan; + struct sja1105_table *table; + int match, rc; - for (vid = vlan->vid_begin; vid <= vlan->vid_end; vid++) { - rc = sja1105_vlan_apply(priv, port, vid, true, vlan->flags & - BRIDGE_VLAN_INFO_UNTAGGED); - if (rc < 0) { - dev_err(ds->dev, "Failed to add VLAN %d to port %d: %d\n", - vid, port, rc); - return; - } - if (vlan->flags & BRIDGE_VLAN_INFO_PVID) { - rc = sja1105_pvid_apply(ds->priv, port, vid); - if (rc < 0) { - dev_err(ds->dev, "Failed to set pvid %d on port %d: %d\n", - vid, port, rc); - return; - } - } + table = &priv->static_config.tables[BLK_IDX_VLAN_LOOKUP]; + + match = sja1105_is_vlan_configured(priv, vid); + if (match < 0) { + rc = sja1105_table_resize(table, table->entry_count + 1); + if (rc) + return rc; + match = table->entry_count - 1; } + + /* Assign pointer after the resize (it's new memory) */ + vlan = table->entries; + + vlan[match].type_entry = SJA1110_VLAN_D_TAG; + vlan[match].vlanid = vid; + vlan[match].vlan_bc |= BIT(port); + + if (allowed_ingress) + vlan[match].vmemb_port |= BIT(port); + else + vlan[match].vmemb_port &= ~BIT(port); + + if (flags & BRIDGE_VLAN_INFO_UNTAGGED) + vlan[match].tag_port &= ~BIT(port); + else + vlan[match].tag_port |= BIT(port); + + return sja1105_dynamic_config_write(priv, BLK_IDX_VLAN_LOOKUP, vid, + &vlan[match], true); } -static int sja1105_vlan_del(struct dsa_switch *ds, int port, - const struct switchdev_obj_port_vlan *vlan) +static int sja1105_vlan_del(struct sja1105_private *priv, int port, u16 vid) { - struct sja1105_private *priv = ds->priv; - u16 vid; - int rc; + struct sja1105_vlan_lookup_entry *vlan; + struct sja1105_table *table; + bool keep = true; + int match, rc; + + table = &priv->static_config.tables[BLK_IDX_VLAN_LOOKUP]; + + match = sja1105_is_vlan_configured(priv, vid); + /* Can't delete a missing entry. */ + if (match < 0) + return 0; + + /* Assign pointer after the resize (it's new memory) */ + vlan = table->entries; + + vlan[match].vlanid = vid; + vlan[match].vlan_bc &= ~BIT(port); + vlan[match].vmemb_port &= ~BIT(port); + /* Also unset tag_port, just so we don't have a confusing bitmap + * (no practical purpose). + */ + vlan[match].tag_port &= ~BIT(port); + + /* If there's no port left as member of this VLAN, + * it's time for it to go. + */ + if (!vlan[match].vmemb_port) + keep = false; + + rc = sja1105_dynamic_config_write(priv, BLK_IDX_VLAN_LOOKUP, vid, + &vlan[match], keep); + if (rc < 0) + return rc; + + if (!keep) + return sja1105_table_delete_entry(table, match); - for (vid = vlan->vid_begin; vid <= vlan->vid_end; vid++) { - rc = sja1105_vlan_apply(priv, port, vid, false, vlan->flags & - BRIDGE_VLAN_INFO_UNTAGGED); - if (rc < 0) { - dev_err(ds->dev, "Failed to remove VLAN %d from port %d: %d\n", - vid, port, rc); - return rc; - } - } return 0; } -/* The programming model for the SJA1105 switch is "all-at-once" via static - * configuration tables. Some of these can be dynamically modified at runtime, - * but not the xMII mode parameters table. - * Furthermode, some PHYs may not have crystals for generating their clocks - * (e.g. RMII). Instead, their 50MHz clock is supplied via the SJA1105 port's - * ref_clk pin. So port clocking needs to be initialized early, before - * connecting to PHYs is attempted, otherwise they won't respond through MDIO. - * Setting correct PHY link speed does not matter now. - * But dsa_slave_phy_setup is called later than sja1105_setup, so the PHY - * bindings are not yet parsed by DSA core. We need to parse early so that we - * can populate the xMII mode parameters table. - */ -static int sja1105_setup(struct dsa_switch *ds) +static int sja1105_bridge_vlan_add(struct dsa_switch *ds, int port, + const struct switchdev_obj_port_vlan *vlan, + struct netlink_ext_ack *extack) { - struct sja1105_dt_port ports[SJA1105_NUM_PORTS]; struct sja1105_private *priv = ds->priv; + u16 flags = vlan->flags; int rc; - rc = sja1105_parse_dt(priv, ports); - if (rc < 0) { - dev_err(ds->dev, "Failed to parse DT: %d\n", rc); - return rc; - } - - /* Error out early if internal delays are required through DT - * and we can't apply them. + /* Be sure to deny alterations to the configuration done by tag_8021q. */ - rc = sja1105_parse_rgmii_delays(priv, ports); - if (rc < 0) { - dev_err(ds->dev, "RGMII delay not supported\n"); - return rc; + if (vid_is_dsa_8021q(vlan->vid)) { + NL_SET_ERR_MSG_MOD(extack, + "Range 3072-4095 reserved for dsa_8021q operation"); + return -EBUSY; } - rc = sja1105_ptp_clock_register(ds); - if (rc < 0) { - dev_err(ds->dev, "Failed to register PTP clock: %d\n", rc); - return rc; - } - /* Create and send configuration down to device */ - rc = sja1105_static_config_load(priv, ports); - if (rc < 0) { - dev_err(ds->dev, "Failed to load static config: %d\n", rc); - return rc; - } - /* Configure the CGU (PHY link modes and speeds) */ - rc = sja1105_clocking_setup(priv); - if (rc < 0) { - dev_err(ds->dev, "Failed to configure MII clocking: %d\n", rc); + /* Always install bridge VLANs as egress-tagged on CPU and DSA ports */ + if (dsa_is_cpu_port(ds, port) || dsa_is_dsa_port(ds, port)) + flags = 0; + + rc = sja1105_vlan_add(priv, port, vlan->vid, flags, true); + if (rc) return rc; - } - /* On SJA1105, VLAN filtering per se is always enabled in hardware. - * The only thing we can do to disable it is lie about what the 802.1Q - * EtherType is. - * So it will still try to apply VLAN filtering, but all ingress - * traffic (except frames received with EtherType of ETH_P_SJA1105) - * will be internally tagged with a distorted VLAN header where the - * TPID is ETH_P_SJA1105, and the VLAN ID is the port pvid. - */ - ds->vlan_filtering_is_global = true; - /* Advertise the 8 egress queues */ - ds->num_tx_queues = SJA1105_NUM_TC; + if (vlan->flags & BRIDGE_VLAN_INFO_PVID) + priv->bridge_pvid[port] = vlan->vid; - /* The DSA/switchdev model brings up switch ports in standalone mode by - * default, and that means vlan_filtering is 0 since they're not under - * a bridge, so it's safe to set up switch tagging at this time. - */ - return sja1105_setup_8021q_tagging(ds, true); + return sja1105_commit_pvid(ds, port); } -static void sja1105_teardown(struct dsa_switch *ds) +static int sja1105_bridge_vlan_del(struct dsa_switch *ds, int port, + const struct switchdev_obj_port_vlan *vlan) { struct sja1105_private *priv = ds->priv; - int port; - - for (port = 0; port < SJA1105_NUM_PORTS; port++) { - struct sja1105_port *sp = &priv->ports[port]; - - if (!dsa_is_user_port(ds, port)) - continue; + int rc; - if (sp->xmit_worker) - kthread_destroy_worker(sp->xmit_worker); - } + rc = sja1105_vlan_del(priv, port, vlan->vid); + if (rc) + return rc; - sja1105_tas_teardown(ds); - sja1105_ptp_clock_unregister(ds); - sja1105_static_config_free(&priv->static_config); + /* In case the pvid was deleted, make sure that untagged packets will + * be dropped. + */ + return sja1105_commit_pvid(ds, port); } -static int sja1105_port_enable(struct dsa_switch *ds, int port, - struct phy_device *phy) +static int sja1105_dsa_8021q_vlan_add(struct dsa_switch *ds, int port, u16 vid, + u16 flags) { - struct net_device *slave; + struct sja1105_private *priv = ds->priv; + bool allowed_ingress = true; + int rc; - if (!dsa_is_user_port(ds, port)) - return 0; + /* Prevent attackers from trying to inject a DSA tag from + * the outside world. + */ + if (dsa_is_user_port(ds, port)) + allowed_ingress = false; - slave = dsa_to_port(ds, port)->slave; + rc = sja1105_vlan_add(priv, port, vid, flags, allowed_ingress); + if (rc) + return rc; - slave->features &= ~NETIF_F_HW_VLAN_CTAG_FILTER; + if (flags & BRIDGE_VLAN_INFO_PVID) + priv->tag_8021q_pvid[port] = vid; - return 0; + return sja1105_commit_pvid(ds, port); } -static void sja1105_port_disable(struct dsa_switch *ds, int port) +static int sja1105_dsa_8021q_vlan_del(struct dsa_switch *ds, int port, u16 vid) { struct sja1105_private *priv = ds->priv; - struct sja1105_port *sp = &priv->ports[port]; - if (!dsa_is_user_port(ds, port)) - return; + return sja1105_vlan_del(priv, port, vid); +} + +static int sja1105_prechangeupper(struct dsa_switch *ds, int port, + struct netdev_notifier_changeupper_info *info) +{ + struct netlink_ext_ack *extack = info->info.extack; + struct net_device *upper = info->upper_dev; + struct dsa_switch_tree *dst = ds->dst; + struct dsa_port *dp; + + if (is_vlan_dev(upper)) { + NL_SET_ERR_MSG_MOD(extack, "8021q uppers are not supported"); + return -EBUSY; + } + + if (netif_is_bridge_master(upper)) { + list_for_each_entry(dp, &dst->ports, list) { + struct net_device *br = dsa_port_bridge_dev_get(dp); + + if (br && br != upper && br_vlan_enabled(br)) { + NL_SET_ERR_MSG_MOD(extack, + "Only one VLAN-aware bridge is supported"); + return -EBUSY; + } + } + } - kthread_cancel_work_sync(&sp->xmit_work); - skb_queue_purge(&sp->xmit_queue); + return 0; } static int sja1105_mgmt_xmit(struct dsa_switch *ds, int port, int slot, @@ -1835,10 +2671,8 @@ static int sja1105_mgmt_xmit(struct dsa_switch *ds, int port, int slot, return NETDEV_TX_OK; } -#define work_to_port(work) \ - container_of((work), struct sja1105_port, xmit_work) -#define tagger_to_sja1105(t) \ - container_of((t), struct sja1105_private, tagger_data) +#define work_to_xmit_work(w) \ + container_of((w), struct sja1105_deferred_xmit_work, work) /* Deferred work is unfortunately necessary because setting up the management * route cannot be done from atomit context (SPI transfer takes a sleepable @@ -1846,25 +2680,41 @@ static int sja1105_mgmt_xmit(struct dsa_switch *ds, int port, int slot, */ static void sja1105_port_deferred_xmit(struct kthread_work *work) { - struct sja1105_port *sp = work_to_port(work); - struct sja1105_tagger_data *tagger_data = sp->data; - struct sja1105_private *priv = tagger_to_sja1105(tagger_data); - int port = sp - priv->ports; - struct sk_buff *skb; + struct sja1105_deferred_xmit_work *xmit_work = work_to_xmit_work(work); + struct sk_buff *clone, *skb = xmit_work->skb; + struct dsa_switch *ds = xmit_work->dp->ds; + struct sja1105_private *priv = ds->priv; + int port = xmit_work->dp->index; - while ((skb = skb_dequeue(&sp->xmit_queue)) != NULL) { - struct sk_buff *clone = DSA_SKB_CB(skb)->clone; + clone = SJA1105_SKB_CB(skb)->clone; - mutex_lock(&priv->mgmt_lock); + mutex_lock(&priv->mgmt_lock); - sja1105_mgmt_xmit(priv->ds, port, 0, skb, !!clone); + sja1105_mgmt_xmit(ds, port, 0, skb, !!clone); - /* The clone, if there, was made by dsa_skb_tx_timestamp */ - if (clone) - sja1105_ptp_txtstamp_skb(priv->ds, port, clone); + /* The clone, if there, was made by dsa_skb_tx_timestamp */ + if (clone) + sja1105_ptp_txtstamp_skb(ds, port, clone); - mutex_unlock(&priv->mgmt_lock); - } + mutex_unlock(&priv->mgmt_lock); + + kfree(xmit_work); +} + +static int sja1105_connect_tag_protocol(struct dsa_switch *ds, + enum dsa_tag_protocol proto) +{ + struct sja1105_private *priv = ds->priv; + struct sja1105_tagger_data *tagger_data; + + if (proto != priv->info->tag_proto) + return -EPROTONOSUPPORT; + + tagger_data = sja1105_tagger_data(ds); + tagger_data->xmit_work_fn = sja1105_port_deferred_xmit; + tagger_data->meta_tstamp_handler = sja1110_process_meta_tstamp; + + return 0; } /* The MAXAGE setting belongs to the L2 Forwarding Parameters table, @@ -1891,6 +2741,31 @@ static int sja1105_set_ageing_time(struct dsa_switch *ds, return sja1105_static_config_reload(priv, SJA1105_AGEING_TIME); } +static int sja1105_change_mtu(struct dsa_switch *ds, int port, int new_mtu) +{ + struct sja1105_l2_policing_entry *policing; + struct sja1105_private *priv = ds->priv; + + new_mtu += VLAN_ETH_HLEN + ETH_FCS_LEN; + + if (dsa_is_cpu_port(ds, port) || dsa_is_dsa_port(ds, port)) + new_mtu += VLAN_HLEN; + + policing = priv->static_config.tables[BLK_IDX_L2_POLICING].entries; + + if (policing[port].maxlen == new_mtu) + return 0; + + policing[port].maxlen = new_mtu; + + return sja1105_static_config_reload(priv, SJA1105_BEST_EFFORT_POLICING); +} + +static int sja1105_get_max_mtu(struct dsa_switch *ds, int port) +{ + return 2043 - VLAN_ETH_HLEN - ETH_FCS_LEN; +} + static int sja1105_port_setup_tc(struct dsa_switch *ds, int port, enum tc_setup_type type, void *type_data) @@ -1898,6 +2773,8 @@ static int sja1105_port_setup_tc(struct dsa_switch *ds, int port, switch (type) { case TC_SETUP_QDISC_TAPRIO: return sja1105_setup_tc_taprio(ds, port, type_data); + case TC_SETUP_QDISC_CBS: + return sja1105_setup_tc_cbs(ds, port, type_data); default: return -EOPNOTSUPP; } @@ -1914,6 +2791,7 @@ static int sja1105_mirror_apply(struct sja1105_private *priv, int from, int to, { struct sja1105_general_params_entry *general_params; struct sja1105_mac_config_entry *mac; + struct dsa_switch *ds = priv->ds; struct sja1105_table *table; bool already_enabled; u64 new_mirr_port; @@ -1924,7 +2802,7 @@ static int sja1105_mirror_apply(struct sja1105_private *priv, int from, int to, mac = priv->static_config.tables[BLK_IDX_MAC_CONFIG].entries; - already_enabled = (general_params->mirr_port != SJA1105_NUM_PORTS); + already_enabled = (general_params->mirr_port != ds->num_ports); if (already_enabled && enabled && general_params->mirr_port != to) { dev_err(priv->ds->dev, "Delete mirroring rules towards port %llu first\n", @@ -1938,7 +2816,7 @@ static int sja1105_mirror_apply(struct sja1105_private *priv, int from, int to, int port; /* Anybody still referencing mirr_port? */ - for (port = 0; port < SJA1105_NUM_PORTS; port++) { + for (port = 0; port < ds->num_ports; port++) { if (mac[port].ing_mirr || mac[port].egr_mirr) { keep = true; break; @@ -1946,7 +2824,7 @@ static int sja1105_mirror_apply(struct sja1105_private *priv, int from, int to, } /* Unset already_enabled for next time */ if (!keep) - new_mirr_port = SJA1105_NUM_PORTS; + new_mirr_port = ds->num_ports; } if (new_mirr_port != general_params->mirr_port) { general_params->mirr_port = new_mirr_port; @@ -1968,7 +2846,7 @@ static int sja1105_mirror_apply(struct sja1105_private *priv, int from, int to, static int sja1105_mirror_add(struct dsa_switch *ds, int port, struct dsa_mall_mirror_tc_entry *mirror, - bool ingress) + bool ingress, struct netlink_ext_ack *extack) { return sja1105_mirror_apply(ds->priv, port, mirror->to_local_port, ingress, true); @@ -1981,32 +2859,316 @@ static void sja1105_mirror_del(struct dsa_switch *ds, int port, mirror->ingress, false); } +static int sja1105_port_policer_add(struct dsa_switch *ds, int port, + struct dsa_mall_policer_tc_entry *policer) +{ + struct sja1105_l2_policing_entry *policing; + struct sja1105_private *priv = ds->priv; + + policing = priv->static_config.tables[BLK_IDX_L2_POLICING].entries; + + /* In hardware, every 8 microseconds the credit level is incremented by + * the value of RATE bytes divided by 64, up to a maximum of SMAX + * bytes. + */ + policing[port].rate = div_u64(512 * policer->rate_bytes_per_sec, + 1000000); + policing[port].smax = policer->burst; + + return sja1105_static_config_reload(priv, SJA1105_BEST_EFFORT_POLICING); +} + +static void sja1105_port_policer_del(struct dsa_switch *ds, int port) +{ + struct sja1105_l2_policing_entry *policing; + struct sja1105_private *priv = ds->priv; + + policing = priv->static_config.tables[BLK_IDX_L2_POLICING].entries; + + policing[port].rate = SJA1105_RATE_MBPS(1000); + policing[port].smax = 65535; + + sja1105_static_config_reload(priv, SJA1105_BEST_EFFORT_POLICING); +} + +static int sja1105_port_set_learning(struct sja1105_private *priv, int port, + bool enabled) +{ + struct sja1105_mac_config_entry *mac; + + mac = priv->static_config.tables[BLK_IDX_MAC_CONFIG].entries; + + mac[port].dyn_learn = enabled; + + return sja1105_dynamic_config_write(priv, BLK_IDX_MAC_CONFIG, port, + &mac[port], true); +} + +static int sja1105_port_ucast_bcast_flood(struct sja1105_private *priv, int to, + struct switchdev_brport_flags flags) +{ + if (flags.mask & BR_FLOOD) { + if (flags.val & BR_FLOOD) + priv->ucast_egress_floods |= BIT(to); + else + priv->ucast_egress_floods &= ~BIT(to); + } + + if (flags.mask & BR_BCAST_FLOOD) { + if (flags.val & BR_BCAST_FLOOD) + priv->bcast_egress_floods |= BIT(to); + else + priv->bcast_egress_floods &= ~BIT(to); + } + + return sja1105_manage_flood_domains(priv); +} + +static int sja1105_port_mcast_flood(struct sja1105_private *priv, int to, + struct switchdev_brport_flags flags, + struct netlink_ext_ack *extack) +{ + struct sja1105_l2_lookup_entry *l2_lookup; + struct sja1105_table *table; + int match; + + table = &priv->static_config.tables[BLK_IDX_L2_LOOKUP]; + l2_lookup = table->entries; + + for (match = 0; match < table->entry_count; match++) + if (l2_lookup[match].macaddr == SJA1105_UNKNOWN_MULTICAST && + l2_lookup[match].mask_macaddr == SJA1105_UNKNOWN_MULTICAST) + break; + + if (match == table->entry_count) { + NL_SET_ERR_MSG_MOD(extack, + "Could not find FDB entry for unknown multicast"); + return -ENOSPC; + } + + if (flags.val & BR_MCAST_FLOOD) + l2_lookup[match].destports |= BIT(to); + else + l2_lookup[match].destports &= ~BIT(to); + + return sja1105_dynamic_config_write(priv, BLK_IDX_L2_LOOKUP, + l2_lookup[match].index, + &l2_lookup[match], + true); +} + +static int sja1105_port_pre_bridge_flags(struct dsa_switch *ds, int port, + struct switchdev_brport_flags flags, + struct netlink_ext_ack *extack) +{ + struct sja1105_private *priv = ds->priv; + + if (flags.mask & ~(BR_LEARNING | BR_FLOOD | BR_MCAST_FLOOD | + BR_BCAST_FLOOD)) + return -EINVAL; + + if (flags.mask & (BR_FLOOD | BR_MCAST_FLOOD) && + !priv->info->can_limit_mcast_flood) { + bool multicast = !!(flags.val & BR_MCAST_FLOOD); + bool unicast = !!(flags.val & BR_FLOOD); + + if (unicast != multicast) { + NL_SET_ERR_MSG_MOD(extack, + "This chip cannot configure multicast flooding independently of unicast"); + return -EINVAL; + } + } + + return 0; +} + +static int sja1105_port_bridge_flags(struct dsa_switch *ds, int port, + struct switchdev_brport_flags flags, + struct netlink_ext_ack *extack) +{ + struct sja1105_private *priv = ds->priv; + int rc; + + if (flags.mask & BR_LEARNING) { + bool learn_ena = !!(flags.val & BR_LEARNING); + + rc = sja1105_port_set_learning(priv, port, learn_ena); + if (rc) + return rc; + } + + if (flags.mask & (BR_FLOOD | BR_BCAST_FLOOD)) { + rc = sja1105_port_ucast_bcast_flood(priv, port, flags); + if (rc) + return rc; + } + + /* For chips that can't offload BR_MCAST_FLOOD independently, there + * is nothing to do here, we ensured the configuration is in sync by + * offloading BR_FLOOD. + */ + if (flags.mask & BR_MCAST_FLOOD && priv->info->can_limit_mcast_flood) { + rc = sja1105_port_mcast_flood(priv, port, flags, + extack); + if (rc) + return rc; + } + + return 0; +} + +/* The programming model for the SJA1105 switch is "all-at-once" via static + * configuration tables. Some of these can be dynamically modified at runtime, + * but not the xMII mode parameters table. + * Furthermode, some PHYs may not have crystals for generating their clocks + * (e.g. RMII). Instead, their 50MHz clock is supplied via the SJA1105 port's + * ref_clk pin. So port clocking needs to be initialized early, before + * connecting to PHYs is attempted, otherwise they won't respond through MDIO. + * Setting correct PHY link speed does not matter now. + * But dsa_slave_phy_setup is called later than sja1105_setup, so the PHY + * bindings are not yet parsed by DSA core. We need to parse early so that we + * can populate the xMII mode parameters table. + */ +static int sja1105_setup(struct dsa_switch *ds) +{ + struct sja1105_private *priv = ds->priv; + int rc; + + if (priv->info->disable_microcontroller) { + rc = priv->info->disable_microcontroller(priv); + if (rc < 0) { + dev_err(ds->dev, + "Failed to disable microcontroller: %pe\n", + ERR_PTR(rc)); + return rc; + } + } + + /* Create and send configuration down to device */ + rc = sja1105_static_config_load(priv); + if (rc < 0) { + dev_err(ds->dev, "Failed to load static config: %d\n", rc); + return rc; + } + + /* Configure the CGU (PHY link modes and speeds) */ + if (priv->info->clocking_setup) { + rc = priv->info->clocking_setup(priv); + if (rc < 0) { + dev_err(ds->dev, + "Failed to configure MII clocking: %pe\n", + ERR_PTR(rc)); + goto out_static_config_free; + } + } + + sja1105_tas_setup(ds); + sja1105_flower_setup(ds); + + rc = sja1105_ptp_clock_register(ds); + if (rc < 0) { + dev_err(ds->dev, "Failed to register PTP clock: %d\n", rc); + goto out_flower_teardown; + } + + rc = sja1105_mdiobus_register(ds); + if (rc < 0) { + dev_err(ds->dev, "Failed to register MDIO bus: %pe\n", + ERR_PTR(rc)); + goto out_ptp_clock_unregister; + } + + rc = sja1105_devlink_setup(ds); + if (rc < 0) + goto out_mdiobus_unregister; + + rtnl_lock(); + rc = dsa_tag_8021q_register(ds, htons(ETH_P_8021Q)); + rtnl_unlock(); + if (rc) + goto out_devlink_teardown; + + /* On SJA1105, VLAN filtering per se is always enabled in hardware. + * The only thing we can do to disable it is lie about what the 802.1Q + * EtherType is. + * So it will still try to apply VLAN filtering, but all ingress + * traffic (except frames received with EtherType of ETH_P_SJA1105) + * will be internally tagged with a distorted VLAN header where the + * TPID is ETH_P_SJA1105, and the VLAN ID is the port pvid. + */ + ds->vlan_filtering_is_global = true; + ds->untag_bridge_pvid = true; + ds->fdb_isolation = true; + /* tag_8021q has 3 bits for the VBID, and the value 0 is reserved */ + ds->max_num_bridges = 7; + + /* Advertise the 8 egress queues */ + ds->num_tx_queues = SJA1105_NUM_TC; + + ds->mtu_enforcement_ingress = true; + ds->assisted_learning_on_cpu_port = true; + + return 0; + +out_devlink_teardown: + sja1105_devlink_teardown(ds); +out_mdiobus_unregister: + sja1105_mdiobus_unregister(ds); +out_ptp_clock_unregister: + sja1105_ptp_clock_unregister(ds); +out_flower_teardown: + sja1105_flower_teardown(ds); + sja1105_tas_teardown(ds); +out_static_config_free: + sja1105_static_config_free(&priv->static_config); + + return rc; +} + +static void sja1105_teardown(struct dsa_switch *ds) +{ + struct sja1105_private *priv = ds->priv; + + rtnl_lock(); + dsa_tag_8021q_unregister(ds); + rtnl_unlock(); + + sja1105_devlink_teardown(ds); + sja1105_mdiobus_unregister(ds); + sja1105_ptp_clock_unregister(ds); + sja1105_flower_teardown(ds); + sja1105_tas_teardown(ds); + sja1105_static_config_free(&priv->static_config); +} + static const struct dsa_switch_ops sja1105_switch_ops = { .get_tag_protocol = sja1105_get_tag_protocol, + .connect_tag_protocol = sja1105_connect_tag_protocol, .setup = sja1105_setup, .teardown = sja1105_teardown, .set_ageing_time = sja1105_set_ageing_time, - .phylink_validate = sja1105_phylink_validate, - .phylink_mac_config = sja1105_mac_config, + .port_change_mtu = sja1105_change_mtu, + .port_max_mtu = sja1105_get_max_mtu, + .phylink_get_caps = sja1105_phylink_get_caps, + .phylink_mac_select_pcs = sja1105_mac_select_pcs, .phylink_mac_link_up = sja1105_mac_link_up, .phylink_mac_link_down = sja1105_mac_link_down, .get_strings = sja1105_get_strings, .get_ethtool_stats = sja1105_get_ethtool_stats, .get_sset_count = sja1105_get_sset_count, .get_ts_info = sja1105_get_ts_info, - .port_enable = sja1105_port_enable, - .port_disable = sja1105_port_disable, .port_fdb_dump = sja1105_fdb_dump, .port_fdb_add = sja1105_fdb_add, .port_fdb_del = sja1105_fdb_del, + .port_fast_age = sja1105_fast_age, .port_bridge_join = sja1105_bridge_join, .port_bridge_leave = sja1105_bridge_leave, + .port_pre_bridge_flags = sja1105_port_pre_bridge_flags, + .port_bridge_flags = sja1105_port_bridge_flags, .port_stp_state_set = sja1105_bridge_stp_state_set, - .port_vlan_prepare = sja1105_vlan_prepare, .port_vlan_filtering = sja1105_vlan_filtering, - .port_vlan_add = sja1105_vlan_add, - .port_vlan_del = sja1105_vlan_del, - .port_mdb_prepare = sja1105_mdb_prepare, + .port_vlan_add = sja1105_bridge_vlan_add, + .port_vlan_del = sja1105_bridge_vlan_del, .port_mdb_add = sja1105_mdb_add, .port_mdb_del = sja1105_mdb_del, .port_hwtstamp_get = sja1105_hwtstamp_get, @@ -2016,13 +3178,25 @@ static const struct dsa_switch_ops sja1105_switch_ops = { .port_setup_tc = sja1105_port_setup_tc, .port_mirror_add = sja1105_mirror_add, .port_mirror_del = sja1105_mirror_del, + .port_policer_add = sja1105_port_policer_add, + .port_policer_del = sja1105_port_policer_del, + .cls_flower_add = sja1105_cls_flower_add, + .cls_flower_del = sja1105_cls_flower_del, + .cls_flower_stats = sja1105_cls_flower_stats, + .devlink_info_get = sja1105_devlink_info_get, + .tag_8021q_vlan_add = sja1105_dsa_8021q_vlan_add, + .tag_8021q_vlan_del = sja1105_dsa_8021q_vlan_del, + .port_prechangeupper = sja1105_prechangeupper, }; +static const struct of_device_id sja1105_dt_ids[]; + static int sja1105_check_device_id(struct sja1105_private *priv) { const struct sja1105_regs *regs = priv->info->regs; u8 prod_id[SJA1105_SIZE_DEVICE_ID] = {0}; struct device *dev = &priv->spidev->dev; + const struct of_device_id *match; u32 device_id; u64 part_no; int rc; @@ -2032,12 +3206,6 @@ static int sja1105_check_device_id(struct sja1105_private *priv) if (rc < 0) return rc; - if (device_id != priv->info->device_id) { - dev_err(dev, "Expected device ID 0x%llx but read 0x%x\n", - priv->info->device_id, device_id); - return -ENODEV; - } - rc = sja1105_xfer_buf(priv, SPI_READ, regs->prod_id, prod_id, SJA1105_SIZE_DEVICE_ID); if (rc < 0) @@ -2045,39 +3213,52 @@ static int sja1105_check_device_id(struct sja1105_private *priv) sja1105_unpack(prod_id, &part_no, 19, 4, SJA1105_SIZE_DEVICE_ID); - if (part_no != priv->info->part_no) { - dev_err(dev, "Expected part number 0x%llx but read 0x%llx\n", - priv->info->part_no, part_no); - return -ENODEV; + for (match = sja1105_dt_ids; match->compatible[0]; match++) { + const struct sja1105_info *info = match->data; + + /* Is what's been probed in our match table at all? */ + if (info->device_id != device_id || info->part_no != part_no) + continue; + + /* But is it what's in the device tree? */ + if (priv->info->device_id != device_id || + priv->info->part_no != part_no) { + dev_warn(dev, "Device tree specifies chip %s but found %s, please fix it!\n", + priv->info->name, info->name); + /* It isn't. No problem, pick that up. */ + priv->info = info; + } + + return 0; } - return 0; + dev_err(dev, "Unexpected {device ID, part number}: 0x%x 0x%llx\n", + device_id, part_no); + + return -ENODEV; } static int sja1105_probe(struct spi_device *spi) { - struct sja1105_tagger_data *tagger_data; struct device *dev = &spi->dev; struct sja1105_private *priv; + size_t max_xfer, max_msg; struct dsa_switch *ds; - int rc, port; + int rc; if (!dev->of_node) { dev_err(dev, "No DTS bindings for SJA1105 driver\n"); return -EINVAL; } + rc = sja1105_hw_reset(dev, 1, 1); + if (rc) + return rc; + priv = devm_kzalloc(dev, sizeof(struct sja1105_private), GFP_KERNEL); if (!priv) return -ENOMEM; - /* Configure the optional reset pin and bring up switch */ - priv->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH); - if (IS_ERR(priv->reset_gpio)) - dev_dbg(dev, "reset-gpios not defined, ignoring\n"); - else - sja1105_hw_reset(priv->reset_gpio, 1, 1); - /* Populate our driver private structure (priv) based on * the device tree node that was probed (spi) */ @@ -2092,6 +3273,33 @@ static int sja1105_probe(struct spi_device *spi) return rc; } + /* In sja1105_xfer, we send spi_messages composed of two spi_transfers: + * a small one for the message header and another one for the current + * chunk of the packed buffer. + * Check that the restrictions imposed by the SPI controller are + * respected: the chunk buffer is smaller than the max transfer size, + * and the total length of the chunk plus its message header is smaller + * than the max message size. + * We do that during probe time since the maximum transfer size is a + * runtime invariant. + */ + max_xfer = spi_max_transfer_size(spi); + max_msg = spi_max_message_size(spi); + + /* We need to send at least one 64-bit word of SPI payload per message + * in order to be able to make useful progress. + */ + if (max_msg < SJA1105_SIZE_SPI_MSG_HEADER + 8) { + dev_err(dev, "SPI master cannot send large enough buffers, aborting\n"); + return -EINVAL; + } + + priv->max_xfer_len = SJA1105_SIZE_SPI_MSG_MAXLEN; + if (priv->max_xfer_len > max_xfer) + priv->max_xfer_len = max_xfer; + if (priv->max_xfer_len > max_msg - SJA1105_SIZE_SPI_MSG_HEADER) + priv->max_xfer_len = max_msg - SJA1105_SIZE_SPI_MSG_HEADER; + priv->info = of_device_get_match_data(dev); /* Detect hardware device */ @@ -2108,67 +3316,53 @@ static int sja1105_probe(struct spi_device *spi) return -ENOMEM; ds->dev = dev; - ds->num_ports = SJA1105_NUM_PORTS; + ds->num_ports = priv->info->num_ports; ds->ops = &sja1105_switch_ops; ds->priv = priv; priv->ds = ds; - tagger_data = &priv->tagger_data; - mutex_init(&priv->ptp_data.lock); + mutex_init(&priv->dynamic_config_lock); mutex_init(&priv->mgmt_lock); + spin_lock_init(&priv->ts_id_lock); - sja1105_tas_setup(ds); - - rc = dsa_register_switch(priv->ds); - if (rc) + rc = sja1105_parse_dt(priv); + if (rc < 0) { + dev_err(ds->dev, "Failed to parse DT: %d\n", rc); return rc; + } - /* Connections between dsa_port and sja1105_port */ - for (port = 0; port < SJA1105_NUM_PORTS; port++) { - struct sja1105_port *sp = &priv->ports[port]; - struct dsa_port *dp = dsa_to_port(ds, port); - struct net_device *slave; - - if (!dsa_is_user_port(ds, port)) - continue; - - dp->priv = sp; - sp->dp = dp; - sp->data = tagger_data; - slave = dp->slave; - kthread_init_work(&sp->xmit_work, sja1105_port_deferred_xmit); - sp->xmit_worker = kthread_create_worker(0, "%s_xmit", - slave->name); - if (IS_ERR(sp->xmit_worker)) { - rc = PTR_ERR(sp->xmit_worker); - dev_err(ds->dev, - "failed to create deferred xmit thread: %d\n", - rc); - goto out; - } - skb_queue_head_init(&sp->xmit_queue); + if (IS_ENABLED(CONFIG_NET_SCH_CBS)) { + priv->cbs = devm_kcalloc(dev, priv->info->num_cbs_shapers, + sizeof(struct sja1105_cbs_entry), + GFP_KERNEL); + if (!priv->cbs) + return -ENOMEM; } - return 0; -out: - while (port-- > 0) { - struct sja1105_port *sp = &priv->ports[port]; + return dsa_register_switch(priv->ds); +} - if (!dsa_is_user_port(ds, port)) - continue; +static void sja1105_remove(struct spi_device *spi) +{ + struct sja1105_private *priv = spi_get_drvdata(spi); - kthread_destroy_worker(sp->xmit_worker); - } - return rc; + if (!priv) + return; + + dsa_unregister_switch(priv->ds); } -static int sja1105_remove(struct spi_device *spi) +static void sja1105_shutdown(struct spi_device *spi) { struct sja1105_private *priv = spi_get_drvdata(spi); - dsa_unregister_switch(priv->ds); - return 0; + if (!priv) + return; + + dsa_switch_shutdown(priv->ds); + + spi_set_drvdata(spi, NULL); } static const struct of_device_id sja1105_dt_ids[] = { @@ -2178,18 +3372,39 @@ static const struct of_device_id sja1105_dt_ids[] = { { .compatible = "nxp,sja1105q", .data = &sja1105q_info }, { .compatible = "nxp,sja1105r", .data = &sja1105r_info }, { .compatible = "nxp,sja1105s", .data = &sja1105s_info }, + { .compatible = "nxp,sja1110a", .data = &sja1110a_info }, + { .compatible = "nxp,sja1110b", .data = &sja1110b_info }, + { .compatible = "nxp,sja1110c", .data = &sja1110c_info }, + { .compatible = "nxp,sja1110d", .data = &sja1110d_info }, { /* sentinel */ }, }; MODULE_DEVICE_TABLE(of, sja1105_dt_ids); +static const struct spi_device_id sja1105_spi_ids[] = { + { "sja1105e" }, + { "sja1105t" }, + { "sja1105p" }, + { "sja1105q" }, + { "sja1105r" }, + { "sja1105s" }, + { "sja1110a" }, + { "sja1110b" }, + { "sja1110c" }, + { "sja1110d" }, + { }, +}; +MODULE_DEVICE_TABLE(spi, sja1105_spi_ids); + static struct spi_driver sja1105_driver = { .driver = { .name = "sja1105", .owner = THIS_MODULE, .of_match_table = of_match_ptr(sja1105_dt_ids), }, + .id_table = sja1105_spi_ids, .probe = sja1105_probe, .remove = sja1105_remove, + .shutdown = sja1105_shutdown, }; module_spi_driver(sja1105_driver); |