1 /*
2  * Copyright 2024 NXP
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #define DT_DRV_COMPAT nxp_enet_qos_mdio
8 
9 #include <zephyr/net/mdio.h>
10 #include <zephyr/drivers/mdio.h>
11 #include <zephyr/kernel.h>
12 #include <zephyr/drivers/pinctrl.h>
13 #include <zephyr/drivers/ethernet/eth_nxp_enet_qos.h>
14 #include <zephyr/sys/util.h>
15 
16 #include <zephyr/logging/log.h>
17 LOG_MODULE_REGISTER(mdio_nxp_enet_qos, CONFIG_MDIO_LOG_LEVEL);
18 
19 struct nxp_enet_qos_mdio_config {
20 	const struct device *enet_dev;
21 };
22 
23 struct nxp_enet_qos_mdio_data {
24 	struct k_mutex mdio_mutex;
25 };
26 
27 struct mdio_transaction {
28 	enum mdio_opcode op;
29 	union {
30 		uint16_t write_data;
31 		uint16_t *read_data;
32 	};
33 	uint8_t portaddr;
34 	uint8_t regaddr;
35 	enet_qos_t *base;
36 	struct k_mutex *mdio_bus_mutex;
37 };
38 
check_busy(enet_qos_t * base)39 static bool check_busy(enet_qos_t *base)
40 {
41 	uint32_t val = base->MAC_MDIO_ADDRESS;
42 
43 	/* Return the busy bit */
44 	return ENET_QOS_REG_GET(MAC_MDIO_ADDRESS, GB, val);
45 }
46 
do_transaction(struct mdio_transaction * mdio)47 static int do_transaction(struct mdio_transaction *mdio)
48 {
49 	enet_qos_t *base = mdio->base;
50 	uint8_t goc_1_code;
51 	int ret;
52 
53 	k_mutex_lock(mdio->mdio_bus_mutex, K_FOREVER);
54 
55 	if (mdio->op == MDIO_OP_C22_WRITE) {
56 		base->MAC_MDIO_DATA =
57 			/* Prepare the data to be written */
58 			ENET_QOS_REG_PREP(MAC_MDIO_DATA, GD, mdio->write_data);
59 		goc_1_code = 0b0;
60 	} else if (mdio->op == MDIO_OP_C22_READ) {
61 		goc_1_code = 0b1;
62 	} else {
63 		ret = -EINVAL;
64 		goto done;
65 	}
66 
67 	base->MAC_MDIO_ADDRESS =
68 		/* OP command */
69 		ENET_QOS_REG_PREP(MAC_MDIO_ADDRESS, GOC_1, goc_1_code) |
70 		ENET_QOS_REG_PREP(MAC_MDIO_ADDRESS, GOC_0, 0b1) |
71 		/* PHY address */
72 		ENET_QOS_REG_PREP(MAC_MDIO_ADDRESS, PA, mdio->portaddr) |
73 		/* Register address */
74 		ENET_QOS_REG_PREP(MAC_MDIO_ADDRESS, RDA, mdio->regaddr);
75 
76 	base->MAC_MDIO_ADDRESS =
77 		/* Start the transaction */
78 		ENET_QOS_REG_PREP(MAC_MDIO_ADDRESS, GB, 0b1);
79 
80 
81 	ret = -ETIMEDOUT;
82 	for (int i = CONFIG_MDIO_NXP_ENET_QOS_RECHECK_COUNT; i > 0; i--) {
83 		if (!check_busy(base)) {
84 			ret = 0;
85 			break;
86 		}
87 		k_busy_wait(CONFIG_MDIO_NXP_ENET_QOS_RECHECK_TIME);
88 	}
89 
90 	if (ret) {
91 		LOG_ERR("MDIO transaction timed out");
92 		goto done;
93 	}
94 
95 	if (mdio->op == MDIO_OP_C22_READ) {
96 		uint32_t val = mdio->base->MAC_MDIO_DATA;
97 
98 		*mdio->read_data =
99 			/* Decipher the read data */
100 			ENET_QOS_REG_GET(MAC_MDIO_DATA, GD, val);
101 	}
102 
103 done:
104 	k_mutex_unlock(mdio->mdio_bus_mutex);
105 
106 	return ret;
107 }
108 
nxp_enet_qos_mdio_read(const struct device * dev,uint8_t portaddr,uint8_t regaddr,uint16_t * read_data)109 static int nxp_enet_qos_mdio_read(const struct device *dev,
110 				  uint8_t portaddr, uint8_t regaddr,
111 				  uint16_t *read_data)
112 {
113 	const struct nxp_enet_qos_mdio_config *config = dev->config;
114 	struct nxp_enet_qos_mdio_data *data = dev->data;
115 	enet_qos_t *base = ENET_QOS_MODULE_CFG(config->enet_dev)->base;
116 	struct mdio_transaction mdio_read = {
117 		.op = MDIO_OP_C22_READ,
118 		.read_data = read_data,
119 		.portaddr = portaddr,
120 		.regaddr = regaddr,
121 		.base = base,
122 		.mdio_bus_mutex = &data->mdio_mutex,
123 	};
124 
125 	return do_transaction(&mdio_read);
126 }
127 
nxp_enet_qos_mdio_write(const struct device * dev,uint8_t portaddr,uint8_t regaddr,uint16_t write_data)128 static int nxp_enet_qos_mdio_write(const struct device *dev,
129 				   uint8_t portaddr, uint8_t regaddr,
130 				   uint16_t write_data)
131 {
132 	const struct nxp_enet_qos_mdio_config *config = dev->config;
133 	struct nxp_enet_qos_mdio_data *data = dev->data;
134 	enet_qos_t *base = ENET_QOS_MODULE_CFG(config->enet_dev)->base;
135 	struct mdio_transaction mdio_write = {
136 		.op = MDIO_OP_C22_WRITE,
137 		.write_data = write_data,
138 		.portaddr = portaddr,
139 		.regaddr = regaddr,
140 		.base = base,
141 		.mdio_bus_mutex = &data->mdio_mutex,
142 	};
143 
144 	return do_transaction(&mdio_write);
145 }
146 
147 static DEVICE_API(mdio, nxp_enet_qos_mdio_api) = {
148 	.read = nxp_enet_qos_mdio_read,
149 	.write = nxp_enet_qos_mdio_write,
150 };
151 
nxp_enet_qos_mdio_init(const struct device * dev)152 static int nxp_enet_qos_mdio_init(const struct device *dev)
153 {
154 	const struct nxp_enet_qos_mdio_config *mdio_config = dev->config;
155 	struct nxp_enet_qos_mdio_data *data = dev->data;
156 	const struct nxp_enet_qos_config *config = ENET_QOS_MODULE_CFG(mdio_config->enet_dev);
157 	uint32_t enet_module_clk_rate;
158 	int ret, divider;
159 
160 	ret = k_mutex_init(&data->mdio_mutex);
161 	if (ret) {
162 		return ret;
163 	}
164 
165 	ret = clock_control_get_rate(config->clock_dev, config->clock_subsys,
166 				     &enet_module_clk_rate);
167 	if (ret) {
168 		return ret;
169 	}
170 
171 	enet_module_clk_rate /= 1000000;
172 	if (enet_module_clk_rate >= 20 && enet_module_clk_rate < 35) {
173 		divider = 2;
174 	} else if (enet_module_clk_rate < 60) {
175 		divider = 3;
176 	} else if (enet_module_clk_rate < 100) {
177 		divider = 0;
178 	} else if (enet_module_clk_rate < 150) {
179 		divider = 1;
180 	} else if (enet_module_clk_rate < 250) {
181 		divider = 4;
182 	} else {
183 		LOG_ERR("ENET QOS clk rate does not allow MDIO");
184 		return -ENOTSUP;
185 	}
186 
187 	config->base->MAC_MDIO_ADDRESS =
188 		/* Configure the MDIO clock range / divider */
189 		ENET_QOS_REG_PREP(MAC_MDIO_ADDRESS, CR, divider);
190 
191 	return 0;
192 }
193 
194 #define NXP_ENET_QOS_MDIO_INIT(inst)						\
195 										\
196 	static const struct nxp_enet_qos_mdio_config				\
197 	nxp_enet_qos_mdio_cfg_##inst = {					\
198 		.enet_dev = DEVICE_DT_GET(DT_INST_PARENT(inst)),		\
199 	};									\
200 										\
201 	static struct nxp_enet_qos_mdio_data nxp_enet_qos_mdio_data_##inst;	\
202 										\
203 	DEVICE_DT_INST_DEFINE(inst, &nxp_enet_qos_mdio_init, NULL,		\
204 			      &nxp_enet_qos_mdio_data_##inst,			\
205 			      &nxp_enet_qos_mdio_cfg_##inst,			\
206 			      POST_KERNEL, CONFIG_MDIO_INIT_PRIORITY,		\
207 			      &nxp_enet_qos_mdio_api);				\
208 
209 DT_INST_FOREACH_STATUS_OKAY(NXP_ENET_QOS_MDIO_INIT)
210