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