{
struct rtl8365mb *mb = priv->chip_data;
struct dsa_switch *ds = &priv->ds;
- int i;
+ struct dsa_port *dp;
/* Per-chip global mutex to protect MIB counter access, since doing
* so requires accessing a series of registers in a particular order.
*/
mutex_init(&mb->mib_lock);
- for (i = 0; i < priv->num_ports; i++) {
- struct rtl8365mb_port *p = &mb->ports[i];
-
- if (dsa_is_unused_port(ds, i))
- continue;
+ dsa_switch_for_each_available_port(dp, ds) {
+ struct rtl8365mb_port *p = &mb->ports[dp->index];
/* Per-port spinlock to protect the stats64 data */
spin_lock_init(&p->stats_lock);
{
struct rtl8365mb *mb = priv->chip_data;
struct dsa_switch *ds = &priv->ds;
- int i;
-
- for (i = 0; i < priv->num_ports; i++) {
- struct rtl8365mb_port *p = &mb->ports[i];
+ struct dsa_port *dp;
- if (dsa_is_unused_port(ds, i))
- continue;
+ dsa_switch_for_each_available_port(dp, ds) {
+ struct rtl8365mb_port *p = &mb->ports[dp->index];
cancel_delayed_work_sync(&p->mib_work);
}
for_each_set_bit(line, &line_changes, priv->num_ports) {
int child_irq = irq_find_mapping(priv->irqdomain, line);
+ if (!child_irq)
+ continue;
+
handle_nested_irq(child_irq);
}
static int rtl8365mb_irq_map(struct irq_domain *domain, unsigned int irq,
irq_hw_number_t hwirq)
{
- irq_set_chip_data(irq, domain->host_data);
+ struct realtek_priv *priv = domain->host_data;
+ struct rtl8365mb *mb = priv->chip_data;
+
+ irq_set_chip_data(irq, priv);
irq_set_chip_and_handler(irq, &rtl8365mb_irq_chip, handle_simple_irq);
irq_set_nested_thread(irq, 1);
irq_set_noprobe(irq);
+ irq_set_parent(irq, mb->irq);
return 0;
}
static int rtl8365mb_irq_setup(struct realtek_priv *priv)
{
struct rtl8365mb *mb = priv->chip_data;
+ struct dsa_switch *ds = &priv->ds;
struct device_node *intc;
+ struct dsa_port *dp;
u32 irq_trig;
int virq;
int irq;
u32 val;
int ret;
- int i;
intc = of_get_child_by_name(priv->dev->of_node, "interrupt-controller");
if (!intc) {
goto out_put_node;
}
+ /* Store the irq so that we know to map and free it during teardown */
+ mb->irq = irq;
+
priv->irqdomain = irq_domain_create_linear(of_fwnode_handle(intc), priv->num_ports,
&rtl8365mb_irqdomain_ops, priv);
if (!priv->irqdomain) {
goto out_put_node;
}
- for (i = 0; i < priv->num_ports; i++) {
- virq = irq_create_mapping(priv->irqdomain, i);
+ dsa_switch_for_each_available_port(dp, ds) {
+ virq = irq_create_mapping(priv->irqdomain, dp->index);
if (!virq) {
dev_err(priv->dev,
"failed to create irq domain mapping\n");
goto out_remove_irqdomain;
}
- /* Store the irq so that we know to free it during teardown */
- mb->irq = irq;
-
ret = rtl8365mb_irq_enable(priv);
if (ret)
goto out_free_irq;
out_free_irq:
free_irq(mb->irq, priv);
- mb->irq = 0;
out_remove_irqdomain:
- for (i = 0; i < priv->num_ports; i++) {
- virq = irq_find_mapping(priv->irqdomain, i);
- irq_dispose_mapping(virq);
+ dsa_switch_for_each_port(dp, ds) {
+ virq = irq_find_mapping(priv->irqdomain, dp->index);
+
+ if (virq)
+ irq_dispose_mapping(virq);
}
irq_domain_remove(priv->irqdomain);
priv->irqdomain = NULL;
out_put_node:
+ mb->irq = 0;
of_node_put(intc);
return ret;
static void rtl8365mb_irq_teardown(struct realtek_priv *priv)
{
struct rtl8365mb *mb = priv->chip_data;
+ struct dsa_switch *ds = &priv->ds;
+ struct dsa_port *dp;
int virq;
- int i;
if (mb->irq) {
free_irq(mb->irq, priv);
}
if (priv->irqdomain) {
- for (i = 0; i < priv->num_ports; i++) {
- virq = irq_find_mapping(priv->irqdomain, i);
- irq_dispose_mapping(virq);
+ /* Unused ports with a linked PHY still have an active IRQ
+ * mapping that must be disposed of during teardown. Loop
+ * through all ports.
+ */
+ dsa_switch_for_each_port(dp, ds) {
+ virq = irq_find_mapping(priv->irqdomain, dp->index);
+
+ if (virq)
+ irq_dispose_mapping(virq);
}
irq_domain_remove(priv->irqdomain);
{
struct realtek_priv *priv = ds->priv;
struct rtl8365mb_cpu *cpu;
- struct dsa_port *cpu_dp;
+ u32 downports_mask = 0;
+ u32 upports_mask = 0;
struct rtl8365mb *mb;
+ struct dsa_port *dp;
int ret;
- int i;
mb = priv->chip_data;
cpu = &mb->cpu;
else if (ret)
dev_info(priv->dev, "no interrupt support\n");
- for (i = 0; i < priv->num_ports; i++) {
+ dsa_switch_for_each_port(dp, ds) {
/* Cascading (DSA links) is not supported yet.
* Historically, the driver has always been broken
* without a dedicated CPU port because CPU tagging
* would be disabled, rendering the switch entirely
* non-functional for DSA operations.
*/
- if (dsa_is_dsa_port(ds, i)) {
+ if (dsa_port_is_dsa(dp)) {
dev_err(priv->dev, "Cascading (DSA link) not supported\n");
ret = -EOPNOTSUPP;
goto out_teardown_irq;
}
}
- /* Configure CPU tagging */
- dsa_switch_for_each_cpu_port(cpu_dp, ds) {
- cpu->mask |= BIT(cpu_dp->index);
+ /* Start with all ports blocked, including unused ports */
+ dsa_switch_for_each_port(dp, ds) {
+ struct rtl8365mb_port *p = &mb->ports[dp->index];
- if (cpu->trap_port == RTL8365MB_MAX_NUM_PORTS)
- cpu->trap_port = cpu_dp->index;
- }
- cpu->enable = cpu->mask > 0;
+ /* Set the initial STP state of all ports to DISABLED, otherwise
+ * ports will still forward frames to the CPU despite being
+ * administratively down by default.
+ */
+ rtl8365mb_port_stp_state_set(ds, dp->index, BR_STATE_DISABLED);
- if (!cpu->enable) {
- dev_err(priv->dev, "no CPU port defined\n");
- ret = -EINVAL;
- goto out_teardown_irq;
- }
+ /* Start with all port completely isolated */
+ ret = rtl8365mb_port_set_isolation(priv, dp->index, 0);
+ if (ret)
+ goto out_teardown_irq;
- ret = rtl8365mb_cpu_config(priv);
- if (ret)
- goto out_teardown_irq;
+ /* Disable learning */
+ ret = rtl8365mb_port_set_learning(priv, dp->index, false);
+ if (ret)
+ goto out_teardown_irq;
- /* Configure ports */
- for (i = 0; i < priv->num_ports; i++) {
- struct rtl8365mb_port *p = &mb->ports[i];
+ /* Set up per-port private data */
+ p->priv = priv;
+ p->index = dp->index;
+
+ /* Collect CPU ports. If we support cascade switches, it should
+ * also include the upstream DSA ports.
+ */
+ if (!dsa_port_is_cpu(dp))
+ continue;
+
+ upports_mask |= BIT(dp->index);
+ }
- if (dsa_is_unused_port(ds, i))
+ /* Configure user ports */
+ dsa_switch_for_each_port(dp, ds) {
+ if (!dsa_port_is_user(dp))
continue;
/* Forward only to the CPU */
- ret = rtl8365mb_port_set_isolation(priv, i, cpu->mask);
+ ret = rtl8365mb_port_set_isolation(priv, dp->index,
+ upports_mask);
if (ret)
goto out_teardown_irq;
- /* Disable learning */
- ret = rtl8365mb_port_set_learning(priv, i, false);
+ /* If we support cascade switches, it should also include the
+ * downstream DSA ports.
+ */
+ downports_mask |= BIT(dp->index);
+ }
+
+ /* Configure CPU tagging */
+ /* If we support cascade switches, it should also include the upstream
+ * DSA ports.
+ */
+ dsa_switch_for_each_cpu_port(dp, ds) {
+ /* Use the first CPU port as trap_port */
+ if (cpu->trap_port == RTL8365MB_MAX_NUM_PORTS)
+ cpu->trap_port = dp->index;
+
+ /* Forward to all user ports */
+ ret = rtl8365mb_port_set_isolation(priv, dp->index,
+ downports_mask);
if (ret)
goto out_teardown_irq;
+ }
- /* Set the initial STP state of all ports to DISABLED, otherwise
- * ports will still forward frames to the CPU despite being
- * administratively down by default.
- */
- rtl8365mb_port_stp_state_set(ds, i, BR_STATE_DISABLED);
+ cpu->mask = upports_mask;
+ cpu->enable = cpu->mask > 0;
- /* Set up per-port private data */
- p->priv = priv;
- p->index = i;
+ if (!cpu->enable) {
+ dev_err(priv->dev, "no CPU port defined\n");
+ ret = -EINVAL;
+ goto out_teardown_irq;
}
+ ret = rtl8365mb_cpu_config(priv);
+ if (ret)
+ goto out_teardown_irq;
+
ret = rtl8365mb_port_change_mtu(ds, cpu->trap_port, ETH_DATA_LEN);
if (ret)
goto out_teardown_irq;