1 /*
2  * Copyright 2023-2024 NXP
3  *
4  * Based on a commit to drivers/ethernet/eth_mcux.c which was:
5  * Copyright (c) 2018 Intel Coporation
6  *
7  * SPDX-License-Identifier: Apache-2.0
8  */
9 
10 #define DT_DRV_COMPAT nxp_enet_ptp_clock
11 
12 #include <zephyr/drivers/ptp_clock.h>
13 #include <zephyr/kernel.h>
14 #include <zephyr/device.h>
15 #include <zephyr/drivers/clock_control.h>
16 #include <zephyr/drivers/pinctrl.h>
17 #include <zephyr/drivers/ethernet/eth_nxp_enet.h>
18 
19 #include <fsl_enet.h>
20 
21 struct ptp_clock_nxp_enet_config {
22 	const struct pinctrl_dev_config *pincfg;
23 	const struct device *module_dev;
24 	const struct device *port;
25 	const struct device *clock_dev;
26 	struct device *clock_subsys;
27 	void (*irq_config_func)(void);
28 };
29 
30 struct ptp_clock_nxp_enet_data {
31 	ENET_Type *base;
32 	double clock_ratio;
33 	enet_handle_t enet_handle;
34 	struct k_mutex ptp_mutex;
35 };
36 
ptp_clock_nxp_enet_set(const struct device * dev,struct net_ptp_time * tm)37 static int ptp_clock_nxp_enet_set(const struct device *dev,
38 				struct net_ptp_time *tm)
39 {
40 	struct ptp_clock_nxp_enet_data *data = dev->data;
41 	enet_ptp_time_t enet_time;
42 
43 	enet_time.second = tm->second;
44 	enet_time.nanosecond = tm->nanosecond;
45 
46 	ENET_Ptp1588SetTimer(data->base, &data->enet_handle, &enet_time);
47 
48 	return 0;
49 }
50 
ptp_clock_nxp_enet_get(const struct device * dev,struct net_ptp_time * tm)51 static int ptp_clock_nxp_enet_get(const struct device *dev,
52 				struct net_ptp_time *tm)
53 {
54 	struct ptp_clock_nxp_enet_data *data = dev->data;
55 	enet_ptp_time_t enet_time;
56 
57 	ENET_Ptp1588GetTimer(data->base, &data->enet_handle, &enet_time);
58 
59 	tm->second = enet_time.second;
60 	tm->nanosecond = enet_time.nanosecond;
61 
62 	return 0;
63 }
64 
ptp_clock_nxp_enet_adjust(const struct device * dev,int increment)65 static int ptp_clock_nxp_enet_adjust(const struct device *dev,
66 					int increment)
67 {
68 	struct ptp_clock_nxp_enet_data *data = dev->data;
69 	int ret = 0;
70 	int key;
71 
72 	if ((increment <= (int32_t)(-NSEC_PER_SEC)) ||
73 			(increment >= (int32_t)NSEC_PER_SEC)) {
74 		ret = -EINVAL;
75 	} else {
76 		key = irq_lock();
77 		if (data->base->ATPER != NSEC_PER_SEC) {
78 			ret = -EBUSY;
79 		} else {
80 			/* Seconds counter is handled by software. Change the
81 			 * period of one software second to adjust the clock.
82 			 */
83 			data->base->ATPER = NSEC_PER_SEC - increment;
84 			ret = 0;
85 		}
86 		irq_unlock(key);
87 	}
88 
89 	return ret;
90 
91 }
92 
ptp_clock_nxp_enet_rate_adjust(const struct device * dev,double ratio)93 static int ptp_clock_nxp_enet_rate_adjust(const struct device *dev,
94 					double ratio)
95 {
96 	const struct ptp_clock_nxp_enet_config *config = dev->config;
97 	struct ptp_clock_nxp_enet_data *data = dev->data;
98 	int corr;
99 	int32_t mul;
100 	double val;
101 	uint32_t enet_ref_pll_rate;
102 
103 	(void) clock_control_get_rate(config->clock_dev, config->clock_subsys,
104 				&enet_ref_pll_rate);
105 	int hw_inc = NSEC_PER_SEC / enet_ref_pll_rate;
106 
107 	/* No change needed. */
108 	if ((ratio > 1.0 && ratio - 1.0 < 0.00000001) ||
109 	   (ratio < 1.0 && 1.0 - ratio < 0.00000001)) {
110 		return 0;
111 	}
112 
113 	ratio *= data->clock_ratio;
114 
115 	/* Limit possible ratio. */
116 	if ((ratio > 1.0 + 1.0/(2 * hw_inc)) ||
117 			(ratio < 1.0 - 1.0/(2 * hw_inc))) {
118 		return -EINVAL;
119 	}
120 
121 	/* Save new ratio. */
122 	data->clock_ratio = ratio;
123 
124 	if (ratio < 1.0) {
125 		corr = hw_inc - 1;
126 		val = 1.0 / (hw_inc * (1.0 - ratio));
127 	} else if (ratio > 1.0) {
128 		corr = hw_inc + 1;
129 		val = 1.0 / (hw_inc * (ratio - 1.0));
130 	} else {
131 		val = 0;
132 		corr = hw_inc;
133 	}
134 
135 	if (val >= INT32_MAX) {
136 		/* Value is too high.
137 		 * It is not possible to adjust the rate of the clock.
138 		 */
139 		mul = 0;
140 	} else {
141 		mul = val;
142 	}
143 
144 	k_mutex_lock(&data->ptp_mutex, K_FOREVER);
145 
146 	ENET_Ptp1588AdjustTimer(data->base, corr, mul);
147 
148 	k_mutex_unlock(&data->ptp_mutex);
149 
150 	return 0;
151 }
152 
nxp_enet_ptp_clock_callback(const struct device * dev,enum nxp_enet_callback_reason event,void * cb_data)153 void nxp_enet_ptp_clock_callback(const struct device *dev,
154 			enum nxp_enet_callback_reason event,
155 			void *cb_data)
156 {
157 	const struct ptp_clock_nxp_enet_config *config = dev->config;
158 	struct ptp_clock_nxp_enet_data *data = dev->data;
159 
160 	if (event == NXP_ENET_MODULE_RESET) {
161 		enet_ptp_config_t ptp_config;
162 		uint32_t enet_ref_pll_rate;
163 		uint8_t ptp_multicast[6] = { 0x01, 0x1B, 0x19, 0x00, 0x00, 0x00 };
164 		uint8_t ptp_peer_multicast[6] = { 0x01, 0x80, 0xC2, 0x00, 0x00, 0x0E };
165 
166 		(void) clock_control_get_rate(config->clock_dev, config->clock_subsys,
167 					&enet_ref_pll_rate);
168 
169 		ENET_AddMulticastGroup(data->base, ptp_multicast);
170 		ENET_AddMulticastGroup(data->base, ptp_peer_multicast);
171 
172 		/* only for ERRATA_2579 */
173 		ptp_config.channel = kENET_PtpTimerChannel3;
174 		ptp_config.ptp1588ClockSrc_Hz = enet_ref_pll_rate;
175 		data->clock_ratio = 1.0;
176 
177 		ENET_Ptp1588SetChannelMode(data->base, kENET_PtpTimerChannel3,
178 				kENET_PtpChannelPulseHighonCompare, true);
179 		ENET_Ptp1588Configure(data->base, &data->enet_handle,
180 				      &ptp_config);
181 	}
182 
183 	if (cb_data != NULL) {
184 		/* Share the mutex with mac driver */
185 		*(uintptr_t *)cb_data = (uintptr_t)&data->ptp_mutex;
186 	}
187 }
188 
ptp_clock_nxp_enet_init(const struct device * port)189 static int ptp_clock_nxp_enet_init(const struct device *port)
190 {
191 	const struct ptp_clock_nxp_enet_config *config = port->config;
192 	struct ptp_clock_nxp_enet_data *data = port->data;
193 	int ret;
194 
195 	data->base = (ENET_Type *)DEVICE_MMIO_GET(config->module_dev);
196 
197 	ret = pinctrl_apply_state(config->pincfg, PINCTRL_STATE_DEFAULT);
198 	if (ret) {
199 		return ret;
200 	}
201 
202 	k_mutex_init(&data->ptp_mutex);
203 
204 	config->irq_config_func();
205 
206 	return 0;
207 }
208 
ptp_clock_nxp_enet_isr(const struct device * dev)209 static void ptp_clock_nxp_enet_isr(const struct device *dev)
210 {
211 	struct ptp_clock_nxp_enet_data *data = dev->data;
212 	enet_ptp_timer_channel_t channel;
213 
214 	unsigned int irq_lock_key = irq_lock();
215 
216 	/* clear channel */
217 	for (channel = kENET_PtpTimerChannel1; channel <= kENET_PtpTimerChannel4; channel++) {
218 		if (ENET_Ptp1588GetChannelStatus(data->base, channel)) {
219 			ENET_Ptp1588ClearChannelStatus(data->base, channel);
220 		}
221 	}
222 
223 	ENET_TimeStampIRQHandler(data->base, &data->enet_handle);
224 
225 	irq_unlock(irq_lock_key);
226 }
227 
228 static DEVICE_API(ptp_clock, ptp_clock_nxp_enet_api) = {
229 	.set = ptp_clock_nxp_enet_set,
230 	.get = ptp_clock_nxp_enet_get,
231 	.adjust = ptp_clock_nxp_enet_adjust,
232 	.rate_adjust = ptp_clock_nxp_enet_rate_adjust,
233 };
234 
235 #define PTP_CLOCK_NXP_ENET_INIT(n)						\
236 	static void nxp_enet_ptp_clock_##n##_irq_config_func(void)		\
237 	{									\
238 		IRQ_CONNECT(DT_INST_IRQ_BY_IDX(n, 0, irq),			\
239 				DT_INST_IRQ_BY_IDX(n, 0, priority),		\
240 				ptp_clock_nxp_enet_isr,				\
241 				DEVICE_DT_INST_GET(n),				\
242 				0);						\
243 		irq_enable(DT_INST_IRQ_BY_IDX(n, 0, irq));			\
244 	}									\
245 										\
246 	PINCTRL_DT_INST_DEFINE(n);						\
247 										\
248 	static const struct ptp_clock_nxp_enet_config				\
249 		ptp_clock_nxp_enet_##n##_config = {				\
250 			.module_dev = DEVICE_DT_GET(DT_INST_PARENT(n)),		\
251 			.pincfg = PINCTRL_DT_INST_DEV_CONFIG_GET(n),		\
252 			.port = DEVICE_DT_INST_GET(n),				\
253 			.clock_dev = DEVICE_DT_GET(DT_INST_CLOCKS_CTLR(n)),	\
254 			.clock_subsys = (void *)				\
255 					DT_INST_CLOCKS_CELL_BY_IDX(n, 0, name),	\
256 			.irq_config_func =					\
257 				nxp_enet_ptp_clock_##n##_irq_config_func,	\
258 		};								\
259 										\
260 	static struct ptp_clock_nxp_enet_data ptp_clock_nxp_enet_##n##_data;	\
261 										\
262 	DEVICE_DT_INST_DEFINE(n, &ptp_clock_nxp_enet_init, NULL,		\
263 				&ptp_clock_nxp_enet_##n##_data,			\
264 				&ptp_clock_nxp_enet_##n##_config,		\
265 				POST_KERNEL, CONFIG_PTP_CLOCK_INIT_PRIORITY,	\
266 				&ptp_clock_nxp_enet_api);
267 
268 DT_INST_FOREACH_STATUS_OKAY(PTP_CLOCK_NXP_ENET_INIT)
269