18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci// 38c2ecf20Sopenharmony_ci// Support for generic time stamping devices on MII buses. 48c2ecf20Sopenharmony_ci// Copyright (C) 2018 Richard Cochran <richardcochran@gmail.com> 58c2ecf20Sopenharmony_ci// 68c2ecf20Sopenharmony_ci 78c2ecf20Sopenharmony_ci#include <linux/mii_timestamper.h> 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_cistatic LIST_HEAD(mii_timestamping_devices); 108c2ecf20Sopenharmony_cistatic DEFINE_MUTEX(tstamping_devices_lock); 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_cistruct mii_timestamping_desc { 138c2ecf20Sopenharmony_ci struct list_head list; 148c2ecf20Sopenharmony_ci struct mii_timestamping_ctrl *ctrl; 158c2ecf20Sopenharmony_ci struct device *device; 168c2ecf20Sopenharmony_ci}; 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci/** 198c2ecf20Sopenharmony_ci * register_mii_tstamp_controller() - registers an MII time stamping device. 208c2ecf20Sopenharmony_ci * 218c2ecf20Sopenharmony_ci * @device: The device to be registered. 228c2ecf20Sopenharmony_ci * @ctrl: Pointer to device's control interface. 238c2ecf20Sopenharmony_ci * 248c2ecf20Sopenharmony_ci * Returns zero on success or non-zero on failure. 258c2ecf20Sopenharmony_ci */ 268c2ecf20Sopenharmony_ciint register_mii_tstamp_controller(struct device *device, 278c2ecf20Sopenharmony_ci struct mii_timestamping_ctrl *ctrl) 288c2ecf20Sopenharmony_ci{ 298c2ecf20Sopenharmony_ci struct mii_timestamping_desc *desc; 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_ci desc = kzalloc(sizeof(*desc), GFP_KERNEL); 328c2ecf20Sopenharmony_ci if (!desc) 338c2ecf20Sopenharmony_ci return -ENOMEM; 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&desc->list); 368c2ecf20Sopenharmony_ci desc->ctrl = ctrl; 378c2ecf20Sopenharmony_ci desc->device = device; 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci mutex_lock(&tstamping_devices_lock); 408c2ecf20Sopenharmony_ci list_add_tail(&mii_timestamping_devices, &desc->list); 418c2ecf20Sopenharmony_ci mutex_unlock(&tstamping_devices_lock); 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_ci return 0; 448c2ecf20Sopenharmony_ci} 458c2ecf20Sopenharmony_ciEXPORT_SYMBOL(register_mii_tstamp_controller); 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_ci/** 488c2ecf20Sopenharmony_ci * unregister_mii_tstamp_controller() - unregisters an MII time stamping device. 498c2ecf20Sopenharmony_ci * 508c2ecf20Sopenharmony_ci * @device: A device previously passed to register_mii_tstamp_controller(). 518c2ecf20Sopenharmony_ci */ 528c2ecf20Sopenharmony_civoid unregister_mii_tstamp_controller(struct device *device) 538c2ecf20Sopenharmony_ci{ 548c2ecf20Sopenharmony_ci struct mii_timestamping_desc *desc; 558c2ecf20Sopenharmony_ci struct list_head *this, *next; 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci mutex_lock(&tstamping_devices_lock); 588c2ecf20Sopenharmony_ci list_for_each_safe(this, next, &mii_timestamping_devices) { 598c2ecf20Sopenharmony_ci desc = list_entry(this, struct mii_timestamping_desc, list); 608c2ecf20Sopenharmony_ci if (desc->device == device) { 618c2ecf20Sopenharmony_ci list_del_init(&desc->list); 628c2ecf20Sopenharmony_ci kfree(desc); 638c2ecf20Sopenharmony_ci break; 648c2ecf20Sopenharmony_ci } 658c2ecf20Sopenharmony_ci } 668c2ecf20Sopenharmony_ci mutex_unlock(&tstamping_devices_lock); 678c2ecf20Sopenharmony_ci} 688c2ecf20Sopenharmony_ciEXPORT_SYMBOL(unregister_mii_tstamp_controller); 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci/** 718c2ecf20Sopenharmony_ci * register_mii_timestamper - Enables a given port of an MII time stamper. 728c2ecf20Sopenharmony_ci * 738c2ecf20Sopenharmony_ci * @node: The device tree node of the MII time stamp controller. 748c2ecf20Sopenharmony_ci * @port: The index of the port to be enabled. 758c2ecf20Sopenharmony_ci * 768c2ecf20Sopenharmony_ci * Returns a valid interface on success or ERR_PTR otherwise. 778c2ecf20Sopenharmony_ci */ 788c2ecf20Sopenharmony_cistruct mii_timestamper *register_mii_timestamper(struct device_node *node, 798c2ecf20Sopenharmony_ci unsigned int port) 808c2ecf20Sopenharmony_ci{ 818c2ecf20Sopenharmony_ci struct mii_timestamper *mii_ts = NULL; 828c2ecf20Sopenharmony_ci struct mii_timestamping_desc *desc; 838c2ecf20Sopenharmony_ci struct list_head *this; 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci mutex_lock(&tstamping_devices_lock); 868c2ecf20Sopenharmony_ci list_for_each(this, &mii_timestamping_devices) { 878c2ecf20Sopenharmony_ci desc = list_entry(this, struct mii_timestamping_desc, list); 888c2ecf20Sopenharmony_ci if (desc->device->of_node == node) { 898c2ecf20Sopenharmony_ci mii_ts = desc->ctrl->probe_channel(desc->device, port); 908c2ecf20Sopenharmony_ci if (!IS_ERR(mii_ts)) { 918c2ecf20Sopenharmony_ci mii_ts->device = desc->device; 928c2ecf20Sopenharmony_ci get_device(desc->device); 938c2ecf20Sopenharmony_ci } 948c2ecf20Sopenharmony_ci break; 958c2ecf20Sopenharmony_ci } 968c2ecf20Sopenharmony_ci } 978c2ecf20Sopenharmony_ci mutex_unlock(&tstamping_devices_lock); 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci return mii_ts ? mii_ts : ERR_PTR(-EPROBE_DEFER); 1008c2ecf20Sopenharmony_ci} 1018c2ecf20Sopenharmony_ciEXPORT_SYMBOL(register_mii_timestamper); 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci/** 1048c2ecf20Sopenharmony_ci * unregister_mii_timestamper - Disables a given MII time stamper. 1058c2ecf20Sopenharmony_ci * 1068c2ecf20Sopenharmony_ci * @mii_ts: An interface obtained via register_mii_timestamper(). 1078c2ecf20Sopenharmony_ci * 1088c2ecf20Sopenharmony_ci */ 1098c2ecf20Sopenharmony_civoid unregister_mii_timestamper(struct mii_timestamper *mii_ts) 1108c2ecf20Sopenharmony_ci{ 1118c2ecf20Sopenharmony_ci struct mii_timestamping_desc *desc; 1128c2ecf20Sopenharmony_ci struct list_head *this; 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ci /* mii_timestamper statically registered by the PHY driver won't use the 1158c2ecf20Sopenharmony_ci * register_mii_timestamper() and thus don't have ->device set. Don't 1168c2ecf20Sopenharmony_ci * try to unregister these. 1178c2ecf20Sopenharmony_ci */ 1188c2ecf20Sopenharmony_ci if (!mii_ts->device) 1198c2ecf20Sopenharmony_ci return; 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci mutex_lock(&tstamping_devices_lock); 1228c2ecf20Sopenharmony_ci list_for_each(this, &mii_timestamping_devices) { 1238c2ecf20Sopenharmony_ci desc = list_entry(this, struct mii_timestamping_desc, list); 1248c2ecf20Sopenharmony_ci if (desc->device == mii_ts->device) { 1258c2ecf20Sopenharmony_ci desc->ctrl->release_channel(desc->device, mii_ts); 1268c2ecf20Sopenharmony_ci put_device(desc->device); 1278c2ecf20Sopenharmony_ci break; 1288c2ecf20Sopenharmony_ci } 1298c2ecf20Sopenharmony_ci } 1308c2ecf20Sopenharmony_ci mutex_unlock(&tstamping_devices_lock); 1318c2ecf20Sopenharmony_ci} 1328c2ecf20Sopenharmony_ciEXPORT_SYMBOL(unregister_mii_timestamper); 133