Commit 2407f5d6 authored by Jonathan Lemon's avatar Jonathan Lemon Committed by David S. Miller
Browse files

ptp: ocp: Add 4 frequency counters



Input signals can be steered to any of the frequency counters.
The counter measures the frequency over a number of seconds:

  echo 0 > freq1/seconds  = turns off measurement
  echo 1 > freq1/seconds  = sets period & turns on measurment.

Signed-off-by: default avatarJonathan Lemon <jonathan.lemon@gmail.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 1aa66a3a
Loading
Loading
Loading
Loading
+165 −7
Original line number Diff line number Diff line
@@ -199,6 +199,15 @@ struct signal_reg {
	u32	repeat_count;
};

struct frequency_reg {
	u32	ctrl;
	u32	status;
};
#define FREQ_STATUS_VALID	BIT(31)
#define FREQ_STATUS_ERROR	BIT(30)
#define FREQ_STATUS_OVERRUN	BIT(29)
#define FREQ_STATUS_MASK	(BIT(24) - 1)

struct ptp_ocp_flash_info {
	const char *name;
	int pci_offset;
@@ -245,6 +254,7 @@ struct ocp_attr_group {

#define OCP_CAP_BASIC	BIT(0)
#define OCP_CAP_SIGNAL	BIT(1)
#define OCP_CAP_FREQ	BIT(2)

struct ptp_ocp_signal {
	ktime_t		period;
@@ -275,6 +285,7 @@ struct ptp_ocp {
	struct dcf_master_reg	__iomem *dcf_out;
	struct dcf_slave_reg	__iomem *dcf_in;
	struct tod_reg		__iomem *nmea_out;
	struct frequency_reg	__iomem *freq_in[4];
	struct ptp_ocp_ext_src	*signal_out[4];
	struct ptp_ocp_ext_src	*pps;
	struct ptp_ocp_ext_src	*ts0;
@@ -576,6 +587,22 @@ static struct ocp_resource ocp_fb_resource[] = {
			},
		},
	},
	{
		OCP_MEM_RESOURCE(freq_in[0]),
		.offset = 0x01200000, .size = 0x10000,
	},
	{
		OCP_MEM_RESOURCE(freq_in[1]),
		.offset = 0x01210000, .size = 0x10000,
	},
	{
		OCP_MEM_RESOURCE(freq_in[2]),
		.offset = 0x01220000, .size = 0x10000,
	},
	{
		OCP_MEM_RESOURCE(freq_in[3]),
		.offset = 0x01230000, .size = 0x10000,
	},
	{
		.setup = ptp_ocp_fb_board_init,
	},
@@ -614,13 +641,17 @@ static struct ocp_selector ptp_ocp_clock[] = {
#define SMA_DISABLE		0x10000

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 },
	{ .name = "10Mhz",	.value = 0x0000 },
	{ .name = "PPS1",	.value = 0x0001 },
	{ .name = "PPS2",	.value = 0x0002 },
	{ .name = "TS1",	.value = 0x0004 },
	{ .name = "TS2",	.value = 0x0008 },
	{ .name = "IRIG",	.value = 0x0010 },
	{ .name = "DCF",	.value = 0x0020 },
	{ .name = "FREQ1",	.value = 0x0100 },
	{ .name = "FREQ2",	.value = 0x0200 },
	{ .name = "FREQ3",	.value = 0x0400 },
	{ .name = "FREQ4",	.value = 0x0800 },
	{ .name = "None",	.value = SMA_DISABLE },
	{ }
};
@@ -1835,6 +1866,8 @@ ptp_ocp_fb_board_init(struct ptp_ocp *bp, struct ocp_resource *r)
	ver = bp->fw_version & 0xffff;
	if (ver >= 19)
		bp->fw_cap |= OCP_CAP_SIGNAL;
	if (ver >= 20)
		bp->fw_cap |= OCP_CAP_FREQ;

	ptp_ocp_tod_init(bp);
	ptp_ocp_nmea_out_init(bp);
@@ -2430,6 +2463,73 @@ static EXT_ATTR_RO(signal, start, 1);
static EXT_ATTR_RO(signal, start, 2);
static EXT_ATTR_RO(signal, start, 3);

static ssize_t
seconds_store(struct device *dev, struct device_attribute *attr,
	      const char *buf, size_t count)
{
	struct dev_ext_attribute *ea = to_ext_attr(attr);
	struct ptp_ocp *bp = dev_get_drvdata(dev);
	int idx = (uintptr_t)ea->var;
	u32 val;
	int err;

	err = kstrtou32(buf, 0, &val);
	if (err)
		return err;
	if (val > 0xff)
		return -EINVAL;

	if (val)
		val = (val << 8) | 0x1;

	iowrite32(val, &bp->freq_in[idx]->ctrl);

	return count;
}

static ssize_t
seconds_show(struct device *dev, struct device_attribute *attr, char *buf)
{
	struct dev_ext_attribute *ea = to_ext_attr(attr);
	struct ptp_ocp *bp = dev_get_drvdata(dev);
	int idx = (uintptr_t)ea->var;
	u32 val;

	val = ioread32(&bp->freq_in[idx]->ctrl);
	if (val & 1)
		val = (val >> 8) & 0xff;
	else
		val = 0;

	return sysfs_emit(buf, "%u\n", val);
}
static EXT_ATTR_RW(freq, seconds, 0);
static EXT_ATTR_RW(freq, seconds, 1);
static EXT_ATTR_RW(freq, seconds, 2);
static EXT_ATTR_RW(freq, seconds, 3);

static ssize_t
frequency_show(struct device *dev, struct device_attribute *attr, char *buf)
{
	struct dev_ext_attribute *ea = to_ext_attr(attr);
	struct ptp_ocp *bp = dev_get_drvdata(dev);
	int idx = (uintptr_t)ea->var;
	u32 val;

	val = ioread32(&bp->freq_in[idx]->status);
	if (val & FREQ_STATUS_ERROR)
		return sysfs_emit(buf, "error\n");
	if (val & FREQ_STATUS_OVERRUN)
		return sysfs_emit(buf, "overrun\n");
	if (val & FREQ_STATUS_VALID)
		return sysfs_emit(buf, "%lu\n", val & FREQ_STATUS_MASK);
	return 0;
}
static EXT_ATTR_RO(freq, frequency, 0);
static EXT_ATTR_RO(freq, frequency, 1);
static EXT_ATTR_RO(freq, frequency, 2);
static EXT_ATTR_RO(freq, frequency, 3);

static ssize_t
serialnum_show(struct device *dev, struct device_attribute *attr, char *buf)
{
@@ -2689,6 +2789,26 @@ DEVICE_SIGNAL_GROUP(gen2, 1);
DEVICE_SIGNAL_GROUP(gen3, 2);
DEVICE_SIGNAL_GROUP(gen4, 3);

#define _DEVICE_FREQ_GROUP_ATTRS(_nr)					\
	static struct attribute *fb_timecard_freq##_nr##_attrs[] = {	\
		&dev_attr_freq##_nr##_seconds.attr.attr,		\
		&dev_attr_freq##_nr##_frequency.attr.attr,		\
		NULL,							\
	}

#define DEVICE_FREQ_GROUP(_name, _nr)					\
	_DEVICE_FREQ_GROUP_ATTRS(_nr);					\
	static const struct attribute_group				\
			fb_timecard_freq##_nr##_group = {		\
		.name = #_name,						\
		.attrs = fb_timecard_freq##_nr##_attrs,			\
}

DEVICE_FREQ_GROUP(freq1, 0);
DEVICE_FREQ_GROUP(freq2, 1);
DEVICE_FREQ_GROUP(freq3, 2);
DEVICE_FREQ_GROUP(freq4, 3);

static struct attribute *fb_timecard_attrs[] = {
	&dev_attr_serialnum.attr,
	&dev_attr_gnss_sync.attr,
@@ -2717,6 +2837,10 @@ static const struct ocp_attr_group fb_timecard_groups[] = {
	{ .cap = OCP_CAP_SIGNAL,    .group = &fb_timecard_signal1_group },
	{ .cap = OCP_CAP_SIGNAL,    .group = &fb_timecard_signal2_group },
	{ .cap = OCP_CAP_SIGNAL,    .group = &fb_timecard_signal3_group },
	{ .cap = OCP_CAP_FREQ,	    .group = &fb_timecard_freq0_group },
	{ .cap = OCP_CAP_FREQ,	    .group = &fb_timecard_freq1_group },
	{ .cap = OCP_CAP_FREQ,	    .group = &fb_timecard_freq2_group },
	{ .cap = OCP_CAP_FREQ,	    .group = &fb_timecard_freq3_group },
	{ },
};

@@ -2781,6 +2905,36 @@ _signal_summary_show(struct seq_file *s, struct ptp_ocp *bp, int nr)
	seq_printf(s, " start:%llu\n", signal->start);
}

static void
_frequency_summary_show(struct seq_file *s, int nr,
			struct frequency_reg __iomem *reg)
{
	char label[8];
	bool on;
	u32 val;

	if (!reg)
		return;

	sprintf(label, "FREQ%d", nr);
	val = ioread32(&reg->ctrl);
	on = val & 1;
	val = (val >> 8) & 0xff;
	seq_printf(s, "%7s: %s, sec:%u",
		   label,
		   on ? " ON" : "OFF",
		   val);

	val = ioread32(&reg->status);
	if (val & FREQ_STATUS_ERROR)
		seq_printf(s, ", error");
	if (val & FREQ_STATUS_OVERRUN)
		seq_printf(s, ", overrun");
	if (val & FREQ_STATUS_VALID)
		seq_printf(s, ", freq %lu Hz", val & FREQ_STATUS_MASK);
	seq_printf(s, "  reg:%x\n", val);
}

static int
ptp_ocp_summary_show(struct seq_file *s, void *data)
{
@@ -2888,6 +3042,10 @@ ptp_ocp_summary_show(struct seq_file *s, void *data)
		for (i = 0; i < 4; i++)
			_signal_summary_show(s, bp, i);

	if (bp->fw_cap & OCP_CAP_FREQ)
		for (i = 0; i < 4; i++)
			_frequency_summary_show(s, i, bp->freq_in[i]);

	if (bp->irig_out) {
		ctrl = ioread32(&bp->irig_out->ctrl);
		on = ctrl & IRIG_M_CTRL_ENABLE;