OSDN Git Service

ptp: ocp: Add debugfs entry for timecard
authorJonathan Lemon <jonathan.lemon@gmail.com>
Wed, 15 Sep 2021 02:16:30 +0000 (19:16 -0700)
committerDavid S. Miller <davem@davemloft.net>
Wed, 15 Sep 2021 10:10:01 +0000 (11:10 +0100)
Provide a view into the timecard internals for debugging.

Signed-off-by: Jonathan Lemon <jonathan.lemon@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/ptp/ptp_ocp.c

index ea12f68..3692442 100644 (file)
@@ -4,6 +4,7 @@
 #include <linux/err.h>
 #include <linux/kernel.h>
 #include <linux/module.h>
+#include <linux/debugfs.h>
 #include <linux/init.h>
 #include <linux/pci.h>
 #include <linux/serial_8250.h>
@@ -208,6 +209,7 @@ struct ptp_ocp {
        struct tod_reg __iomem  *tod;
        struct pps_reg __iomem  *pps_to_ext;
        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;
@@ -224,6 +226,7 @@ struct ptp_ocp {
        struct platform_device  *spi_flash;
        struct clk_hw           *i2c_clk;
        struct timer_list       watchdog;
+       struct dentry           *debug_root;
        time64_t                gnss_lost;
        int                     id;
        int                     n_irqs;
@@ -355,6 +358,10 @@ static struct ocp_resource ocp_fb_resource[] = {
                .offset = 0x00020000, .size = 0x1000,
        },
        {
+               OCP_MEM_RESOURCE(pps_select),
+               .offset = 0x00130000, .size = 0x1000,
+       },
+       {
                OCP_MEM_RESOURCE(sma),
                .offset = 0x00140000, .size = 0x1000,
        },
@@ -1815,6 +1822,225 @@ static struct attribute *timecard_attrs[] = {
 };
 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;
+       char *buf;
+       bool on;
+
+       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->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);
+       }
+
+       /* 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
 ptp_ocp_dev_release(struct device *dev)
 {
@@ -1918,6 +2144,8 @@ ptp_ocp_complete(struct ptp_ocp *bp)
        if (device_add_groups(&bp->dev, timecard_groups))
                pr_err("device add groups failed\n");
 
+       ptp_ocp_debugfs_add_device(bp);
+
        return 0;
 }
 
@@ -1988,6 +2216,7 @@ ptp_ocp_detach_sysfs(struct ptp_ocp *bp)
 static void
 ptp_ocp_detach(struct ptp_ocp *bp)
 {
+       ptp_ocp_debugfs_remove_device(bp);
        ptp_ocp_detach_sysfs(bp);
        if (timer_pending(&bp->watchdog))
                del_timer_sync(&bp->watchdog);
@@ -2157,6 +2386,8 @@ ptp_ocp_init(void)
        const char *what;
        int err;
 
+       ptp_ocp_debugfs_init();
+
        what = "timecard class";
        err = class_register(&timecard_class);
        if (err)
@@ -2179,6 +2410,7 @@ out_register:
 out_notifier:
        class_unregister(&timecard_class);
 out:
+       ptp_ocp_debugfs_fini();
        pr_err(KBUILD_MODNAME ": failed to register %s: %d\n", what, err);
        return err;
 }
@@ -2189,6 +2421,7 @@ ptp_ocp_fini(void)
        bus_unregister_notifier(&i2c_bus_type, &ptp_ocp_i2c_notifier);
        pci_unregister_driver(&ptp_ocp_driver);
        class_unregister(&timecard_class);
+       ptp_ocp_debugfs_fini();
 }
 
 module_init(ptp_ocp_init);