Merge branch 'mlxsw-Enable-disable-PTP-shapers'

Ido Schimmel says:

====================
mlxsw: Enable/disable PTP shapers

Shalom says:

In order to get more accurate hardware time stamping in Spectrum-1, the
driver needs to apply a shaper on the port for speeds lower than 40Gbps.
This shaper is called a PTP shaper and it is applied on hierarchy 0,
which is the port hierarchy. This shaper may affect the shaper rates of
all hierarchies.

This patchset adds the ability to enable or disable the PTP shaper on
the port in two scenarios:
 1. When the user wants to enable/disable the hardware time stamping
 2. When the port is brought up or down (including port speed change)

Patch #1 adds the QEEC.ptps field that is used for enabling or disabling
the PTP shaper on a port.

Patch #2 adds a note about disabling the PTP shaper when calling to
mlxsw_sp_port_ets_maxrate_set().

Patch #3 adds the QPSC register that is responsible for configuring the
PTP shaper parameters per speed.

Patch #4 sets the PTP shaper parameters during the ptp_init().

Patch #5 adds new operation for getting the port's speed.

Patch #6 enables/disables the PTP shaper when turning on or off the
hardware time stamping.

Patch #7 enables/disables the PTP shaper when the port's status has
changed (including port speed change).

Patch #8 applies the PTP shaper enable/disable logic by filling the PTP
shaper parameters array.
====================

Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
David S. Miller 2019-07-05 15:28:57 -07:00
commit 60a6127c5e
5 changed files with 350 additions and 30 deletions

View File

