* [PATCH 0/1 v3 net-next] Add OpenCompute timecard driver
@ 2020-12-04 3:51 Jonathan Lemon
2020-12-04 3:51 ` [PATCH 1/1 v3 net-next] ptp: Add clock driver for the OpenCompute TimeCard Jonathan Lemon
0 siblings, 1 reply; 6+ messages in thread
From: Jonathan Lemon @ 2020-12-04 3:51 UTC (permalink / raw)
To: richardcochran, netdev, kuba; +Cc: kernel-team
From: Jonathan Lemon <bsd@fb.com>
v2->v3:
remove dev_info() informational lines
v1->v2:
make the driver dependent on CONFIG_PCI, for the test robot.
move the config option under PTP_1588_CLOCK hierarcy
v1:
initial submission
Jonathan Lemon (1):
ptp: Add clock driver for the OpenCompute TimeCard.
drivers/ptp/Kconfig | 14 ++
drivers/ptp/Makefile | 1 +
drivers/ptp/ptp_ocp.c | 400 ++++++++++++++++++++++++++++++++++++++++++
3 files changed, 415 insertions(+)
create mode 100644 drivers/ptp/ptp_ocp.c
--
2.24.1
^ permalink raw reply [flat|nested] 6+ messages in thread
* [PATCH 1/1 v3 net-next] ptp: Add clock driver for the OpenCompute TimeCard.
2020-12-04 3:51 [PATCH 0/1 v3 net-next] Add OpenCompute timecard driver Jonathan Lemon
@ 2020-12-04 3:51 ` Jonathan Lemon
2020-12-04 13:40 ` Richard Cochran
2020-12-05 21:57 ` Jakub Kicinski
0 siblings, 2 replies; 6+ messages in thread
From: Jonathan Lemon @ 2020-12-04 3:51 UTC (permalink / raw)
To: richardcochran, netdev, kuba; +Cc: kernel-team
The OpenCompute time card is an atomic clock along with
a GPS receiver that provides a Grandmaster clock source
for a PTP enabled network.
More information is available at http://www.timingcard.com/
Signed-off-by: Jonathan Lemon <jonathan.lemon@gmail.com>
---
drivers/ptp/Kconfig | 14 ++
drivers/ptp/Makefile | 1 +
drivers/ptp/ptp_ocp.c | 398 ++++++++++++++++++++++++++++++++++++++++++
3 files changed, 413 insertions(+)
create mode 100644 drivers/ptp/ptp_ocp.c
diff --git a/drivers/ptp/Kconfig b/drivers/ptp/Kconfig
index 942f72d8151d..476d7c7fe70a 100644
--- a/drivers/ptp/Kconfig
+++ b/drivers/ptp/Kconfig
@@ -151,4 +151,18 @@ config PTP_1588_CLOCK_VMW
To compile this driver as a module, choose M here: the module
will be called ptp_vmw.
+config PTP_1588_CLOCK_OCP
+ tristate "OpenCompute TimeCard as PTP clock"
+ depends on PTP_1588_CLOCK
+ depends on HAS_IOMEM && PCI
+ default n
+ help
+ This driver adds support for an OpenCompute time card.
+
+ The OpenCompute time card is an atomic clock along with
+ a GPS receiver that provides a Grandmaster clock source
+ for a PTP enabled network.
+
+ More information is available at http://www.timingcard.com/
+
endmenu
diff --git a/drivers/ptp/Makefile b/drivers/ptp/Makefile
index 7aff75f745dc..db5aef3bddc6 100644
--- a/drivers/ptp/Makefile
+++ b/drivers/ptp/Makefile
@@ -15,3 +15,4 @@ ptp-qoriq-$(CONFIG_DEBUG_FS) += ptp_qoriq_debugfs.o
obj-$(CONFIG_PTP_1588_CLOCK_IDTCM) += ptp_clockmatrix.o
obj-$(CONFIG_PTP_1588_CLOCK_IDT82P33) += ptp_idt82p33.o
obj-$(CONFIG_PTP_1588_CLOCK_VMW) += ptp_vmw.o
+obj-$(CONFIG_PTP_1588_CLOCK_OCP) += ptp_ocp.o
diff --git a/drivers/ptp/ptp_ocp.c b/drivers/ptp/ptp_ocp.c
new file mode 100644
index 000000000000..ff322789da12
--- /dev/null
+++ b/drivers/ptp/ptp_ocp.c
@@ -0,0 +1,398 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/* Copyright (c) 2020 Facebook */
+
+#include <linux/err.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/pci.h>
+#include <linux/ptp_clock_kernel.h>
+
+static const struct pci_device_id ptp_ocp_pcidev_id[] = {
+ { PCI_DEVICE(0x1d9b, 0x0400) },
+ { 0 }
+};
+MODULE_DEVICE_TABLE(pci, ptp_ocp_pcidev_id);
+
+#define OCP_REGISTER_OFFSET 0x01000000
+
+struct ocp_reg {
+ u32 ctrl;
+ u32 status;
+ u32 select;
+ u32 version;
+ u32 time_ns;
+ u32 time_sec;
+ u32 __pad0[2];
+ u32 adjust_ns;
+ u32 adjust_sec;
+ u32 __pad1[2];
+ u32 offset_ns;
+ u32 offset_window_ns;
+};
+
+#define OCP_CTRL_ENABLE BIT(0)
+#define OCP_CTRL_ADJUST_TIME BIT(1)
+#define OCP_CTRL_ADJUST_OFFSET BIT(2)
+#define OCP_CTRL_READ_TIME_REQ BIT(30)
+#define OCP_CTRL_READ_TIME_DONE BIT(31)
+
+#define OCP_STATUS_IN_SYNC BIT(0)
+
+#define OCP_SELECT_CLK_NONE 0
+#define OCP_SELECT_CLK_REG 6
+
+struct tod_reg {
+ u32 ctrl;
+ u32 status;
+ u32 uart_polarity;
+ u32 version;
+ u32 correction_sec;
+ u32 __pad0[3];
+ u32 uart_baud;
+ u32 __pad1[3];
+ u32 utc_status;
+ u32 leap;
+};
+
+#define TOD_REGISTER_OFFSET 0x01050000
+
+#define TOD_CTRL_PROTOCOL BIT(28)
+#define TOD_CTRL_DISABLE_FMT_A BIT(17)
+#define TOD_CTRL_DISABLE_FMT_B BIT(16)
+#define TOD_CTRL_ENABLE BIT(0)
+#define TOD_CTRL_GNSS_MASK ((1U << 4) - 1)
+#define TOD_CTRL_GNSS_SHIFT 24
+
+#define TOD_STATUS_UTC_MASK 0xff
+#define TOD_STATUS_UTC_VALID BIT(8)
+#define TOD_STATUS_LEAP_VALID BIT(16)
+
+struct ptp_ocp {
+ struct pci_dev *pdev;
+ spinlock_t lock;
+ void __iomem *base;
+ struct ocp_reg __iomem *reg;
+ struct tod_reg __iomem *tod;
+ struct ptp_clock *ptp;
+ struct ptp_clock_info ptp_info;
+};
+
+static int
+__ptp_ocp_gettime_locked(struct ptp_ocp *bp, struct timespec64 *ts,
+ struct ptp_system_timestamp *sts)
+{
+ u32 ctrl, time_sec, time_ns;
+ int i;
+
+ ctrl = ioread32(&bp->reg->ctrl);
+ ctrl |= OCP_CTRL_READ_TIME_REQ;
+
+ ptp_read_system_prets(sts);
+ iowrite32(ctrl, &bp->reg->ctrl);
+
+ for (i = 0; i < 100; i++) {
+ ctrl = ioread32(&bp->reg->ctrl);
+ if (ctrl & OCP_CTRL_READ_TIME_DONE)
+ break;
+ }
+ ptp_read_system_postts(sts);
+
+ time_ns = ioread32(&bp->reg->time_ns);
+ time_sec = ioread32(&bp->reg->time_sec);
+
+ ts->tv_sec = time_sec;
+ ts->tv_nsec = time_ns;
+
+ return ctrl & OCP_CTRL_READ_TIME_DONE ? 0 : -ETIMEDOUT;
+}
+
+static int
+ptp_ocp_gettimex(struct ptp_clock_info *ptp_info, struct timespec64 *ts,
+ struct ptp_system_timestamp *sts)
+{
+ struct ptp_ocp *bp = container_of(ptp_info, struct ptp_ocp, ptp_info);
+ unsigned long flags;
+ int err;
+
+ spin_lock_irqsave(&bp->lock, flags);
+ err = __ptp_ocp_gettime_locked(bp, ts, sts);
+ spin_unlock_irqrestore(&bp->lock, flags);
+
+ return err;
+}
+
+static void
+__ptp_ocp_settime_locked(struct ptp_ocp *bp, const struct timespec64 *ts)
+{
+ u32 ctrl, time_sec, time_ns;
+ u32 select;
+
+ time_ns = ts->tv_nsec;
+ time_sec = ts->tv_sec;
+
+ select = ioread32(&bp->reg->select);
+ iowrite32(OCP_SELECT_CLK_REG, &bp->reg->select);
+
+ iowrite32(time_ns, &bp->reg->adjust_ns);
+ iowrite32(time_sec, &bp->reg->adjust_sec);
+
+ ctrl = ioread32(&bp->reg->ctrl);
+ ctrl |= OCP_CTRL_ADJUST_TIME;
+ iowrite32(ctrl, &bp->reg->ctrl);
+
+ /* restore clock selection */
+ iowrite32(select >> 16, &bp->reg->select);
+}
+
+static int
+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);
+ unsigned long flags;
+
+ if (ioread32(&bp->reg->status) & OCP_STATUS_IN_SYNC)
+ return 0;
+
+ spin_lock_irqsave(&bp->lock, flags);
+ __ptp_ocp_settime_locked(bp, ts);
+ spin_unlock_irqrestore(&bp->lock, flags);
+
+ return 0;
+}
+
+static int
+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 timespec64 ts;
+ unsigned long flags;
+ int err;
+
+ if (ioread32(&bp->reg->status) & OCP_STATUS_IN_SYNC)
+ return 0;
+
+ spin_lock_irqsave(&bp->lock, flags);
+ err = __ptp_ocp_gettime_locked(bp, &ts, NULL);
+ if (likely(!err)) {
+ timespec64_add_ns(&ts, delta_ns);
+ __ptp_ocp_settime_locked(bp, &ts);
+ }
+ spin_unlock_irqrestore(&bp->lock, flags);
+
+ return err;
+}
+
+static int
+ptp_ocp_null_adjfine(struct ptp_clock_info *ptp_info, long scaled_ppm)
+{
+ if (scaled_ppm == 0)
+ return 0;
+
+ return -EOPNOTSUPP;
+}
+
+static const struct ptp_clock_info ptp_ocp_clock_info = {
+ .owner = THIS_MODULE,
+ .name = KBUILD_MODNAME,
+ .max_adj = 100000000,
+ .gettimex64 = ptp_ocp_gettimex,
+ .settime64 = ptp_ocp_settime,
+ .adjtime = ptp_ocp_adjtime,
+ .adjfine = ptp_ocp_null_adjfine,
+};
+
+static int
+ptp_ocp_check_clock(struct ptp_ocp *bp)
+{
+ struct timespec64 ts;
+ bool sync;
+ u32 ctrl;
+
+ /* make sure clock is enabled */
+ ctrl = ioread32(&bp->reg->ctrl);
+ ctrl |= OCP_CTRL_ENABLE;
+ iowrite32(ctrl, &bp->reg->ctrl);
+
+ if ((ioread32(&bp->reg->ctrl) & OCP_CTRL_ENABLE) == 0) {
+ dev_err(&bp->pdev->dev, "clock not enabled\n");
+ return -ENODEV;
+ }
+
+ sync = ioread32(&bp->reg->status) & OCP_STATUS_IN_SYNC;
+ if (!sync) {
+ ktime_get_real_ts64(&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");
+
+ return 0;
+}
+
+static void
+ptp_ocp_tod_info(struct ptp_ocp *bp)
+{
+ static const char * const proto_name[] = {
+ "NMEA", "NMEA_ZDA", "NMEA_RMC", "NMEA_none",
+ "UBX", "UBX_UTC", "UBX_LS", "UBX_none"
+ };
+ static const char * const gnss_name[] = {
+ "ALL", "COMBINED", "GPS", "GLONASS", "GALILEO", "BEIDOU",
+ };
+ u32 version, ctrl, reg;
+ int idx;
+
+ version = ioread32(&bp->tod->version);
+ dev_info(&bp->pdev->dev, "TOD Version %d.%d.%d\n",
+ version >> 24, (version >> 16) & 0xff, version & 0xffff);
+
+ ctrl = ioread32(&bp->tod->ctrl);
+ 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);
+ idx = ctrl & TOD_CTRL_PROTOCOL ? 4 : 0;
+ idx += (ctrl >> 16) & 3;
+ dev_info(&bp->pdev->dev, "control: %x\n", ctrl);
+ dev_info(&bp->pdev->dev, "TOD Protocol %s %s\n", proto_name[idx],
+ ctrl & TOD_CTRL_ENABLE ? "enabled" : "");
+
+ idx = (ctrl >> TOD_CTRL_GNSS_SHIFT) & TOD_CTRL_GNSS_MASK;
+ if (idx < ARRAY_SIZE(gnss_name))
+ dev_info(&bp->pdev->dev, "GNSS %s\n", gnss_name[idx]);
+
+ reg = ioread32(&bp->tod->status);
+ dev_info(&bp->pdev->dev, "status: %x\n", reg);
+
+ reg = ioread32(&bp->tod->correction_sec);
+ dev_info(&bp->pdev->dev, "correction: %d\n", reg);
+
+ reg = ioread32(&bp->tod->utc_status);
+ dev_info(&bp->pdev->dev, "utc_status: %x\n", reg);
+ dev_info(&bp->pdev->dev, "utc_offset: %d valid:%d leap_valid:%d\n",
+ reg & TOD_STATUS_UTC_MASK, reg & TOD_STATUS_UTC_VALID ? 1 : 0,
+ reg & TOD_STATUS_LEAP_VALID ? 1 : 0);
+}
+
+static void
+ptp_ocp_info(struct ptp_ocp *bp)
+{
+ static const char * const clock_name[] = {
+ "NO", "TOD", "IRIG", "PPS", "PTP", "RTC", "REGS", "EXT"
+ };
+ 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,
+ clock_name[select & 7],
+ ptp_clock_index(bp->ptp));
+
+ ptp_ocp_tod_info(bp);
+}
+
+static int
+ptp_ocp_probe(struct pci_dev *pdev, const struct pci_device_id *id)
+{
+ struct ptp_ocp *bp;
+ int err;
+
+ bp = kzalloc(sizeof(*bp), GFP_KERNEL);
+ if (!bp)
+ return -ENOMEM;
+ bp->pdev = pdev;
+ pci_set_drvdata(pdev, bp);
+
+ err = pci_enable_device(pdev);
+ if (err) {
+ dev_err(&pdev->dev, "pci_enable_device\n");
+ goto out_free;
+ }
+
+ err = pci_request_regions(pdev, KBUILD_MODNAME);
+ if (err) {
+ dev_err(&pdev->dev, "pci_request_region\n");
+ goto out_disable;
+ }
+
+ bp->base = pci_ioremap_bar(pdev, 0);
+ if (!bp->base) {
+ dev_err(&pdev->dev, "io_remap bar0\n");
+ err = -ENOMEM;
+ goto out;
+ }
+ bp->reg = bp->base + OCP_REGISTER_OFFSET;
+ bp->tod = bp->base + TOD_REGISTER_OFFSET;
+ bp->ptp_info = ptp_ocp_clock_info;
+ spin_lock_init(&bp->lock);
+
+ err = ptp_ocp_check_clock(bp);
+ if (err)
+ goto out;
+
+ bp->ptp = ptp_clock_register(&bp->ptp_info, &pdev->dev);
+ if (IS_ERR(bp->ptp)) {
+ dev_err(&pdev->dev, "ptp_clock_register\n");
+ err = PTR_ERR(bp->ptp);
+ goto out;
+ }
+
+ ptp_ocp_info(bp);
+
+ return 0;
+
+out:
+ pci_release_regions(pdev);
+out_disable:
+ pci_disable_device(pdev);
+out_free:
+ kfree(bp);
+
+ return err;
+}
+
+static void
+ptp_ocp_remove(struct pci_dev *pdev)
+{
+ struct ptp_ocp *bp = pci_get_drvdata(pdev);
+
+ ptp_clock_unregister(bp->ptp);
+ pci_iounmap(pdev, bp->base);
+ pci_release_regions(pdev);
+ pci_disable_device(pdev);
+ pci_set_drvdata(pdev, NULL);
+ kfree(bp);
+}
+
+static struct pci_driver ptp_ocp_driver = {
+ .name = KBUILD_MODNAME,
+ .id_table = ptp_ocp_pcidev_id,
+ .probe = ptp_ocp_probe,
+ .remove = ptp_ocp_remove,
+};
+
+static int __init
+ptp_ocp_init(void)
+{
+ int err;
+
+ err = pci_register_driver(&ptp_ocp_driver);
+ return err;
+}
+
+static void __exit
+ptp_ocp_fini(void)
+{
+ pci_unregister_driver(&ptp_ocp_driver);
+}
+
+module_init(ptp_ocp_init);
+module_exit(ptp_ocp_fini);
+
+MODULE_DESCRIPTION("OpenCompute TimeCard driver");
+MODULE_LICENSE("GPL v2");
--
2.24.1
^ permalink raw reply related [flat|nested] 6+ messages in thread
* Re: [PATCH 1/1 v3 net-next] ptp: Add clock driver for the OpenCompute TimeCard.
2020-12-04 3:51 ` [PATCH 1/1 v3 net-next] ptp: Add clock driver for the OpenCompute TimeCard Jonathan Lemon
@ 2020-12-04 13:40 ` Richard Cochran
2020-12-05 21:57 ` Jakub Kicinski
2020-12-05 21:57 ` Jakub Kicinski
1 sibling, 1 reply; 6+ messages in thread
From: Richard Cochran @ 2020-12-04 13:40 UTC (permalink / raw)
To: Jonathan Lemon; +Cc: netdev, kuba, kernel-team
On Thu, Dec 03, 2020 at 07:51:28PM -0800, Jonathan Lemon wrote:
> The OpenCompute time card is an atomic clock along with
> a GPS receiver that provides a Grandmaster clock source
> for a PTP enabled network.
>
> More information is available at http://www.timingcard.com/
>
> Signed-off-by: Jonathan Lemon <jonathan.lemon@gmail.com>
Acked-by: Richard Cochran <richardcochran@gmail.com>
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [PATCH 1/1 v3 net-next] ptp: Add clock driver for the OpenCompute TimeCard.
2020-12-04 3:51 ` [PATCH 1/1 v3 net-next] ptp: Add clock driver for the OpenCompute TimeCard Jonathan Lemon
2020-12-04 13:40 ` Richard Cochran
@ 2020-12-05 21:57 ` Jakub Kicinski
2020-12-05 22:02 ` Jakub Kicinski
1 sibling, 1 reply; 6+ messages in thread
From: Jakub Kicinski @ 2020-12-05 21:57 UTC (permalink / raw)
To: Jonathan Lemon; +Cc: richardcochran, netdev, kernel-team
On Thu, 3 Dec 2020 19:51:28 -0800 Jonathan Lemon wrote:
> +static int __init
> +ptp_ocp_init(void)
> +{
> + int err;
> +
> + err = pci_register_driver(&ptp_ocp_driver);
> + return err;
> +}
> +
> +static void __exit
> +ptp_ocp_fini(void)
> +{
> + pci_unregister_driver(&ptp_ocp_driver);
> +}
> +
> +module_init(ptp_ocp_init);
> +module_exit(ptp_ocp_fini);
FWIW if you want to send a follow up you can replace all this with:
module_pci_driver(ptp_ocp_driver);
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [PATCH 1/1 v3 net-next] ptp: Add clock driver for the OpenCompute TimeCard.
2020-12-04 13:40 ` Richard Cochran
@ 2020-12-05 21:57 ` Jakub Kicinski
0 siblings, 0 replies; 6+ messages in thread
From: Jakub Kicinski @ 2020-12-05 21:57 UTC (permalink / raw)
To: Richard Cochran, Jonathan Lemon; +Cc: netdev, kernel-team
On Fri, 4 Dec 2020 05:40:11 -0800 Richard Cochran wrote:
> On Thu, Dec 03, 2020 at 07:51:28PM -0800, Jonathan Lemon wrote:
> > The OpenCompute time card is an atomic clock along with
> > a GPS receiver that provides a Grandmaster clock source
> > for a PTP enabled network.
> >
> > More information is available at http://www.timingcard.com/
> >
> > Signed-off-by: Jonathan Lemon <jonathan.lemon@gmail.com>
>
> Acked-by: Richard Cochran <richardcochran@gmail.com>
Applied, thanks!
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [PATCH 1/1 v3 net-next] ptp: Add clock driver for the OpenCompute TimeCard.
2020-12-05 21:57 ` Jakub Kicinski
@ 2020-12-05 22:02 ` Jakub Kicinski
0 siblings, 0 replies; 6+ messages in thread
From: Jakub Kicinski @ 2020-12-05 22:02 UTC (permalink / raw)
To: Jonathan Lemon; +Cc: richardcochran, netdev, kernel-team
On Sat, 5 Dec 2020 13:57:18 -0800 Jakub Kicinski wrote:
> On Thu, 3 Dec 2020 19:51:28 -0800 Jonathan Lemon wrote:
> > +static int __init
> > +ptp_ocp_init(void)
> > +{
> > + int err;
> > +
> > + err = pci_register_driver(&ptp_ocp_driver);
> > + return err;
> > +}
> > +
> > +static void __exit
> > +ptp_ocp_fini(void)
> > +{
> > + pci_unregister_driver(&ptp_ocp_driver);
> > +}
> > +
> > +module_init(ptp_ocp_init);
> > +module_exit(ptp_ocp_fini);
>
> FWIW if you want to send a follow up you can replace all this with:
>
> module_pci_driver(ptp_ocp_driver);
Also consider adding yourself a MAINTAINERS entry.
^ permalink raw reply [flat|nested] 6+ messages in thread
end of thread, other threads:[~2020-12-05 22:03 UTC | newest]
Thread overview: 6+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2020-12-04 3:51 [PATCH 0/1 v3 net-next] Add OpenCompute timecard driver Jonathan Lemon
2020-12-04 3:51 ` [PATCH 1/1 v3 net-next] ptp: Add clock driver for the OpenCompute TimeCard Jonathan Lemon
2020-12-04 13:40 ` Richard Cochran
2020-12-05 21:57 ` Jakub Kicinski
2020-12-05 21:57 ` Jakub Kicinski
2020-12-05 22:02 ` Jakub Kicinski
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).