summaryrefslogtreecommitdiff
path: root/drivers/ptp/ptp_ocp.c
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2020-12-16 00:22:29 +0300
committerLinus Torvalds <torvalds@linux-foundation.org>2020-12-16 00:22:29 +0300
commitd635a69dd4981cc51f90293f5f64268620ed1565 (patch)
tree5e0a758b402ea7d624c25c3a343545dd29e80f31 /drivers/ptp/ptp_ocp.c
parentac73e3dc8acd0a3be292755db30388c3580f5674 (diff)
parentefd5a1584537698220578227e6467638307c2a0b (diff)
downloadlinux-d635a69dd4981cc51f90293f5f64268620ed1565.tar.xz
Merge tag 'net-next-5.11' of git://git.kernel.org/pub/scm/linux/kernel/git/netdev/net-next
Pull networking updates from Jakub Kicinski: "Core: - support "prefer busy polling" NAPI operation mode, where we defer softirq for some time expecting applications to periodically busy poll - AF_XDP: improve efficiency by more batching and hindering the adjacency cache prefetcher - af_packet: make packet_fanout.arr size configurable up to 64K - tcp: optimize TCP zero copy receive in presence of partial or unaligned reads making zero copy a performance win for much smaller messages - XDP: add bulk APIs for returning / freeing frames - sched: support fragmenting IP packets as they come out of conntrack - net: allow virtual netdevs to forward UDP L4 and fraglist GSO skbs BPF: - BPF switch from crude rlimit-based to memcg-based memory accounting - BPF type format information for kernel modules and related tracing enhancements - BPF implement task local storage for BPF LSM - allow the FENTRY/FEXIT/RAW_TP tracing programs to use bpf_sk_storage Protocols: - mptcp: improve multiple xmit streams support, memory accounting and many smaller improvements - TLS: support CHACHA20-POLY1305 cipher - seg6: add support for SRv6 End.DT4/DT6 behavior - sctp: Implement RFC 6951: UDP Encapsulation of SCTP - ppp_generic: add ability to bridge channels directly - bridge: Connectivity Fault Management (CFM) support as is defined in IEEE 802.1Q section 12.14. Drivers: - mlx5: make use of the new auxiliary bus to organize the driver internals - mlx5: more accurate port TX timestamping support - mlxsw: - improve the efficiency of offloaded next hop updates by using the new nexthop object API - support blackhole nexthops - support IEEE 802.1ad (Q-in-Q) bridging - rtw88: major bluetooth co-existance improvements - iwlwifi: support new 6 GHz frequency band - ath11k: Fast Initial Link Setup (FILS) - mt7915: dual band concurrent (DBDC) support - net: ipa: add basic support for IPA v4.5 Refactor: - a few pieces of in_interrupt() cleanup work from Sebastian Andrzej Siewior - phy: add support for shared interrupts; get rid of multiple driver APIs and have the drivers write a full IRQ handler, slight growth of driver code should be compensated by the simpler API which also allows shared IRQs - add common code for handling netdev per-cpu counters - move TX packet re-allocation from Ethernet switch tag drivers to a central place - improve efficiency and rename nla_strlcpy - number of W=1 warning cleanups as we now catch those in a patchwork build bot Old code removal: - wan: delete the DLCI / SDLA drivers - wimax: move to staging - wifi: remove old WDS wifi bridging support" * tag 'net-next-5.11' of git://git.kernel.org/pub/scm/linux/kernel/git/netdev/net-next: (1922 commits) net: hns3: fix expression that is currently always true net: fix proc_fs init handling in af_packet and tls nfc: pn533: convert comma to semicolon af_vsock: Assign the vsock transport considering the vsock address flags af_vsock: Set VMADDR_FLAG_TO_HOST flag on the receive path vsock_addr: Check for supported flag values vm_sockets: Add VMADDR_FLAG_TO_HOST vsock flag vm_sockets: Add flags field in the vsock address data structure net: Disable NETIF_F_HW_TLS_TX when HW_CSUM is disabled tcp: Add logic to check for SYN w/ data in tcp_simple_retransmit net: mscc: ocelot: install MAC addresses in .ndo_set_rx_mode from process context nfc: s3fwrn5: Release the nfc firmware net: vxget: clean up sparse warnings mlxsw: spectrum_router: Use eXtended mezzanine to offload IPv4 router mlxsw: spectrum: Set KVH XLT cache mode for Spectrum2/3 mlxsw: spectrum_router_xm: Introduce basic XM cache flushing mlxsw: reg: Add Router LPM Cache Enable Register mlxsw: reg: Add Router LPM Cache ML Delete Register mlxsw: spectrum_router_xm: Implement L-value tracking for M-index mlxsw: reg: Add XM Router M Table Register ...
Diffstat (limited to 'drivers/ptp/ptp_ocp.c')
-rw-r--r--drivers/ptp/ptp_ocp.c398
1 files changed, 398 insertions, 0 deletions
diff --git a/drivers/ptp/ptp_ocp.c b/drivers/ptp/ptp_ocp.c
new file mode 100644
index 000000000000..530e5f90095e
--- /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");