mlxsw: spectrum_ptp: Add implementation for physical hardware clock operations
Implement physical hardware clock operations. The main difference between the existing operations of Spectrum-1 and the new operations of Spectrum-2 is the usage of UTC hardware clock instead of FRC. Add support for init() and fini() functions for PTP clock in Spectrum-2. Signed-off-by: Danielle Ratson <danieller@nvidia.com> Signed-off-by: Amit Cohen <amcohen@nvidia.com> Reviewed-by: Petr Machata <petrm@nvidia.com> Signed-off-by: Ido Schimmel <idosch@nvidia.com> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
parent
bbd300570a
commit
a5bf8e5e8b
|
@ -339,6 +339,153 @@ void mlxsw_sp1_ptp_clock_fini(struct mlxsw_sp_ptp_clock *clock_common)
|
||||||
kfree(clock);
|
kfree(clock);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static u64 mlxsw_sp2_ptp_read_utc(struct mlxsw_sp_ptp_clock *clock,
|
||||||
|
struct ptp_system_timestamp *sts)
|
||||||
|
{
|
||||||
|
struct mlxsw_core *mlxsw_core = clock->core;
|
||||||
|
u32 utc_sec1, utc_sec2, utc_nsec;
|
||||||
|
|
||||||
|
utc_sec1 = mlxsw_core_read_utc_sec(mlxsw_core);
|
||||||
|
ptp_read_system_prets(sts);
|
||||||
|
utc_nsec = mlxsw_core_read_utc_nsec(mlxsw_core);
|
||||||
|
ptp_read_system_postts(sts);
|
||||||
|
utc_sec2 = mlxsw_core_read_utc_sec(mlxsw_core);
|
||||||
|
|
||||||
|
if (utc_sec1 != utc_sec2) {
|
||||||
|
/* Wrap around. */
|
||||||
|
ptp_read_system_prets(sts);
|
||||||
|
utc_nsec = mlxsw_core_read_utc_nsec(mlxsw_core);
|
||||||
|
ptp_read_system_postts(sts);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (u64)utc_sec2 * NSEC_PER_SEC + utc_nsec;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
mlxsw_sp2_ptp_phc_settime(struct mlxsw_sp_ptp_clock *clock, u64 nsec)
|
||||||
|
{
|
||||||
|
struct mlxsw_core *mlxsw_core = clock->core;
|
||||||
|
char mtutc_pl[MLXSW_REG_MTUTC_LEN];
|
||||||
|
u32 sec, nsec_rem;
|
||||||
|
|
||||||
|
sec = div_u64_rem(nsec, NSEC_PER_SEC, &nsec_rem);
|
||||||
|
mlxsw_reg_mtutc_pack(mtutc_pl,
|
||||||
|
MLXSW_REG_MTUTC_OPERATION_SET_TIME_IMMEDIATE,
|
||||||
|
0, sec, nsec_rem, 0);
|
||||||
|
return mlxsw_reg_write(mlxsw_core, MLXSW_REG(mtutc), mtutc_pl);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int mlxsw_sp2_ptp_adjfine(struct ptp_clock_info *ptp, long scaled_ppm)
|
||||||
|
{
|
||||||
|
struct mlxsw_sp_ptp_clock *clock =
|
||||||
|
container_of(ptp, struct mlxsw_sp_ptp_clock, ptp_info);
|
||||||
|
s32 ppb = scaled_ppm_to_ppb(scaled_ppm);
|
||||||
|
|
||||||
|
/* In Spectrum-2 and newer ASICs, the frequency adjustment in MTUTC is
|
||||||
|
* reversed, positive values mean to decrease the frequency. Adjust the
|
||||||
|
* sign of PPB to this behavior.
|
||||||
|
*/
|
||||||
|
return mlxsw_sp_ptp_phc_adjfreq(clock, -ppb);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int mlxsw_sp2_ptp_adjtime(struct ptp_clock_info *ptp, s64 delta)
|
||||||
|
{
|
||||||
|
struct mlxsw_sp_ptp_clock *clock =
|
||||||
|
container_of(ptp, struct mlxsw_sp_ptp_clock, ptp_info);
|
||||||
|
struct mlxsw_core *mlxsw_core = clock->core;
|
||||||
|
char mtutc_pl[MLXSW_REG_MTUTC_LEN];
|
||||||
|
|
||||||
|
/* HW time adjustment range is s16. If out of range, set time instead. */
|
||||||
|
if (delta < S16_MIN || delta > S16_MAX) {
|
||||||
|
u64 nsec;
|
||||||
|
|
||||||
|
nsec = mlxsw_sp2_ptp_read_utc(clock, NULL);
|
||||||
|
nsec += delta;
|
||||||
|
|
||||||
|
return mlxsw_sp2_ptp_phc_settime(clock, nsec);
|
||||||
|
}
|
||||||
|
|
||||||
|
mlxsw_reg_mtutc_pack(mtutc_pl,
|
||||||
|
MLXSW_REG_MTUTC_OPERATION_ADJUST_TIME,
|
||||||
|
0, 0, 0, delta);
|
||||||
|
return mlxsw_reg_write(mlxsw_core, MLXSW_REG(mtutc), mtutc_pl);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int mlxsw_sp2_ptp_gettimex(struct ptp_clock_info *ptp,
|
||||||
|
struct timespec64 *ts,
|
||||||
|
struct ptp_system_timestamp *sts)
|
||||||
|
{
|
||||||
|
struct mlxsw_sp_ptp_clock *clock =
|
||||||
|
container_of(ptp, struct mlxsw_sp_ptp_clock, ptp_info);
|
||||||
|
u64 nsec;
|
||||||
|
|
||||||
|
nsec = mlxsw_sp2_ptp_read_utc(clock, sts);
|
||||||
|
*ts = ns_to_timespec64(nsec);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int mlxsw_sp2_ptp_settime(struct ptp_clock_info *ptp,
|
||||||
|
const struct timespec64 *ts)
|
||||||
|
{
|
||||||
|
struct mlxsw_sp_ptp_clock *clock =
|
||||||
|
container_of(ptp, struct mlxsw_sp_ptp_clock, ptp_info);
|
||||||
|
u64 nsec = timespec64_to_ns(ts);
|
||||||
|
|
||||||
|
return mlxsw_sp2_ptp_phc_settime(clock, nsec);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct ptp_clock_info mlxsw_sp2_ptp_clock_info = {
|
||||||
|
.owner = THIS_MODULE,
|
||||||
|
.name = "mlxsw_sp_clock",
|
||||||
|
.max_adj = MLXSW_REG_MTUTC_MAX_FREQ_ADJ,
|
||||||
|
.adjfine = mlxsw_sp2_ptp_adjfine,
|
||||||
|
.adjtime = mlxsw_sp2_ptp_adjtime,
|
||||||
|
.gettimex64 = mlxsw_sp2_ptp_gettimex,
|
||||||
|
.settime64 = mlxsw_sp2_ptp_settime,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct mlxsw_sp_ptp_clock *
|
||||||
|
mlxsw_sp2_ptp_clock_init(struct mlxsw_sp *mlxsw_sp, struct device *dev)
|
||||||
|
{
|
||||||
|
struct mlxsw_sp_ptp_clock *clock;
|
||||||
|
int err;
|
||||||
|
|
||||||
|
clock = kzalloc(sizeof(*clock), GFP_KERNEL);
|
||||||
|
if (!clock)
|
||||||
|
return ERR_PTR(-ENOMEM);
|
||||||
|
|
||||||
|
clock->core = mlxsw_sp->core;
|
||||||
|
|
||||||
|
clock->ptp_info = mlxsw_sp2_ptp_clock_info;
|
||||||
|
|
||||||
|
err = mlxsw_sp2_ptp_phc_settime(clock, 0);
|
||||||
|
if (err) {
|
||||||
|
dev_err(dev, "setting UTC time failed %d\n", err);
|
||||||
|
goto err_ptp_phc_settime;
|
||||||
|
}
|
||||||
|
|
||||||
|
clock->ptp = ptp_clock_register(&clock->ptp_info, dev);
|
||||||
|
if (IS_ERR(clock->ptp)) {
|
||||||
|
err = PTR_ERR(clock->ptp);
|
||||||
|
dev_err(dev, "ptp_clock_register failed %d\n", err);
|
||||||
|
goto err_ptp_clock_register;
|
||||||
|
}
|
||||||
|
|
||||||
|
return clock;
|
||||||
|
|
||||||
|
err_ptp_clock_register:
|
||||||
|
err_ptp_phc_settime:
|
||||||
|
kfree(clock);
|
||||||
|
return ERR_PTR(err);
|
||||||
|
}
|
||||||
|
|
||||||
|
void mlxsw_sp2_ptp_clock_fini(struct mlxsw_sp_ptp_clock *clock)
|
||||||
|
{
|
||||||
|
ptp_clock_unregister(clock->ptp);
|
||||||
|
kfree(clock);
|
||||||
|
}
|
||||||
|
|
||||||
static int mlxsw_sp_ptp_parse(struct sk_buff *skb,
|
static int mlxsw_sp_ptp_parse(struct sk_buff *skb,
|
||||||
u8 *p_domain_number,
|
u8 *p_domain_number,
|
||||||
u8 *p_message_type,
|
u8 *p_message_type,
|
||||||
|
|
|
@ -57,6 +57,11 @@ void mlxsw_sp1_get_stats_strings(u8 **p);
|
||||||
void mlxsw_sp1_get_stats(struct mlxsw_sp_port *mlxsw_sp_port,
|
void mlxsw_sp1_get_stats(struct mlxsw_sp_port *mlxsw_sp_port,
|
||||||
u64 *data, int data_index);
|
u64 *data, int data_index);
|
||||||
|
|
||||||
|
struct mlxsw_sp_ptp_clock *
|
||||||
|
mlxsw_sp2_ptp_clock_init(struct mlxsw_sp *mlxsw_sp, struct device *dev);
|
||||||
|
|
||||||
|
void mlxsw_sp2_ptp_clock_fini(struct mlxsw_sp_ptp_clock *clock);
|
||||||
|
|
||||||
struct mlxsw_sp_ptp_state *mlxsw_sp2_ptp_init(struct mlxsw_sp *mlxsw_sp);
|
struct mlxsw_sp_ptp_state *mlxsw_sp2_ptp_init(struct mlxsw_sp *mlxsw_sp);
|
||||||
|
|
||||||
void mlxsw_sp2_ptp_fini(struct mlxsw_sp_ptp_state *ptp_state);
|
void mlxsw_sp2_ptp_fini(struct mlxsw_sp_ptp_state *ptp_state);
|
||||||
|
@ -140,6 +145,16 @@ static inline void mlxsw_sp1_get_stats(struct mlxsw_sp_port *mlxsw_sp_port,
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline struct mlxsw_sp_ptp_clock *
|
||||||
|
mlxsw_sp2_ptp_clock_init(struct mlxsw_sp *mlxsw_sp, struct device *dev)
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void mlxsw_sp2_ptp_clock_fini(struct mlxsw_sp_ptp_clock *clock)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
static inline struct mlxsw_sp_ptp_state *
|
static inline struct mlxsw_sp_ptp_state *
|
||||||
mlxsw_sp2_ptp_init(struct mlxsw_sp *mlxsw_sp)
|
mlxsw_sp2_ptp_init(struct mlxsw_sp *mlxsw_sp)
|
||||||
{
|
{
|
||||||
|
@ -151,16 +166,6 @@ static inline void mlxsw_sp2_ptp_fini(struct mlxsw_sp_ptp_state *ptp_state)
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static inline struct mlxsw_sp_ptp_clock *
|
|
||||||
mlxsw_sp2_ptp_clock_init(struct mlxsw_sp *mlxsw_sp, struct device *dev)
|
|
||||||
{
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void mlxsw_sp2_ptp_clock_fini(struct mlxsw_sp_ptp_clock *clock)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void mlxsw_sp2_ptp_receive(struct mlxsw_sp *mlxsw_sp,
|
static inline void mlxsw_sp2_ptp_receive(struct mlxsw_sp *mlxsw_sp,
|
||||||
struct sk_buff *skb, u16 local_port)
|
struct sk_buff *skb, u16 local_port)
|
||||||
{
|
{
|
||||||
|
|
Loading…
Reference in New Issue