1 /*
2 * Copyright (c) 2023 PHOENIX CONTACT Electronics GmbH
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #include <zephyr/logging/log.h>
8 LOG_MODULE_REGISTER(mdio_adin2111, CONFIG_MDIO_LOG_LEVEL);
9
10 #define DT_DRV_COMPAT adi_adin2111_mdio
11
12 #include <stdint.h>
13 #include <errno.h>
14 #include <zephyr/device.h>
15 #include <zephyr/kernel.h>
16 #include <zephyr/drivers/mdio.h>
17 #include <zephyr/drivers/ethernet/eth_adin2111.h>
18
19 /* MDIO ready check retry delay */
20 #define ADIN2111_MDIO_READY_AWAIT_DELAY_POLL_US 5U
21 /* Number of retries for MDIO ready check */
22 #define ADIN2111_MDIO_READY_AWAIT_RETRY_COUNT 10U
23
24 /* MDIO Access Register 1 */
25 #define ADIN2111_MDIOACC0 0x20U
26 /* MDIO Access Register 2 */
27 #define ADIN2111_MDIOACC1 0x21U
28
29 /* MDIO MDIOACC Transaction Done */
30 #define ADIN211_MDIOACC_MDIO_TRDONE BIT(31)
31
32 struct mdio_adin2111_config {
33 const struct device *adin;
34 };
35
mdio_adin2111_wait_ready(const struct device * dev,uint16_t reg,uint32_t * out)36 static int mdio_adin2111_wait_ready(const struct device *dev, uint16_t reg,
37 uint32_t *out)
38 {
39 const struct mdio_adin2111_config *const cfg = dev->config;
40 uint32_t count;
41 int ret;
42
43 for (count = 0U; count < ADIN2111_MDIO_READY_AWAIT_RETRY_COUNT; ++count) {
44 ret = eth_adin2111_reg_read(cfg->adin, reg, out);
45 if (ret >= 0) {
46 if ((*out) & ADIN211_MDIOACC_MDIO_TRDONE) {
47 break;
48 }
49 ret = -ETIMEDOUT;
50 }
51 k_sleep(K_USEC(ADIN2111_MDIO_READY_AWAIT_DELAY_POLL_US));
52 }
53
54 return ret;
55 }
56
57
mdio_adin2111_read_c45(const struct device * dev,uint8_t prtad,uint8_t devad,uint16_t regad,uint16_t * data)58 static int mdio_adin2111_read_c45(const struct device *dev, uint8_t prtad,
59 uint8_t devad, uint16_t regad,
60 uint16_t *data)
61 {
62 const struct mdio_adin2111_config *const cfg = dev->config;
63 uint32_t rdy;
64 uint32_t cmd;
65 int ret;
66
67 /* address op */
68 cmd = (prtad & 0x1FU) << 21;
69 cmd |= (devad & 0x1FU) << 16;
70 cmd |= regad;
71
72 ret = eth_adin2111_reg_write(cfg->adin, ADIN2111_MDIOACC0, cmd);
73 if (ret < 0) {
74 return ret;
75 }
76
77 /* read op */
78 cmd = (cmd & ~UINT16_MAX) | (0x3U << 26);
79
80 ret = eth_adin2111_reg_write(cfg->adin, ADIN2111_MDIOACC1, cmd);
81 if (ret < 0) {
82 return ret;
83 }
84
85 ret = mdio_adin2111_wait_ready(dev, ADIN2111_MDIOACC1, &rdy);
86 if (ret < 0) {
87 return ret;
88 }
89
90 /* read out */
91 ret = eth_adin2111_reg_read(cfg->adin, ADIN2111_MDIOACC1, &cmd);
92
93 *data = cmd & UINT16_MAX;
94
95 return ret;
96 }
97
mdio_adin2111_write_c45(const struct device * dev,uint8_t prtad,uint8_t devad,uint16_t regad,uint16_t data)98 static int mdio_adin2111_write_c45(const struct device *dev, uint8_t prtad,
99 uint8_t devad, uint16_t regad,
100 uint16_t data)
101 {
102 const struct mdio_adin2111_config *const cfg = dev->config;
103
104 uint32_t rdy;
105 uint32_t cmd;
106 int ret;
107
108 /* address op */
109 cmd = (prtad & 0x1FU) << 21;
110 cmd |= (devad & 0x1FU) << 16;
111 cmd |= regad;
112
113 ret = eth_adin2111_reg_write(cfg->adin, ADIN2111_MDIOACC0, cmd);
114 if (ret < 0) {
115 return ret;
116 }
117
118 /* write op */
119 cmd |= BIT(26);
120 cmd = (cmd & ~UINT16_MAX) | data;
121
122 ret = eth_adin2111_reg_write(cfg->adin, ADIN2111_MDIOACC1, cmd);
123 if (ret < 0) {
124 return ret;
125 }
126
127 ret = mdio_adin2111_wait_ready(dev, ADIN2111_MDIOACC1, &rdy);
128
129 return ret;
130 }
131
mdio_adin2111_read(const struct device * dev,uint8_t prtad,uint8_t regad,uint16_t * data)132 static int mdio_adin2111_read(const struct device *dev, uint8_t prtad,
133 uint8_t regad, uint16_t *data)
134 {
135 const struct mdio_adin2111_config *const cfg = dev->config;
136 uint32_t read;
137 uint32_t cmd;
138 int ret;
139
140 cmd = BIT(28);
141 cmd |= 0x3U << 26;
142 cmd |= (prtad & 0x1FU) << 21;
143 cmd |= (regad & 0x1FU) << 16;
144
145 ret = eth_adin2111_reg_write(cfg->adin, ADIN2111_MDIOACC0, cmd);
146 if (ret >= 0) {
147 ret = mdio_adin2111_wait_ready(dev, ADIN2111_MDIOACC0, &read);
148 *data = read & UINT16_MAX;
149 }
150
151 return ret;
152 }
153
mdio_adin2111_write(const struct device * dev,uint8_t prtad,uint8_t regad,uint16_t data)154 static int mdio_adin2111_write(const struct device *dev, uint8_t prtad,
155 uint8_t regad, uint16_t data)
156 {
157 const struct mdio_adin2111_config *const cfg = dev->config;
158 uint32_t cmd;
159 uint32_t rdy;
160 int ret;
161
162 cmd = BIT(28);
163 cmd |= BIT(26);
164 cmd |= (prtad & 0x1FU) << 21;
165 cmd |= (regad & 0x1FU) << 16;
166 cmd |= data;
167
168 ret = eth_adin2111_reg_write(cfg->adin, ADIN2111_MDIOACC0, cmd);
169 if (ret >= 0) {
170 ret = mdio_adin2111_wait_ready(dev, ADIN2111_MDIOACC0, &rdy);
171 }
172
173 return ret;
174 }
175
mdio_adin2111_bus_enable(const struct device * dev)176 static void mdio_adin2111_bus_enable(const struct device *dev)
177 {
178 const struct mdio_adin2111_config *const cfg = dev->config;
179
180 eth_adin2111_lock(cfg->adin, K_FOREVER);
181 }
182
mdio_adin2111_bus_disable(const struct device * dev)183 static void mdio_adin2111_bus_disable(const struct device *dev)
184 {
185 const struct mdio_adin2111_config *const cfg = dev->config;
186
187 eth_adin2111_unlock(cfg->adin);
188 }
189
190 static const struct mdio_driver_api mdio_adin2111_api = {
191 .read = mdio_adin2111_read,
192 .write = mdio_adin2111_write,
193 .read_c45 = mdio_adin2111_read_c45,
194 .write_c45 = mdio_adin2111_write_c45,
195 .bus_enable = mdio_adin2111_bus_enable,
196 .bus_disable = mdio_adin2111_bus_disable
197 };
198
199 #define ADIN2111_MDIO_INIT(n) \
200 static const struct mdio_adin2111_config mdio_adin2111_config_##n = { \
201 .adin = DEVICE_DT_GET(DT_INST_BUS(n)), \
202 }; \
203 DEVICE_DT_INST_DEFINE(n, NULL, NULL, \
204 NULL, &mdio_adin2111_config_##n, \
205 POST_KERNEL, CONFIG_MDIO_INIT_PRIORITY, \
206 &mdio_adin2111_api);
207
208 DT_INST_FOREACH_STATUS_OKAY(ADIN2111_MDIO_INIT)
209