Commit 923990f6 authored by David S. Miller's avatar David S. Miller

Merge branch 'ptp-ocp-timecard-v13-fw'

Jonathan Lemon says:

====================
timecard updates for v13 firmware

This update mainly deals with features for the TimeCard v13 firmware.

The signals provided from the external SMA connectors can be steered
to different locations, and the generated SMA signals can be chosen.

Future timecard revisions will allow selectable I/O on any of the
SMA connectors, so name the attributes appropriately, and set up
the ABI in preparation for the new features.

The update also adds support for IRIG-B and DCF formats, as well
as NMEA output.

A ts_window_adjust tunable is also provided to fine-tune the
PHC:SYS time mapping.
--
v1: Earlier reviewed series was for v10 firmware, this is expanded to
    include the v13 features.
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents c6887214 d7050a2b
What: /sys/class/timecard/
Date: September 2021
Contact: Jonathan Lemon <jonathan.lemon@gmail.com>
Description: This directory contains files and directories
providing a standardized interface to the ancillary
features of the OpenCompute timecard.
What: /sys/class/timecard/ocpN/
Date: September 2021
Contact: Jonathan Lemon <jonathan.lemon@gmail.com>
Description: This directory contains the attributes of the Nth timecard
registered.
What: /sys/class/timecard/ocpN/available_clock_sources
Date: September 2021
Contact: Jonathan Lemon <jonathan.lemon@gmail.com>
Description: (RO) The list of available time sources that the PHC
uses for clock adjustments.
==== =================================================
NONE no adjustments
PPS adjustments come from the PPS1 selector (default)
TOD adjustments from the GNSS/TOD module
IRIG adjustments from external IRIG-B signal
DCF adjustments from external DCF signal
==== =================================================
What: /sys/class/timecard/ocpN/available_sma_inputs
Date: September 2021
Contact: Jonathan Lemon <jonathan.lemon@gmail.com>
Description: (RO) Set of available destinations (sinks) for a SMA
input signal.
===== ================================================
10Mhz signal is used as the 10Mhz reference clock
PPS1 signal is sent to the PPS1 selector
PPS2 signal is sent to the PPS2 selector
TS1 signal is sent to timestamper 1
TS2 signal is sent to timestamper 2
IRIG signal is sent to the IRIG-B module
DCF signal is sent to the DCF module
===== ================================================
What: /sys/class/timecard/ocpN/available_sma_outputs
Date: May 2021
Contact: Jonathan Lemon <jonathan.lemon@gmail.com>
Description: (RO) Set of available sources for a SMA output signal.
===== ================================================
10Mhz output is from the 10Mhz reference clock
PHC output PPS is from the PHC clock
MAC output PPS is from the Miniature Atomic Clock
GNSS output PPS is from the GNSS module
GNSS2 output PPS is from the second GNSS module
IRIG output is from the PHC, in IRIG-B format
DCF output is from the PHC, in DCF format
===== ================================================
What: /sys/class/timecard/ocpN/clock_source
Date: September 2021
Contact: Jonathan Lemon <jonathan.lemon@gmail.com>
Description: (RW) Contains the current synchronization source used by
the PHC. May be changed by writing one of the listed
values from the available_clock_sources attribute set.
What: /sys/class/timecard/ocpN/gnss_sync
Date: September 2021
Contact: Jonathan Lemon <jonathan.lemon@gmail.com>
Description: (RO) Indicates whether a valid GNSS signal is received,
or when the signal was lost.
What: /sys/class/timecard/ocpN/i2c
Date: September 2021
Contact: Jonathan Lemon <jonathan.lemon@gmail.com>
Description: This optional attribute links to the associated i2c device.
What: /sys/class/timecard/ocpN/irig_b_mode
Date: September 2021
Contact: Jonathan Lemon <jonathan.lemon@gmail.com>
Description: (RW) An integer from 0-7 indicating the timecode format
of the IRIG-B output signal: B00<n>
What: /sys/class/timecard/ocpN/pps
Date: September 2021
Contact: Jonathan Lemon <jonathan.lemon@gmail.com>
Description: This optional attribute links to the associated PPS device.
What: /sys/class/timecard/ocpN/ptp
Date: September 2021
Contact: Jonathan Lemon <jonathan.lemon@gmail.com>
Description: This attribute links to the associated PTP device.
What: /sys/class/timecard/ocpN/serialnum
Date: September 2021
Contact: Jonathan Lemon <jonathan.lemon@gmail.com>
Description: (RO) Provides the serial number of the timecard.
What: /sys/class/timecard/ocpN/sma1
What: /sys/class/timecard/ocpN/sma2
What: /sys/class/timecard/ocpN/sma3
What: /sys/class/timecard/ocpN/sma4
Date: September 2021
Contact: Jonathan Lemon <jonathan.lemon@gmail.com>
Description: (RW) These attributes specify the direction of the signal
on the associated SMA connectors, and also the signal sink
or source.
The display format of the attribute is a space separated
list of signals, prefixed by the input/output direction.
The signal direction may be changed (if supported) by
prefixing the signal list with either "in:" or "out:".
If neither prefix is present, then the direction is unchanged.
The output signal may be changed by writing one of the listed
values from the available_sma_outputs attribute set.
The input destinations may be changed by writing multiple
values from the available_sma_inputs attribute set,
separated by spaces. If there are duplicated input
destinations between connectors, the lowest numbered SMA
connector is given priority.
Note that not all input combinations may make sense.
The 10Mhz reference clock input is currently only valid
on SMA1 and may not be combined with other destination sinks.
What: /sys/class/timecard/ocpN/ts_window_adjust
Date: September 2021
Contact: Jonathan Lemon <jonathan.lemon@gmail.com>
Description: (RW) When retrieving the PHC with the PTP SYS_OFFSET_EXTENDED
ioctl, a system timestamp is made before and after the PHC
time is retrieved. The midpoint between the two system
timestamps is usually taken to be the SYS time associated
with the PHC time. This estimate may be wrong, as it depends
on PCI latencies, and when the PHC time was latched
The attribute value reduces the end timestamp by the given
number of nanoseconds, so the computed midpoint matches the
retrieved PHC time.
The initial value is set based on measured PCI latency and
the estimated point where the FPGA latches the PHC time. This
value may be changed by writing an unsigned integer.
What: /sys/class/timecard/ocpN/ttyGNSS
What: /sys/class/timecard/ocpN/ttyGNSS2
Date: September 2021
Contact: Jonathan Lemon <jonathan.lemon@gmail.com>
Description: These optional attributes link to the TTY serial ports
associated with the GNSS devices.
What: /sys/class/timecard/ocpN/ttyMAC
Date: September 2021
Contact: Jonathan Lemon <jonathan.lemon@gmail.com>
Description: This optional attribute links to the TTY serial port
associated with the Miniature Atomic Clock.
What: /sys/class/timecard/ocpN/ttyNMEA
Date: September 2021
Contact: Jonathan Lemon <jonathan.lemon@gmail.com>
Description: This optional attribute links to the TTY serial port
which outputs the PHC time in NMEA ZDA format.
What: /sys/class/timecard/ocpN/utc_tai_offset
Date: September 2021
Contact: Jonathan Lemon <jonathan.lemon@gmail.com>
Description: (RW) The DCF and IRIG output signals are in UTC, while the
TimeCard operates on TAI. This attribute allows setting the
offset in seconds, which is added to the TAI timebase for
these formats.
The offset may be changed by writing an unsigned integer.
...@@ -4,6 +4,7 @@ ...@@ -4,6 +4,7 @@
#include <linux/err.h> #include <linux/err.h>
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/debugfs.h>
#include <linux/init.h> #include <linux/init.h>
#include <linux/pci.h> #include <linux/pci.h>
#include <linux/serial_8250.h> #include <linux/serial_8250.h>
...@@ -72,7 +73,7 @@ struct tod_reg { ...@@ -72,7 +73,7 @@ struct tod_reg {
u32 status; u32 status;
u32 uart_polarity; u32 uart_polarity;
u32 version; u32 version;
u32 correction_sec; u32 adj_sec;
u32 __pad0[3]; u32 __pad0[3];
u32 uart_baud; u32 uart_baud;
u32 __pad1[3]; u32 __pad1[3];
...@@ -124,6 +125,55 @@ struct img_reg { ...@@ -124,6 +125,55 @@ struct img_reg {
u32 version; u32 version;
}; };
struct gpio_reg {
u32 gpio1;
u32 __pad0;
u32 gpio2;
u32 __pad1;
};
struct irig_master_reg {
u32 ctrl;
u32 status;
u32 __pad0;
u32 version;
u32 adj_sec;
u32 mode_ctrl;
};
#define IRIG_M_CTRL_ENABLE BIT(0)
struct irig_slave_reg {
u32 ctrl;
u32 status;
u32 __pad0;
u32 version;
u32 adj_sec;
u32 mode_ctrl;
};
#define IRIG_S_CTRL_ENABLE BIT(0)
struct dcf_master_reg {
u32 ctrl;
u32 status;
u32 __pad0;
u32 version;
u32 adj_sec;
};
#define DCF_M_CTRL_ENABLE BIT(0)
struct dcf_slave_reg {
u32 ctrl;
u32 status;
u32 __pad0;
u32 version;
u32 adj_sec;
};
#define DCF_S_CTRL_ENABLE BIT(0)
struct ptp_ocp_flash_info { struct ptp_ocp_flash_info {
const char *name; const char *name;
int pci_offset; int pci_offset;
...@@ -131,11 +181,17 @@ struct ptp_ocp_flash_info { ...@@ -131,11 +181,17 @@ struct ptp_ocp_flash_info {
void *data; void *data;
}; };
struct ptp_ocp_ext_info { struct ptp_ocp_i2c_info {
const char *name; const char *name;
unsigned long fixed_rate;
size_t data_size;
void *data;
};
struct ptp_ocp_ext_info {
int index; int index;
irqreturn_t (*irq_fcn)(int irq, void *priv); irqreturn_t (*irq_fcn)(int irq, void *priv);
int (*enable)(void *priv, bool enable); int (*enable)(void *priv, u32 req, bool enable);
}; };
struct ptp_ocp_ext_src { struct ptp_ocp_ext_src {
...@@ -153,9 +209,17 @@ struct ptp_ocp { ...@@ -153,9 +209,17 @@ struct ptp_ocp {
struct tod_reg __iomem *tod; struct tod_reg __iomem *tod;
struct pps_reg __iomem *pps_to_ext; struct pps_reg __iomem *pps_to_ext;
struct pps_reg __iomem *pps_to_clk; struct pps_reg __iomem *pps_to_clk;
struct gpio_reg __iomem *pps_select;
struct gpio_reg __iomem *sma;
struct irig_master_reg __iomem *irig_out;
struct irig_slave_reg __iomem *irig_in;
struct dcf_master_reg __iomem *dcf_out;
struct dcf_slave_reg __iomem *dcf_in;
struct tod_reg __iomem *nmea_out;
struct ptp_ocp_ext_src *pps; struct ptp_ocp_ext_src *pps;
struct ptp_ocp_ext_src *ts0; struct ptp_ocp_ext_src *ts0;
struct ptp_ocp_ext_src *ts1; struct ptp_ocp_ext_src *ts1;
struct ptp_ocp_ext_src *ts2;
struct img_reg __iomem *image; struct img_reg __iomem *image;
struct ptp_clock *ptp; struct ptp_clock *ptp;
struct ptp_clock_info ptp_info; struct ptp_clock_info ptp_info;
...@@ -163,16 +227,25 @@ struct ptp_ocp { ...@@ -163,16 +227,25 @@ struct ptp_ocp {
struct platform_device *spi_flash; struct platform_device *spi_flash;
struct clk_hw *i2c_clk; struct clk_hw *i2c_clk;
struct timer_list watchdog; struct timer_list watchdog;
struct dentry *debug_root;
time64_t gnss_lost; time64_t gnss_lost;
int id; int id;
int n_irqs; int n_irqs;
int gnss_port; int gnss_port;
int gnss2_port;
int mac_port; /* miniature atomic clock */ int mac_port; /* miniature atomic clock */
int nmea_port;
u8 serial[6]; u8 serial[6];
int flash_start;
bool has_serial; bool has_serial;
u32 pps_req_map;
int flash_start;
u32 utc_tai_offset;
u32 ts_window_adjust;
}; };
#define OCP_REQ_TIMESTAMP BIT(0)
#define OCP_REQ_PPS BIT(1)
struct ocp_resource { struct ocp_resource {
unsigned long offset; unsigned long offset;
int size; int size;
...@@ -180,6 +253,7 @@ struct ocp_resource { ...@@ -180,6 +253,7 @@ struct ocp_resource {
int (*setup)(struct ptp_ocp *bp, struct ocp_resource *r); int (*setup)(struct ptp_ocp *bp, struct ocp_resource *r);
void *extra; void *extra;
unsigned long bp_offset; unsigned long bp_offset;
const char * const name;
}; };
static int ptp_ocp_register_mem(struct ptp_ocp *bp, struct ocp_resource *r); static int ptp_ocp_register_mem(struct ptp_ocp *bp, struct ocp_resource *r);
...@@ -189,7 +263,7 @@ static int ptp_ocp_register_serial(struct ptp_ocp *bp, struct ocp_resource *r); ...@@ -189,7 +263,7 @@ static int ptp_ocp_register_serial(struct ptp_ocp *bp, struct ocp_resource *r);
static int ptp_ocp_register_ext(struct ptp_ocp *bp, struct ocp_resource *r); static int ptp_ocp_register_ext(struct ptp_ocp *bp, struct ocp_resource *r);
static int ptp_ocp_fb_board_init(struct ptp_ocp *bp, struct ocp_resource *r); static int ptp_ocp_fb_board_init(struct ptp_ocp *bp, struct ocp_resource *r);
static irqreturn_t ptp_ocp_ts_irq(int irq, void *priv); static irqreturn_t ptp_ocp_ts_irq(int irq, void *priv);
static int ptp_ocp_ts_enable(void *priv, bool enable); static int ptp_ocp_ts_enable(void *priv, u32 req, bool enable);
#define bp_assign_entry(bp, res, val) ({ \ #define bp_assign_entry(bp, res, val) ({ \
uintptr_t addr = (uintptr_t)(bp) + (res)->bp_offset; \ uintptr_t addr = (uintptr_t)(bp) + (res)->bp_offset; \
...@@ -197,7 +271,7 @@ static int ptp_ocp_ts_enable(void *priv, bool enable); ...@@ -197,7 +271,7 @@ static int ptp_ocp_ts_enable(void *priv, bool enable);
}) })
#define OCP_RES_LOCATION(member) \ #define OCP_RES_LOCATION(member) \
.bp_offset = offsetof(struct ptp_ocp, member) .name = #member, .bp_offset = offsetof(struct ptp_ocp, member)
#define OCP_MEM_RESOURCE(member) \ #define OCP_MEM_RESOURCE(member) \
OCP_RES_LOCATION(member), .setup = ptp_ocp_register_mem OCP_RES_LOCATION(member), .setup = ptp_ocp_register_mem
...@@ -215,16 +289,17 @@ static int ptp_ocp_ts_enable(void *priv, bool enable); ...@@ -215,16 +289,17 @@ static int ptp_ocp_ts_enable(void *priv, bool enable);
OCP_RES_LOCATION(member), .setup = ptp_ocp_register_ext OCP_RES_LOCATION(member), .setup = ptp_ocp_register_ext
/* This is the MSI vector mapping used. /* This is the MSI vector mapping used.
* 0: N/C * 0: TS3 (and PPS)
* 1: TS0 * 1: TS0
* 2: TS1 * 2: TS1
* 3: GPS * 3: GNSS
* 4: GPS2 (n/c) * 4: GNSS2
* 5: MAC * 5: MAC
* 6: SPI IMU (inertial measurement unit) * 6: TS2
* 7: I2C oscillator * 7: I2C controller
* 8: HWICAP * 8: HWICAP (notused)
* 9: SPI Flash * 9: SPI Flash
* 10: NMEA
*/ */
static struct ocp_resource ocp_fb_resource[] = { static struct ocp_resource ocp_fb_resource[] = {
...@@ -236,7 +311,7 @@ static struct ocp_resource ocp_fb_resource[] = { ...@@ -236,7 +311,7 @@ static struct ocp_resource ocp_fb_resource[] = {
OCP_EXT_RESOURCE(ts0), OCP_EXT_RESOURCE(ts0),
.offset = 0x01010000, .size = 0x10000, .irq_vec = 1, .offset = 0x01010000, .size = 0x10000, .irq_vec = 1,
.extra = &(struct ptp_ocp_ext_info) { .extra = &(struct ptp_ocp_ext_info) {
.name = "ts0", .index = 0, .index = 0,
.irq_fcn = ptp_ocp_ts_irq, .irq_fcn = ptp_ocp_ts_irq,
.enable = ptp_ocp_ts_enable, .enable = ptp_ocp_ts_enable,
}, },
...@@ -245,7 +320,25 @@ static struct ocp_resource ocp_fb_resource[] = { ...@@ -245,7 +320,25 @@ static struct ocp_resource ocp_fb_resource[] = {
OCP_EXT_RESOURCE(ts1), OCP_EXT_RESOURCE(ts1),
.offset = 0x01020000, .size = 0x10000, .irq_vec = 2, .offset = 0x01020000, .size = 0x10000, .irq_vec = 2,
.extra = &(struct ptp_ocp_ext_info) { .extra = &(struct ptp_ocp_ext_info) {
.name = "ts1", .index = 1, .index = 1,
.irq_fcn = ptp_ocp_ts_irq,
.enable = ptp_ocp_ts_enable,
},
},
{
OCP_EXT_RESOURCE(ts2),
.offset = 0x01060000, .size = 0x10000, .irq_vec = 6,
.extra = &(struct ptp_ocp_ext_info) {
.index = 2,
.irq_fcn = ptp_ocp_ts_irq,
.enable = ptp_ocp_ts_enable,
},
},
{
OCP_EXT_RESOURCE(pps),
.offset = 0x010C0000, .size = 0x10000, .irq_vec = 0,
.extra = &(struct ptp_ocp_ext_info) {
.index = 3,
.irq_fcn = ptp_ocp_ts_irq, .irq_fcn = ptp_ocp_ts_irq,
.enable = ptp_ocp_ts_enable, .enable = ptp_ocp_ts_enable,
}, },
...@@ -262,22 +355,62 @@ static struct ocp_resource ocp_fb_resource[] = { ...@@ -262,22 +355,62 @@ static struct ocp_resource ocp_fb_resource[] = {
OCP_MEM_RESOURCE(tod), OCP_MEM_RESOURCE(tod),
.offset = 0x01050000, .size = 0x10000, .offset = 0x01050000, .size = 0x10000,
}, },
{
OCP_MEM_RESOURCE(irig_in),
.offset = 0x01070000, .size = 0x10000,
},
{
OCP_MEM_RESOURCE(irig_out),
.offset = 0x01080000, .size = 0x10000,
},
{
OCP_MEM_RESOURCE(dcf_in),
.offset = 0x01090000, .size = 0x10000,
},
{
OCP_MEM_RESOURCE(dcf_out),
.offset = 0x010A0000, .size = 0x10000,
},
{
OCP_MEM_RESOURCE(nmea_out),
.offset = 0x010B0000, .size = 0x10000,
},
{ {
OCP_MEM_RESOURCE(image), OCP_MEM_RESOURCE(image),
.offset = 0x00020000, .size = 0x1000, .offset = 0x00020000, .size = 0x1000,
}, },
{
OCP_MEM_RESOURCE(pps_select),
.offset = 0x00130000, .size = 0x1000,
},
{
OCP_MEM_RESOURCE(sma),
.offset = 0x00140000, .size = 0x1000,
},
{ {
OCP_I2C_RESOURCE(i2c_ctrl), OCP_I2C_RESOURCE(i2c_ctrl),
.offset = 0x00150000, .size = 0x10000, .irq_vec = 7, .offset = 0x00150000, .size = 0x10000, .irq_vec = 7,
.extra = &(struct ptp_ocp_i2c_info) {
.name = "xiic-i2c",
.fixed_rate = 50000000,
},
}, },
{ {
OCP_SERIAL_RESOURCE(gnss_port), OCP_SERIAL_RESOURCE(gnss_port),
.offset = 0x00160000 + 0x1000, .irq_vec = 3, .offset = 0x00160000 + 0x1000, .irq_vec = 3,
}, },
{
OCP_SERIAL_RESOURCE(gnss2_port),
.offset = 0x00170000 + 0x1000, .irq_vec = 4,
},
{ {
OCP_SERIAL_RESOURCE(mac_port), OCP_SERIAL_RESOURCE(mac_port),
.offset = 0x00180000 + 0x1000, .irq_vec = 5, .offset = 0x00180000 + 0x1000, .irq_vec = 5,
}, },
{
OCP_SERIAL_RESOURCE(nmea_port),
.offset = 0x00190000 + 0x1000, .irq_vec = 10,
},
{ {
OCP_SPI_RESOURCE(spi_flash), OCP_SPI_RESOURCE(spi_flash),
.offset = 0x00310000, .size = 0x10000, .irq_vec = 9, .offset = 0x00310000, .size = 0x10000, .irq_vec = 9,
...@@ -309,10 +442,12 @@ MODULE_DEVICE_TABLE(pci, ptp_ocp_pcidev_id); ...@@ -309,10 +442,12 @@ MODULE_DEVICE_TABLE(pci, ptp_ocp_pcidev_id);
static DEFINE_MUTEX(ptp_ocp_lock); static DEFINE_MUTEX(ptp_ocp_lock);
static DEFINE_IDR(ptp_ocp_idr); static DEFINE_IDR(ptp_ocp_idr);
static struct { struct ocp_selector {
const char *name; const char *name;
int value; int value;
} ptp_ocp_clock[] = { };
static struct ocp_selector ptp_ocp_clock[] = {
{ .name = "NONE", .value = 0 }, { .name = "NONE", .value = 0 },
{ .name = "TOD", .value = 1 }, { .name = "TOD", .value = 1 },
{ .name = "IRIG", .value = 2 }, { .name = "IRIG", .value = 2 },
...@@ -322,33 +457,71 @@ static struct { ...@@ -322,33 +457,71 @@ static struct {
{ .name = "DCF", .value = 6 }, { .name = "DCF", .value = 6 },
{ .name = "REGS", .value = 0xfe }, { .name = "REGS", .value = 0xfe },
{ .name = "EXT", .value = 0xff }, { .name = "EXT", .value = 0xff },
{ }
};
static struct ocp_selector ptp_ocp_sma_in[] = {
{ .name = "10Mhz", .value = 0x00 },
{ .name = "PPS1", .value = 0x01 },
{ .name = "PPS2", .value = 0x02 },
{ .name = "TS1", .value = 0x04 },
{ .name = "TS2", .value = 0x08 },
{ .name = "IRIG", .value = 0x10 },
{ .name = "DCF", .value = 0x20 },
{ }
};
static struct ocp_selector ptp_ocp_sma_out[] = {
{ .name = "10Mhz", .value = 0x00 },
{ .name = "PHC", .value = 0x01 },
{ .name = "MAC", .value = 0x02 },
{ .name = "GNSS", .value = 0x04 },
{ .name = "GNSS2", .value = 0x08 },
{ .name = "IRIG", .value = 0x10 },
{ .name = "DCF", .value = 0x20 },
{ }
}; };
static const char * static const char *
ptp_ocp_clock_name_from_val(int val) ptp_ocp_select_name_from_val(struct ocp_selector *tbl, int val)
{ {
int i; int i;
for (i = 0; i < ARRAY_SIZE(ptp_ocp_clock); i++) for (i = 0; tbl[i].name; i++)
if (ptp_ocp_clock[i].value == val) if (tbl[i].value == val)
return ptp_ocp_clock[i].name; return tbl[i].name;
return NULL; return NULL;
} }
static int static int
ptp_ocp_clock_val_from_name(const char *name) ptp_ocp_select_val_from_name(struct ocp_selector *tbl, const char *name)
{ {
const char *clk; const char *select;
int i; int i;
for (i = 0; i < ARRAY_SIZE(ptp_ocp_clock); i++) { for (i = 0; tbl[i].name; i++) {
clk = ptp_ocp_clock[i].name; select = tbl[i].name;
if (!strncasecmp(name, clk, strlen(clk))) if (!strncasecmp(name, select, strlen(select)))
return ptp_ocp_clock[i].value; return tbl[i].value;
} }
return -EINVAL; return -EINVAL;
} }
static ssize_t
ptp_ocp_select_table_show(struct ocp_selector *tbl, char *buf)
{
ssize_t count;
int i;
count = 0;
for (i = 0; tbl[i].name; i++)
count += sysfs_emit_at(buf, count, "%s ", tbl[i].name);
if (count)
count--;
count += sysfs_emit_at(buf, count, "\n");
return count;
}
static int static int
__ptp_ocp_gettime_locked(struct ptp_ocp *bp, struct timespec64 *ts, __ptp_ocp_gettime_locked(struct ptp_ocp *bp, struct timespec64 *ts,
struct ptp_system_timestamp *sts) struct ptp_system_timestamp *sts)
...@@ -356,10 +529,9 @@ __ptp_ocp_gettime_locked(struct ptp_ocp *bp, struct timespec64 *ts, ...@@ -356,10 +529,9 @@ __ptp_ocp_gettime_locked(struct ptp_ocp *bp, struct timespec64 *ts,
u32 ctrl, time_sec, time_ns; u32 ctrl, time_sec, time_ns;
int i; int i;
ctrl = ioread32(&bp->reg->ctrl);
ctrl |= OCP_CTRL_READ_TIME_REQ;
ptp_read_system_prets(sts); ptp_read_system_prets(sts);
ctrl = OCP_CTRL_READ_TIME_REQ | OCP_CTRL_ENABLE;
iowrite32(ctrl, &bp->reg->ctrl); iowrite32(ctrl, &bp->reg->ctrl);
for (i = 0; i < 100; i++) { for (i = 0; i < 100; i++) {
...@@ -369,6 +541,12 @@ __ptp_ocp_gettime_locked(struct ptp_ocp *bp, struct timespec64 *ts, ...@@ -369,6 +541,12 @@ __ptp_ocp_gettime_locked(struct ptp_ocp *bp, struct timespec64 *ts,
} }
ptp_read_system_postts(sts); ptp_read_system_postts(sts);
if (sts && bp->ts_window_adjust) {
s64 ns = timespec64_to_ns(&sts->post_ts);
sts->post_ts = ns_to_timespec64(ns - bp->ts_window_adjust);
}
time_ns = ioread32(&bp->reg->time_ns); time_ns = ioread32(&bp->reg->time_ns);
time_sec = ioread32(&bp->reg->time_sec); time_sec = ioread32(&bp->reg->time_sec);
...@@ -408,8 +586,7 @@ __ptp_ocp_settime_locked(struct ptp_ocp *bp, const struct timespec64 *ts) ...@@ -408,8 +586,7 @@ __ptp_ocp_settime_locked(struct ptp_ocp *bp, const struct timespec64 *ts)
iowrite32(time_ns, &bp->reg->adjust_ns); iowrite32(time_ns, &bp->reg->adjust_ns);
iowrite32(time_sec, &bp->reg->adjust_sec); iowrite32(time_sec, &bp->reg->adjust_sec);
ctrl = ioread32(&bp->reg->ctrl); ctrl = OCP_CTRL_ADJUST_TIME | OCP_CTRL_ENABLE;
ctrl |= OCP_CTRL_ADJUST_TIME;
iowrite32(ctrl, &bp->reg->ctrl); iowrite32(ctrl, &bp->reg->ctrl);
/* restore clock selection */ /* restore clock selection */
...@@ -422,9 +599,6 @@ ptp_ocp_settime(struct ptp_clock_info *ptp_info, const struct timespec64 *ts) ...@@ -422,9 +599,6 @@ ptp_ocp_settime(struct ptp_clock_info *ptp_info, const struct timespec64 *ts)
struct ptp_ocp *bp = container_of(ptp_info, struct ptp_ocp, ptp_info); struct ptp_ocp *bp = container_of(ptp_info, struct ptp_ocp, ptp_info);
unsigned long flags; unsigned long flags;
if (ioread32(&bp->reg->status) & OCP_STATUS_IN_SYNC)
return 0;
spin_lock_irqsave(&bp->lock, flags); spin_lock_irqsave(&bp->lock, flags);
__ptp_ocp_settime_locked(bp, ts); __ptp_ocp_settime_locked(bp, ts);
spin_unlock_irqrestore(&bp->lock, flags); spin_unlock_irqrestore(&bp->lock, flags);
...@@ -432,26 +606,39 @@ ptp_ocp_settime(struct ptp_clock_info *ptp_info, const struct timespec64 *ts) ...@@ -432,26 +606,39 @@ ptp_ocp_settime(struct ptp_clock_info *ptp_info, const struct timespec64 *ts)
return 0; return 0;
} }
static void
__ptp_ocp_adjtime_locked(struct ptp_ocp *bp, u64 adj_val)
{
u32 select, ctrl;
select = ioread32(&bp->reg->select);
iowrite32(OCP_SELECT_CLK_REG, &bp->reg->select);
iowrite32(adj_val, &bp->reg->offset_ns);
iowrite32(adj_val & 0x7f, &bp->reg->offset_window_ns);
ctrl = OCP_CTRL_ADJUST_OFFSET | OCP_CTRL_ENABLE;
iowrite32(ctrl, &bp->reg->ctrl);
/* restore clock selection */
iowrite32(select >> 16, &bp->reg->select);
}
static int static int
ptp_ocp_adjtime(struct ptp_clock_info *ptp_info, s64 delta_ns) ptp_ocp_adjtime(struct ptp_clock_info *ptp_info, s64 delta_ns)
{ {
struct ptp_ocp *bp = container_of(ptp_info, struct ptp_ocp, ptp_info); struct ptp_ocp *bp = container_of(ptp_info, struct ptp_ocp, ptp_info);
struct timespec64 ts;
unsigned long flags; unsigned long flags;
int err; u32 adj_ns, sign;
if (ioread32(&bp->reg->status) & OCP_STATUS_IN_SYNC) sign = delta_ns < 0 ? BIT(31) : 0;
return 0; adj_ns = sign ? -delta_ns : delta_ns;
spin_lock_irqsave(&bp->lock, flags); spin_lock_irqsave(&bp->lock, flags);
err = __ptp_ocp_gettime_locked(bp, &ts, NULL); __ptp_ocp_adjtime_locked(bp, sign | adj_ns);
if (likely(!err)) {
timespec64_add_ns(&ts, delta_ns);
__ptp_ocp_settime_locked(bp, &ts);
}
spin_unlock_irqrestore(&bp->lock, flags); spin_unlock_irqrestore(&bp->lock, flags);
return err; return 0;
} }
static int static int
...@@ -464,7 +651,7 @@ ptp_ocp_null_adjfine(struct ptp_clock_info *ptp_info, long scaled_ppm) ...@@ -464,7 +651,7 @@ ptp_ocp_null_adjfine(struct ptp_clock_info *ptp_info, long scaled_ppm)
} }
static int static int
ptp_ocp_adjphase(struct ptp_clock_info *ptp_info, s32 phase_ns) ptp_ocp_null_adjphase(struct ptp_clock_info *ptp_info, s32 phase_ns)
{ {
return -EOPNOTSUPP; return -EOPNOTSUPP;
} }
...@@ -475,10 +662,12 @@ ptp_ocp_enable(struct ptp_clock_info *ptp_info, struct ptp_clock_request *rq, ...@@ -475,10 +662,12 @@ ptp_ocp_enable(struct ptp_clock_info *ptp_info, struct ptp_clock_request *rq,
{ {
struct ptp_ocp *bp = container_of(ptp_info, struct ptp_ocp, ptp_info); struct ptp_ocp *bp = container_of(ptp_info, struct ptp_ocp, ptp_info);
struct ptp_ocp_ext_src *ext = NULL; struct ptp_ocp_ext_src *ext = NULL;
u32 req;
int err; int err;
switch (rq->type) { switch (rq->type) {
case PTP_CLK_REQ_EXTTS: case PTP_CLK_REQ_EXTTS:
req = OCP_REQ_TIMESTAMP;
switch (rq->extts.index) { switch (rq->extts.index) {
case 0: case 0:
ext = bp->ts0; ext = bp->ts0;
...@@ -486,18 +675,33 @@ ptp_ocp_enable(struct ptp_clock_info *ptp_info, struct ptp_clock_request *rq, ...@@ -486,18 +675,33 @@ ptp_ocp_enable(struct ptp_clock_info *ptp_info, struct ptp_clock_request *rq,
case 1: case 1:
ext = bp->ts1; ext = bp->ts1;
break; break;
case 2:
ext = bp->ts2;
break;
case 3:
ext = bp->pps;
break;
} }
break; break;
case PTP_CLK_REQ_PPS: case PTP_CLK_REQ_PPS:
req = OCP_REQ_PPS;
ext = bp->pps; ext = bp->pps;
break; break;
case PTP_CLK_REQ_PEROUT:
if (on &&
(rq->perout.period.sec != 1 || rq->perout.period.nsec != 0))
return -EINVAL;
/* This is a request for 1PPS on an output SMA.
* Allow, but assume manual configuration.
*/
return 0;
default: default:
return -EOPNOTSUPP; return -EOPNOTSUPP;
} }
err = -ENXIO; err = -ENXIO;
if (ext) if (ext)
err = ext->info->enable(ext, on); err = ext->info->enable(ext, req, on);
return err; return err;
} }
...@@ -510,10 +714,11 @@ static const struct ptp_clock_info ptp_ocp_clock_info = { ...@@ -510,10 +714,11 @@ static const struct ptp_clock_info ptp_ocp_clock_info = {
.settime64 = ptp_ocp_settime, .settime64 = ptp_ocp_settime,
.adjtime = ptp_ocp_adjtime, .adjtime = ptp_ocp_adjtime,
.adjfine = ptp_ocp_null_adjfine, .adjfine = ptp_ocp_null_adjfine,
.adjphase = ptp_ocp_adjphase, .adjphase = ptp_ocp_null_adjphase,
.enable = ptp_ocp_enable, .enable = ptp_ocp_enable,
.pps = true, .pps = true,
.n_ext_ts = 2, .n_ext_ts = 4,
.n_per_out = 1,
}; };
static void static void
...@@ -526,8 +731,7 @@ __ptp_ocp_clear_drift_locked(struct ptp_ocp *bp) ...@@ -526,8 +731,7 @@ __ptp_ocp_clear_drift_locked(struct ptp_ocp *bp)
iowrite32(0, &bp->reg->drift_ns); iowrite32(0, &bp->reg->drift_ns);
ctrl = ioread32(&bp->reg->ctrl); ctrl = OCP_CTRL_ADJUST_DRIFT | OCP_CTRL_ENABLE;
ctrl |= OCP_CTRL_ADJUST_DRIFT;
iowrite32(ctrl, &bp->reg->ctrl); iowrite32(ctrl, &bp->reg->ctrl);
/* restore clock selection */ /* restore clock selection */
...@@ -559,6 +763,28 @@ ptp_ocp_watchdog(struct timer_list *t) ...@@ -559,6 +763,28 @@ ptp_ocp_watchdog(struct timer_list *t)
mod_timer(&bp->watchdog, jiffies + HZ); mod_timer(&bp->watchdog, jiffies + HZ);
} }
static void
ptp_ocp_estimate_pci_timing(struct ptp_ocp *bp)
{
ktime_t start, end;
ktime_t delay;
u32 ctrl;
ctrl = ioread32(&bp->reg->ctrl);
ctrl = OCP_CTRL_READ_TIME_REQ | OCP_CTRL_ENABLE;
iowrite32(ctrl, &bp->reg->ctrl);
start = ktime_get_ns();
ctrl = ioread32(&bp->reg->ctrl);
end = ktime_get_ns();
delay = end - start;
bp->ts_window_adjust = (delay >> 5) * 3;
}
static int static int
ptp_ocp_init_clock(struct ptp_ocp *bp) ptp_ocp_init_clock(struct ptp_ocp *bp)
{ {
...@@ -566,9 +792,7 @@ ptp_ocp_init_clock(struct ptp_ocp *bp) ...@@ -566,9 +792,7 @@ ptp_ocp_init_clock(struct ptp_ocp *bp)
bool sync; bool sync;
u32 ctrl; u32 ctrl;
/* make sure clock is enabled */ ctrl = OCP_CTRL_ENABLE;
ctrl = ioread32(&bp->reg->ctrl);
ctrl |= OCP_CTRL_ENABLE;
iowrite32(ctrl, &bp->reg->ctrl); iowrite32(ctrl, &bp->reg->ctrl);
/* NO DRIFT Correction */ /* NO DRIFT Correction */
...@@ -587,22 +811,57 @@ ptp_ocp_init_clock(struct ptp_ocp *bp) ...@@ -587,22 +811,57 @@ ptp_ocp_init_clock(struct ptp_ocp *bp)
return -ENODEV; return -ENODEV;
} }
ptp_ocp_estimate_pci_timing(bp);
sync = ioread32(&bp->reg->status) & OCP_STATUS_IN_SYNC; sync = ioread32(&bp->reg->status) & OCP_STATUS_IN_SYNC;
if (!sync) { if (!sync) {
ktime_get_real_ts64(&ts); ktime_get_clocktai_ts64(&ts);
ptp_ocp_settime(&bp->ptp_info, &ts); ptp_ocp_settime(&bp->ptp_info, &ts);
} }
if (!ptp_ocp_gettimex(&bp->ptp_info, &ts, NULL))
dev_info(&bp->pdev->dev, "Time: %lld.%ld, %s\n",
ts.tv_sec, ts.tv_nsec,
sync ? "in-sync" : "UNSYNCED");
/* If there is a clock supervisor, then enable the watchdog */
if (bp->pps_to_clk) {
timer_setup(&bp->watchdog, ptp_ocp_watchdog, 0); timer_setup(&bp->watchdog, ptp_ocp_watchdog, 0);
mod_timer(&bp->watchdog, jiffies + HZ); mod_timer(&bp->watchdog, jiffies + HZ);
}
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
ptp_ocp_tod_init(struct ptp_ocp *bp)
{
u32 ctrl, reg;
ctrl = ioread32(&bp->tod->ctrl);
ctrl |= TOD_CTRL_PROTOCOL | TOD_CTRL_ENABLE;
ctrl &= ~(TOD_CTRL_DISABLE_FMT_A | TOD_CTRL_DISABLE_FMT_B);
iowrite32(ctrl, &bp->tod->ctrl);
reg = ioread32(&bp->tod->utc_status);
if (reg & TOD_STATUS_UTC_VALID)
ptp_ocp_utc_distribute(bp, reg & TOD_STATUS_UTC_MASK);
}
static void static void
ptp_ocp_tod_info(struct ptp_ocp *bp) ptp_ocp_tod_info(struct ptp_ocp *bp)
{ {
...@@ -620,11 +879,6 @@ ptp_ocp_tod_info(struct ptp_ocp *bp) ...@@ -620,11 +879,6 @@ ptp_ocp_tod_info(struct ptp_ocp *bp)
dev_info(&bp->pdev->dev, "TOD Version %d.%d.%d\n", dev_info(&bp->pdev->dev, "TOD Version %d.%d.%d\n",
version >> 24, (version >> 16) & 0xff, version & 0xffff); version >> 24, (version >> 16) & 0xff, version & 0xffff);
ctrl = ioread32(&bp->tod->ctrl);
ctrl |= TOD_CTRL_PROTOCOL | TOD_CTRL_ENABLE;
ctrl &= ~(TOD_CTRL_DISABLE_FMT_A | TOD_CTRL_DISABLE_FMT_B);
iowrite32(ctrl, &bp->tod->ctrl);
ctrl = ioread32(&bp->tod->ctrl); ctrl = ioread32(&bp->tod->ctrl);
idx = ctrl & TOD_CTRL_PROTOCOL ? 4 : 0; idx = ctrl & TOD_CTRL_PROTOCOL ? 4 : 0;
idx += (ctrl >> 16) & 3; idx += (ctrl >> 16) & 3;
...@@ -639,7 +893,7 @@ ptp_ocp_tod_info(struct ptp_ocp *bp) ...@@ -639,7 +893,7 @@ ptp_ocp_tod_info(struct ptp_ocp *bp)
reg = ioread32(&bp->tod->status); reg = ioread32(&bp->tod->status);
dev_info(&bp->pdev->dev, "status: %x\n", reg); dev_info(&bp->pdev->dev, "status: %x\n", reg);
reg = ioread32(&bp->tod->correction_sec); reg = ioread32(&bp->tod->adj_sec);
dev_info(&bp->pdev->dev, "correction: %d\n", reg); dev_info(&bp->pdev->dev, "correction: %d\n", reg);
reg = ioread32(&bp->tod->utc_status); reg = ioread32(&bp->tod->utc_status);
...@@ -695,6 +949,9 @@ ptp_ocp_get_serial_number(struct ptp_ocp *bp) ...@@ -695,6 +949,9 @@ ptp_ocp_get_serial_number(struct ptp_ocp *bp)
struct device *dev; struct device *dev;
int err; int err;
if (!bp->i2c_ctrl)
return;
dev = device_find_child(&bp->i2c_ctrl->dev, NULL, ptp_ocp_firstchild); dev = device_find_child(&bp->i2c_ctrl->dev, NULL, ptp_ocp_firstchild);
if (!dev) { if (!dev) {
dev_err(&bp->pdev->dev, "Can't find I2C adapter\n"); dev_err(&bp->pdev->dev, "Can't find I2C adapter\n");
...@@ -720,21 +977,6 @@ ptp_ocp_get_serial_number(struct ptp_ocp *bp) ...@@ -720,21 +977,6 @@ ptp_ocp_get_serial_number(struct ptp_ocp *bp)
put_device(dev); put_device(dev);
} }
static void
ptp_ocp_info(struct ptp_ocp *bp)
{
u32 version, select;
version = ioread32(&bp->reg->version);
select = ioread32(&bp->reg->select);
dev_info(&bp->pdev->dev, "Version %d.%d.%d, clock %s, device ptp%d\n",
version >> 24, (version >> 16) & 0xff, version & 0xffff,
ptp_ocp_clock_name_from_val(select >> 16),
ptp_clock_index(bp->ptp));
ptp_ocp_tod_info(bp);
}
static struct device * static struct device *
ptp_ocp_find_flash(struct ptp_ocp *bp) ptp_ocp_find_flash(struct ptp_ocp *bp)
{ {
...@@ -910,18 +1152,6 @@ ptp_ocp_register_spi(struct ptp_ocp *bp, struct ocp_resource *r) ...@@ -910,18 +1152,6 @@ ptp_ocp_register_spi(struct ptp_ocp *bp, struct ocp_resource *r)
unsigned long start; unsigned long start;
int id; int id;
/* XXX hack to work around old FPGA */
if (bp->n_irqs < 10) {
dev_err(&bp->pdev->dev, "FPGA does not have SPI devices\n");
return 0;
}
if (r->irq_vec > bp->n_irqs) {
dev_err(&bp->pdev->dev, "spi device irq %d out of range\n",
r->irq_vec);
return 0;
}
start = pci_resource_start(pdev, 0) + r->offset; start = pci_resource_start(pdev, 0) + r->offset;
ptp_ocp_set_mem_resource(&res[0], start, r->size); ptp_ocp_set_mem_resource(&res[0], start, r->size);
ptp_ocp_set_irq_resource(&res[1], pci_irq_vector(pdev, r->irq_vec)); ptp_ocp_set_irq_resource(&res[1], pci_irq_vector(pdev, r->irq_vec));
...@@ -944,41 +1174,41 @@ ptp_ocp_register_spi(struct ptp_ocp *bp, struct ocp_resource *r) ...@@ -944,41 +1174,41 @@ ptp_ocp_register_spi(struct ptp_ocp *bp, struct ocp_resource *r)
static struct platform_device * static struct platform_device *
ptp_ocp_i2c_bus(struct pci_dev *pdev, struct ocp_resource *r, int id) ptp_ocp_i2c_bus(struct pci_dev *pdev, struct ocp_resource *r, int id)
{ {
struct ptp_ocp_i2c_info *info;
struct resource res[2]; struct resource res[2];
unsigned long start; unsigned long start;
info = r->extra;
start = pci_resource_start(pdev, 0) + r->offset; start = pci_resource_start(pdev, 0) + r->offset;
ptp_ocp_set_mem_resource(&res[0], start, r->size); ptp_ocp_set_mem_resource(&res[0], start, r->size);
ptp_ocp_set_irq_resource(&res[1], pci_irq_vector(pdev, r->irq_vec)); ptp_ocp_set_irq_resource(&res[1], pci_irq_vector(pdev, r->irq_vec));
return platform_device_register_resndata(&pdev->dev, "xiic-i2c", return platform_device_register_resndata(&pdev->dev, info->name,
id, res, 2, NULL, 0); id, res, 2,
info->data, info->data_size);
} }
static int static int
ptp_ocp_register_i2c(struct ptp_ocp *bp, struct ocp_resource *r) ptp_ocp_register_i2c(struct ptp_ocp *bp, struct ocp_resource *r)
{ {
struct pci_dev *pdev = bp->pdev; struct pci_dev *pdev = bp->pdev;
struct ptp_ocp_i2c_info *info;
struct platform_device *p; struct platform_device *p;
struct clk_hw *clk; struct clk_hw *clk;
char buf[32]; char buf[32];
int id; int id;
if (r->irq_vec > bp->n_irqs) { info = r->extra;
dev_err(&bp->pdev->dev, "i2c device irq %d out of range\n",
r->irq_vec);
return 0;
}
id = pci_dev_id(bp->pdev); id = pci_dev_id(bp->pdev);
sprintf(buf, "AXI.%d", id); sprintf(buf, "AXI.%d", id);
clk = clk_hw_register_fixed_rate(&pdev->dev, buf, NULL, 0, 50000000); clk = clk_hw_register_fixed_rate(&pdev->dev, buf, NULL, 0,
info->fixed_rate);
if (IS_ERR(clk)) if (IS_ERR(clk))
return PTR_ERR(clk); return PTR_ERR(clk);
bp->i2c_clk = clk; bp->i2c_clk = clk;
sprintf(buf, "xiic-i2c.%d", id); sprintf(buf, "%s.%d", info->name, id);
devm_clk_hw_register_clkdev(&pdev->dev, clk, NULL, buf); devm_clk_hw_register_clkdev(&pdev->dev, clk, NULL, buf);
p = ptp_ocp_i2c_bus(bp->pdev, r, id); p = ptp_ocp_i2c_bus(bp->pdev, r, id);
if (IS_ERR(p)) if (IS_ERR(p))
...@@ -997,26 +1227,51 @@ ptp_ocp_ts_irq(int irq, void *priv) ...@@ -997,26 +1227,51 @@ ptp_ocp_ts_irq(int irq, void *priv)
struct ptp_clock_event ev; struct ptp_clock_event ev;
u32 sec, nsec; u32 sec, nsec;
if (ext == ext->bp->pps) {
if (ext->bp->pps_req_map & OCP_REQ_PPS) {
ev.type = PTP_CLOCK_PPS;
ptp_clock_event(ext->bp->ptp, &ev);
}
if ((ext->bp->pps_req_map & ~OCP_REQ_PPS) == 0)
goto out;
}
/* XXX should fix API - this converts s/ns -> ts -> s/ns */ /* XXX should fix API - this converts s/ns -> ts -> s/ns */
sec = ioread32(&reg->time_sec); sec = ioread32(&reg->time_sec);
nsec = ioread32(&reg->time_ns); nsec = ioread32(&reg->time_ns);
ev.type = PTP_CLOCK_EXTTS; ev.type = PTP_CLOCK_EXTTS;
ev.index = ext->info->index; ev.index = ext->info->index;
ev.timestamp = sec * 1000000000ULL + nsec; ev.timestamp = sec * NSEC_PER_SEC + nsec;
ptp_clock_event(ext->bp->ptp, &ev); ptp_clock_event(ext->bp->ptp, &ev);
out:
iowrite32(1, &reg->intr); /* write 1 to ack */ iowrite32(1, &reg->intr); /* write 1 to ack */
return IRQ_HANDLED; return IRQ_HANDLED;
} }
static int static int
ptp_ocp_ts_enable(void *priv, bool enable) ptp_ocp_ts_enable(void *priv, u32 req, bool enable)
{ {
struct ptp_ocp_ext_src *ext = priv; struct ptp_ocp_ext_src *ext = priv;
struct ts_reg __iomem *reg = ext->mem; struct ts_reg __iomem *reg = ext->mem;
struct ptp_ocp *bp = ext->bp;
if (ext == bp->pps) {
u32 old_map = bp->pps_req_map;
if (enable)
bp->pps_req_map |= req;
else
bp->pps_req_map &= ~req;
/* if no state change, just return */
if ((!!old_map ^ !!bp->pps_req_map) == 0)
return 0;
}
if (enable) { if (enable) {
iowrite32(1, &reg->enable); iowrite32(1, &reg->enable);
...@@ -1033,7 +1288,7 @@ ptp_ocp_ts_enable(void *priv, bool enable) ...@@ -1033,7 +1288,7 @@ ptp_ocp_ts_enable(void *priv, bool enable)
static void static void
ptp_ocp_unregister_ext(struct ptp_ocp_ext_src *ext) ptp_ocp_unregister_ext(struct ptp_ocp_ext_src *ext)
{ {
ext->info->enable(ext, false); ext->info->enable(ext, ~0, false);
pci_free_irq(ext->bp->pdev, ext->irq_vec, ext); pci_free_irq(ext->bp->pdev, ext->irq_vec, ext);
kfree(ext); kfree(ext);
} }
...@@ -1059,7 +1314,7 @@ ptp_ocp_register_ext(struct ptp_ocp *bp, struct ocp_resource *r) ...@@ -1059,7 +1314,7 @@ ptp_ocp_register_ext(struct ptp_ocp *bp, struct ocp_resource *r)
ext->irq_vec = r->irq_vec; ext->irq_vec = r->irq_vec;
err = pci_request_irq(pdev, r->irq_vec, ext->info->irq_fcn, NULL, err = pci_request_irq(pdev, r->irq_vec, ext->info->irq_fcn, NULL,
ext, "ocp%d.%s", bp->id, ext->info->name); ext, "ocp%d.%s", bp->id, r->name);
if (err) { if (err) {
dev_err(&pdev->dev, "Could not get irq %d\n", r->irq_vec); dev_err(&pdev->dev, "Could not get irq %d\n", r->irq_vec);
goto out; goto out;
...@@ -1101,12 +1356,6 @@ ptp_ocp_register_serial(struct ptp_ocp *bp, struct ocp_resource *r) ...@@ -1101,12 +1356,6 @@ ptp_ocp_register_serial(struct ptp_ocp *bp, struct ocp_resource *r)
{ {
int port; int port;
if (r->irq_vec > bp->n_irqs) {
dev_err(&bp->pdev->dev, "serial device irq %d out of range\n",
r->irq_vec);
return 0;
}
port = ptp_ocp_serial_line(bp, r); port = ptp_ocp_serial_line(bp, r);
if (port < 0) if (port < 0)
return port; return port;
...@@ -1130,15 +1379,40 @@ ptp_ocp_register_mem(struct ptp_ocp *bp, struct ocp_resource *r) ...@@ -1130,15 +1379,40 @@ ptp_ocp_register_mem(struct ptp_ocp *bp, struct ocp_resource *r)
return 0; return 0;
} }
static void
ptp_ocp_nmea_out_init(struct ptp_ocp *bp)
{
if (!bp->nmea_out)
return;
iowrite32(0, &bp->nmea_out->ctrl); /* disable */
iowrite32(7, &bp->nmea_out->uart_baud); /* 115200 */
iowrite32(1, &bp->nmea_out->ctrl); /* enable */
}
/* FB specific board initializers; last "resource" registered. */ /* FB specific board initializers; last "resource" registered. */
static int static int
ptp_ocp_fb_board_init(struct ptp_ocp *bp, struct ocp_resource *r) ptp_ocp_fb_board_init(struct ptp_ocp *bp, struct ocp_resource *r)
{ {
bp->flash_start = 1024 * 4096; bp->flash_start = 1024 * 4096;
ptp_ocp_tod_init(bp);
ptp_ocp_nmea_out_init(bp);
return ptp_ocp_init_clock(bp); return ptp_ocp_init_clock(bp);
} }
static bool
ptp_ocp_allow_irq(struct ptp_ocp *bp, struct ocp_resource *r)
{
bool allow = !r->irq_vec || r->irq_vec < bp->n_irqs;
if (!allow)
dev_err(&bp->pdev->dev, "irq %d out of range, skipping %s\n",
r->irq_vec, r->name);
return allow;
}
static int static int
ptp_ocp_register_resources(struct ptp_ocp *bp, kernel_ulong_t driver_data) ptp_ocp_register_resources(struct ptp_ocp *bp, kernel_ulong_t driver_data)
{ {
...@@ -1147,49 +1421,505 @@ ptp_ocp_register_resources(struct ptp_ocp *bp, kernel_ulong_t driver_data) ...@@ -1147,49 +1421,505 @@ ptp_ocp_register_resources(struct ptp_ocp *bp, kernel_ulong_t driver_data)
table = (struct ocp_resource *)driver_data; table = (struct ocp_resource *)driver_data;
for (r = table; r->setup; r++) { for (r = table; r->setup; r++) {
if (!ptp_ocp_allow_irq(bp, r))
continue;
err = r->setup(bp, r); err = r->setup(bp, r);
if (err) if (err) {
dev_err(&bp->pdev->dev,
"Could not register %s: err %d\n",
r->name, err);
break; break;
} }
}
return err; return err;
} }
static ssize_t static void
serialnum_show(struct device *dev, struct device_attribute *attr, char *buf) ptp_ocp_enable_fpga(u32 __iomem *reg, u32 bit, bool enable)
{ {
struct ptp_ocp *bp = dev_get_drvdata(dev); u32 ctrl;
bool on;
if (!bp->has_serial)
ptp_ocp_get_serial_number(bp); ctrl = ioread32(reg);
on = ctrl & bit;
return sysfs_emit(buf, "%pM\n", bp->serial); if (on ^ enable) {
ctrl &= ~bit;
ctrl |= enable ? bit : 0;
iowrite32(ctrl, reg);
}
} }
static DEVICE_ATTR_RO(serialnum);
static ssize_t static void
gnss_sync_show(struct device *dev, struct device_attribute *attr, char *buf) ptp_ocp_irig_out(struct ptp_ocp *bp, bool enable)
{ {
struct ptp_ocp *bp = dev_get_drvdata(dev); return ptp_ocp_enable_fpga(&bp->irig_out->ctrl,
ssize_t ret; IRIG_M_CTRL_ENABLE, enable);
}
if (bp->gnss_lost)
ret = sysfs_emit(buf, "LOST @ %ptT\n", &bp->gnss_lost);
else
ret = sysfs_emit(buf, "SYNC\n");
return ret; static void
ptp_ocp_irig_in(struct ptp_ocp *bp, bool enable)
{
return ptp_ocp_enable_fpga(&bp->irig_in->ctrl,
IRIG_S_CTRL_ENABLE, enable);
} }
static DEVICE_ATTR_RO(gnss_sync);
static ssize_t static void
clock_source_show(struct device *dev, struct device_attribute *attr, char *buf) ptp_ocp_dcf_out(struct ptp_ocp *bp, bool enable)
{
return ptp_ocp_enable_fpga(&bp->dcf_out->ctrl,
DCF_M_CTRL_ENABLE, enable);
}
static void
ptp_ocp_dcf_in(struct ptp_ocp *bp, bool enable)
{
return ptp_ocp_enable_fpga(&bp->dcf_in->ctrl,
DCF_S_CTRL_ENABLE, enable);
}
static void
__handle_signal_outputs(struct ptp_ocp *bp, u32 val)
{
ptp_ocp_irig_out(bp, val & 0x00100010);
ptp_ocp_dcf_out(bp, val & 0x00200020);
}
static void
__handle_signal_inputs(struct ptp_ocp *bp, u32 val)
{
ptp_ocp_irig_in(bp, val & 0x00100010);
ptp_ocp_dcf_in(bp, val & 0x00200020);
}
/*
* ANT0 == gps (in)
* ANT1 == sma1 (in)
* ANT2 == sma2 (in)
* ANT3 == sma3 (out)
* ANT4 == sma4 (out)
*/
enum ptp_ocp_sma_mode {
SMA_MODE_IN,
SMA_MODE_OUT,
};
static struct ptp_ocp_sma_connector {
enum ptp_ocp_sma_mode mode;
bool fixed_mode;
u16 default_out_idx;
} ptp_ocp_sma_map[4] = {
{
.mode = SMA_MODE_IN,
.fixed_mode = true,
},
{
.mode = SMA_MODE_IN,
.fixed_mode = true,
},
{
.mode = SMA_MODE_OUT,
.fixed_mode = true,
.default_out_idx = 0, /* 10Mhz */
},
{
.mode = SMA_MODE_OUT,
.fixed_mode = true,
.default_out_idx = 1, /* PHC */
},
};
static ssize_t
ptp_ocp_show_output(u32 val, char *buf, int default_idx)
{
const char *name;
ssize_t count;
count = sysfs_emit(buf, "OUT: ");
name = ptp_ocp_select_name_from_val(ptp_ocp_sma_out, val);
if (!name)
name = ptp_ocp_sma_out[default_idx].name;
count += sysfs_emit_at(buf, count, "%s\n", name);
return count;
}
static ssize_t
ptp_ocp_show_inputs(u32 val, char *buf, const char *zero_in)
{
const char *name;
ssize_t count;
int i;
count = sysfs_emit(buf, "IN: ");
for (i = 0; i < ARRAY_SIZE(ptp_ocp_sma_in); i++) {
if (val & ptp_ocp_sma_in[i].value) {
name = ptp_ocp_sma_in[i].name;
count += sysfs_emit_at(buf, count, "%s ", name);
}
}
if (!val && zero_in)
count += sysfs_emit_at(buf, count, "%s ", zero_in);
if (count)
count--;
count += sysfs_emit_at(buf, count, "\n");
return count;
}
static int
sma_parse_inputs(const char *buf, enum ptp_ocp_sma_mode *mode)
{
struct ocp_selector *tbl[] = { ptp_ocp_sma_in, ptp_ocp_sma_out };
int idx, count, dir;
char **argv;
int ret;
argv = argv_split(GFP_KERNEL, buf, &count);
if (!argv)
return -ENOMEM;
ret = -EINVAL;
if (!count)
goto out;
idx = 0;
dir = *mode == SMA_MODE_IN ? 0 : 1;
if (!strcasecmp("IN:", argv[idx])) {
dir = 0;
idx++;
}
if (!strcasecmp("OUT:", argv[0])) {
dir = 1;
idx++;
}
*mode = dir == 0 ? SMA_MODE_IN : SMA_MODE_OUT;
ret = 0;
for (; idx < count; idx++)
ret |= ptp_ocp_select_val_from_name(tbl[dir], argv[idx]);
if (ret < 0)
ret = -EINVAL;
out:
argv_free(argv);
return ret;
}
static ssize_t
ptp_ocp_sma_show(struct ptp_ocp *bp, int sma_nr, u32 val, char *buf,
const char *zero_in)
{
struct ptp_ocp_sma_connector *sma = &ptp_ocp_sma_map[sma_nr - 1];
if (sma->mode == SMA_MODE_IN)
return ptp_ocp_show_inputs(val, buf, zero_in);
return ptp_ocp_show_output(val, buf, sma->default_out_idx);
}
static ssize_t
sma1_show(struct device *dev, struct device_attribute *attr, char *buf)
{
struct ptp_ocp *bp = dev_get_drvdata(dev);
u32 val;
val = ioread32(&bp->sma->gpio1) & 0x3f;
return ptp_ocp_sma_show(bp, 1, val, buf, ptp_ocp_sma_in[0].name);
}
static ssize_t
sma2_show(struct device *dev, struct device_attribute *attr, char *buf)
{
struct ptp_ocp *bp = dev_get_drvdata(dev);
u32 val;
val = (ioread32(&bp->sma->gpio1) >> 16) & 0x3f;
return ptp_ocp_sma_show(bp, 2, val, buf, NULL);
}
static ssize_t
sma3_show(struct device *dev, struct device_attribute *attr, char *buf)
{
struct ptp_ocp *bp = dev_get_drvdata(dev);
u32 val;
val = ioread32(&bp->sma->gpio2) & 0x3f;
return ptp_ocp_sma_show(bp, 3, val, buf, NULL);
}
static ssize_t
sma4_show(struct device *dev, struct device_attribute *attr, char *buf)
{
struct ptp_ocp *bp = dev_get_drvdata(dev);
u32 val;
val = (ioread32(&bp->sma->gpio2) >> 16) & 0x3f;
return ptp_ocp_sma_show(bp, 4, val, buf, NULL);
}
static void
ptp_ocp_sma_store_output(struct ptp_ocp *bp, u32 val, u32 shift)
{
unsigned long flags;
u32 gpio, mask;
mask = 0xffff << (16 - shift);
spin_lock_irqsave(&bp->lock, flags);
gpio = ioread32(&bp->sma->gpio2);
gpio = (gpio & mask) | (val << shift);
__handle_signal_outputs(bp, gpio);
iowrite32(gpio, &bp->sma->gpio2);
spin_unlock_irqrestore(&bp->lock, flags);
}
static void
ptp_ocp_sma_store_inputs(struct ptp_ocp *bp, u32 val, u32 shift)
{
unsigned long flags;
u32 gpio, mask;
mask = 0xffff << (16 - shift);
spin_lock_irqsave(&bp->lock, flags);
gpio = ioread32(&bp->sma->gpio1);
gpio = (gpio & mask) | (val << shift);
__handle_signal_inputs(bp, gpio);
iowrite32(gpio, &bp->sma->gpio1);
spin_unlock_irqrestore(&bp->lock, flags);
}
static ssize_t
ptp_ocp_sma_store(struct ptp_ocp *bp, const char *buf, int sma_nr, u32 shift)
{
struct ptp_ocp_sma_connector *sma = &ptp_ocp_sma_map[sma_nr - 1];
enum ptp_ocp_sma_mode mode;
int val;
mode = sma->mode;
val = sma_parse_inputs(buf, &mode);
if (val < 0)
return val;
if (mode != sma->mode && sma->fixed_mode)
return -EOPNOTSUPP;
if (mode != sma->mode) {
pr_err("Mode changes not supported yet.\n");
return -EOPNOTSUPP;
}
if (sma->mode == SMA_MODE_IN)
ptp_ocp_sma_store_inputs(bp, val, shift);
else
ptp_ocp_sma_store_output(bp, val, shift);
return 0;
}
static ssize_t
sma1_store(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
struct ptp_ocp *bp = dev_get_drvdata(dev);
int err;
err = ptp_ocp_sma_store(bp, buf, 1, 0);
return err ? err : count;
}
static ssize_t
sma2_store(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
struct ptp_ocp *bp = dev_get_drvdata(dev);
int err;
err = ptp_ocp_sma_store(bp, buf, 2, 16);
return err ? err : count;
}
static ssize_t
sma3_store(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
struct ptp_ocp *bp = dev_get_drvdata(dev);
int err;
err = ptp_ocp_sma_store(bp, buf, 3, 0);
return err ? err : count;
}
static ssize_t
sma4_store(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
struct ptp_ocp *bp = dev_get_drvdata(dev);
int err;
err = ptp_ocp_sma_store(bp, buf, 4, 16);
return err ? err : count;
}
static DEVICE_ATTR_RW(sma1);
static DEVICE_ATTR_RW(sma2);
static DEVICE_ATTR_RW(sma3);
static DEVICE_ATTR_RW(sma4);
static ssize_t
available_sma_inputs_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
return ptp_ocp_select_table_show(ptp_ocp_sma_in, buf);
}
static DEVICE_ATTR_RO(available_sma_inputs);
static ssize_t
available_sma_outputs_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
return ptp_ocp_select_table_show(ptp_ocp_sma_out, buf);
}
static DEVICE_ATTR_RO(available_sma_outputs);
static ssize_t
serialnum_show(struct device *dev, struct device_attribute *attr, char *buf)
{
struct ptp_ocp *bp = dev_get_drvdata(dev);
if (!bp->has_serial)
ptp_ocp_get_serial_number(bp);
return sysfs_emit(buf, "%pM\n", bp->serial);
}
static DEVICE_ATTR_RO(serialnum);
static ssize_t
gnss_sync_show(struct device *dev, struct device_attribute *attr, char *buf)
{
struct ptp_ocp *bp = dev_get_drvdata(dev);
ssize_t ret;
if (bp->gnss_lost)
ret = sysfs_emit(buf, "LOST @ %ptT\n", &bp->gnss_lost);
else
ret = sysfs_emit(buf, "SYNC\n");
return ret;
}
static DEVICE_ATTR_RO(gnss_sync);
static ssize_t
utc_tai_offset_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct ptp_ocp *bp = dev_get_drvdata(dev);
return sysfs_emit(buf, "%d\n", bp->utc_tai_offset);
}
static ssize_t
utc_tai_offset_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct ptp_ocp *bp = dev_get_drvdata(dev);
int err;
u32 val;
err = kstrtou32(buf, 0, &val);
if (err)
return err;
ptp_ocp_utc_distribute(bp, val);
return count;
}
static DEVICE_ATTR_RW(utc_tai_offset);
static ssize_t
ts_window_adjust_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct ptp_ocp *bp = dev_get_drvdata(dev);
return sysfs_emit(buf, "%d\n", bp->ts_window_adjust);
}
static ssize_t
ts_window_adjust_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct ptp_ocp *bp = dev_get_drvdata(dev);
int err;
u32 val;
err = kstrtou32(buf, 0, &val);
if (err)
return err;
bp->ts_window_adjust = val;
return count;
}
static DEVICE_ATTR_RW(ts_window_adjust);
static ssize_t
irig_b_mode_show(struct device *dev, struct device_attribute *attr, char *buf)
{
struct ptp_ocp *bp = dev_get_drvdata(dev);
u32 val;
val = ioread32(&bp->irig_out->ctrl);
val = (val >> 16) & 0x07;
return sysfs_emit(buf, "%d\n", val);
}
static ssize_t
irig_b_mode_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;
u32 reg;
u8 val;
err = kstrtou8(buf, 0, &val);
if (err)
return err;
if (val > 7)
return -EINVAL;
reg = ((val & 0x7) << 16);
spin_lock_irqsave(&bp->lock, flags);
iowrite32(0, &bp->irig_out->ctrl); /* disable */
iowrite32(reg, &bp->irig_out->ctrl); /* change mode */
iowrite32(reg | IRIG_M_CTRL_ENABLE, &bp->irig_out->ctrl);
spin_unlock_irqrestore(&bp->lock, flags);
return count;
}
static DEVICE_ATTR_RW(irig_b_mode);
static ssize_t
clock_source_show(struct device *dev, struct device_attribute *attr, char *buf)
{ {
struct ptp_ocp *bp = dev_get_drvdata(dev); struct ptp_ocp *bp = dev_get_drvdata(dev);
const char *p; const char *p;
u32 select; u32 select;
select = ioread32(&bp->reg->select); select = ioread32(&bp->reg->select);
p = ptp_ocp_clock_name_from_val(select >> 16); p = ptp_ocp_select_name_from_val(ptp_ocp_clock, select >> 16);
return sysfs_emit(buf, "%s\n", p); return sysfs_emit(buf, "%s\n", p);
} }
...@@ -1202,7 +1932,7 @@ clock_source_store(struct device *dev, struct device_attribute *attr, ...@@ -1202,7 +1932,7 @@ clock_source_store(struct device *dev, struct device_attribute *attr,
unsigned long flags; unsigned long flags;
int val; int val;
val = ptp_ocp_clock_val_from_name(buf); val = ptp_ocp_select_val_from_name(ptp_ocp_clock, buf);
if (val < 0) if (val < 0)
return val; return val;
...@@ -1218,19 +1948,7 @@ static ssize_t ...@@ -1218,19 +1948,7 @@ static ssize_t
available_clock_sources_show(struct device *dev, available_clock_sources_show(struct device *dev,
struct device_attribute *attr, char *buf) struct device_attribute *attr, char *buf)
{ {
const char *clk; return ptp_ocp_select_table_show(ptp_ocp_clock, buf);
ssize_t count;
int i;
count = 0;
for (i = 0; i < ARRAY_SIZE(ptp_ocp_clock); i++) {
clk = ptp_ocp_clock[i].name;
count += sysfs_emit_at(buf, count, "%s ", clk);
}
if (count)
count--;
count += sysfs_emit_at(buf, count, "\n");
return count;
} }
static DEVICE_ATTR_RO(available_clock_sources); static DEVICE_ATTR_RO(available_clock_sources);
...@@ -1239,10 +1957,258 @@ static struct attribute *timecard_attrs[] = { ...@@ -1239,10 +1957,258 @@ static struct attribute *timecard_attrs[] = {
&dev_attr_gnss_sync.attr, &dev_attr_gnss_sync.attr,
&dev_attr_clock_source.attr, &dev_attr_clock_source.attr,
&dev_attr_available_clock_sources.attr, &dev_attr_available_clock_sources.attr,
&dev_attr_sma1.attr,
&dev_attr_sma2.attr,
&dev_attr_sma3.attr,
&dev_attr_sma4.attr,
&dev_attr_available_sma_inputs.attr,
&dev_attr_available_sma_outputs.attr,
&dev_attr_irig_b_mode.attr,
&dev_attr_utc_tai_offset.attr,
&dev_attr_ts_window_adjust.attr,
NULL, NULL,
}; };
ATTRIBUTE_GROUPS(timecard); ATTRIBUTE_GROUPS(timecard);
static const char *
gpio_map(u32 gpio, u32 bit, const char *pri, const char *sec, const char *def)
{
const char *ans;
if (gpio & (1 << bit))
ans = pri;
else if (gpio & (1 << (bit + 16)))
ans = sec;
else
ans = def;
return ans;
}
static void
gpio_multi_map(char *buf, u32 gpio, u32 bit,
const char *pri, const char *sec, const char *def)
{
char *ans = buf;
strcpy(ans, def);
if (gpio & (1 << bit))
ans += sprintf(ans, "%s ", pri);
if (gpio & (1 << (bit + 16)))
ans += sprintf(ans, "%s ", sec);
}
static int
ptp_ocp_summary_show(struct seq_file *s, void *data)
{
struct device *dev = s->private;
struct ptp_system_timestamp sts;
u32 sma_in, sma_out, ctrl, val;
struct ts_reg __iomem *ts_reg;
struct timespec64 ts;
struct ptp_ocp *bp;
const char *src;
bool on, map;
char *buf;
buf = (char *)__get_free_page(GFP_KERNEL);
if (!buf)
return -ENOMEM;
bp = dev_get_drvdata(dev);
sma_in = ioread32(&bp->sma->gpio1);
sma_out = ioread32(&bp->sma->gpio2);
seq_printf(s, "%7s: /dev/ptp%d\n", "PTP", ptp_clock_index(bp->ptp));
sma1_show(dev, NULL, buf);
seq_printf(s, " sma1: %s", buf);
sma2_show(dev, NULL, buf);
seq_printf(s, " sma2: %s", buf);
sma3_show(dev, NULL, buf);
seq_printf(s, " sma3: %s", buf);
sma4_show(dev, NULL, buf);
seq_printf(s, " sma4: %s", buf);
if (bp->ts0) {
ts_reg = bp->ts0->mem;
on = ioread32(&ts_reg->enable);
src = "GNSS";
seq_printf(s, "%7s: %s, src: %s\n", "TS0",
on ? " ON" : "OFF", src);
}
if (bp->ts1) {
ts_reg = bp->ts1->mem;
on = ioread32(&ts_reg->enable);
src = gpio_map(sma_in, 2, "sma1", "sma2", "----");
seq_printf(s, "%7s: %s, src: %s\n", "TS1",
on ? " ON" : "OFF", src);
}
if (bp->ts2) {
ts_reg = bp->ts2->mem;
on = ioread32(&ts_reg->enable);
src = gpio_map(sma_in, 3, "sma1", "sma2", "----");
seq_printf(s, "%7s: %s, src: %s\n", "TS2",
on ? " ON" : "OFF", src);
}
if (bp->pps) {
ts_reg = bp->pps->mem;
src = "PHC";
on = ioread32(&ts_reg->enable);
map = !!(bp->pps_req_map & OCP_REQ_TIMESTAMP);
seq_printf(s, "%7s: %s, src: %s\n", "TS3",
on & map ? " ON" : "OFF", src);
map = !!(bp->pps_req_map & OCP_REQ_PPS);
seq_printf(s, "%7s: %s, src: %s\n", "PPS",
on & map ? " ON" : "OFF", src);
}
if (bp->irig_out) {
ctrl = ioread32(&bp->irig_out->ctrl);
on = ctrl & IRIG_M_CTRL_ENABLE;
val = ioread32(&bp->irig_out->status);
gpio_multi_map(buf, sma_out, 4, "sma3", "sma4", "----");
seq_printf(s, "%7s: %s, error: %d, mode %d, out: %s\n", "IRIG",
on ? " ON" : "OFF", val, (ctrl >> 16), buf);
}
if (bp->irig_in) {
on = ioread32(&bp->irig_in->ctrl) & IRIG_S_CTRL_ENABLE;
val = ioread32(&bp->irig_in->status);
src = gpio_map(sma_in, 4, "sma1", "sma2", "----");
seq_printf(s, "%7s: %s, error: %d, src: %s\n", "IRIG in",
on ? " ON" : "OFF", val, src);
}
if (bp->dcf_out) {
on = ioread32(&bp->dcf_out->ctrl) & DCF_M_CTRL_ENABLE;
val = ioread32(&bp->dcf_out->status);
gpio_multi_map(buf, sma_out, 5, "sma3", "sma4", "----");
seq_printf(s, "%7s: %s, error: %d, out: %s\n", "DCF",
on ? " ON" : "OFF", val, buf);
}
if (bp->dcf_in) {
on = ioread32(&bp->dcf_in->ctrl) & DCF_S_CTRL_ENABLE;
val = ioread32(&bp->dcf_in->status);
src = gpio_map(sma_in, 5, "sma1", "sma2", "----");
seq_printf(s, "%7s: %s, error: %d, src: %s\n", "DCF in",
on ? " ON" : "OFF", val, src);
}
if (bp->nmea_out) {
on = ioread32(&bp->nmea_out->ctrl) & 1;
val = ioread32(&bp->nmea_out->status);
seq_printf(s, "%7s: %s, error: %d\n", "NMEA",
on ? " ON" : "OFF", val);
}
/* compute src for PPS1, used below. */
if (bp->pps_select) {
val = ioread32(&bp->pps_select->gpio1);
if (val & 0x01)
src = gpio_map(sma_in, 0, "sma1", "sma2", "----");
else if (val & 0x02)
src = "MAC";
else if (val & 0x04)
src = "GNSS";
else
src = "----";
} else {
src = "?";
}
/* assumes automatic switchover/selection */
val = ioread32(&bp->reg->select);
switch (val >> 16) {
case 0:
sprintf(buf, "----");
break;
case 2:
sprintf(buf, "IRIG");
break;
case 3:
sprintf(buf, "%s via PPS1", src);
break;
case 6:
sprintf(buf, "DCF");
break;
default:
strcpy(buf, "unknown");
break;
}
val = ioread32(&bp->reg->status);
seq_printf(s, "%7s: %s, state: %s\n", "PHC src", buf,
val & OCP_STATUS_IN_SYNC ? "sync" : "unsynced");
/* reuses PPS1 src from earlier */
seq_printf(s, "MAC PPS1 src: %s\n", src);
src = gpio_map(sma_in, 1, "sma1", "sma2", "GNSS2");
seq_printf(s, "MAC PPS2 src: %s\n", src);
if (!ptp_ocp_gettimex(&bp->ptp_info, &ts, &sts)) {
struct timespec64 sys_ts;
s64 pre_ns, post_ns, ns;
pre_ns = timespec64_to_ns(&sts.pre_ts);
post_ns = timespec64_to_ns(&sts.post_ts);
ns = (pre_ns + post_ns) / 2;
ns += (s64)bp->utc_tai_offset * NSEC_PER_SEC;
sys_ts = ns_to_timespec64(ns);
seq_printf(s, "%7s: %lld.%ld == %ptT TAI\n", "PHC",
ts.tv_sec, ts.tv_nsec, &ts);
seq_printf(s, "%7s: %lld.%ld == %ptT UTC offset %d\n", "SYS",
sys_ts.tv_sec, sys_ts.tv_nsec, &sys_ts,
bp->utc_tai_offset);
seq_printf(s, "%7s: PHC:SYS offset: %lld window: %lld\n", "",
timespec64_to_ns(&ts) - ns,
post_ns - pre_ns);
}
free_page((unsigned long)buf);
return 0;
}
DEFINE_SHOW_ATTRIBUTE(ptp_ocp_summary);
static struct dentry *ptp_ocp_debugfs_root;
static void
ptp_ocp_debugfs_add_device(struct ptp_ocp *bp)
{
struct dentry *d;
d = debugfs_create_dir(dev_name(&bp->dev), ptp_ocp_debugfs_root);
bp->debug_root = d;
debugfs_create_file("summary", 0444, bp->debug_root,
&bp->dev, &ptp_ocp_summary_fops);
}
static void
ptp_ocp_debugfs_remove_device(struct ptp_ocp *bp)
{
debugfs_remove_recursive(bp->debug_root);
}
static void
ptp_ocp_debugfs_init(void)
{
ptp_ocp_debugfs_root = debugfs_create_dir("timecard", NULL);
}
static void
ptp_ocp_debugfs_fini(void)
{
debugfs_remove_recursive(ptp_ocp_debugfs_root);
}
static void static void
ptp_ocp_dev_release(struct device *dev) ptp_ocp_dev_release(struct device *dev)
{ {
...@@ -1270,7 +2236,9 @@ ptp_ocp_device_init(struct ptp_ocp *bp, struct pci_dev *pdev) ...@@ -1270,7 +2236,9 @@ ptp_ocp_device_init(struct ptp_ocp *bp, struct pci_dev *pdev)
bp->ptp_info = ptp_ocp_clock_info; bp->ptp_info = ptp_ocp_clock_info;
spin_lock_init(&bp->lock); spin_lock_init(&bp->lock);
bp->gnss_port = -1; bp->gnss_port = -1;
bp->gnss2_port = -1;
bp->mac_port = -1; bp->mac_port = -1;
bp->nmea_port = -1;
bp->pdev = pdev; bp->pdev = pdev;
device_initialize(&bp->dev); device_initialize(&bp->dev);
...@@ -1332,10 +2300,18 @@ ptp_ocp_complete(struct ptp_ocp *bp) ...@@ -1332,10 +2300,18 @@ ptp_ocp_complete(struct ptp_ocp *bp)
sprintf(buf, "ttyS%d", bp->gnss_port); sprintf(buf, "ttyS%d", bp->gnss_port);
ptp_ocp_link_child(bp, buf, "ttyGNSS"); ptp_ocp_link_child(bp, buf, "ttyGNSS");
} }
if (bp->gnss2_port != -1) {
sprintf(buf, "ttyS%d", bp->gnss2_port);
ptp_ocp_link_child(bp, buf, "ttyGNSS2");
}
if (bp->mac_port != -1) { if (bp->mac_port != -1) {
sprintf(buf, "ttyS%d", bp->mac_port); sprintf(buf, "ttyS%d", bp->mac_port);
ptp_ocp_link_child(bp, buf, "ttyMAC"); ptp_ocp_link_child(bp, buf, "ttyMAC");
} }
if (bp->nmea_port != -1) {
sprintf(buf, "ttyS%d", bp->nmea_port);
ptp_ocp_link_child(bp, buf, "ttyNMEA");
}
sprintf(buf, "ptp%d", ptp_clock_index(bp->ptp)); sprintf(buf, "ptp%d", ptp_clock_index(bp->ptp));
ptp_ocp_link_child(bp, buf, "ptp"); ptp_ocp_link_child(bp, buf, "ptp");
...@@ -1346,13 +2322,53 @@ ptp_ocp_complete(struct ptp_ocp *bp) ...@@ -1346,13 +2322,53 @@ ptp_ocp_complete(struct ptp_ocp *bp)
if (device_add_groups(&bp->dev, timecard_groups)) if (device_add_groups(&bp->dev, timecard_groups))
pr_err("device add groups failed\n"); pr_err("device add groups failed\n");
ptp_ocp_debugfs_add_device(bp);
return 0; return 0;
} }
static void static void
ptp_ocp_resource_summary(struct ptp_ocp *bp) ptp_ocp_phc_info(struct ptp_ocp *bp)
{ {
struct timespec64 ts;
u32 version, select;
bool sync;
version = ioread32(&bp->reg->version);
select = ioread32(&bp->reg->select);
dev_info(&bp->pdev->dev, "Version %d.%d.%d, clock %s, device ptp%d\n",
version >> 24, (version >> 16) & 0xff, version & 0xffff,
ptp_ocp_select_name_from_val(ptp_ocp_clock, select >> 16),
ptp_clock_index(bp->ptp));
sync = ioread32(&bp->reg->status) & OCP_STATUS_IN_SYNC;
if (!ptp_ocp_gettimex(&bp->ptp_info, &ts, NULL))
dev_info(&bp->pdev->dev, "Time: %lld.%ld, %s\n",
ts.tv_sec, ts.tv_nsec,
sync ? "in-sync" : "UNSYNCED");
}
static void
ptp_ocp_serial_info(struct device *dev, const char *name, int port, int baud)
{
if (port != -1)
dev_info(dev, "%5s: /dev/ttyS%-2d @ %6d\n", name, port, baud);
}
static void
ptp_ocp_info(struct ptp_ocp *bp)
{
static int nmea_baud[] = {
1200, 2400, 4800, 9600, 19200, 38400,
57600, 115200, 230400, 460800, 921600,
1000000, 2000000
};
struct device *dev = &bp->pdev->dev; struct device *dev = &bp->pdev->dev;
u32 reg;
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);
...@@ -1365,10 +2381,17 @@ ptp_ocp_resource_summary(struct ptp_ocp *bp) ...@@ -1365,10 +2381,17 @@ ptp_ocp_resource_summary(struct ptp_ocp *bp)
dev_info(dev, "golden image, version %d\n", dev_info(dev, "golden image, version %d\n",
ver >> 16); ver >> 16);
} }
if (bp->gnss_port != -1) ptp_ocp_serial_info(dev, "GNSS", bp->gnss_port, 115200);
dev_info(dev, "GNSS @ /dev/ttyS%d 115200\n", bp->gnss_port); ptp_ocp_serial_info(dev, "GNSS2", bp->gnss2_port, 115200);
if (bp->mac_port != -1) ptp_ocp_serial_info(dev, "MAC", bp->mac_port, 57600);
dev_info(dev, "MAC @ /dev/ttyS%d 57600\n", bp->mac_port); if (bp->nmea_out && bp->nmea_port != -1) {
int baud = -1;
reg = ioread32(&bp->nmea_out->uart_baud);
if (reg < ARRAY_SIZE(nmea_baud))
baud = nmea_baud[reg];
ptp_ocp_serial_info(dev, "NMEA", bp->nmea_port, baud);
}
} }
static void static void
...@@ -1386,6 +2409,7 @@ ptp_ocp_detach_sysfs(struct ptp_ocp *bp) ...@@ -1386,6 +2409,7 @@ ptp_ocp_detach_sysfs(struct ptp_ocp *bp)
static void static void
ptp_ocp_detach(struct ptp_ocp *bp) ptp_ocp_detach(struct ptp_ocp *bp)
{ {
ptp_ocp_debugfs_remove_device(bp);
ptp_ocp_detach_sysfs(bp); ptp_ocp_detach_sysfs(bp);
if (timer_pending(&bp->watchdog)) if (timer_pending(&bp->watchdog))
del_timer_sync(&bp->watchdog); del_timer_sync(&bp->watchdog);
...@@ -1393,12 +2417,18 @@ ptp_ocp_detach(struct ptp_ocp *bp) ...@@ -1393,12 +2417,18 @@ ptp_ocp_detach(struct ptp_ocp *bp)
ptp_ocp_unregister_ext(bp->ts0); ptp_ocp_unregister_ext(bp->ts0);
if (bp->ts1) if (bp->ts1)
ptp_ocp_unregister_ext(bp->ts1); ptp_ocp_unregister_ext(bp->ts1);
if (bp->ts2)
ptp_ocp_unregister_ext(bp->ts2);
if (bp->pps) if (bp->pps)
ptp_ocp_unregister_ext(bp->pps); ptp_ocp_unregister_ext(bp->pps);
if (bp->gnss_port != -1) if (bp->gnss_port != -1)
serial8250_unregister_port(bp->gnss_port); serial8250_unregister_port(bp->gnss_port);
if (bp->gnss2_port != -1)
serial8250_unregister_port(bp->gnss2_port);
if (bp->mac_port != -1) if (bp->mac_port != -1)
serial8250_unregister_port(bp->mac_port); serial8250_unregister_port(bp->mac_port);
if (bp->nmea_port != -1)
serial8250_unregister_port(bp->nmea_port);
if (bp->spi_flash) if (bp->spi_flash)
platform_device_unregister(bp->spi_flash); platform_device_unregister(bp->spi_flash);
if (bp->i2c_ctrl) if (bp->i2c_ctrl)
...@@ -1445,7 +2475,7 @@ ptp_ocp_probe(struct pci_dev *pdev, const struct pci_device_id *id) ...@@ -1445,7 +2475,7 @@ ptp_ocp_probe(struct pci_dev *pdev, const struct pci_device_id *id)
* allow this - if not all of the IRQ's are returned, skip the * allow this - if not all of the IRQ's are returned, skip the
* extra devices and just register the clock. * extra devices and just register the clock.
*/ */
err = pci_alloc_irq_vectors(pdev, 1, 10, PCI_IRQ_MSI | PCI_IRQ_MSIX); err = pci_alloc_irq_vectors(pdev, 1, 11, PCI_IRQ_MSI | PCI_IRQ_MSIX);
if (err < 0) { if (err < 0) {
dev_err(&pdev->dev, "alloc_irq_vectors err: %d\n", err); dev_err(&pdev->dev, "alloc_irq_vectors err: %d\n", err);
goto out; goto out;
...@@ -1470,7 +2500,6 @@ ptp_ocp_probe(struct pci_dev *pdev, const struct pci_device_id *id) ...@@ -1470,7 +2500,6 @@ ptp_ocp_probe(struct pci_dev *pdev, const struct pci_device_id *id)
goto out; goto out;
ptp_ocp_info(bp); ptp_ocp_info(bp);
ptp_ocp_resource_summary(bp);
return 0; return 0;
...@@ -1554,6 +2583,8 @@ ptp_ocp_init(void) ...@@ -1554,6 +2583,8 @@ ptp_ocp_init(void)
const char *what; const char *what;
int err; int err;
ptp_ocp_debugfs_init();
what = "timecard class"; what = "timecard class";
err = class_register(&timecard_class); err = class_register(&timecard_class);
if (err) if (err)
...@@ -1576,6 +2607,7 @@ ptp_ocp_init(void) ...@@ -1576,6 +2607,7 @@ ptp_ocp_init(void)
out_notifier: out_notifier:
class_unregister(&timecard_class); class_unregister(&timecard_class);
out: out:
ptp_ocp_debugfs_fini();
pr_err(KBUILD_MODNAME ": failed to register %s: %d\n", what, err); pr_err(KBUILD_MODNAME ": failed to register %s: %d\n", what, err);
return err; return err;
} }
...@@ -1586,6 +2618,7 @@ ptp_ocp_fini(void) ...@@ -1586,6 +2618,7 @@ ptp_ocp_fini(void)
bus_unregister_notifier(&i2c_bus_type, &ptp_ocp_i2c_notifier); bus_unregister_notifier(&i2c_bus_type, &ptp_ocp_i2c_notifier);
pci_unregister_driver(&ptp_ocp_driver); pci_unregister_driver(&ptp_ocp_driver);
class_unregister(&timecard_class); class_unregister(&timecard_class);
ptp_ocp_debugfs_fini();
} }
module_init(ptp_ocp_init); module_init(ptp_ocp_init);
......
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