Loading...
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 | // 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; if (!mii_ts) return; /* mii_timestamper statically registered by the PHY driver won't use the * register_mii_timestamper() and thus don't have ->device set. Don't * try to unregister these. */ if (!mii_ts->device) return; 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); |