| // 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); |