]> git.proxmox.com Git - mirror_ubuntu-kernels.git/commitdiff
net: Add a layer for non-PHY MII time stamping drivers.
authorRichard Cochran <richardcochran@gmail.com>
Thu, 26 Dec 2019 02:16:16 +0000 (18:16 -0800)
committerDavid S. Miller <davem@davemloft.net>
Thu, 26 Dec 2019 03:51:33 +0000 (19:51 -0800)
While PHY time stamping drivers can simply attach their interface
directly to the PHY instance, stand alone drivers require support in
order to manage their services.  Non-PHY MII time stamping drivers
have a control interface over another bus like I2C, SPI, UART, or via
a memory mapped peripheral.  The controller device will be associated
with one or more time stamping channels, each of which sits snoops in
on a MII bus.

This patch provides a glue layer that will enable time stamping
channels to find their controlling device.

Signed-off-by: Richard Cochran <richardcochran@gmail.com>
Reviewed-by: Andrew Lunn <andrew@lunn.ch>
Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/net/phy/Makefile
drivers/net/phy/mii_timestamper.c [new file with mode: 0644]
include/linux/mii_timestamper.h
net/Kconfig

index d846b4dc1c68846a16973a4dfd64bf5b5341ae3c..fe5badf13b651eface589a7d59c9b2f4873351ae 100644 (file)
@@ -43,6 +43,8 @@ obj-$(CONFIG_MDIO_SUN4I)      += mdio-sun4i.o
 obj-$(CONFIG_MDIO_THUNDER)     += mdio-thunder.o
 obj-$(CONFIG_MDIO_XGENE)       += mdio-xgene.o
 
+obj-$(CONFIG_NETWORK_PHY_TIMESTAMPING) += mii_timestamper.o
+
 obj-$(CONFIG_SFP)              += sfp.o
 sfp-obj-$(CONFIG_SFP)          += sfp-bus.o
 obj-y                          += $(sfp-obj-y) $(sfp-obj-m)
diff --git a/drivers/net/phy/mii_timestamper.c b/drivers/net/phy/mii_timestamper.c
new file mode 100644 (file)
index 0000000..2f12c5d
--- /dev/null
@@ -0,0 +1,125 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// Support for generic time stamping devices on MII buses.
+// Copyright (C) 2018 Richard Cochran <richardcochran@gmail.com>
+//
+
+#include <linux/mii_timestamper.h>
+
+static LIST_HEAD(mii_timestamping_devices);
+static DEFINE_MUTEX(tstamping_devices_lock);
+
+struct mii_timestamping_desc {
+       struct list_head list;
+       struct mii_timestamping_ctrl *ctrl;
+       struct device *device;
+};
+
+/**
+ * register_mii_tstamp_controller() - registers an MII time stamping device.
+ *
+ * @device:    The device to be registered.
+ * @ctrl:      Pointer to device's control interface.
+ *
+ * Returns zero on success or non-zero on failure.
+ */
+int register_mii_tstamp_controller(struct device *device,
+                                  struct mii_timestamping_ctrl *ctrl)
+{
+       struct mii_timestamping_desc *desc;
+
+       desc = kzalloc(sizeof(*desc), GFP_KERNEL);
+       if (!desc)
+               return -ENOMEM;
+
+       INIT_LIST_HEAD(&desc->list);
+       desc->ctrl = ctrl;
+       desc->device = device;
+
+       mutex_lock(&tstamping_devices_lock);
+       list_add_tail(&mii_timestamping_devices, &desc->list);
+       mutex_unlock(&tstamping_devices_lock);
+
+       return 0;
+}
+EXPORT_SYMBOL(register_mii_tstamp_controller);
+
+/**
+ * unregister_mii_tstamp_controller() - unregisters an MII time stamping device.
+ *
+ * @device:    A device previously passed to register_mii_tstamp_controller().
+ */
+void unregister_mii_tstamp_controller(struct device *device)
+{
+       struct mii_timestamping_desc *desc;
+       struct list_head *this, *next;
+
+       mutex_lock(&tstamping_devices_lock);
+       list_for_each_safe(this, next, &mii_timestamping_devices) {
+               desc = list_entry(this, struct mii_timestamping_desc, list);
+               if (desc->device == device) {
+                       list_del_init(&desc->list);
+                       kfree(desc);
+                       break;
+               }
+       }
+       mutex_unlock(&tstamping_devices_lock);
+}
+EXPORT_SYMBOL(unregister_mii_tstamp_controller);
+
+/**
+ * register_mii_timestamper - Enables a given port of an MII time stamper.
+ *
+ * @node:      The device tree node of the MII time stamp controller.
+ * @port:      The index of the port to be enabled.
+ *
+ * Returns a valid interface on success or ERR_PTR otherwise.
+ */
+struct mii_timestamper *register_mii_timestamper(struct device_node *node,
+                                                unsigned int port)
+{
+       struct mii_timestamper *mii_ts = NULL;
+       struct mii_timestamping_desc *desc;
+       struct list_head *this;
+
+       mutex_lock(&tstamping_devices_lock);
+       list_for_each(this, &mii_timestamping_devices) {
+               desc = list_entry(this, struct mii_timestamping_desc, list);
+               if (desc->device->of_node == node) {
+                       mii_ts = desc->ctrl->probe_channel(desc->device, port);
+                       if (!IS_ERR(mii_ts)) {
+                               mii_ts->device = desc->device;
+                               get_device(desc->device);
+                       }
+                       break;
+               }
+       }
+       mutex_unlock(&tstamping_devices_lock);
+
+       return mii_ts ? mii_ts : ERR_PTR(-EPROBE_DEFER);
+}
+EXPORT_SYMBOL(register_mii_timestamper);
+
+/**
+ * unregister_mii_timestamper - Disables a given MII time stamper.
+ *
+ * @mii_ts:    An interface obtained via register_mii_timestamper().
+ *
+ */
+void unregister_mii_timestamper(struct mii_timestamper *mii_ts)
+{
+       struct mii_timestamping_desc *desc;
+       struct list_head *this;
+
+       mutex_lock(&tstamping_devices_lock);
+       list_for_each(this, &mii_timestamping_devices) {
+               desc = list_entry(this, struct mii_timestamping_desc, list);
+               if (desc->device == mii_ts->device) {
+                       desc->ctrl->release_channel(desc->device, mii_ts);
+                       put_device(desc->device);
+                       break;
+               }
+       }
+       mutex_unlock(&tstamping_devices_lock);
+}
+EXPORT_SYMBOL(unregister_mii_timestamper);
index 36002386029ce04b5fdb115d8cc3718baa24240a..fa940bbaf8ae4cbaf250bf77fd0b72afa2e130f6 100644 (file)
@@ -33,10 +33,15 @@ struct phy_device;
  *             the phy_device mutex.
  *
  * @ts_info:   Handles ethtool queries for hardware time stamping.
