On Sat, 16 Jan 2021 13:49:22 +0100 Kurt Kanzenbach wrote: > The switch has support for the 802.1Qbv Time Aware Shaper (TAS). Traffic > schedules may be configured individually on each front port. Each port has > eight > egress queues. The traffic is mapped to a traffic class respectively via the > PCP > field of a VLAN tagged frame. > > The TAPRIO Qdisc already implements that. Therefore, this interface can simply > be reused. Add .port_setup_tc() accordingly. > > The activation of a schedule on a port is split into two parts: > > * Programming the necessary gate control list (GCL) > * Setup delayed work for starting the schedule > > The hardware supports starting a schedule up to eight seconds in the future. > The > TAPRIO interface provides an absolute base time. Therefore, periodic delayed > work is leveraged to check whether a schedule may be started or not. > > Signed-off-by: Kurt Kanzenbach <k...@linutronix.de>
> +static bool hellcreek_schedule_startable(struct hellcreek *hellcreek, int > port) > +{ > + struct hellcreek_port *hellcreek_port = &hellcreek->ports[port]; > + s64 base_time_ns, current_ns; > + > + /* The switch allows a schedule to be started only eight seconds within > + * the future. Therefore, check the current PTP time if the schedule is > + * startable or not. > + */ > + > + /* Use the "cached" time. That should be alright, as it's updated quite > + * frequently in the PTP code. > + */ > + mutex_lock(&hellcreek->ptp_lock); > + current_ns = hellcreek->seconds * NSEC_PER_SEC + hellcreek->last_ts; > + mutex_unlock(&hellcreek->ptp_lock); > + > + /* Calculate difference to admin base time */ > + base_time_ns = ktime_to_ns(hellcreek_port->current_schedule->base_time); > + > + if (base_time_ns - current_ns < (s64)8 * NSEC_PER_SEC) > + return true; > + > + return false; nit: return base_time_ns - current_ns < (s64)8 * NSEC_PER_SEC; > +static int hellcreek_port_set_schedule(struct dsa_switch *ds, int port, > + struct tc_taprio_qopt_offload *taprio) > +{ > + struct hellcreek *hellcreek = ds->priv; > + struct hellcreek_port *hellcreek_port; > + bool startable; > + u16 ctrl; > + > + hellcreek_port = &hellcreek->ports[port]; > + > + dev_dbg(hellcreek->dev, "Configure traffic schedule on port %d\n", > + port); > + > + /* First cancel delayed work */ > + cancel_delayed_work_sync(&hellcreek_port->schedule_work); > + > + mutex_lock(&hellcreek->reg_lock); > + > + if (hellcreek_port->current_schedule) { > + taprio_offload_free(hellcreek_port->current_schedule); > + hellcreek_port->current_schedule = NULL; > + } > + hellcreek_port->current_schedule = taprio_offload_get(taprio); > + > + /* Then select port */ > + hellcreek_select_tgd(hellcreek, port); > + > + /* Enable gating and keep defaults */ > + ctrl = (0xff << TR_TGDCTRL_ADMINGATESTATES_SHIFT) | TR_TGDCTRL_GATE_EN; > + hellcreek_write(hellcreek, ctrl, TR_TGDCTRL); > + > + /* Cancel pending schedule */ > + hellcreek_write(hellcreek, 0x00, TR_ESTCMD); > + > + /* Setup a new schedule */ > + hellcreek_setup_gcl(hellcreek, port, hellcreek_port->current_schedule); > + > + /* Configure cycle time */ > + hellcreek_set_cycle_time(hellcreek, hellcreek_port->current_schedule); > + > + /* Check starting time */ > + startable = hellcreek_schedule_startable(hellcreek, port); > + if (startable) { > + hellcreek_start_schedule(hellcreek, port); > + mutex_unlock(&hellcreek->reg_lock); > + return 0; > + } > + > + mutex_unlock(&hellcreek->reg_lock); > + > + /* Schedule periodic schedule check */ > + schedule_delayed_work(&hellcreek_port->schedule_work, > + HELLCREEK_SCHEDULE_PERIOD); Why schedule this work every 2 seconds rather than scheduling it $start_time - 8 sec + epsilon? > +static bool hellcreek_validate_schedule(struct hellcreek *hellcreek, > + struct tc_taprio_qopt_offload *schedule) > +{ > + /* Does this hellcreek version support Qbv in hardware? */ > + if (!hellcreek->pdata->qbv_support) > + return false; > + > + /* cycle time can only be 32bit */ > + if (schedule->cycle_time > (u32)-1) > + return false; > + > + /* cycle time extension is not supported */ > + if (schedule->cycle_time_extension) > + return false; What's the story with entries[i].command? I see most drivers validate the command is what they expect. > + return true; > +} > + > +static int hellcreek_port_setup_tc(struct dsa_switch *ds, int port, > + enum tc_setup_type type, void *type_data) > +{ > + struct tc_taprio_qopt_offload *taprio = type_data; > + struct hellcreek *hellcreek = ds->priv; > + > + if (type != TC_SETUP_QDISC_TAPRIO) > + return -EOPNOTSUPP; > + > + if (!hellcreek_validate_schedule(hellcreek, taprio)) > + return -EOPNOTSUPP; > + > + if (taprio->enable) > + return hellcreek_port_set_schedule(ds, port, taprio); > + > + return hellcreek_port_del_schedule(ds, port); > +} > + > static const struct dsa_switch_ops hellcreek_ds_ops = { > .get_ethtool_stats = hellcreek_get_ethtool_stats, > .get_sset_count = hellcreek_get_sset_count,