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