aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net/phy/mii_timestamper.c
blob: 51ae0593a04f2f0d6362469f93f319f9efc1fe94 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
// 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);