+ * @device:    Remembers the device to which the instance belongs.
  *
  * Drivers for PHY time stamping devices should embed their
  * mii_timestamper within a private structure, obtaining a reference
  * to it using container_of().
+ *
+ * Drivers for non-PHY time stamping devices should return a pointer
+ * to a mii_timestamper from the probe_channel() callback of their
+ * mii_timestamping_ctrl interface.
  */
 struct mii_timestamper {
        bool (*rxtstamp)(struct mii_timestamper *mii_ts,
@@ -53,6 +58,64 @@ struct mii_timestamper {
 
        int  (*ts_info)(struct mii_timestamper *mii_ts,
                        struct ethtool_ts_info *ts_info);
+
+       struct device *device;
+};
+
+/**
+ * struct mii_timestamping_ctrl - MII time stamping controller interface.
+ *
+ * @probe_channel:     Callback into the controller driver announcing the
+ *                     presence of the 'port' channel.  The 'device' field
+ *                     had been passed to register_mii_tstamp_controller().
+ *                     The driver must return either a pointer to a valid
+ *                     MII timestamper instance or PTR_ERR.
+ *
+ * @release_channel:   Releases an instance obtained via .probe_channel.
+ */
+struct mii_timestamping_ctrl {
+       struct mii_timestamper *(*probe_channel)(struct device *device,
+                                                unsigned int port);
+       void (*release_channel)(struct device *device,
+                               struct mii_timestamper *mii_ts);
 };
 
+#ifdef CONFIG_NETWORK_PHY_TIMESTAMPING
+
+int register_mii_tstamp_controller(struct device *device,
+                                  struct mii_timestamping_ctrl *ctrl);
+
+void unregister_mii_tstamp_controller(struct device *device);
+
+struct mii_timestamper *register_mii_timestamper(struct device_node *node,
+                                                unsigned int port);
+
+void unregister_mii_timestamper(struct mii_timestamper *mii_ts);
+
+#else
+
+static inline
+int register_mii_tstamp_controller(struct device *device,
+                                  struct mii_timestamping_ctrl *ctrl)
+{
+       return -EOPNOTSUPP;
+}
+
+static inline void unregister_mii_tstamp_controller(struct device *device)
+{
+}
+
+static inline
+struct mii_timestamper *register_mii_timestamper(struct device_node *node,
+                                                unsigned int port)
+{
+       return NULL;
+}
+
+static inline void unregister_mii_timestamper(struct mii_timestamper *mii_ts)
+{
+}
+
+#endif
+
 #endif
index bd191f978a2356255b1d06ca32a56b0710c6638c..52af65e5d28c3b486e552a25a3f7eb8882d4dcaf 100644 (file)
@@ -108,9 +108,10 @@ config NETWORK_PHY_TIMESTAMPING
        bool "Timestamping in PHY devices"
        select NET_PTP_CLASSIFY
        help
-         This allows timestamping of network packets by PHYs with
-         hardware timestamping capabilities. This option adds some
-         overhead in the transmit and receive paths.
+         This allows timestamping of network packets by PHYs (or
+         other MII bus snooping devices) with hardware timestamping
+         capabilities. This option adds some overhead in the transmit
+         and receive paths.
 
          If you are unsure how to answer this question, answer N.