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