Commit 25bf4df4 authored by David S. Miller's avatar David S. Miller

Merge branch 'ptp-ocp-next'

Jonathan Lemon says:

====================
ptp: ocp: TOD and monitoring updates

Add a series of patches for monitoring the status of the
driver and adjusting TOD handling, especially around leap seconds.

Add documentation for the new sysfs nodes.
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 01e2d157 4db07317
...@@ -63,6 +63,18 @@ Description: (RW) Contains the current synchronization source used by ...@@ -63,6 +63,18 @@ Description: (RW) Contains the current synchronization source used by
the PHC. May be changed by writing one of the listed the PHC. May be changed by writing one of the listed
values from the available_clock_sources attribute set. values from the available_clock_sources attribute set.
What: /sys/class/timecard/ocpN/clock_status_drift
Date: March 2022
Contact: Jonathan Lemon <jonathan.lemon@gmail.com>
Description: (RO) Contains the current drift value used by the firmware
for internal disciplining of the atomic clock.
What: /sys/class/timecard/ocpN/clock_status_offset
Date: March 2022
Contact: Jonathan Lemon <jonathan.lemon@gmail.com>
Description: (RO) Contains the current offset value used by the firmware
for internal disciplining of the atomic clock.
What: /sys/class/timecard/ocpN/gnss_sync What: /sys/class/timecard/ocpN/gnss_sync
Date: September 2021 Date: September 2021
Contact: Jonathan Lemon <jonathan.lemon@gmail.com> Contact: Jonathan Lemon <jonathan.lemon@gmail.com>
...@@ -126,6 +138,16 @@ Description: (RW) These attributes specify the direction of the signal ...@@ -126,6 +138,16 @@ Description: (RW) These attributes specify the direction of the signal
The 10Mhz reference clock input is currently only valid The 10Mhz reference clock input is currently only valid
on SMA1 and may not be combined with other destination sinks. on SMA1 and may not be combined with other destination sinks.
What: /sys/class/timecard/ocpN/tod_correction
Date: March 2022
Contact: Jonathan Lemon <jonathan.lemon@gmail.com>
Description: (RW) The incoming GNSS signal is in UTC time, and the NMEA
format messages do not provide a TAI offset. This sets the
correction value for the incoming time.
If UBX_LS is enabled, this should be 0, and the offset is
taken from the UBX-NAV-TIMELS message.
What: /sys/class/timecard/ocpN/ts_window_adjust What: /sys/class/timecard/ocpN/ts_window_adjust
Date: September 2021 Date: September 2021
Contact: Jonathan Lemon <jonathan.lemon@gmail.com> Contact: Jonathan Lemon <jonathan.lemon@gmail.com>
......
...@@ -52,6 +52,8 @@ struct ocp_reg { ...@@ -52,6 +52,8 @@ struct ocp_reg {
u32 servo_offset_i; u32 servo_offset_i;
u32 servo_drift_p; u32 servo_drift_p;
u32 servo_drift_i; u32 servo_drift_i;
u32 status_offset;
u32 status_drift;
}; };
#define OCP_CTRL_ENABLE BIT(0) #define OCP_CTRL_ENABLE BIT(0)
...@@ -90,6 +92,7 @@ struct tod_reg { ...@@ -90,6 +92,7 @@ struct tod_reg {
#define TOD_STATUS_UTC_MASK 0xff #define TOD_STATUS_UTC_MASK 0xff
#define TOD_STATUS_UTC_VALID BIT(8) #define TOD_STATUS_UTC_VALID BIT(8)
#define TOD_STATUS_LEAP_ANNOUNCE BIT(12)
#define TOD_STATUS_LEAP_VALID BIT(16) #define TOD_STATUS_LEAP_VALID BIT(16)
struct ts_reg { struct ts_reg {
...@@ -738,12 +741,31 @@ __ptp_ocp_clear_drift_locked(struct ptp_ocp *bp) ...@@ -738,12 +741,31 @@ __ptp_ocp_clear_drift_locked(struct ptp_ocp *bp)
iowrite32(select >> 16, &bp->reg->select); iowrite32(select >> 16, &bp->reg->select);
} }
static void
ptp_ocp_utc_distribute(struct ptp_ocp *bp, u32 val)
{
unsigned long flags;
spin_lock_irqsave(&bp->lock, flags);
bp->utc_tai_offset = val;
if (bp->irig_out)
iowrite32(val, &bp->irig_out->adj_sec);
if (bp->dcf_out)
iowrite32(val, &bp->dcf_out->adj_sec);
if (bp->nmea_out)
iowrite32(val, &bp->nmea_out->adj_sec);
spin_unlock_irqrestore(&bp->lock, flags);
}
static void static void
ptp_ocp_watchdog(struct timer_list *t) ptp_ocp_watchdog(struct timer_list *t)
{ {
struct ptp_ocp *bp = from_timer(bp, t, watchdog); struct ptp_ocp *bp = from_timer(bp, t, watchdog);
unsigned long flags; unsigned long flags;
u32 status; u32 status, utc_offset;
status = ioread32(&bp->pps_to_clk->status); status = ioread32(&bp->pps_to_clk->status);
...@@ -760,6 +782,17 @@ ptp_ocp_watchdog(struct timer_list *t) ...@@ -760,6 +782,17 @@ ptp_ocp_watchdog(struct timer_list *t)
bp->gnss_lost = 0; bp->gnss_lost = 0;
} }
/* if GNSS provides correct data we can rely on
* it to get leap second information
*/
if (bp->tod) {
status = ioread32(&bp->tod->utc_status);
utc_offset = status & TOD_STATUS_UTC_MASK;
if (status & TOD_STATUS_UTC_VALID &&
utc_offset != bp->utc_tai_offset)
ptp_ocp_utc_distribute(bp, utc_offset);
}
mod_timer(&bp->watchdog, jiffies + HZ); mod_timer(&bp->watchdog, jiffies + HZ);
} }
...@@ -828,25 +861,6 @@ ptp_ocp_init_clock(struct ptp_ocp *bp) ...@@ -828,25 +861,6 @@ ptp_ocp_init_clock(struct ptp_ocp *bp)
return 0; return 0;
} }
static void
ptp_ocp_utc_distribute(struct ptp_ocp *bp, u32 val)
{
unsigned long flags;
spin_lock_irqsave(&bp->lock, flags);
bp->utc_tai_offset = val;
if (bp->irig_out)
iowrite32(val, &bp->irig_out->adj_sec);
if (bp->dcf_out)
iowrite32(val, &bp->dcf_out->adj_sec);
if (bp->nmea_out)
iowrite32(val, &bp->nmea_out->adj_sec);
spin_unlock_irqrestore(&bp->lock, flags);
}
static void static void
ptp_ocp_tod_init(struct ptp_ocp *bp) ptp_ocp_tod_init(struct ptp_ocp *bp)
{ {
...@@ -862,45 +876,26 @@ ptp_ocp_tod_init(struct ptp_ocp *bp) ...@@ -862,45 +876,26 @@ ptp_ocp_tod_init(struct ptp_ocp *bp)
ptp_ocp_utc_distribute(bp, reg & TOD_STATUS_UTC_MASK); ptp_ocp_utc_distribute(bp, reg & TOD_STATUS_UTC_MASK);
} }
static void static const char *
ptp_ocp_tod_info(struct ptp_ocp *bp) ptp_ocp_tod_proto_name(const int idx)
{ {
static const char * const proto_name[] = { static const char * const proto_name[] = {
"NMEA", "NMEA_ZDA", "NMEA_RMC", "NMEA_none", "NMEA", "NMEA_ZDA", "NMEA_RMC", "NMEA_none",
"UBX", "UBX_UTC", "UBX_LS", "UBX_none" "UBX", "UBX_UTC", "UBX_LS", "UBX_none"
}; };
return proto_name[idx];
}
static const char *
ptp_ocp_tod_gnss_name(int idx)
{
static const char * const gnss_name[] = { static const char * const gnss_name[] = {
"ALL", "COMBINED", "GPS", "GLONASS", "GALILEO", "BEIDOU", "ALL", "COMBINED", "GPS", "GLONASS", "GALILEO", "BEIDOU",
"Unknown"
}; };
u32 version, ctrl, reg; if (idx > ARRAY_SIZE(gnss_name))
int idx; idx = ARRAY_SIZE(gnss_name) - 1;
return gnss_name[idx];
version = ioread32(&bp->tod->version);
dev_info(&bp->pdev->dev, "TOD Version %d.%d.%d\n",
version >> 24, (version >> 16) & 0xff, version & 0xffff);
ctrl = ioread32(&bp->tod->ctrl);
idx = ctrl & TOD_CTRL_PROTOCOL ? 4 : 0;
idx += (ctrl >> 16) & 3;
dev_info(&bp->pdev->dev, "control: %x\n", ctrl);
dev_info(&bp->pdev->dev, "TOD Protocol %s %s\n", proto_name[idx],
ctrl & TOD_CTRL_ENABLE ? "enabled" : "");
idx = (ctrl >> TOD_CTRL_GNSS_SHIFT) & TOD_CTRL_GNSS_MASK;
if (idx < ARRAY_SIZE(gnss_name))
dev_info(&bp->pdev->dev, "GNSS %s\n", gnss_name[idx]);
reg = ioread32(&bp->tod->status);
dev_info(&bp->pdev->dev, "status: %x\n", reg);
reg = ioread32(&bp->tod->adj_sec);
dev_info(&bp->pdev->dev, "correction: %d\n", reg);
reg = ioread32(&bp->tod->utc_status);
dev_info(&bp->pdev->dev, "utc_status: %x\n", reg);
dev_info(&bp->pdev->dev, "utc_offset: %d valid:%d leap_valid:%d\n",
reg & TOD_STATUS_UTC_MASK, reg & TOD_STATUS_UTC_VALID ? 1 : 0,
reg & TOD_STATUS_LEAP_VALID ? 1 : 0);
} }
static int static int
...@@ -1953,6 +1948,76 @@ available_clock_sources_show(struct device *dev, ...@@ -1953,6 +1948,76 @@ available_clock_sources_show(struct device *dev,
} }
static DEVICE_ATTR_RO(available_clock_sources); static DEVICE_ATTR_RO(available_clock_sources);
static ssize_t
clock_status_drift_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct ptp_ocp *bp = dev_get_drvdata(dev);
u32 val;
int res;
val = ioread32(&bp->reg->status_drift);
res = (val & ~INT_MAX) ? -1 : 1;
res *= (val & INT_MAX);
return sysfs_emit(buf, "%d\n", res);
}
static DEVICE_ATTR_RO(clock_status_drift);
static ssize_t
clock_status_offset_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct ptp_ocp *bp = dev_get_drvdata(dev);
u32 val;
int res;
val = ioread32(&bp->reg->status_offset);
res = (val & ~INT_MAX) ? -1 : 1;
res *= (val & INT_MAX);
return sysfs_emit(buf, "%d\n", res);
}
static DEVICE_ATTR_RO(clock_status_offset);
static ssize_t
tod_correction_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct ptp_ocp *bp = dev_get_drvdata(dev);
u32 val;
int res;
val = ioread32(&bp->tod->adj_sec);
res = (val & ~INT_MAX) ? -1 : 1;
res *= (val & INT_MAX);
return sysfs_emit(buf, "%d\n", res);
}
static ssize_t
tod_correction_store(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
struct ptp_ocp *bp = dev_get_drvdata(dev);
unsigned long flags;
int err, res;
u32 val = 0;
err = kstrtos32(buf, 0, &res);
if (err)
return err;
if (res < 0) {
res *= -1;
val |= BIT(31);
}
val |= res;
spin_lock_irqsave(&bp->lock, flags);
iowrite32(val, &bp->tod->adj_sec);
spin_unlock_irqrestore(&bp->lock, flags);
return count;
}
static DEVICE_ATTR_RW(tod_correction);
static struct attribute *timecard_attrs[] = { static struct attribute *timecard_attrs[] = {
&dev_attr_serialnum.attr, &dev_attr_serialnum.attr,
&dev_attr_gnss_sync.attr, &dev_attr_gnss_sync.attr,
...@@ -1964,9 +2029,12 @@ static struct attribute *timecard_attrs[] = { ...@@ -1964,9 +2029,12 @@ static struct attribute *timecard_attrs[] = {
&dev_attr_sma4.attr, &dev_attr_sma4.attr,
&dev_attr_available_sma_inputs.attr, &dev_attr_available_sma_inputs.attr,
&dev_attr_available_sma_outputs.attr, &dev_attr_available_sma_outputs.attr,
&dev_attr_clock_status_drift.attr,
&dev_attr_clock_status_offset.attr,
&dev_attr_irig_b_mode.attr, &dev_attr_irig_b_mode.attr,
&dev_attr_utc_tai_offset.attr, &dev_attr_utc_tai_offset.attr,
&dev_attr_ts_window_adjust.attr, &dev_attr_ts_window_adjust.attr,
&dev_attr_tod_correction.attr,
NULL, NULL,
}; };
ATTRIBUTE_GROUPS(timecard); ATTRIBUTE_GROUPS(timecard);
...@@ -2179,6 +2247,57 @@ ptp_ocp_summary_show(struct seq_file *s, void *data) ...@@ -2179,6 +2247,57 @@ ptp_ocp_summary_show(struct seq_file *s, void *data)
} }
DEFINE_SHOW_ATTRIBUTE(ptp_ocp_summary); DEFINE_SHOW_ATTRIBUTE(ptp_ocp_summary);
static int
ptp_ocp_tod_status_show(struct seq_file *s, void *data)
{
struct device *dev = s->private;
struct ptp_ocp *bp;
u32 val;
int idx;
bp = dev_get_drvdata(dev);
val = ioread32(&bp->tod->ctrl);
if (!(val & TOD_CTRL_ENABLE)) {
seq_printf(s, "TOD Slave disabled\n");
return 0;
}
seq_printf(s, "TOD Slave enabled, Control Register 0x%08X\n", val);
idx = val & TOD_CTRL_PROTOCOL ? 4 : 0;
idx += (val >> 16) & 3;
seq_printf(s, "Protocol %s\n", ptp_ocp_tod_proto_name(idx));
idx = (val >> TOD_CTRL_GNSS_SHIFT) & TOD_CTRL_GNSS_MASK;
seq_printf(s, "GNSS %s\n", ptp_ocp_tod_gnss_name(idx));
val = ioread32(&bp->tod->version);
seq_printf(s, "TOD Version %d.%d.%d\n",
val >> 24, (val >> 16) & 0xff, val & 0xffff);
val = ioread32(&bp->tod->status);
seq_printf(s, "Status register: 0x%08X\n", val);
val = ioread32(&bp->tod->adj_sec);
idx = (val & ~INT_MAX) ? -1 : 1;
idx *= (val & INT_MAX);
seq_printf(s, "Correction seconds: %d\n", idx);
val = ioread32(&bp->tod->utc_status);
seq_printf(s, "UTC status register: 0x%08X\n", val);
seq_printf(s, "UTC offset: %d valid:%d\n",
val & TOD_STATUS_UTC_MASK, val & TOD_STATUS_UTC_VALID ? 1 : 0);
seq_printf(s, "Leap second info valid:%d, Leap second announce %d\n",
val & TOD_STATUS_LEAP_VALID ? 1 : 0,
val & TOD_STATUS_LEAP_ANNOUNCE ? 1 : 0);
val = ioread32(&bp->tod->leap);
seq_printf(s, "Time to next leap second (in sec): %d\n", (s32) val);
return 0;
}
DEFINE_SHOW_ATTRIBUTE(ptp_ocp_tod_status);
static struct dentry *ptp_ocp_debugfs_root; static struct dentry *ptp_ocp_debugfs_root;
static void static void
...@@ -2190,6 +2309,9 @@ ptp_ocp_debugfs_add_device(struct ptp_ocp *bp) ...@@ -2190,6 +2309,9 @@ ptp_ocp_debugfs_add_device(struct ptp_ocp *bp)
bp->debug_root = d; bp->debug_root = d;
debugfs_create_file("summary", 0444, bp->debug_root, debugfs_create_file("summary", 0444, bp->debug_root,
&bp->dev, &ptp_ocp_summary_fops); &bp->dev, &ptp_ocp_summary_fops);
if (bp->tod)
debugfs_create_file("tod_status", 0444, bp->debug_root,
&bp->dev, &ptp_ocp_tod_status_fops);
} }
static void static void
...@@ -2368,8 +2490,6 @@ ptp_ocp_info(struct ptp_ocp *bp) ...@@ -2368,8 +2490,6 @@ ptp_ocp_info(struct ptp_ocp *bp)
u32 reg; u32 reg;
ptp_ocp_phc_info(bp); ptp_ocp_phc_info(bp);
if (bp->tod)
ptp_ocp_tod_info(bp);
if (bp->image) { if (bp->image) {
u32 ver = ioread32(&bp->image->version); u32 ver = ioread32(&bp->image->version);
......
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