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