1 | // SPDX-License-Identifier: GPL-2.0 |
2 | // |
3 | // Support for generic time stamping devices on MII buses. |
4 | // Copyright (C) 2018 Richard Cochran <richardcochran@gmail.com> |
5 | // |
6 | |
7 | #include <linux/mii_timestamper.h> |
8 | |
9 | static LIST_HEAD(mii_timestamping_devices); |
10 | static DEFINE_MUTEX(tstamping_devices_lock); |
11 | |
12 | struct mii_timestamping_desc { |
13 | struct list_head list; |
14 | struct mii_timestamping_ctrl *ctrl; |
15 | struct device *device; |
16 | }; |
17 | |
18 | /** |
19 | * register_mii_tstamp_controller() - registers an MII time stamping device. |
20 | * |
21 | * @device: The device to be registered. |
22 | * @ctrl: Pointer to device's control interface. |
23 | * |
24 | * Returns zero on success or non-zero on failure. |
25 | */ |
26 | int register_mii_tstamp_controller(struct device *device, |
27 | struct mii_timestamping_ctrl *ctrl) |
28 | { |
29 | struct mii_timestamping_desc *desc; |
30 | |
31 | desc = kzalloc(size: sizeof(*desc), GFP_KERNEL); |
32 | if (!desc) |
33 | return -ENOMEM; |
34 | |
35 | INIT_LIST_HEAD(list: &desc->list); |
36 | desc->ctrl = ctrl; |
37 | desc->device = device; |
38 | |
39 | mutex_lock(&tstamping_devices_lock); |
40 | list_add_tail(new: &mii_timestamping_devices, head: &desc->list); |
41 | mutex_unlock(lock: &tstamping_devices_lock); |
42 | |
43 | return 0; |
44 | } |
45 | EXPORT_SYMBOL(register_mii_tstamp_controller); |
46 | |
47 | /** |
48 | * unregister_mii_tstamp_controller() - unregisters an MII time stamping device. |
49 | * |
50 | * @device: A device previously passed to register_mii_tstamp_controller(). |
51 | */ |
52 | void unregister_mii_tstamp_controller(struct device *device) |
53 | { |
54 | struct mii_timestamping_desc *desc; |
55 | struct list_head *this, *next; |
56 | |
57 | mutex_lock(&tstamping_devices_lock); |
58 | list_for_each_safe(this, next, &mii_timestamping_devices) { |
59 | desc = list_entry(this, struct mii_timestamping_desc, list); |
60 | if (desc->device == device) { |
61 | list_del_init(entry: &desc->list); |
62 | kfree(objp: desc); |
63 | break; |
64 | } |
65 | } |
66 | mutex_unlock(lock: &tstamping_devices_lock); |
67 | } |
68 | EXPORT_SYMBOL(unregister_mii_tstamp_controller); |
69 | |
70 | /** |
71 | * register_mii_timestamper - Enables a given port of an MII time stamper. |
72 | * |
73 | * @node: The device tree node of the MII time stamp controller. |
74 | * @port: The index of the port to be enabled. |
75 | * |
76 | * Returns a valid interface on success or ERR_PTR otherwise. |
77 | */ |
78 | struct mii_timestamper *register_mii_timestamper(struct device_node *node, |
79 | unsigned int port) |
80 | { |
81 | struct mii_timestamper *mii_ts = NULL; |
82 | struct mii_timestamping_desc *desc; |
83 | struct list_head *this; |
84 | |
85 | mutex_lock(&tstamping_devices_lock); |
86 | list_for_each(this, &mii_timestamping_devices) { |
87 | desc = list_entry(this, struct mii_timestamping_desc, list); |
88 | if (desc->device->of_node == node) { |
89 | mii_ts = desc->ctrl->probe_channel(desc->device, port); |
90 | if (!IS_ERR(ptr: mii_ts)) { |
91 | mii_ts->device = desc->device; |
92 | get_device(dev: desc->device); |
93 | } |
94 | break; |
95 | } |
96 | } |
97 | mutex_unlock(lock: &tstamping_devices_lock); |
98 | |
99 | return mii_ts ? mii_ts : ERR_PTR(error: -EPROBE_DEFER); |
100 | } |
101 | EXPORT_SYMBOL(register_mii_timestamper); |
102 | |
103 | /** |
104 | * unregister_mii_timestamper - Disables a given MII time stamper. |
105 | * |
106 | * @mii_ts: An interface obtained via register_mii_timestamper(). |
107 | * |
108 | */ |
109 | void unregister_mii_timestamper(struct mii_timestamper *mii_ts) |
110 | { |
111 | struct mii_timestamping_desc *desc; |
112 | struct list_head *this; |
113 | |
114 | if (!mii_ts) |
115 | return; |
116 | |
117 | /* mii_timestamper statically registered by the PHY driver won't use the |
118 | * register_mii_timestamper() and thus don't have ->device set. Don't |
119 | * try to unregister these. |
120 | */ |
121 | if (!mii_ts->device) |
122 | return; |
123 | |
124 | mutex_lock(&tstamping_devices_lock); |
125 | list_for_each(this, &mii_timestamping_devices) { |
126 | desc = list_entry(this, struct mii_timestamping_desc, list); |
127 | if (desc->device == mii_ts->device) { |
128 | desc->ctrl->release_channel(desc->device, mii_ts); |
129 | put_device(dev: desc->device); |
130 | break; |
131 | } |
132 | } |
133 | mutex_unlock(lock: &tstamping_devices_lock); |
134 | } |
135 | EXPORT_SYMBOL(unregister_mii_timestamper); |
136 | |