1767ff483SRichard Cochran // SPDX-License-Identifier: GPL-2.0
2767ff483SRichard Cochran //
3767ff483SRichard Cochran // Support for generic time stamping devices on MII buses.
4767ff483SRichard Cochran // Copyright (C) 2018 Richard Cochran <richardcochran@gmail.com>
5767ff483SRichard Cochran //
6767ff483SRichard Cochran 
7767ff483SRichard Cochran #include <linux/mii_timestamper.h>
8767ff483SRichard Cochran 
9767ff483SRichard Cochran static LIST_HEAD(mii_timestamping_devices);
10767ff483SRichard Cochran static DEFINE_MUTEX(tstamping_devices_lock);
11767ff483SRichard Cochran 
12767ff483SRichard Cochran struct mii_timestamping_desc {
13767ff483SRichard Cochran 	struct list_head list;
14767ff483SRichard Cochran 	struct mii_timestamping_ctrl *ctrl;
15767ff483SRichard Cochran 	struct device *device;
16767ff483SRichard Cochran };
17767ff483SRichard Cochran 
18767ff483SRichard Cochran /**
19767ff483SRichard Cochran  * register_mii_tstamp_controller() - registers an MII time stamping device.
20767ff483SRichard Cochran  *
21767ff483SRichard Cochran  * @device:	The device to be registered.
22767ff483SRichard Cochran  * @ctrl:	Pointer to device's control interface.
23767ff483SRichard Cochran  *
24767ff483SRichard Cochran  * Returns zero on success or non-zero on failure.
25767ff483SRichard Cochran  */
register_mii_tstamp_controller(struct device * device,struct mii_timestamping_ctrl * ctrl)26767ff483SRichard Cochran int register_mii_tstamp_controller(struct device *device,
27767ff483SRichard Cochran 				   struct mii_timestamping_ctrl *ctrl)
28767ff483SRichard Cochran {
29767ff483SRichard Cochran 	struct mii_timestamping_desc *desc;
30767ff483SRichard Cochran 
31767ff483SRichard Cochran 	desc = kzalloc(sizeof(*desc), GFP_KERNEL);
32767ff483SRichard Cochran 	if (!desc)
33767ff483SRichard Cochran 		return -ENOMEM;
34767ff483SRichard Cochran 
35767ff483SRichard Cochran 	INIT_LIST_HEAD(&desc->list);
36767ff483SRichard Cochran 	desc->ctrl = ctrl;
37767ff483SRichard Cochran 	desc->device = device;
38767ff483SRichard Cochran 
39767ff483SRichard Cochran 	mutex_lock(&tstamping_devices_lock);
40767ff483SRichard Cochran 	list_add_tail(&mii_timestamping_devices, &desc->list);
41767ff483SRichard Cochran 	mutex_unlock(&tstamping_devices_lock);
42767ff483SRichard Cochran 
43767ff483SRichard Cochran 	return 0;
44767ff483SRichard Cochran }
45767ff483SRichard Cochran EXPORT_SYMBOL(register_mii_tstamp_controller);
46767ff483SRichard Cochran 
47767ff483SRichard Cochran /**
48767ff483SRichard Cochran  * unregister_mii_tstamp_controller() - unregisters an MII time stamping device.
49767ff483SRichard Cochran  *
50767ff483SRichard Cochran  * @device:	A device previously passed to register_mii_tstamp_controller().
51767ff483SRichard Cochran  */
unregister_mii_tstamp_controller(struct device * device)52767ff483SRichard Cochran void unregister_mii_tstamp_controller(struct device *device)
53767ff483SRichard Cochran {
54767ff483SRichard Cochran 	struct mii_timestamping_desc *desc;
55767ff483SRichard Cochran 	struct list_head *this, *next;
56767ff483SRichard Cochran 
57767ff483SRichard Cochran 	mutex_lock(&tstamping_devices_lock);
58767ff483SRichard Cochran 	list_for_each_safe(this, next, &mii_timestamping_devices) {
59767ff483SRichard Cochran 		desc = list_entry(this, struct mii_timestamping_desc, list);
60767ff483SRichard Cochran 		if (desc->device == device) {
61767ff483SRichard Cochran 			list_del_init(&desc->list);
62767ff483SRichard Cochran 			kfree(desc);
63767ff483SRichard Cochran 			break;
64767ff483SRichard Cochran 		}
65767ff483SRichard Cochran 	}
66767ff483SRichard Cochran 	mutex_unlock(&tstamping_devices_lock);
67767ff483SRichard Cochran }
68767ff483SRichard Cochran EXPORT_SYMBOL(unregister_mii_tstamp_controller);
69767ff483SRichard Cochran 
70767ff483SRichard Cochran /**
71767ff483SRichard Cochran  * register_mii_timestamper - Enables a given port of an MII time stamper.
72767ff483SRichard Cochran  *
73767ff483SRichard Cochran  * @node:	The device tree node of the MII time stamp controller.
74767ff483SRichard Cochran  * @port:	The index of the port to be enabled.
75767ff483SRichard Cochran  *
76767ff483SRichard Cochran  * Returns a valid interface on success or ERR_PTR otherwise.
77767ff483SRichard Cochran  */
register_mii_timestamper(struct device_node * node,unsigned int port)78767ff483SRichard Cochran struct mii_timestamper *register_mii_timestamper(struct device_node *node,
79767ff483SRichard Cochran 						 unsigned int port)
80767ff483SRichard Cochran {
81767ff483SRichard Cochran 	struct mii_timestamper *mii_ts = NULL;
82767ff483SRichard Cochran 	struct mii_timestamping_desc *desc;
83767ff483SRichard Cochran 	struct list_head *this;
84767ff483SRichard Cochran 
85767ff483SRichard Cochran 	mutex_lock(&tstamping_devices_lock);
86767ff483SRichard Cochran 	list_for_each(this, &mii_timestamping_devices) {
87767ff483SRichard Cochran 		desc = list_entry(this, struct mii_timestamping_desc, list);
88767ff483SRichard Cochran 		if (desc->device->of_node == node) {
89767ff483SRichard Cochran 			mii_ts = desc->ctrl->probe_channel(desc->device, port);
90767ff483SRichard Cochran 			if (!IS_ERR(mii_ts)) {
91767ff483SRichard Cochran 				mii_ts->device = desc->device;
92767ff483SRichard Cochran 				get_device(desc->device);
93767ff483SRichard Cochran 			}
94767ff483SRichard Cochran 			break;
95767ff483SRichard Cochran 		}
96767ff483SRichard Cochran 	}
97767ff483SRichard Cochran 	mutex_unlock(&tstamping_devices_lock);
98767ff483SRichard Cochran 
99767ff483SRichard Cochran 	return mii_ts ? mii_ts : ERR_PTR(-EPROBE_DEFER);
100767ff483SRichard Cochran }
101767ff483SRichard Cochran EXPORT_SYMBOL(register_mii_timestamper);
102767ff483SRichard Cochran 
103767ff483SRichard Cochran /**
104767ff483SRichard Cochran  * unregister_mii_timestamper - Disables a given MII time stamper.
105767ff483SRichard Cochran  *
106767ff483SRichard Cochran  * @mii_ts:	An interface obtained via register_mii_timestamper().
107767ff483SRichard Cochran  *
108767ff483SRichard Cochran  */
unregister_mii_timestamper(struct mii_timestamper * mii_ts)109767ff483SRichard Cochran void unregister_mii_timestamper(struct mii_timestamper *mii_ts)
110767ff483SRichard Cochran {
111767ff483SRichard Cochran 	struct mii_timestamping_desc *desc;
112767ff483SRichard Cochran 	struct list_head *this;
113767ff483SRichard Cochran 
114*b9926da0SCalvin Johnson 	if (!mii_ts)
115*b9926da0SCalvin Johnson 		return;
116*b9926da0SCalvin Johnson 
1172e1bf3a7SMichael Walle 	/* mii_timestamper statically registered by the PHY driver won't use the
1182e1bf3a7SMichael Walle 	 * register_mii_timestamper() and thus don't have ->device set. Don't
1192e1bf3a7SMichael Walle 	 * try to unregister these.
1202e1bf3a7SMichael Walle 	 */
1212e1bf3a7SMichael Walle 	if (!mii_ts->device)
1222e1bf3a7SMichael Walle 		return;
1232e1bf3a7SMichael Walle 
124767ff483SRichard Cochran 	mutex_lock(&tstamping_devices_lock);
125767ff483SRichard Cochran 	list_for_each(this, &mii_timestamping_devices) {
126767ff483SRichard Cochran 		desc = list_entry(this, struct mii_timestamping_desc, list);
127767ff483SRichard Cochran 		if (desc->device == mii_ts->device) {
128767ff483SRichard Cochran 			desc->ctrl->release_channel(desc->device, mii_ts);
129767ff483SRichard Cochran 			put_device(desc->device);
130767ff483SRichard Cochran 			break;
131767ff483SRichard Cochran 		}
132767ff483SRichard Cochran 	}
133767ff483SRichard Cochran 	mutex_unlock(&tstamping_devices_lock);
134767ff483SRichard Cochran }
135767ff483SRichard Cochran EXPORT_SYMBOL(unregister_mii_timestamper);
136