| // 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_obj(*desc); | |
| 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); |