Commit 8670dc33 authored by Xiaoliang Yang's avatar Xiaoliang Yang Committed by David S. Miller

net: dsa: felix: update base time of time-aware shaper when adjusting PTP time

When adjusting the PTP clock, the base time of the TAS configuration
will become unreliable. We need reset the TAS configuration by using a
new base time.

For example, if the driver gets a base time 0 of Qbv configuration from
user, and current time is 20000. The driver will set the TAS base time
to be 20000. After the PTP clock adjustment, the current time becomes
10000. If the TAS base time is still 20000, it will be a future time,
and TAS entry list will stop running. Another example, if the current
time becomes to be 10000000 after PTP clock adjust, a large time offset
can cause the hardware to hang.

This patch introduces a tas_clock_adjust() function to reset the TAS
module by using a new base time after the PTP clock adjustment. This can
avoid issues above.

Due to PTP clock adjustment can occur at any time, it may conflict with
the TAS configuration. We introduce a new TAS lock to serialize the
access to the TAS registers.
Signed-off-by: default avatarXiaoliang Yang <xiaoliang.yang_1@nxp.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent c205035e
...@@ -1196,10 +1196,13 @@ static void vsc9959_tas_gcl_set(struct ocelot *ocelot, const u32 gcl_ix, ...@@ -1196,10 +1196,13 @@ static void vsc9959_tas_gcl_set(struct ocelot *ocelot, const u32 gcl_ix,
static int vsc9959_qos_port_tas_set(struct ocelot *ocelot, int port, static int vsc9959_qos_port_tas_set(struct ocelot *ocelot, int port,
struct tc_taprio_qopt_offload *taprio) struct tc_taprio_qopt_offload *taprio)
{ {
struct ocelot_port *ocelot_port = ocelot->ports[port];
struct timespec64 base_ts; struct timespec64 base_ts;
int ret, i; int ret, i;
u32 val; u32 val;
mutex_lock(&ocelot->tas_lock);
if (!taprio->enable) { if (!taprio->enable) {
ocelot_rmw_rix(ocelot, ocelot_rmw_rix(ocelot,
QSYS_TAG_CONFIG_INIT_GATE_STATE(0xFF), QSYS_TAG_CONFIG_INIT_GATE_STATE(0xFF),
...@@ -1207,15 +1210,20 @@ static int vsc9959_qos_port_tas_set(struct ocelot *ocelot, int port, ...@@ -1207,15 +1210,20 @@ static int vsc9959_qos_port_tas_set(struct ocelot *ocelot, int port,
QSYS_TAG_CONFIG_INIT_GATE_STATE_M, QSYS_TAG_CONFIG_INIT_GATE_STATE_M,
QSYS_TAG_CONFIG, port); QSYS_TAG_CONFIG, port);
mutex_unlock(&ocelot->tas_lock);
return 0; return 0;
} }
if (taprio->cycle_time > NSEC_PER_SEC || if (taprio->cycle_time > NSEC_PER_SEC ||
taprio->cycle_time_extension >= NSEC_PER_SEC) taprio->cycle_time_extension >= NSEC_PER_SEC) {
return -EINVAL; ret = -EINVAL;
goto err;
}
if (taprio->num_entries > VSC9959_TAS_GCL_ENTRY_MAX) if (taprio->num_entries > VSC9959_TAS_GCL_ENTRY_MAX) {
return -ERANGE; ret = -ERANGE;
goto err;
}
/* Enable guard band. The switch will schedule frames without taking /* Enable guard band. The switch will schedule frames without taking
* their length into account. Thus we'll always need to enable the * their length into account. Thus we'll always need to enable the
...@@ -1236,8 +1244,10 @@ static int vsc9959_qos_port_tas_set(struct ocelot *ocelot, int port, ...@@ -1236,8 +1244,10 @@ static int vsc9959_qos_port_tas_set(struct ocelot *ocelot, int port,
* config is pending, need reset the TAS module * config is pending, need reset the TAS module
*/ */
val = ocelot_read(ocelot, QSYS_PARAM_STATUS_REG_8); val = ocelot_read(ocelot, QSYS_PARAM_STATUS_REG_8);
if (val & QSYS_PARAM_STATUS_REG_8_CONFIG_PENDING) if (val & QSYS_PARAM_STATUS_REG_8_CONFIG_PENDING) {
return -EBUSY; ret = -EBUSY;
goto err;
}
ocelot_rmw_rix(ocelot, ocelot_rmw_rix(ocelot,
QSYS_TAG_CONFIG_ENABLE | QSYS_TAG_CONFIG_ENABLE |
...@@ -1248,6 +1258,8 @@ static int vsc9959_qos_port_tas_set(struct ocelot *ocelot, int port, ...@@ -1248,6 +1258,8 @@ static int vsc9959_qos_port_tas_set(struct ocelot *ocelot, int port,
QSYS_TAG_CONFIG_SCH_TRAFFIC_QUEUES_M, QSYS_TAG_CONFIG_SCH_TRAFFIC_QUEUES_M,
QSYS_TAG_CONFIG, port); QSYS_TAG_CONFIG, port);
ocelot_port->base_time = taprio->base_time;
vsc9959_new_base_time(ocelot, taprio->base_time, vsc9959_new_base_time(ocelot, taprio->base_time,
taprio->cycle_time, &base_ts); taprio->cycle_time, &base_ts);
ocelot_write(ocelot, base_ts.tv_nsec, QSYS_PARAM_CFG_REG_1); ocelot_write(ocelot, base_ts.tv_nsec, QSYS_PARAM_CFG_REG_1);
...@@ -1271,9 +1283,67 @@ static int vsc9959_qos_port_tas_set(struct ocelot *ocelot, int port, ...@@ -1271,9 +1283,67 @@ static int vsc9959_qos_port_tas_set(struct ocelot *ocelot, int port,
!(val & QSYS_TAS_PARAM_CFG_CTRL_CONFIG_CHANGE), !(val & QSYS_TAS_PARAM_CFG_CTRL_CONFIG_CHANGE),
10, 100000); 10, 100000);
err:
mutex_unlock(&ocelot->tas_lock);
return ret; return ret;
} }
static void vsc9959_tas_clock_adjust(struct ocelot *ocelot)
{
struct ocelot_port *ocelot_port;
struct timespec64 base_ts;
u64 cycletime;
int port;
u32 val;
mutex_lock(&ocelot->tas_lock);
for (port = 0; port < ocelot->num_phys_ports; port++) {
val = ocelot_read_rix(ocelot, QSYS_TAG_CONFIG, port);
if (!(val & QSYS_TAG_CONFIG_ENABLE))
continue;
ocelot_rmw(ocelot,
QSYS_TAS_PARAM_CFG_CTRL_PORT_NUM(port),
QSYS_TAS_PARAM_CFG_CTRL_PORT_NUM_M,
QSYS_TAS_PARAM_CFG_CTRL);
ocelot_rmw_rix(ocelot,
QSYS_TAG_CONFIG_INIT_GATE_STATE(0xFF),
QSYS_TAG_CONFIG_ENABLE |
QSYS_TAG_CONFIG_INIT_GATE_STATE_M,
QSYS_TAG_CONFIG, port);
cycletime = ocelot_read(ocelot, QSYS_PARAM_CFG_REG_4);
ocelot_port = ocelot->ports[port];
vsc9959_new_base_time(ocelot, ocelot_port->base_time,
cycletime, &base_ts);
ocelot_write(ocelot, base_ts.tv_nsec, QSYS_PARAM_CFG_REG_1);
ocelot_write(ocelot, lower_32_bits(base_ts.tv_sec),
QSYS_PARAM_CFG_REG_2);
val = upper_32_bits(base_ts.tv_sec);
ocelot_rmw(ocelot,
QSYS_PARAM_CFG_REG_3_BASE_TIME_SEC_MSB(val),
QSYS_PARAM_CFG_REG_3_BASE_TIME_SEC_MSB_M,
QSYS_PARAM_CFG_REG_3);
ocelot_rmw(ocelot, QSYS_TAS_PARAM_CFG_CTRL_CONFIG_CHANGE,
QSYS_TAS_PARAM_CFG_CTRL_CONFIG_CHANGE,
QSYS_TAS_PARAM_CFG_CTRL);
ocelot_rmw_rix(ocelot,
QSYS_TAG_CONFIG_INIT_GATE_STATE(0xFF) |
QSYS_TAG_CONFIG_ENABLE,
QSYS_TAG_CONFIG_ENABLE |
QSYS_TAG_CONFIG_INIT_GATE_STATE_M,
QSYS_TAG_CONFIG, port);
}
mutex_unlock(&ocelot->tas_lock);
}
static int vsc9959_qos_port_cbs_set(struct dsa_switch *ds, int port, static int vsc9959_qos_port_cbs_set(struct dsa_switch *ds, int port,
struct tc_cbs_qopt_offload *cbs_qopt) struct tc_cbs_qopt_offload *cbs_qopt)
{ {
...@@ -2210,6 +2280,7 @@ static const struct ocelot_ops vsc9959_ops = { ...@@ -2210,6 +2280,7 @@ static const struct ocelot_ops vsc9959_ops = {
.psfp_filter_del = vsc9959_psfp_filter_del, .psfp_filter_del = vsc9959_psfp_filter_del,
.psfp_stats_get = vsc9959_psfp_stats_get, .psfp_stats_get = vsc9959_psfp_stats_get,
.cut_through_fwd = vsc9959_cut_through_fwd, .cut_through_fwd = vsc9959_cut_through_fwd,
.tas_clock_adjust = vsc9959_tas_clock_adjust,
}; };
static const struct felix_info felix_info_vsc9959 = { static const struct felix_info felix_info_vsc9959 = {
......
...@@ -3367,6 +3367,7 @@ int ocelot_init(struct ocelot *ocelot) ...@@ -3367,6 +3367,7 @@ int ocelot_init(struct ocelot *ocelot)
mutex_init(&ocelot->ptp_lock); mutex_init(&ocelot->ptp_lock);
mutex_init(&ocelot->mact_lock); mutex_init(&ocelot->mact_lock);
mutex_init(&ocelot->fwd_domain_lock); mutex_init(&ocelot->fwd_domain_lock);
mutex_init(&ocelot->tas_lock);
spin_lock_init(&ocelot->ptp_clock_lock); spin_lock_init(&ocelot->ptp_clock_lock);
spin_lock_init(&ocelot->ts_id_lock); spin_lock_init(&ocelot->ts_id_lock);
snprintf(queue_name, sizeof(queue_name), "%s-stats", snprintf(queue_name, sizeof(queue_name), "%s-stats",
......
...@@ -72,6 +72,10 @@ int ocelot_ptp_settime64(struct ptp_clock_info *ptp, ...@@ -72,6 +72,10 @@ int ocelot_ptp_settime64(struct ptp_clock_info *ptp,
ocelot_write_rix(ocelot, val, PTP_PIN_CFG, TOD_ACC_PIN); ocelot_write_rix(ocelot, val, PTP_PIN_CFG, TOD_ACC_PIN);
spin_unlock_irqrestore(&ocelot->ptp_clock_lock, flags); spin_unlock_irqrestore(&ocelot->ptp_clock_lock, flags);
if (ocelot->ops->tas_clock_adjust)
ocelot->ops->tas_clock_adjust(ocelot);
return 0; return 0;
} }
EXPORT_SYMBOL(ocelot_ptp_settime64); EXPORT_SYMBOL(ocelot_ptp_settime64);
...@@ -105,6 +109,9 @@ int ocelot_ptp_adjtime(struct ptp_clock_info *ptp, s64 delta) ...@@ -105,6 +109,9 @@ int ocelot_ptp_adjtime(struct ptp_clock_info *ptp, s64 delta)
ocelot_write_rix(ocelot, val, PTP_PIN_CFG, TOD_ACC_PIN); ocelot_write_rix(ocelot, val, PTP_PIN_CFG, TOD_ACC_PIN);
spin_unlock_irqrestore(&ocelot->ptp_clock_lock, flags); spin_unlock_irqrestore(&ocelot->ptp_clock_lock, flags);
if (ocelot->ops->tas_clock_adjust)
ocelot->ops->tas_clock_adjust(ocelot);
} else { } else {
/* Fall back using ocelot_ptp_settime64 which is not exact. */ /* Fall back using ocelot_ptp_settime64 which is not exact. */
struct timespec64 ts; struct timespec64 ts;
...@@ -117,6 +124,7 @@ int ocelot_ptp_adjtime(struct ptp_clock_info *ptp, s64 delta) ...@@ -117,6 +124,7 @@ int ocelot_ptp_adjtime(struct ptp_clock_info *ptp, s64 delta)
ocelot_ptp_settime64(ptp, &ts); ocelot_ptp_settime64(ptp, &ts);
} }
return 0; return 0;
} }
EXPORT_SYMBOL(ocelot_ptp_adjtime); EXPORT_SYMBOL(ocelot_ptp_adjtime);
......
...@@ -575,6 +575,7 @@ struct ocelot_ops { ...@@ -575,6 +575,7 @@ struct ocelot_ops {
int (*psfp_stats_get)(struct ocelot *ocelot, struct flow_cls_offload *f, int (*psfp_stats_get)(struct ocelot *ocelot, struct flow_cls_offload *f,
struct flow_stats *stats); struct flow_stats *stats);
void (*cut_through_fwd)(struct ocelot *ocelot); void (*cut_through_fwd)(struct ocelot *ocelot);
void (*tas_clock_adjust)(struct ocelot *ocelot);
}; };
struct ocelot_vcap_policer { struct ocelot_vcap_policer {
...@@ -691,6 +692,9 @@ struct ocelot_port { ...@@ -691,6 +692,9 @@ struct ocelot_port {
int bridge_num; int bridge_num;
int speed; int speed;
/* Store the AdminBaseTime of EST fetched from userspace. */
s64 base_time;
}; };
struct ocelot { struct ocelot {
...@@ -757,6 +761,9 @@ struct ocelot { ...@@ -757,6 +761,9 @@ struct ocelot {
/* Lock for serializing forwarding domain changes */ /* Lock for serializing forwarding domain changes */
struct mutex fwd_domain_lock; struct mutex fwd_domain_lock;
/* Lock for serializing Time-Aware Shaper changes */
struct mutex tas_lock;
struct workqueue_struct *owq; struct workqueue_struct *owq;
u8 ptp:1; u8 ptp:1;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment