summaryrefslogtreecommitdiff
path: root/drivers/ptp/ptp_ocp.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/ptp/ptp_ocp.c')
-rw-r--r--drivers/ptp/ptp_ocp.c172
1 files changed, 111 insertions, 61 deletions
diff --git a/drivers/ptp/ptp_ocp.c b/drivers/ptp/ptp_ocp.c
index 5feecaadde8e..65fe05cac8c4 100644
--- a/drivers/ptp/ptp_ocp.c
+++ b/drivers/ptp/ptp_ocp.c
@@ -25,8 +25,7 @@
#include <linux/crc16.h>
#include <linux/dpll.h>
-#define PCI_VENDOR_ID_FACEBOOK 0x1d9b
-#define PCI_DEVICE_ID_FACEBOOK_TIMECARD 0x0400
+#define PCI_DEVICE_ID_META_TIMECARD 0x0400
#define PCI_VENDOR_ID_CELESTICA 0x18d4
#define PCI_DEVICE_ID_CELESTICA_TIMECARD 0x1008
@@ -315,6 +314,8 @@ struct ptp_ocp_serial_port {
#define OCP_BOARD_ID_LEN 13
#define OCP_SERIAL_LEN 6
#define OCP_SMA_NUM 4
+#define OCP_SIGNAL_NUM 4
+#define OCP_FREQ_NUM 4
enum {
PORT_GNSS,
@@ -342,8 +343,8 @@ 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 frequency_reg __iomem *freq_in[OCP_FREQ_NUM];
+ struct ptp_ocp_ext_src *signal_out[OCP_SIGNAL_NUM];
struct ptp_ocp_ext_src *pps;
struct ptp_ocp_ext_src *ts0;
struct ptp_ocp_ext_src *ts1;
@@ -378,10 +379,12 @@ struct ptp_ocp {
u32 utc_tai_offset;
u32 ts_window_adjust;
u64 fw_cap;
- struct ptp_ocp_signal signal[4];
+ struct ptp_ocp_signal signal[OCP_SIGNAL_NUM];
struct ptp_ocp_sma_connector sma[OCP_SMA_NUM];
const struct ocp_sma_op *sma_op;
struct dpll_device *dpll;
+ int signals_nr;
+ int freq_in_nr;
};
#define OCP_REQ_TIMESTAMP BIT(0)
@@ -1026,7 +1029,7 @@ static struct ocp_resource ocp_adva_resource[] = {
};
static const struct pci_device_id ptp_ocp_pcidev_id[] = {
- { PCI_DEVICE_DATA(FACEBOOK, TIMECARD, &ocp_fb_resource) },
+ { PCI_DEVICE_DATA(META, TIMECARD, &ocp_fb_resource) },
{ PCI_DEVICE_DATA(CELESTICA, TIMECARD, &ocp_fb_resource) },
{ PCI_DEVICE_DATA(OROLIA, ARTCARD, &ocp_art_resource) },
{ PCI_DEVICE_DATA(ADVA, TIMECARD, &ocp_adva_resource) },
@@ -1481,6 +1484,8 @@ static const struct ptp_clock_info ptp_ocp_clock_info = {
.pps = true,
.n_ext_ts = 6,
.n_per_out = 5,
+ .supported_extts_flags = PTP_STRICT_FLAGS | PTP_RISING_EDGE,
+ .supported_perout_flags = PTP_PEROUT_DUTY_CYCLE | PTP_PEROUT_PHASE,
};
static void
@@ -1522,7 +1527,7 @@ ptp_ocp_utc_distribute(struct ptp_ocp *bp, u32 val)
static void
ptp_ocp_watchdog(struct timer_list *t)
{
- struct ptp_ocp *bp = from_timer(bp, t, watchdog);
+ struct ptp_ocp *bp = timer_container_of(bp, t, watchdog);
unsigned long flags;
u32 status, utc_offset;
@@ -2067,6 +2072,7 @@ ptp_ocp_signal_set(struct ptp_ocp *bp, int gen, struct ptp_ocp_signal *s)
if (!s->start) {
/* roundup() does not work on 32-bit systems */
s->start = DIV64_U64_ROUND_UP(start_ns, s->period);
+ s->start *= s->period;
s->start = ktime_add(s->start, s->phase);
}
@@ -2218,6 +2224,9 @@ ptp_ocp_ts_enable(void *priv, u32 req, bool enable)
static void
ptp_ocp_unregister_ext(struct ptp_ocp_ext_src *ext)
{
+ if (!ext)
+ return;
+
ext->info->enable(ext, ~0, false);
pci_free_irq(ext->bp->pdev, ext->irq_vec, ext);
kfree(ext);
@@ -2367,7 +2376,7 @@ ptp_ocp_attr_group_add(struct ptp_ocp *bp,
if (attr_tbl[i].cap & bp->fw_cap)
count++;
- bp->attr_group = kcalloc(count + 1, sizeof(struct attribute_group *),
+ bp->attr_group = kcalloc(count + 1, sizeof(*bp->attr_group),
GFP_KERNEL);
if (!bp->attr_group)
return -ENOMEM;
@@ -2541,7 +2550,7 @@ ptp_ocp_sma_fb_init(struct ptp_ocp *bp)
for (i = 0; i < OCP_SMA_NUM; i++) {
bp->sma[i].fixed_fcn = true;
bp->sma[i].fixed_dir = true;
- bp->sma[1].dpll_prop.capabilities &=
+ bp->sma[i].dpll_prop.capabilities &=
~DPLL_PIN_CAPABILITIES_DIRECTION_CAN_CHANGE;
}
return;
@@ -2573,12 +2582,60 @@ static const struct ocp_sma_op ocp_fb_sma_op = {
.set_output = ptp_ocp_sma_fb_set_output,
};
+static int
+ptp_ocp_sma_adva_set_output(struct ptp_ocp *bp, int sma_nr, u32 val)
+{
+ u32 reg, mask, shift;
+ unsigned long flags;
+ u32 __iomem *gpio;
+
+ gpio = sma_nr > 2 ? &bp->sma_map1->gpio2 : &bp->sma_map2->gpio2;
+ shift = sma_nr & 1 ? 0 : 16;
+
+ mask = 0xffff << (16 - shift);
+
+ spin_lock_irqsave(&bp->lock, flags);
+
+ reg = ioread32(gpio);
+ reg = (reg & mask) | (val << shift);
+
+ iowrite32(reg, gpio);
+
+ spin_unlock_irqrestore(&bp->lock, flags);
+
+ return 0;
+}
+
+static int
+ptp_ocp_sma_adva_set_inputs(struct ptp_ocp *bp, int sma_nr, u32 val)
+{
+ u32 reg, mask, shift;
+ unsigned long flags;
+ u32 __iomem *gpio;
+
+ gpio = sma_nr > 2 ? &bp->sma_map2->gpio1 : &bp->sma_map1->gpio1;
+ shift = sma_nr & 1 ? 0 : 16;
+
+ mask = 0xffff << (16 - shift);
+
+ spin_lock_irqsave(&bp->lock, flags);
+
+ reg = ioread32(gpio);
+ reg = (reg & mask) | (val << shift);
+
+ iowrite32(reg, gpio);
+
+ spin_unlock_irqrestore(&bp->lock, flags);
+
+ return 0;
+}
+
static const struct ocp_sma_op ocp_adva_sma_op = {
.tbl = { ptp_ocp_adva_sma_in, ptp_ocp_adva_sma_out },
.init = ptp_ocp_sma_fb_init,
.get = ptp_ocp_sma_fb_get,
- .set_inputs = ptp_ocp_sma_fb_set_inputs,
- .set_output = ptp_ocp_sma_fb_set_output,
+ .set_inputs = ptp_ocp_sma_adva_set_inputs,
+ .set_output = ptp_ocp_sma_adva_set_output,
};
static int
@@ -2644,6 +2701,8 @@ ptp_ocp_fb_board_init(struct ptp_ocp *bp, struct ocp_resource *r)
bp->eeprom_map = fb_eeprom_map;
bp->fw_version = ioread32(&bp->image->version);
bp->sma_op = &ocp_fb_sma_op;
+ bp->signals_nr = 4;
+ bp->freq_in_nr = 4;
ptp_ocp_fb_set_version(bp);
@@ -2809,6 +2868,8 @@ ptp_ocp_art_board_init(struct ptp_ocp *bp, struct ocp_resource *r)
bp->fw_version = ioread32(&bp->reg->version);
bp->fw_tag = 2;
bp->sma_op = &ocp_art_sma_op;
+ bp->signals_nr = 4;
+ bp->freq_in_nr = 4;
/* Enable MAC serial port during initialisation */
iowrite32(1, &bp->board_config->mro50_serial_activate);
@@ -2835,6 +2896,8 @@ ptp_ocp_adva_board_init(struct ptp_ocp *bp, struct ocp_resource *r)
bp->flash_start = 0xA00000;
bp->eeprom_map = fb_eeprom_map;
bp->sma_op = &ocp_adva_sma_op;
+ bp->signals_nr = 2;
+ bp->freq_in_nr = 2;
version = ioread32(&bp->image->version);
/* if lower 16 bits are empty, this is the fw loader. */
@@ -3189,20 +3252,16 @@ signal_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);
struct ptp_ocp_signal *signal;
+ int gen = (uintptr_t)ea->var;
struct timespec64 ts;
- ssize_t count;
- int i;
-
- i = (uintptr_t)ea->var;
- signal = &bp->signal[i];
- count = sysfs_emit(buf, "%llu %d %llu %d", signal->period,
- signal->duty, signal->phase, signal->polarity);
+ signal = &bp->signal[gen];
ts = ktime_to_timespec64(signal->start);
- count += sysfs_emit_at(buf, count, " %ptT TAI\n", &ts);
- return count;
+ return sysfs_emit(buf, "%llu %d %llu %d %ptT TAI\n",
+ signal->period, signal->duty, signal->phase, signal->polarity,
+ &ts.tv_sec);
}
static EXT_ATTR_RW(signal, signal, 0);
static EXT_ATTR_RW(signal, signal, 1);
@@ -3369,6 +3428,12 @@ ptp_ocp_tty_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);
+ /*
+ * NOTE: This output does not include a trailing newline for backward
+ * compatibility. Existing userspace software uses this value directly
+ * as a device path (e.g., "/dev/ttyS4"), and adding a newline would
+ * break those applications. Do not add a newline to this output.
+ */
return sysfs_emit(buf, "ttyS%d", bp->port[(uintptr_t)ea->var].line);
}
@@ -3692,7 +3757,7 @@ DEVICE_FREQ_GROUP(freq4, 3);
static ssize_t
disciplining_config_read(struct file *filp, struct kobject *kobj,
- struct bin_attribute *bin_attr, char *buf,
+ const struct bin_attribute *bin_attr, char *buf,
loff_t off, size_t count)
{
struct ptp_ocp *bp = dev_get_drvdata(kobj_to_dev(kobj));
@@ -3727,7 +3792,7 @@ out:
static ssize_t
disciplining_config_write(struct file *filp, struct kobject *kobj,
- struct bin_attribute *bin_attr, char *buf,
+ const struct bin_attribute *bin_attr, char *buf,
loff_t off, size_t count)
{
struct ptp_ocp *bp = dev_get_drvdata(kobj_to_dev(kobj));
@@ -3750,11 +3815,11 @@ disciplining_config_write(struct file *filp, struct kobject *kobj,
return err;
}
-static BIN_ATTR_RW(disciplining_config, OCP_ART_CONFIG_SIZE);
+static const BIN_ATTR_RW(disciplining_config, OCP_ART_CONFIG_SIZE);
static ssize_t
temperature_table_read(struct file *filp, struct kobject *kobj,
- struct bin_attribute *bin_attr, char *buf,
+ const struct bin_attribute *bin_attr, char *buf,
loff_t off, size_t count)
{
struct ptp_ocp *bp = dev_get_drvdata(kobj_to_dev(kobj));
@@ -3789,7 +3854,7 @@ out:
static ssize_t
temperature_table_write(struct file *filp, struct kobject *kobj,
- struct bin_attribute *bin_attr, char *buf,
+ const struct bin_attribute *bin_attr, char *buf,
loff_t off, size_t count)
{
struct ptp_ocp *bp = dev_get_drvdata(kobj_to_dev(kobj));
@@ -3812,7 +3877,7 @@ temperature_table_write(struct file *filp, struct kobject *kobj,
return err;
}
-static BIN_ATTR_RW(temperature_table, OCP_ART_TEMP_TABLE_SIZE);
+static const BIN_ATTR_RW(temperature_table, OCP_ART_TEMP_TABLE_SIZE);
static struct attribute *fb_timecard_attrs[] = {
&dev_attr_serialnum.attr,
@@ -3867,7 +3932,7 @@ static struct attribute *art_timecard_attrs[] = {
NULL,
};
-static struct bin_attribute *bin_art_timecard_attrs[] = {
+static const struct bin_attribute *const bin_art_timecard_attrs[] = {
&bin_attr_disciplining_config,
&bin_attr_temperature_table,
NULL,
@@ -3955,13 +4020,10 @@ _signal_summary_show(struct seq_file *s, struct ptp_ocp *bp, int nr)
{
struct signal_reg __iomem *reg = bp->signal_out[nr]->mem;
struct ptp_ocp_signal *signal = &bp->signal[nr];
- char label[8];
+ char label[16];
bool on;
u32 val;
- if (!signal)
- return;
-
on = signal->running;
sprintf(label, "GEN%d", nr + 1);
seq_printf(s, "%7s: %s, period:%llu duty:%d%% phase:%llu pol:%d",
@@ -3981,7 +4043,7 @@ static void
_frequency_summary_show(struct seq_file *s, int nr,
struct frequency_reg __iomem *reg)
{
- char label[8];
+ char label[16];
bool on;
u32 val;
@@ -4125,11 +4187,11 @@ ptp_ocp_summary_show(struct seq_file *s, void *data)
}
if (bp->fw_cap & OCP_CAP_SIGNAL)
- for (i = 0; i < 4; i++)
+ for (i = 0; i < bp->signals_nr; i++)
_signal_summary_show(s, bp, i);
if (bp->fw_cap & OCP_CAP_FREQ)
- for (i = 0; i < 4; i++)
+ for (i = 0; i < bp->freq_in_nr; i++)
_frequency_summary_show(s, i, bp->freq_in[i]);
if (bp->irig_out) {
@@ -4229,11 +4291,9 @@ ptp_ocp_summary_show(struct seq_file *s, void *data)
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: %ptSp == %ptS TAI\n", "PHC", &ts, &ts);
+ seq_printf(s, "%7s: %ptSp == %ptS UTC offset %d\n", "SYS",
+ &sys_ts, &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);
@@ -4420,7 +4480,7 @@ ptp_ocp_complete(struct ptp_ocp *bp)
pps = pps_lookup_dev(bp->ptp);
if (pps)
- ptp_ocp_symlink(bp, pps->dev, "pps");
+ ptp_ocp_symlink(bp, &pps->dev, "pps");
ptp_ocp_debugfs_add_device(bp);
@@ -4441,9 +4501,8 @@ ptp_ocp_phc_info(struct ptp_ocp *bp)
ptp_clock_index(bp->ptp));
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,
- bp->sync ? "in-sync" : "UNSYNCED");
+ dev_info(&bp->pdev->dev, "Time: %ptSp, %s\n",
+ &ts, bp->sync ? "in-sync" : "UNSYNCED");
}
static void
@@ -4497,23 +4556,15 @@ ptp_ocp_detach(struct ptp_ocp *bp)
ptp_ocp_debugfs_remove_device(bp);
ptp_ocp_detach_sysfs(bp);
ptp_ocp_attr_group_del(bp);
- if (timer_pending(&bp->watchdog))
- del_timer_sync(&bp->watchdog);
- if (bp->ts0)
- ptp_ocp_unregister_ext(bp->ts0);
- if (bp->ts1)
- ptp_ocp_unregister_ext(bp->ts1);
- if (bp->ts2)
- ptp_ocp_unregister_ext(bp->ts2);
- if (bp->ts3)
- ptp_ocp_unregister_ext(bp->ts3);
- if (bp->ts4)
- ptp_ocp_unregister_ext(bp->ts4);
- if (bp->pps)
- ptp_ocp_unregister_ext(bp->pps);
+ timer_delete_sync(&bp->watchdog);
+ ptp_ocp_unregister_ext(bp->ts0);
+ ptp_ocp_unregister_ext(bp->ts1);
+ ptp_ocp_unregister_ext(bp->ts2);
+ ptp_ocp_unregister_ext(bp->ts3);
+ ptp_ocp_unregister_ext(bp->ts4);
+ ptp_ocp_unregister_ext(bp->pps);
for (i = 0; i < 4; i++)
- if (bp->signal_out[i])
- ptp_ocp_unregister_ext(bp->signal_out[i]);
+ ptp_ocp_unregister_ext(bp->signal_out[i]);
for (i = 0; i < __PORT_COUNT; i++)
if (bp->port[i].line != -1)
serial8250_unregister_port(bp->port[i].line);
@@ -4766,8 +4817,7 @@ ptp_ocp_probe(struct pci_dev *pdev, const struct pci_device_id *id)
return 0;
out_dpll:
- while (i) {
- --i;
+ while (i--) {
dpll_pin_unregister(bp->dpll, bp->sma[i].dpll_pin, &dpll_pins_ops, &bp->sma[i]);
dpll_pin_put(bp->sma[i].dpll_pin);
}