1 /*
2 * Copyright 2023-2024 NXP
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #define DT_DRV_COMPAT nxp_enet_mdio
8
9 #include <zephyr/kernel.h>
10 #include <zephyr/device.h>
11 #include <zephyr/net/mdio.h>
12 #include <zephyr/drivers/mdio.h>
13 #include <zephyr/drivers/ethernet/eth_nxp_enet.h>
14 #include <zephyr/drivers/pinctrl.h>
15 #include <zephyr/drivers/clock_control.h>
16 #include <zephyr/sys_clock.h>
17
18 struct nxp_enet_mdio_config {
19 const struct pinctrl_dev_config *pincfg;
20 const struct device *module_dev;
21 const struct device *clock_dev;
22 clock_control_subsys_t clock_subsys;
23 uint32_t mdc_freq;
24 bool disable_preamble;
25 };
26
27 struct nxp_enet_mdio_data {
28 ENET_Type *base;
29 struct k_mutex mdio_mutex;
30 struct k_sem mdio_sem;
31 bool interrupt_up;
32 };
33
34 /*
35 * This function is used for both read and write operations
36 * in order to wait for the completion of an MDIO transaction.
37 * It returns -ETIMEDOUT if timeout occurs as specified in DT,
38 * otherwise returns 0 if EIR MII bit is set indicting completed
39 * operation, otherwise -EIO.
40 */
nxp_enet_mdio_wait_xfer(const struct device * dev)41 static int nxp_enet_mdio_wait_xfer(const struct device *dev)
42 {
43 struct nxp_enet_mdio_data *data = dev->data;
44
45 /* This function will not make sense from IRQ context */
46 if (k_is_in_isr()) {
47 return -EWOULDBLOCK;
48 }
49
50 if (!data->interrupt_up) {
51 /* If the interrupt is not available to use yet, just busy wait */
52 k_busy_wait(CONFIG_MDIO_NXP_ENET_TIMEOUT);
53 k_sem_give(&data->mdio_sem);
54 }
55
56 /* Wait for the MDIO transaction to finish or time out */
57 k_sem_take(&data->mdio_sem, K_USEC(CONFIG_MDIO_NXP_ENET_TIMEOUT));
58
59 return 0;
60 }
61
62 /* MDIO Read API implementation */
nxp_enet_mdio_read(const struct device * dev,uint8_t prtad,uint8_t regad,uint16_t * read_data)63 static int nxp_enet_mdio_read(const struct device *dev,
64 uint8_t prtad, uint8_t regad, uint16_t *read_data)
65 {
66 struct nxp_enet_mdio_data *data = dev->data;
67 int ret;
68
69 /* Only one MDIO bus operation attempt at a time */
70 (void)k_mutex_lock(&data->mdio_mutex, K_FOREVER);
71
72 /*
73 * Clear the bit (W1C) that indicates MDIO transfer is ready to
74 * prepare to wait for it to be set once this read is done
75 */
76 data->base->EIR = ENET_EIR_MII_MASK;
77
78 /*
79 * Write MDIO frame to MII management register which will
80 * send the read command and data out to the MDIO bus as this frame:
81 * ST = start, 1 means start
82 * OP = operation, 2 means read
83 * PA = PHY/Port address
84 * RA = Register/Device Address
85 * TA = Turnaround, must be 2 to be valid
86 * data = data to be written to the PHY register
87 */
88 data->base->MMFR = ENET_MMFR_ST(0x1U) |
89 ENET_MMFR_OP(MDIO_OP_C22_READ) |
90 ENET_MMFR_PA(prtad) |
91 ENET_MMFR_RA(regad) |
92 ENET_MMFR_TA(0x2U);
93
94 ret = nxp_enet_mdio_wait_xfer(dev);
95 if (ret) {
96 (void)k_mutex_unlock(&data->mdio_mutex);
97 return ret;
98 }
99
100 /* The data is received in the same register that we wrote the command to */
101 *read_data = (data->base->MMFR & ENET_MMFR_DATA_MASK) >> ENET_MMFR_DATA_SHIFT;
102
103 /* Clear the same bit as before because the event has been handled */
104 data->base->EIR = ENET_EIR_MII_MASK;
105
106 /* This MDIO interaction is finished */
107 (void)k_mutex_unlock(&data->mdio_mutex);
108
109 return ret;
110 }
111
112 /* MDIO Write API implementation */
nxp_enet_mdio_write(const struct device * dev,uint8_t prtad,uint8_t regad,uint16_t write_data)113 static int nxp_enet_mdio_write(const struct device *dev,
114 uint8_t prtad, uint8_t regad, uint16_t write_data)
115 {
116 struct nxp_enet_mdio_data *data = dev->data;
117 int ret;
118
119 /* Only one MDIO bus operation attempt at a time */
120 (void)k_mutex_lock(&data->mdio_mutex, K_FOREVER);
121
122 /*
123 * Clear the bit (W1C) that indicates MDIO transfer is ready to
124 * prepare to wait for it to be set once this write is done
125 */
126 data->base->EIR = ENET_EIR_MII_MASK;
127
128 /*
129 * Write MDIO frame to MII management register which will
130 * send the write command and data out to the MDIO bus as this frame:
131 * ST = start, 1 means start
132 * OP = operation, 1 means write
133 * PA = PHY/Port address
134 * RA = Register/Device Address
135 * TA = Turnaround, must be 2 to be valid
136 * data = data to be written to the PHY register
137 */
138 data->base->MMFR = ENET_MMFR_ST(0x1U) |
139 ENET_MMFR_OP(MDIO_OP_C22_WRITE) |
140 ENET_MMFR_PA(prtad) |
141 ENET_MMFR_RA(regad) |
142 ENET_MMFR_TA(0x2U) |
143 write_data;
144
145 ret = nxp_enet_mdio_wait_xfer(dev);
146 if (ret) {
147 (void)k_mutex_unlock(&data->mdio_mutex);
148 return ret;
149 }
150
151 /* Clear the same bit as before because the event has been handled */
152 data->base->EIR = ENET_EIR_MII_MASK;
153
154 /* This MDIO interaction is finished */
155 (void)k_mutex_unlock(&data->mdio_mutex);
156
157 return ret;
158 }
159
160 static DEVICE_API(mdio, nxp_enet_mdio_api) = {
161 .read = nxp_enet_mdio_read,
162 .write = nxp_enet_mdio_write,
163 };
164
nxp_enet_mdio_isr_cb(const struct device * dev)165 static void nxp_enet_mdio_isr_cb(const struct device *dev)
166 {
167 struct nxp_enet_mdio_data *data = dev->data;
168
169 data->base->EIR = ENET_EIR_MII_MASK;
170
171 k_sem_give(&data->mdio_sem);
172 }
173
nxp_enet_mdio_post_module_reset_init(const struct device * dev)174 static void nxp_enet_mdio_post_module_reset_init(const struct device *dev)
175 {
176 const struct nxp_enet_mdio_config *config = dev->config;
177 struct nxp_enet_mdio_data *data = dev->data;
178 uint32_t enet_module_clock_rate;
179
180 /* Set up MSCR register */
181 (void) clock_control_get_rate(config->clock_dev, config->clock_subsys,
182 &enet_module_clock_rate);
183 uint32_t mii_speed = (enet_module_clock_rate + 2 * config->mdc_freq - 1) /
184 (2 * config->mdc_freq) - 1;
185 uint32_t holdtime = (10 + NSEC_PER_SEC / enet_module_clock_rate - 1) /
186 (NSEC_PER_SEC / enet_module_clock_rate) - 1;
187 uint32_t mscr = ENET_MSCR_MII_SPEED(mii_speed) | ENET_MSCR_HOLDTIME(holdtime) |
188 (config->disable_preamble ? ENET_MSCR_DIS_PRE_MASK : 0);
189 data->base->MSCR = mscr;
190 }
191
nxp_enet_mdio_callback(const struct device * dev,enum nxp_enet_callback_reason event,void * cb_data)192 void nxp_enet_mdio_callback(const struct device *dev,
193 enum nxp_enet_callback_reason event, void *cb_data)
194 {
195 struct nxp_enet_mdio_data *data = dev->data;
196
197 ARG_UNUSED(cb_data);
198
199 switch (event) {
200 case NXP_ENET_MODULE_RESET:
201 nxp_enet_mdio_post_module_reset_init(dev);
202 break;
203 case NXP_ENET_INTERRUPT:
204 nxp_enet_mdio_isr_cb(dev);
205 break;
206 case NXP_ENET_INTERRUPT_ENABLED:
207 /* IRQ was enabled in NVIC, now enable in enet */
208 data->interrupt_up = true;
209 data->base->EIMR |= ENET_EIMR_MII_MASK;
210 break;
211 default:
212 break;
213 }
214 }
215
nxp_enet_mdio_init(const struct device * dev)216 static int nxp_enet_mdio_init(const struct device *dev)
217 {
218 const struct nxp_enet_mdio_config *config = dev->config;
219 struct nxp_enet_mdio_data *data = dev->data;
220 int ret = 0;
221
222 data->base = (ENET_Type *)DEVICE_MMIO_GET(config->module_dev);
223
224 ret = pinctrl_apply_state(config->pincfg, PINCTRL_STATE_DEFAULT);
225 if (ret) {
226 return ret;
227 }
228
229 ret = k_mutex_init(&data->mdio_mutex);
230 if (ret) {
231 return ret;
232 }
233
234 ret = k_sem_init(&data->mdio_sem, 0, 1);
235 if (ret) {
236 return ret;
237 }
238
239 /* All operations done after module reset should be done during device init too */
240 nxp_enet_mdio_post_module_reset_init(dev);
241
242 return ret;
243 }
244
245 #define NXP_ENET_MDIO_INIT(inst) \
246 PINCTRL_DT_INST_DEFINE(inst); \
247 \
248 static const struct nxp_enet_mdio_config nxp_enet_mdio_cfg_##inst = { \
249 .module_dev = DEVICE_DT_GET(DT_INST_PARENT(inst)), \
250 .pincfg = PINCTRL_DT_INST_DEV_CONFIG_GET(inst), \
251 .clock_dev = DEVICE_DT_GET(DT_CLOCKS_CTLR(DT_INST_PARENT(inst))), \
252 .clock_subsys = (void *) DT_CLOCKS_CELL_BY_IDX( \
253 DT_INST_PARENT(inst), 0, name), \
254 .disable_preamble = DT_INST_PROP(inst, suppress_preamble), \
255 .mdc_freq = DT_INST_PROP(inst, clock_frequency), \
256 }; \
257 \
258 static struct nxp_enet_mdio_data nxp_enet_mdio_data_##inst; \
259 \
260 DEVICE_DT_INST_DEFINE(inst, &nxp_enet_mdio_init, NULL, \
261 &nxp_enet_mdio_data_##inst, &nxp_enet_mdio_cfg_##inst, \
262 POST_KERNEL, CONFIG_MDIO_INIT_PRIORITY, \
263 &nxp_enet_mdio_api);
264
265
266 DT_INST_FOREACH_STATUS_OKAY(NXP_ENET_MDIO_INIT)
267