@ -3515,6 +3515,18 @@ MLXSW_ITEM32(reg, qeec, next_element_index, 0x08, 0, 8);
*/ */
MLXSW_ITEM32(reg, qeec, mise, 0x0C, 31, 1); MLXSW_ITEM32(reg, qeec, mise, 0x0C, 31, 1);
/* reg_qeec_ptps
* PTP shaper
* 0: regular shaper mode
* 1: PTP oriented shaper
* Allowed only for hierarchy 0
* Not supported for CPU port
* Note that ptps mode may affect the shaper rates of all hierarchies
* Supported only on Spectrum-1
* Access: RW
*/
MLXSW_ITEM32(reg, qeec, ptps, 0x0C, 29, 1);
enum { enum {
MLXSW_REG_QEEC_BYTES_MODE, MLXSW_REG_QEEC_BYTES_MODE,
MLXSW_REG_QEEC_PACKETS_MODE, MLXSW_REG_QEEC_PACKETS_MODE,
@ -3601,6 +3613,16 @@ static inline void mlxsw_reg_qeec_pack(char *payload, u8 local_port,
mlxsw_reg_qeec_next_element_index_set(payload, next_index); mlxsw_reg_qeec_next_element_index_set(payload, next_index);
} }
static inline void mlxsw_reg_qeec_ptps_pack(char *payload, u8 local_port,
bool ptps)
{
MLXSW_REG_ZERO(qeec, payload);
mlxsw_reg_qeec_local_port_set(payload, local_port);
mlxsw_reg_qeec_element_hierarchy_set(payload,
MLXSW_REG_QEEC_HIERARCY_PORT);
mlxsw_reg_qeec_ptps_set(payload, ptps);
}
/* QRWE - QoS ReWrite Enable /* QRWE - QoS ReWrite Enable
* ------------------------- * -------------------------
* This register configures the rewrite enable per receive port. * This register configures the rewrite enable per receive port.
@ -3814,6 +3836,112 @@ mlxsw_reg_qtctm_pack(char *payload, u8 local_port, bool mc)
mlxsw_reg_qtctm_mc_set(payload, mc); mlxsw_reg_qtctm_mc_set(payload, mc);
} }
/* QPSC - QoS PTP Shaper Configuration Register
* --------------------------------------------
* The QPSC allows advanced configuration of the shapers when QEEC.ptps=1.
* Supported only on Spectrum-1.
*/
#define MLXSW_REG_QPSC_ID 0x401B
#define MLXSW_REG_QPSC_LEN 0x28
MLXSW_REG_DEFINE(qpsc, MLXSW_REG_QPSC_ID, MLXSW_REG_QPSC_LEN);
enum mlxsw_reg_qpsc_port_speed {
MLXSW_REG_QPSC_PORT_SPEED_100M,
MLXSW_REG_QPSC_PORT_SPEED_1G,
MLXSW_REG_QPSC_PORT_SPEED_10G,
MLXSW_REG_QPSC_PORT_SPEED_25G,
};
/* reg_qpsc_port_speed
* Port speed.
* Access: Index
*/
MLXSW_ITEM32(reg, qpsc, port_speed, 0x00, 0, 4);
/* reg_qpsc_shaper_time_exp
* The base-time-interval for updating the shapers tokens (for all hierarchies).
* shaper_update_rate = 2 ^ shaper_time_exp * (1 + shaper_time_mantissa) * 32nSec
* shaper_rate = 64bit * shaper_inc / shaper_update_rate
* Access: RW
*/
MLXSW_ITEM32(reg, qpsc, shaper_time_exp, 0x04, 16, 4);
/* reg_qpsc_shaper_time_mantissa
* The base-time-interval for updating the shapers tokens (for all hierarchies).
* shaper_update_rate = 2 ^ shaper_time_exp * (1 + shaper_time_mantissa) * 32nSec
* shaper_rate = 64bit * shaper_inc / shaper_update_rate
* Access: RW
*/
MLXSW_ITEM32(reg, qpsc, shaper_time_mantissa, 0x04, 0, 5);
/* reg_qpsc_shaper_inc
* Number of tokens added to shaper on each update.
* Units of 8B.
* Access: RW
*/
MLXSW_ITEM32(reg, qpsc, shaper_inc, 0x08, 0, 5);
/* reg_qpsc_shaper_bs
* Max shaper Burst size.
* Burst size is 2 ^ max_shaper_bs * 512 [bits]
* Range is: 5..25 (from 2KB..2GB)
* Access: RW
*/
MLXSW_ITEM32(reg, qpsc, shaper_bs, 0x0C, 0, 6);
/* reg_qpsc_ptsc_we
* Write enable to port_to_shaper_credits.
* Access: WO
*/
MLXSW_ITEM32(reg, qpsc, ptsc_we, 0x10, 31, 1);
/* reg_qpsc_port_to_shaper_credits
* For split ports: range 1..57
* For non-split ports: range 1..112
* Written only when ptsc_we is set.
* Access: RW
*/
MLXSW_ITEM32(reg, qpsc, port_to_shaper_credits, 0x10, 0, 8);
/* reg_qpsc_ing_timestamp_inc
* Ingress timestamp increment.
* 2's complement.
* The timestamp of MTPPTR at ingress will be incremented by this value. Global
* value for all ports.
* Same units as used by MTPPTR.
* Access: RW
*/
MLXSW_ITEM32(reg, qpsc, ing_timestamp_inc, 0x20, 0, 32);
/* reg_qpsc_egr_timestamp_inc
* Egress timestamp increment.
* 2's complement.
* The timestamp of MTPPTR at egress will be incremented by this value. Global
* value for all ports.
* Same units as used by MTPPTR.
* Access: RW
*/
MLXSW_ITEM32(reg, qpsc, egr_timestamp_inc, 0x24, 0, 32);
static inline void
mlxsw_reg_qpsc_pack(char *payload, enum mlxsw_reg_qpsc_port_speed port_speed,
u8 shaper_time_exp, u8 shaper_time_mantissa, u8 shaper_inc,
u8 shaper_bs, u8 port_to_shaper_credits,
int ing_timestamp_inc, int egr_timestamp_inc)
{
MLXSW_REG_ZERO(qpsc, payload);
mlxsw_reg_qpsc_port_speed_set(payload, port_speed);
mlxsw_reg_qpsc_shaper_time_exp_set(payload, shaper_time_exp);
mlxsw_reg_qpsc_shaper_time_mantissa_set(payload, shaper_time_mantissa);
mlxsw_reg_qpsc_shaper_inc_set(payload, shaper_inc);
mlxsw_reg_qpsc_shaper_bs_set(payload, shaper_bs);
mlxsw_reg_qpsc_ptsc_we_set(payload, true);
mlxsw_reg_qpsc_port_to_shaper_credits_set(payload, port_to_shaper_credits);
mlxsw_reg_qpsc_ing_timestamp_inc_set(payload, ing_timestamp_inc);
mlxsw_reg_qpsc_egr_timestamp_inc_set(payload, egr_timestamp_inc);
}
/* PMLP - Ports Module to Local Port Register /* PMLP - Ports Module to Local Port Register
* ------------------------------------------ * ------------------------------------------
* Configures the assignment of modules to local ports. * Configures the assignment of modules to local ports.
@ -10374,6 +10502,7 @@ static const struct mlxsw_reg_info *mlxsw_reg_infos[] = {
MLXSW_REG(qpdsm), MLXSW_REG(qpdsm),
MLXSW_REG(qpdpm), MLXSW_REG(qpdpm),
MLXSW_REG(qtctm), MLXSW_REG(qtctm),
MLXSW_REG(qpsc),
MLXSW_REG(pmlp), MLXSW_REG(pmlp),
MLXSW_REG(pmtu), MLXSW_REG(pmtu),
MLXSW_REG(ptys), MLXSW_REG(ptys),

View File

@ -171,6 +171,7 @@ struct mlxsw_sp_ptp_ops {
struct hwtstamp_config *config); struct hwtstamp_config *config);
int (*hwtstamp_set)(struct mlxsw_sp_port *mlxsw_sp_port, int (*hwtstamp_set)(struct mlxsw_sp_port *mlxsw_sp_port,
struct hwtstamp_config *config); struct hwtstamp_config *config);
void (*shaper_work)(struct work_struct *work);
int (*get_ts_info)(struct mlxsw_sp *mlxsw_sp, int (*get_ts_info)(struct mlxsw_sp *mlxsw_sp,
struct ethtool_ts_info *info); struct ethtool_ts_info *info);
}; };
@ -2655,28 +2656,33 @@ mlxsw_sp1_from_ptys_link(struct mlxsw_sp *mlxsw_sp, u32 ptys_eth_proto,
} }
} }
static u32
mlxsw_sp1_from_ptys_speed(struct mlxsw_sp *mlxsw_sp, u32 ptys_eth_proto)
{
int i;
for (i = 0; i < MLXSW_SP1_PORT_LINK_MODE_LEN; i++) {
if (ptys_eth_proto & mlxsw_sp1_port_link_mode[i].mask)
return mlxsw_sp1_port_link_mode[i].speed;
}
return SPEED_UNKNOWN;
}
static void static void
mlxsw_sp1_from_ptys_speed_duplex(struct mlxsw_sp *mlxsw_sp, bool carrier_ok, mlxsw_sp1_from_ptys_speed_duplex(struct mlxsw_sp *mlxsw_sp, bool carrier_ok,
u32 ptys_eth_proto, u32 ptys_eth_proto,
struct ethtool_link_ksettings *cmd) struct ethtool_link_ksettings *cmd)
{ {
u32 speed = SPEED_UNKNOWN; cmd->base.speed = SPEED_UNKNOWN;
u8 duplex = DUPLEX_UNKNOWN; cmd->base.duplex = DUPLEX_UNKNOWN;
int i;
if (!carrier_ok) if (!carrier_ok)
goto out; return;
for (i = 0; i < MLXSW_SP1_PORT_LINK_MODE_LEN; i++) { cmd->base.speed = mlxsw_sp1_from_ptys_speed(mlxsw_sp, ptys_eth_proto);
if (ptys_eth_proto & mlxsw_sp1_port_link_mode[i].mask) { if (cmd->base.speed != SPEED_UNKNOWN)
speed = mlxsw_sp1_port_link_mode[i].speed; cmd->base.duplex = DUPLEX_FULL;
duplex = DUPLEX_FULL;
break;
}
}
out:
cmd->base.speed = speed;
cmd->base.duplex = duplex;
} }
static u32 static u32
@ -2747,6 +2753,7 @@ static const struct mlxsw_sp_port_type_speed_ops
mlxsw_sp1_port_type_speed_ops = { mlxsw_sp1_port_type_speed_ops = {
.from_ptys_supported_port = mlxsw_sp1_from_ptys_supported_port, .from_ptys_supported_port = mlxsw_sp1_from_ptys_supported_port,
.from_ptys_link = mlxsw_sp1_from_ptys_link, .from_ptys_link = mlxsw_sp1_from_ptys_link,
.from_ptys_speed = mlxsw_sp1_from_ptys_speed,
.from_ptys_speed_duplex = mlxsw_sp1_from_ptys_speed_duplex, .from_ptys_speed_duplex = mlxsw_sp1_from_ptys_speed_duplex,
.to_ptys_advert_link = mlxsw_sp1_to_ptys_advert_link, .to_ptys_advert_link = mlxsw_sp1_to_ptys_advert_link,
.to_ptys_speed = mlxsw_sp1_to_ptys_speed, .to_ptys_speed = mlxsw_sp1_to_ptys_speed,
@ -2997,28 +3004,33 @@ mlxsw_sp2_from_ptys_link(struct mlxsw_sp *mlxsw_sp, u32 ptys_eth_proto,
} }
} }
static u32
mlxsw_sp2_from_ptys_speed(struct mlxsw_sp *mlxsw_sp, u32 ptys_eth_proto)
{
int i;
for (i = 0; i < MLXSW_SP2_PORT_LINK_MODE_LEN; i++) {
if (ptys_eth_proto & mlxsw_sp2_port_link_mode[i].mask)
return mlxsw_sp2_port_link_mode[i].speed;
}
return SPEED_UNKNOWN;
}
static void static void
mlxsw_sp2_from_ptys_speed_duplex(struct mlxsw_sp *mlxsw_sp, bool carrier_ok, mlxsw_sp2_from_ptys_speed_duplex(struct mlxsw_sp *mlxsw_sp, bool carrier_ok,
u32 ptys_eth_proto, u32 ptys_eth_proto,
struct ethtool_link_ksettings *cmd) struct ethtool_link_ksettings *cmd)
{ {
u32 speed = SPEED_UNKNOWN; cmd->base.speed = SPEED_UNKNOWN;
u8 duplex = DUPLEX_UNKNOWN; cmd->base.duplex = DUPLEX_UNKNOWN;
int i;
if (!carrier_ok) if (!carrier_ok)
goto out; return;
for (i = 0; i < MLXSW_SP2_PORT_LINK_MODE_LEN; i++) { cmd->base.speed = mlxsw_sp2_from_ptys_speed(mlxsw_sp, ptys_eth_proto);
if (ptys_eth_proto & mlxsw_sp2_port_link_mode[i].mask) { if (cmd->base.speed != SPEED_UNKNOWN)
speed = mlxsw_sp2_port_link_mode[i].speed; cmd->base.duplex = DUPLEX_FULL;
duplex = DUPLEX_FULL;
break;
}
}
out:
cmd->base.speed = speed;
cmd->base.duplex = duplex;
} }
static bool static bool
@ -3129,6 +3141,7 @@ static const struct mlxsw_sp_port_type_speed_ops
mlxsw_sp2_port_type_speed_ops = { mlxsw_sp2_port_type_speed_ops = {
.from_ptys_supported_port = mlxsw_sp2_from_ptys_supported_port, .from_ptys_supported_port = mlxsw_sp2_from_ptys_supported_port,
.from_ptys_link = mlxsw_sp2_from_ptys_link, .from_ptys_link = mlxsw_sp2_from_ptys_link,
.from_ptys_speed = mlxsw_sp2_from_ptys_speed,
.from_ptys_speed_duplex = mlxsw_sp2_from_ptys_speed_duplex, .from_ptys_speed_duplex = mlxsw_sp2_from_ptys_speed_duplex,
.to_ptys_advert_link = mlxsw_sp2_to_ptys_advert_link, .to_ptys_advert_link = mlxsw_sp2_to_ptys_advert_link,
.to_ptys_speed = mlxsw_sp2_to_ptys_speed, .to_ptys_speed = mlxsw_sp2_to_ptys_speed,
@ -3457,8 +3470,9 @@ static int mlxsw_sp_port_ets_init(struct mlxsw_sp_port *mlxsw_sp_port)
return err; return err;
} }
/* Make sure the max shaper is disabled in all hierarchies that /* Make sure the max shaper is disabled in all hierarchies that support
* support it. * it. Note that this disables ptps (PTP shaper), but that is intended
* for the initial configuration.
*/ */
err = mlxsw_sp_port_ets_maxrate_set(mlxsw_sp_port, err = mlxsw_sp_port_ets_maxrate_set(mlxsw_sp_port,
MLXSW_REG_QEEC_HIERARCY_PORT, 0, 0, MLXSW_REG_QEEC_HIERARCY_PORT, 0, 0,
@ -3703,6 +3717,9 @@ static int mlxsw_sp_port_create(struct mlxsw_sp *mlxsw_sp, u8 local_port,
} }
mlxsw_sp_port->default_vlan = mlxsw_sp_port_vlan; mlxsw_sp_port->default_vlan = mlxsw_sp_port_vlan;
INIT_DELAYED_WORK(&mlxsw_sp_port->ptp.shaper_dw,
mlxsw_sp->ptp_ops->shaper_work);
mlxsw_sp->ports[local_port] = mlxsw_sp_port; mlxsw_sp->ports[local_port] = mlxsw_sp_port;
err = register_netdev(dev); err = register_netdev(dev);
if (err) { if (err) {
@ -3757,6 +3774,7 @@ static void mlxsw_sp_port_remove(struct mlxsw_sp *mlxsw_sp, u8 local_port)
struct mlxsw_sp_port *mlxsw_sp_port = mlxsw_sp->ports[local_port]; struct mlxsw_sp_port *mlxsw_sp_port = mlxsw_sp->ports[local_port];
cancel_delayed_work_sync(&mlxsw_sp_port->periodic_hw_stats.update_dw); cancel_delayed_work_sync(&mlxsw_sp_port->periodic_hw_stats.update_dw);
cancel_delayed_work_sync(&mlxsw_sp_port->ptp.shaper_dw);
mlxsw_sp_port_ptp_clear(mlxsw_sp_port); mlxsw_sp_port_ptp_clear(mlxsw_sp_port);
mlxsw_core_port_clear(mlxsw_sp->core, local_port, mlxsw_sp); mlxsw_core_port_clear(mlxsw_sp->core, local_port, mlxsw_sp);
unregister_netdev(mlxsw_sp_port->dev); /* This calls ndo_stop */ unregister_netdev(mlxsw_sp_port->dev); /* This calls ndo_stop */
@ -4042,6 +4060,7 @@ static void mlxsw_sp_pude_event_func(const struct mlxsw_reg_info *reg,
if (status == MLXSW_PORT_OPER_STATUS_UP) { if (status == MLXSW_PORT_OPER_STATUS_UP) {
netdev_info(mlxsw_sp_port->dev, "link up\n"); netdev_info(mlxsw_sp_port->dev, "link up\n");
netif_carrier_on(mlxsw_sp_port->dev); netif_carrier_on(mlxsw_sp_port->dev);
mlxsw_core_schedule_dw(&mlxsw_sp_port->ptp.shaper_dw, 0);
} else { } else {
netdev_info(mlxsw_sp_port->dev, "link down\n"); netdev_info(mlxsw_sp_port->dev, "link down\n");
netif_carrier_off(mlxsw_sp_port->dev); netif_carrier_off(mlxsw_sp_port->dev);
@ -4559,6 +4578,7 @@ static const struct mlxsw_sp_ptp_ops mlxsw_sp1_ptp_ops = {
.transmitted = mlxsw_sp1_ptp_transmitted, .transmitted = mlxsw_sp1_ptp_transmitted,
.hwtstamp_get = mlxsw_sp1_ptp_hwtstamp_get, .hwtstamp_get = mlxsw_sp1_ptp_hwtstamp_get,
.hwtstamp_set = mlxsw_sp1_ptp_hwtstamp_set, .hwtstamp_set = mlxsw_sp1_ptp_hwtstamp_set,
.shaper_work = mlxsw_sp1_ptp_shaper_work,
.get_ts_info = mlxsw_sp1_ptp_get_ts_info, .get_ts_info = mlxsw_sp1_ptp_get_ts_info,
}; };
@ -4571,6 +4591,7 @@ static const struct mlxsw_sp_ptp_ops mlxsw_sp2_ptp_ops = {
.transmitted = mlxsw_sp2_ptp_transmitted, .transmitted = mlxsw_sp2_ptp_transmitted,
.hwtstamp_get = mlxsw_sp2_ptp_hwtstamp_get, .hwtstamp_get = mlxsw_sp2_ptp_hwtstamp_get,
.hwtstamp_set = mlxsw_sp2_ptp_hwtstamp_set, .hwtstamp_set = mlxsw_sp2_ptp_hwtstamp_set,
.shaper_work = mlxsw_sp2_ptp_shaper_work,
.get_ts_info = mlxsw_sp2_ptp_get_ts_info, .get_ts_info = mlxsw_sp2_ptp_get_ts_info,
}; };

View File

@ -267,6 +267,7 @@ struct mlxsw_sp_port {
struct mlxsw_sp_acl_block *ing_acl_block; struct mlxsw_sp_acl_block *ing_acl_block;
struct mlxsw_sp_acl_block *eg_acl_block; struct mlxsw_sp_acl_block *eg_acl_block;
struct { struct {
struct delayed_work shaper_dw;
struct hwtstamp_config hwtstamp_config; struct hwtstamp_config hwtstamp_config;
u16 ing_types; u16 ing_types;
u16 egr_types; u16 egr_types;
@ -279,6 +280,7 @@ struct mlxsw_sp_port_type_speed_ops {
struct ethtool_link_ksettings *cmd); struct ethtool_link_ksettings *cmd);
void (*from_ptys_link)(struct mlxsw_sp *mlxsw_sp, u32 ptys_eth_proto, void (*from_ptys_link)(struct mlxsw_sp *mlxsw_sp, u32 ptys_eth_proto,
unsigned long *mode); unsigned long *mode);
u32 (*from_ptys_speed)(struct mlxsw_sp *mlxsw_sp, u32 ptys_eth_proto);
void (*from_ptys_speed_duplex)(struct mlxsw_sp *mlxsw_sp, void (*from_ptys_speed_duplex)(struct mlxsw_sp *mlxsw_sp,
bool carrier_ok, u32 ptys_eth_proto, bool carrier_ok, u32 ptys_eth_proto,
struct ethtool_link_ksettings *cmd); struct ethtool_link_ksettings *cmd);

View File

@ -753,12 +753,101 @@ static int mlxsw_sp1_ptp_mtpppc_set(struct mlxsw_sp *mlxsw_sp,
return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(mtpppc), mtpppc_pl); return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(mtpppc), mtpppc_pl);
} }
struct mlxsw_sp1_ptp_shaper_params {
u32 ethtool_speed;
enum mlxsw_reg_qpsc_port_speed port_speed;
u8 shaper_time_exp;
u8 shaper_time_mantissa;
u8 shaper_inc;
u8 shaper_bs;
u8 port_to_shaper_credits;
int ing_timestamp_inc;
int egr_timestamp_inc;
};
static const struct mlxsw_sp1_ptp_shaper_params
mlxsw_sp1_ptp_shaper_params[] = {
{
.ethtool_speed = SPEED_100,
.port_speed = MLXSW_REG_QPSC_PORT_SPEED_100M,
.shaper_time_exp = 4,
.shaper_time_mantissa = 12,
.shaper_inc = 9,
.shaper_bs = 1,
.port_to_shaper_credits = 1,
.ing_timestamp_inc = -313,
.egr_timestamp_inc = 313,
},
{
.ethtool_speed = SPEED_1000,
.port_speed = MLXSW_REG_QPSC_PORT_SPEED_1G,
.shaper_time_exp = 0,
.shaper_time_mantissa = 12,
.shaper_inc = 6,
.shaper_bs = 0,
.port_to_shaper_credits = 1,
.ing_timestamp_inc = -35,
.egr_timestamp_inc = 35,
},
{
.ethtool_speed = SPEED_10000,
.port_speed = MLXSW_REG_QPSC_PORT_SPEED_10G,
.shaper_time_exp = 0,
.shaper_time_mantissa = 2,
.shaper_inc = 14,
.shaper_bs = 1,
.port_to_shaper_credits = 1,
.ing_timestamp_inc = -11,
.egr_timestamp_inc = 11,
},
{
.ethtool_speed = SPEED_25000,
.port_speed = MLXSW_REG_QPSC_PORT_SPEED_25G,
.shaper_time_exp = 0,
.shaper_time_mantissa = 0,
.shaper_inc = 11,
.shaper_bs = 1,
.port_to_shaper_credits = 1,
.ing_timestamp_inc = -14,
.egr_timestamp_inc = 14,
},
};
#define MLXSW_SP1_PTP_SHAPER_PARAMS_LEN ARRAY_SIZE(mlxsw_sp1_ptp_shaper_params)
static int mlxsw_sp1_ptp_shaper_params_set(struct mlxsw_sp *mlxsw_sp)
{
const struct mlxsw_sp1_ptp_shaper_params *params;
char qpsc_pl[MLXSW_REG_QPSC_LEN];
int i, err;
for (i = 0; i < MLXSW_SP1_PTP_SHAPER_PARAMS_LEN; i++) {
params = &mlxsw_sp1_ptp_shaper_params[i];
mlxsw_reg_qpsc_pack(qpsc_pl, params->port_speed,
params->shaper_time_exp,
params->shaper_time_mantissa,
params->shaper_inc, params->shaper_bs,
params->port_to_shaper_credits,
params->ing_timestamp_inc,
params->egr_timestamp_inc);
err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(qpsc), qpsc_pl);
if (err)
return err;
}
return 0;
}
struct mlxsw_sp_ptp_state *mlxsw_sp1_ptp_init(struct mlxsw_sp *mlxsw_sp) struct mlxsw_sp_ptp_state *mlxsw_sp1_ptp_init(struct mlxsw_sp *mlxsw_sp)
{ {
struct mlxsw_sp_ptp_state *ptp_state; struct mlxsw_sp_ptp_state *ptp_state;
u16 message_type; u16 message_type;
int err; int err;
err = mlxsw_sp1_ptp_shaper_params_set(mlxsw_sp);
if (err)
return ERR_PTR(err);
ptp_state = kzalloc(sizeof(*ptp_state), GFP_KERNEL); ptp_state = kzalloc(sizeof(*ptp_state), GFP_KERNEL);
if (!ptp_state) if (!ptp_state)
return ERR_PTR(-ENOMEM); return ERR_PTR(-ENOMEM);
@ -907,6 +996,71 @@ static int mlxsw_sp1_ptp_mtpppc_update(struct mlxsw_sp_port *mlxsw_sp_port,
ing_types, egr_types); ing_types, egr_types);
} }
static bool mlxsw_sp1_ptp_hwtstamp_enabled(struct mlxsw_sp_port *mlxsw_sp_port)
{
return mlxsw_sp_port->ptp.ing_types || mlxsw_sp_port->ptp.egr_types;
}
static int
mlxsw_sp1_ptp_port_shaper_set(struct mlxsw_sp_port *mlxsw_sp_port, bool enable)
{
struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
char qeec_pl[MLXSW_REG_QEEC_LEN];
mlxsw_reg_qeec_ptps_pack(qeec_pl, mlxsw_sp_port->local_port, enable);
return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(qeec), qeec_pl);
}
static int mlxsw_sp1_ptp_port_shaper_check(struct mlxsw_sp_port *mlxsw_sp_port)
{
const struct mlxsw_sp_port_type_speed_ops *port_type_speed_ops;
struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
char ptys_pl[MLXSW_REG_PTYS_LEN];
u32 eth_proto_oper, speed;
bool ptps = false;
int err, i;
if (!mlxsw_sp1_ptp_hwtstamp_enabled(mlxsw_sp_port))
return mlxsw_sp1_ptp_port_shaper_set(mlxsw_sp_port, false);
port_type_speed_ops = mlxsw_sp->port_type_speed_ops;
port_type_speed_ops->reg_ptys_eth_pack(mlxsw_sp, ptys_pl,
mlxsw_sp_port->local_port, 0,
false);
err = mlxsw_reg_query(mlxsw_sp->core, MLXSW_REG(ptys), ptys_pl);
if (err)
return err;
port_type_speed_ops->reg_ptys_eth_unpack(mlxsw_sp, ptys_pl, NULL, NULL,
&eth_proto_oper);
speed = port_type_speed_ops->from_ptys_speed(mlxsw_sp, eth_proto_oper);
for (i = 0; i < MLXSW_SP1_PTP_SHAPER_PARAMS_LEN; i++) {
if (mlxsw_sp1_ptp_shaper_params[i].ethtool_speed == speed) {
ptps = true;
break;
}
}
return mlxsw_sp1_ptp_port_shaper_set(mlxsw_sp_port, ptps);
}
void mlxsw_sp1_ptp_shaper_work(struct work_struct *work)
{
struct delayed_work *dwork = to_delayed_work(work);
struct mlxsw_sp_port *mlxsw_sp_port;
int err;
mlxsw_sp_port = container_of(dwork, struct mlxsw_sp_port,
ptp.shaper_dw);
if (!mlxsw_sp1_ptp_hwtstamp_enabled(mlxsw_sp_port))
return;
err = mlxsw_sp1_ptp_port_shaper_check(mlxsw_sp_port);
if (err)
netdev_err(mlxsw_sp_port->dev, "Failed to set up PTP shaper\n");
}
int mlxsw_sp1_ptp_hwtstamp_set(struct mlxsw_sp_port *mlxsw_sp_port, int mlxsw_sp1_ptp_hwtstamp_set(struct mlxsw_sp_port *mlxsw_sp_port,
struct hwtstamp_config *config) struct hwtstamp_config *config)
{ {
@ -928,6 +1082,10 @@ int mlxsw_sp1_ptp_hwtstamp_set(struct mlxsw_sp_port *mlxsw_sp_port,
mlxsw_sp_port->ptp.ing_types = ing_types; mlxsw_sp_port->ptp.ing_types = ing_types;
mlxsw_sp_port->ptp.egr_types = egr_types; mlxsw_sp_port->ptp.egr_types = egr_types;
err = mlxsw_sp1_ptp_port_shaper_check(mlxsw_sp_port);
if (err)
return err;
/* Notify the ioctl caller what we are actually timestamping. */ /* Notify the ioctl caller what we are actually timestamping. */
config->rx_filter = rx_filter; config->rx_filter = rx_filter;

View File

@ -54,6 +54,8 @@ int mlxsw_sp1_ptp_hwtstamp_get(struct mlxsw_sp_port *mlxsw_sp_port,
int mlxsw_sp1_ptp_hwtstamp_set(struct mlxsw_sp_port *mlxsw_sp_port, int mlxsw_sp1_ptp_hwtstamp_set(struct mlxsw_sp_port *mlxsw_sp_port,
struct hwtstamp_config *config); struct hwtstamp_config *config);
void mlxsw_sp1_ptp_shaper_work(struct work_struct *work);
int mlxsw_sp1_ptp_get_ts_info(struct mlxsw_sp *mlxsw_sp, int mlxsw_sp1_ptp_get_ts_info(struct mlxsw_sp *mlxsw_sp,
struct ethtool_ts_info *info); struct ethtool_ts_info *info);
@ -113,6 +115,10 @@ mlxsw_sp1_ptp_hwtstamp_set(struct mlxsw_sp_port *mlxsw_sp_port,
return -EOPNOTSUPP; return -EOPNOTSUPP;
} }
static inline void mlxsw_sp1_ptp_shaper_work(struct work_struct *work)
{
}
static inline int mlxsw_sp1_ptp_get_ts_info(struct mlxsw_sp *mlxsw_sp, static inline int mlxsw_sp1_ptp_get_ts_info(struct mlxsw_sp *mlxsw_sp,
struct ethtool_ts_info *info) struct ethtool_ts_info *info)
{ {
@ -167,6 +173,10 @@ mlxsw_sp2_ptp_hwtstamp_set(struct mlxsw_sp_port *mlxsw_sp_port,
return -EOPNOTSUPP; return -EOPNOTSUPP;
} }
static inline void mlxsw_sp2_ptp_shaper_work(struct work_struct *work)
{
}
static inline int mlxsw_sp2_ptp_get_ts_info(struct mlxsw_sp *mlxsw_sp, static inline int mlxsw_sp2_ptp_get_ts_info(struct mlxsw_sp *mlxsw_sp,
struct ethtool_ts_info *info) struct ethtool_ts_info *info)
{ {