Commit 2fa8d3af authored by Brandon Streiff's avatar Brandon Streiff Committed by David S. Miller

net: dsa: mv88e6xxx: expose switch time as a PTP hardware clock

This patch adds basic support for exposing the 32-bit timestamp counter
inside the mv88e6xxx switch as a ptp_clock.

Adjfine implemented by Richard Cochran.
Andrew Lunn: fix return value of PTP stub function.
Signed-off-by: default avatarBrandon Streiff <brandon.streiff@ni.com>
Signed-off-by: default avatarRichard Cochran <richardcochran@gmail.com>
Signed-off-by: default avatarAndrew Lunn <andrew@lunn.ch>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 0d632c3d
...@@ -18,3 +18,13 @@ config NET_DSA_MV88E6XXX_GLOBAL2 ...@@ -18,3 +18,13 @@ config NET_DSA_MV88E6XXX_GLOBAL2
It is required on most chips. If the chip you compile the support for It is required on most chips. If the chip you compile the support for
doesn't have such registers set, say N here. In doubt, say Y. doesn't have such registers set, say N here. In doubt, say Y.
config NET_DSA_MV88E6XXX_PTP
bool "PTP support for Marvell 88E6xxx"
default n
depends on NET_DSA_MV88E6XXX_GLOBAL2
imply NETWORK_PHY_TIMESTAMPING
imply PTP_1588_CLOCK
help
Say Y to enable PTP hardware timestamping on Marvell 88E6xxx switch
chips that support it.
...@@ -8,4 +8,5 @@ mv88e6xxx-$(CONFIG_NET_DSA_MV88E6XXX_GLOBAL2) += global2.o ...@@ -8,4 +8,5 @@ mv88e6xxx-$(CONFIG_NET_DSA_MV88E6XXX_GLOBAL2) += global2.o
mv88e6xxx-$(CONFIG_NET_DSA_MV88E6XXX_GLOBAL2) += global2_avb.o mv88e6xxx-$(CONFIG_NET_DSA_MV88E6XXX_GLOBAL2) += global2_avb.o
mv88e6xxx-objs += phy.o mv88e6xxx-objs += phy.o
mv88e6xxx-objs += port.o mv88e6xxx-objs += port.o
mv88e6xxx-$(CONFIG_NET_DSA_MV88E6XXX_PTP) += ptp.o
mv88e6xxx-objs += serdes.o mv88e6xxx-objs += serdes.o
...@@ -38,6 +38,7 @@ ...@@ -38,6 +38,7 @@
#include "global2.h" #include "global2.h"
#include "phy.h" #include "phy.h"
#include "port.h" #include "port.h"
#include "ptp.h"
#include "serdes.h" #include "serdes.h"
static void assert_reg_lock(struct mv88e6xxx_chip *chip) static void assert_reg_lock(struct mv88e6xxx_chip *chip)
...@@ -2092,6 +2093,13 @@ static int mv88e6xxx_setup(struct dsa_switch *ds) ...@@ -2092,6 +2093,13 @@ static int mv88e6xxx_setup(struct dsa_switch *ds)
if (err) if (err)
goto unlock; goto unlock;
/* Setup PTP Hardware Clock */
if (chip->info->ptp_support) {
err = mv88e6xxx_ptp_setup(chip);
if (err)
goto unlock;
}
unlock: unlock:
mutex_unlock(&chip->reg_lock); mutex_unlock(&chip->reg_lock);
...@@ -3484,6 +3492,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { ...@@ -3484,6 +3492,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
.pvt = true, .pvt = true,
.multi_chip = true, .multi_chip = true,
.tag_protocol = DSA_TAG_PROTO_DSA, .tag_protocol = DSA_TAG_PROTO_DSA,
.ptp_support = true,
.ops = &mv88e6191_ops, .ops = &mv88e6191_ops,
}, },
...@@ -3504,6 +3513,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { ...@@ -3504,6 +3513,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
.pvt = true, .pvt = true,
.multi_chip = true, .multi_chip = true,
.tag_protocol = DSA_TAG_PROTO_EDSA, .tag_protocol = DSA_TAG_PROTO_EDSA,
.ptp_support = true,
.ops = &mv88e6240_ops, .ops = &mv88e6240_ops,
}, },
...@@ -3524,6 +3534,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { ...@@ -3524,6 +3534,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
.pvt = true, .pvt = true,
.multi_chip = true, .multi_chip = true,
.tag_protocol = DSA_TAG_PROTO_DSA, .tag_protocol = DSA_TAG_PROTO_DSA,
.ptp_support = true,
.ops = &mv88e6290_ops, .ops = &mv88e6290_ops,
}, },
...@@ -3543,6 +3554,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { ...@@ -3543,6 +3554,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
.pvt = true, .pvt = true,
.multi_chip = true, .multi_chip = true,
.tag_protocol = DSA_TAG_PROTO_EDSA, .tag_protocol = DSA_TAG_PROTO_EDSA,
.ptp_support = true,
.ops = &mv88e6320_ops, .ops = &mv88e6320_ops,
}, },
...@@ -3561,6 +3573,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { ...@@ -3561,6 +3573,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
.atu_move_port_mask = 0xf, .atu_move_port_mask = 0xf,
.multi_chip = true, .multi_chip = true,
.tag_protocol = DSA_TAG_PROTO_EDSA, .tag_protocol = DSA_TAG_PROTO_EDSA,
.ptp_support = true,
.ops = &mv88e6321_ops, .ops = &mv88e6321_ops,
}, },
...@@ -3580,6 +3593,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { ...@@ -3580,6 +3593,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
.pvt = true, .pvt = true,
.multi_chip = true, .multi_chip = true,
.tag_protocol = DSA_TAG_PROTO_EDSA, .tag_protocol = DSA_TAG_PROTO_EDSA,
.ptp_support = true,
.ops = &mv88e6341_ops, .ops = &mv88e6341_ops,
}, },
...@@ -3640,6 +3654,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { ...@@ -3640,6 +3654,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
.pvt = true, .pvt = true,
.multi_chip = true, .multi_chip = true,
.tag_protocol = DSA_TAG_PROTO_EDSA, .tag_protocol = DSA_TAG_PROTO_EDSA,
.ptp_support = true,
.ops = &mv88e6352_ops, .ops = &mv88e6352_ops,
}, },
[MV88E6390] = { [MV88E6390] = {
...@@ -3659,6 +3674,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { ...@@ -3659,6 +3674,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
.pvt = true, .pvt = true,
.multi_chip = true, .multi_chip = true,
.tag_protocol = DSA_TAG_PROTO_DSA, .tag_protocol = DSA_TAG_PROTO_DSA,
.ptp_support = true,
.ops = &mv88e6390_ops, .ops = &mv88e6390_ops,
}, },
[MV88E6390X] = { [MV88E6390X] = {
...@@ -3678,6 +3694,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = { ...@@ -3678,6 +3694,7 @@ static const struct mv88e6xxx_info mv88e6xxx_table[] = {
.pvt = true, .pvt = true,
.multi_chip = true, .multi_chip = true,
.tag_protocol = DSA_TAG_PROTO_DSA, .tag_protocol = DSA_TAG_PROTO_DSA,
.ptp_support = true,
.ops = &mv88e6390x_ops, .ops = &mv88e6390x_ops,
}, },
}; };
...@@ -4031,6 +4048,9 @@ static void mv88e6xxx_remove(struct mdio_device *mdiodev) ...@@ -4031,6 +4048,9 @@ static void mv88e6xxx_remove(struct mdio_device *mdiodev)
struct dsa_switch *ds = dev_get_drvdata(&mdiodev->dev); struct dsa_switch *ds = dev_get_drvdata(&mdiodev->dev);
struct mv88e6xxx_chip *chip = ds->priv; struct mv88e6xxx_chip *chip = ds->priv;
if (chip->info->ptp_support)
mv88e6xxx_ptp_free(chip);
mv88e6xxx_phy_destroy(chip); mv88e6xxx_phy_destroy(chip);
mv88e6xxx_unregister_switch(chip); mv88e6xxx_unregister_switch(chip);
mv88e6xxx_mdios_unregister(chip); mv88e6xxx_mdios_unregister(chip);
......
...@@ -16,6 +16,8 @@ ...@@ -16,6 +16,8 @@
#include <linux/irq.h> #include <linux/irq.h>
#include <linux/gpio/consumer.h> #include <linux/gpio/consumer.h>
#include <linux/phy.h> #include <linux/phy.h>
#include <linux/ptp_clock_kernel.h>
#include <linux/timecounter.h>
#include <net/dsa.h> #include <net/dsa.h>
#ifndef UINT64_MAX #ifndef UINT64_MAX
...@@ -126,6 +128,9 @@ struct mv88e6xxx_info { ...@@ -126,6 +128,9 @@ struct mv88e6xxx_info {
*/ */
u8 atu_move_port_mask; u8 atu_move_port_mask;
const struct mv88e6xxx_ops *ops; const struct mv88e6xxx_ops *ops;
/* Supports PTP */
bool ptp_support;
}; };
struct mv88e6xxx_atu_entry { struct mv88e6xxx_atu_entry {
...@@ -210,6 +215,16 @@ struct mv88e6xxx_chip { ...@@ -210,6 +215,16 @@ struct mv88e6xxx_chip {
int watchdog_irq; int watchdog_irq;
int atu_prob_irq; int atu_prob_irq;
int vtu_prob_irq; int vtu_prob_irq;
/* This cyclecounter abstracts the switch PTP time.
* reg_lock must be held for any operation that read()s.
*/
struct cyclecounter tstamp_cc;
struct timecounter tstamp_tc;
struct delayed_work overflow_work;
struct ptp_clock *ptp_clock;
struct ptp_clock_info ptp_clock_info;
}; };
struct mv88e6xxx_bus_ops { struct mv88e6xxx_bus_ops {
......
/*
* Marvell 88E6xxx Switch PTP support
*
* Copyright (c) 2008 Marvell Semiconductor
*
* Copyright (c) 2017 National Instruments
* Erik Hons <erik.hons@ni.com>
* Brandon Streiff <brandon.streiff@ni.com>
* Dane Wagner <dane.wagner@ni.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*/
#include "chip.h"
#include "global2.h"
#include "ptp.h"
/* Raw timestamps are in units of 8-ns clock periods. */
#define CC_SHIFT 28
#define CC_MULT (8 << CC_SHIFT)
#define CC_MULT_NUM (1 << 9)
#define CC_MULT_DEM 15625ULL
#define TAI_EVENT_WORK_INTERVAL msecs_to_jiffies(100)
#define cc_to_chip(cc) container_of(cc, struct mv88e6xxx_chip, tstamp_cc)
#define ptp_to_chip(ptp) container_of(ptp, struct mv88e6xxx_chip, \
ptp_clock_info)
#define dw_overflow_to_chip(dw) container_of(dw, struct mv88e6xxx_chip, \
overflow_work)
static int mv88e6xxx_tai_read(struct mv88e6xxx_chip *chip, int addr,
u16 *data, int len)
{
if (!chip->info->ops->avb_ops->tai_read)
return -EOPNOTSUPP;
return chip->info->ops->avb_ops->tai_read(chip, addr, data, len);
}
static u64 mv88e6xxx_ptp_clock_read(const struct cyclecounter *cc)
{
struct mv88e6xxx_chip *chip = cc_to_chip(cc);
u16 phc_time[2];
int err;
err = mv88e6xxx_tai_read(chip, MV88E6XXX_TAI_TIME_LO, phc_time,
ARRAY_SIZE(phc_time));
if (err)
return 0;
else
return ((u32)phc_time[1] << 16) | phc_time[0];
}
static int mv88e6xxx_ptp_adjfine(struct ptp_clock_info *ptp, long scaled_ppm)
{
struct mv88e6xxx_chip *chip = ptp_to_chip(ptp);
int neg_adj = 0;
u32 diff, mult;
u64 adj;
if (scaled_ppm < 0) {
neg_adj = 1;
scaled_ppm = -scaled_ppm;
}
mult = CC_MULT;
adj = CC_MULT_NUM;
adj *= scaled_ppm;
diff = div_u64(adj, CC_MULT_DEM);
mutex_lock(&chip->reg_lock);
timecounter_read(&chip->tstamp_tc);
chip->tstamp_cc.mult = neg_adj ? mult - diff : mult + diff;
mutex_unlock(&chip->reg_lock);
return 0;
}
static int mv88e6xxx_ptp_adjtime(struct ptp_clock_info *ptp, s64 delta)
{
struct mv88e6xxx_chip *chip = ptp_to_chip(ptp);
mutex_lock(&chip->reg_lock);
timecounter_adjtime(&chip->tstamp_tc, delta);
mutex_unlock(&chip->reg_lock);
return 0;
}
static int mv88e6xxx_ptp_gettime(struct ptp_clock_info *ptp,
struct timespec64 *ts)
{
struct mv88e6xxx_chip *chip = ptp_to_chip(ptp);
u64 ns;
mutex_lock(&chip->reg_lock);
ns = timecounter_read(&chip->tstamp_tc);
mutex_unlock(&chip->reg_lock);
*ts = ns_to_timespec64(ns);
return 0;
}
static int mv88e6xxx_ptp_settime(struct ptp_clock_info *ptp,
const struct timespec64 *ts)
{
struct mv88e6xxx_chip *chip = ptp_to_chip(ptp);
u64 ns;
ns = timespec64_to_ns(ts);
mutex_lock(&chip->reg_lock);
timecounter_init(&chip->tstamp_tc, &chip->tstamp_cc, ns);
mutex_unlock(&chip->reg_lock);
return 0;
}
static int mv88e6xxx_ptp_enable(struct ptp_clock_info *ptp,
struct ptp_clock_request *rq, int on)
{
return -EOPNOTSUPP;
}
static int mv88e6xxx_ptp_verify(struct ptp_clock_info *ptp, unsigned int pin,
enum ptp_pin_function func, unsigned int chan)
{
return -EOPNOTSUPP;
}
/* With a 125MHz input clock, the 32-bit timestamp counter overflows in ~34.3
* seconds; this task forces periodic reads so that we don't miss any.
*/
#define MV88E6XXX_TAI_OVERFLOW_PERIOD (HZ * 16)
static void mv88e6xxx_ptp_overflow_check(struct work_struct *work)
{
struct delayed_work *dw = to_delayed_work(work);
struct mv88e6xxx_chip *chip = dw_overflow_to_chip(dw);
struct timespec64 ts;
mv88e6xxx_ptp_gettime(&chip->ptp_clock_info, &ts);
schedule_delayed_work(&chip->overflow_work,
MV88E6XXX_TAI_OVERFLOW_PERIOD);
}
int mv88e6xxx_ptp_setup(struct mv88e6xxx_chip *chip)
{
/* Set up the cycle counter */
memset(&chip->tstamp_cc, 0, sizeof(chip->tstamp_cc));
chip->tstamp_cc.read = mv88e6xxx_ptp_clock_read;
chip->tstamp_cc.mask = CYCLECOUNTER_MASK(32);
chip->tstamp_cc.mult = CC_MULT;
chip->tstamp_cc.shift = CC_SHIFT;
timecounter_init(&chip->tstamp_tc, &chip->tstamp_cc,
ktime_to_ns(ktime_get_real()));
INIT_DELAYED_WORK(&chip->overflow_work, mv88e6xxx_ptp_overflow_check);
chip->ptp_clock_info.owner = THIS_MODULE;
snprintf(chip->ptp_clock_info.name, sizeof(chip->ptp_clock_info.name),
dev_name(chip->dev));
chip->ptp_clock_info.max_adj = 1000000;
chip->ptp_clock_info.adjfine = mv88e6xxx_ptp_adjfine;
chip->ptp_clock_info.adjtime = mv88e6xxx_ptp_adjtime;
chip->ptp_clock_info.gettime64 = mv88e6xxx_ptp_gettime;
chip->ptp_clock_info.settime64 = mv88e6xxx_ptp_settime;
chip->ptp_clock_info.enable = mv88e6xxx_ptp_enable;
chip->ptp_clock_info.verify = mv88e6xxx_ptp_verify;
chip->ptp_clock = ptp_clock_register(&chip->ptp_clock_info, chip->dev);
if (IS_ERR(chip->ptp_clock))
return PTR_ERR(chip->ptp_clock);
schedule_delayed_work(&chip->overflow_work,
MV88E6XXX_TAI_OVERFLOW_PERIOD);
return 0;
}
void mv88e6xxx_ptp_free(struct mv88e6xxx_chip *chip)
{
if (chip->ptp_clock) {
cancel_delayed_work_sync(&chip->overflow_work);
ptp_clock_unregister(chip->ptp_clock);
chip->ptp_clock = NULL;
}
}
/*
* Marvell 88E6xxx Switch PTP support
*
* Copyright (c) 2008 Marvell Semiconductor
*
* Copyright (c) 2017 National Instruments
* Erik Hons <erik.hons@ni.com>
* Brandon Streiff <brandon.streiff@ni.com>
* Dane Wagner <dane.wagner@ni.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*/
#ifndef _MV88E6XXX_PTP_H
#define _MV88E6XXX_PTP_H
#include "chip.h"
/* Offset 0x00: TAI Global Config */
#define MV88E6XXX_TAI_CFG 0x00
/* Offset 0x01: Timestamp Clock Period (ps) */
#define MV88E6XXX_TAI_CLOCK_PERIOD 0x01
/* Offset 0x02/0x03: Trigger Generation Amount */
#define MV88E6XXX_TAI_TRIG_GEN_AMOUNT_LO 0x02
#define MV88E6XXX_TAI_TRIG_GEN_AMOUNT_HI 0x03
/* Offset 0x04: Clock Compensation */
#define MV88E6XXX_TAI_TRIG_CLOCK_COMP 0x04
/* Offset 0x05: Trigger Configuration */
#define MV88E6XXX_TAI_TRIG_CFG 0x05
/* Offset 0x06: Ingress Rate Limiter Clock Generation Amount */
#define MV88E6XXX_TAI_IRL_AMOUNT 0x06
/* Offset 0x07: Ingress Rate Limiter Compensation */
#define MV88E6XXX_TAI_IRL_COMP 0x07
/* Offset 0x08: Ingress Rate Limiter Compensation */
#define MV88E6XXX_TAI_IRL_COMP_PS 0x08
/* Offset 0x09: Event Status */
#define MV88E6XXX_TAI_EVENT_STATUS 0x09
/* Offset 0x0A/0x0B: Event Time */
#define MV88E6XXX_TAI_EVENT_TIME_LO 0x0a
#define MV88E6XXX_TAI_EVENT_TYPE_HI 0x0b
/* Offset 0x0E/0x0F: PTP Global Time */
#define MV88E6XXX_TAI_TIME_LO 0x0e
#define MV88E6XXX_TAI_TIME_HI 0x0f
/* Offset 0x10/0x11: Trig Generation Time */
#define MV88E6XXX_TAI_TRIG_TIME_LO 0x10
#define MV88E6XXX_TAI_TRIG_TIME_HI 0x11
/* Offset 0x12: Lock Status */
#define MV88E6XXX_TAI_LOCK_STATUS 0x12
#ifdef CONFIG_NET_DSA_MV88E6XXX_PTP
int mv88e6xxx_ptp_setup(struct mv88e6xxx_chip *chip);
void mv88e6xxx_ptp_free(struct mv88e6xxx_chip *chip);
#else /* !CONFIG_NET_DSA_MV88E6XXX_PTP */
static inline int mv88e6xxx_ptp_setup(struct mv88e6xxx_chip *chip)
{
return 0;
}
static void mv88e6xxx_ptp_free(struct mv88e6xxx_chip *chip)
{
}
#endif /* CONFIG_NET_DSA_MV88E6XXX_PTP */
#endif /* _MV88E6XXX_PTP_H */
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