1 /*
2  * Copyright (c) 2019 Interay Solutions B.V.
3  * Copyright (c) 2019 Oane Kingma
4  *
5  * SPDX-License-Identifier: Apache-2.0
6  */
7 
8 /* SiLabs Giant Gecko GG11 Ethernet PHY driver. */
9 
10 #include <errno.h>
11 #include <zephyr/kernel.h>
12 #include <zephyr/net/mii.h>
13 #include "phy_gecko.h"
14 
15 #include <zephyr/logging/log.h>
16 LOG_MODULE_REGISTER(eth_gecko_phy, CONFIG_ETHERNET_LOG_LEVEL);
17 
18 /* Maximum time to establish a link through auto-negotiation for
19  * 10BASE-T, 100BASE-TX is 3.7s, to add an extra margin the timeout
20  * is set at 4s.
21  */
22 #define PHY_AUTONEG_TIMEOUT_MS   4000
23 
24 /* Enable MDIO serial bus between MAC and PHY. */
mdio_bus_enable(ETH_TypeDef * eth)25 static void mdio_bus_enable(ETH_TypeDef *eth)
26 {
27 	eth->NETWORKCTRL |= ETH_NETWORKCTRL_MANPORTEN;
28 }
29 
30 /* Enable MDIO serial bus between MAC and PHY. */
mdio_bus_disable(ETH_TypeDef * eth)31 static void mdio_bus_disable(ETH_TypeDef *eth)
32 {
33 	eth->NETWORKCTRL &= ~ETH_NETWORKCTRL_MANPORTEN;
34 }
35 
36 /* Wait PHY operation complete. */
mdio_bus_wait(ETH_TypeDef * eth)37 static int mdio_bus_wait(ETH_TypeDef *eth)
38 {
39 	uint32_t retries = 100U;  /* will wait up to 1 s */
40 
41 	while (!(eth->NETWORKSTATUS & ETH_NETWORKSTATUS_MANDONE)) {
42 		if (retries-- == 0U) {
43 			LOG_ERR("timeout");
44 			return -ETIMEDOUT;
45 		}
46 
47 		k_sleep(K_MSEC(10));
48 	}
49 
50 	return 0;
51 }
52 
53 /* Send command to PHY over MDIO serial bus */
mdio_bus_send(ETH_TypeDef * eth,uint8_t phy_addr,uint8_t reg_addr,uint8_t rw,uint16_t data)54 static int mdio_bus_send(ETH_TypeDef *eth, uint8_t phy_addr, uint8_t reg_addr,
55 			 uint8_t rw, uint16_t data)
56 {
57 	int retval;
58 
59 	/* Write PHY management register */
60 	eth->PHYMNGMNT = ETH_PHYMNGMNT_WRITE0_DEFAULT
61 		| ETH_PHYMNGMNT_WRITE1
62 		| ((rw ? 0x02 : 0x01) << _ETH_PHYMNGMNT_OPERATION_SHIFT)
63 		| ((phy_addr << _ETH_PHYMNGMNT_PHYADDR_SHIFT)
64 			& _ETH_PHYMNGMNT_PHYADDR_MASK)
65 		| ((reg_addr << _ETH_PHYMNGMNT_REGADDR_SHIFT)
66 			& _ETH_PHYMNGMNT_REGADDR_MASK)
67 		| (0x2 << _ETH_PHYMNGMNT_WRITE10_SHIFT)
68 		| (data & _ETH_PHYMNGMNT_PHYRWDATA_MASK);
69 
70 	/* Wait until PHY is ready */
71 	retval = mdio_bus_wait(eth);
72 	if (retval < 0) {
73 		return retval;
74 	}
75 
76 	return 0;
77 }
78 
79 /* Read PHY register. */
phy_read(const struct phy_gecko_dev * phy,uint8_t reg_addr,uint32_t * value)80 static int phy_read(const struct phy_gecko_dev *phy, uint8_t reg_addr,
81 		    uint32_t *value)
82 {
83 	ETH_TypeDef *const eth = phy->regs;
84 	uint8_t phy_addr = phy->address;
85 	int retval;
86 
87 	retval = mdio_bus_send(eth, phy_addr, reg_addr, 1, 0);
88 	if (retval < 0) {
89 		return retval;
90 	}
91 
92 	/* Read data */
93 	*value = eth->PHYMNGMNT & _ETH_PHYMNGMNT_PHYRWDATA_MASK;
94 
95 	return 0;
96 }
97 
98 /* Write PHY register. */
phy_write(const struct phy_gecko_dev * phy,uint8_t reg_addr,uint32_t value)99 static int phy_write(const struct phy_gecko_dev *phy, uint8_t reg_addr,
100 		     uint32_t value)
101 {
102 	ETH_TypeDef *const eth = phy->regs;
103 	uint8_t phy_addr = phy->address;
104 
105 	return mdio_bus_send(eth, phy_addr, reg_addr, 0, value);
106 }
107 
108 /* Issue a PHY soft reset. */
phy_soft_reset(const struct phy_gecko_dev * phy)109 static int phy_soft_reset(const struct phy_gecko_dev *phy)
110 {
111 	uint32_t phy_reg;
112 	uint32_t retries = 12U;
113 	int retval;
114 
115 	/* Issue a soft reset */
116 	retval = phy_write(phy, MII_BMCR, MII_BMCR_RESET);
117 	if (retval < 0) {
118 		return retval;
119 	}
120 
121 	/* Wait up to 0.6s for the reset sequence to finish. According to
122 	 * IEEE 802.3, Section 2, Subsection 22.2.4.1.1 a PHY reset may take
123 	 * up to 0.5 s.
124 	 */
125 	do {
126 		if (retries-- == 0U) {
127 			return -ETIMEDOUT;
128 		}
129 
130 		k_sleep(K_MSEC(50));
131 
132 		retval = phy_read(phy, MII_BMCR, &phy_reg);
133 		if (retval < 0) {
134 			return retval;
135 		}
136 	} while (phy_reg & MII_BMCR_RESET);
137 
138 	return 0;
139 }
140 
phy_gecko_init(const struct phy_gecko_dev * phy)141 int phy_gecko_init(const struct phy_gecko_dev *phy)
142 {
143 	ETH_TypeDef *const eth = phy->regs;
144 	int phy_id;
145 
146 	mdio_bus_enable(eth);
147 
148 	LOG_INF("Soft Reset of ETH PHY");
149 	phy_soft_reset(phy);
150 
151 	/* Verify that the PHY device is responding */
152 	phy_id = phy_gecko_id_get(phy);
153 	if (phy_id == 0xFFFFFFFF) {
154 		LOG_ERR("Unable to detect a valid PHY");
155 		return -1;
156 	}
157 
158 	LOG_INF("PHYID: 0x%X at addr: %d", phy_id, phy->address);
159 
160 	mdio_bus_disable(eth);
161 
162 	return 0;
163 }
164 
phy_gecko_id_get(const struct phy_gecko_dev * phy)165 uint32_t phy_gecko_id_get(const struct phy_gecko_dev *phy)
166 {
167 	ETH_TypeDef *const eth = phy->regs;
168 	uint32_t phy_reg;
169 	uint32_t phy_id;
170 
171 	mdio_bus_enable(eth);
172 
173 	if (phy_read(phy, MII_PHYID1R, &phy_reg) < 0) {
174 		return 0xFFFFFFFF;
175 	}
176 
177 	phy_id = (phy_reg & 0xFFFF) << 16;
178 
179 	if (phy_read(phy, MII_PHYID2R, &phy_reg) < 0) {
180 		return 0xFFFFFFFF;
181 	}
182 
183 	phy_id |= (phy_reg & 0xFFFF);
184 
185 	mdio_bus_disable(eth);
186 
187 	return phy_id;
188 }
189 
phy_gecko_auto_negotiate(const struct phy_gecko_dev * phy,uint32_t * status)190 int phy_gecko_auto_negotiate(const struct phy_gecko_dev *phy,
191 				uint32_t *status)
192 {
193 	ETH_TypeDef *const eth = phy->regs;
194 	uint32_t val;
195 	uint32_t ability_adv;
196 	uint32_t ability_rcvd;
197 	uint32_t retries = PHY_AUTONEG_TIMEOUT_MS / 100;
198 	int retval;
199 
200 	mdio_bus_enable(eth);
201 
202 	LOG_DBG("Starting ETH PHY auto-negotiate sequence");
203 
204 	/* Read PHY default advertising parameters */
205 	retval = phy_read(phy, MII_ANAR, &ability_adv);
206 	if (retval < 0) {
207 		goto auto_negotiate_exit;
208 	}
209 
210 	/* Configure and start auto-negotiation process */
211 	retval = phy_read(phy, MII_BMCR, &val);
212 	if (retval < 0) {
213 		goto auto_negotiate_exit;
214 	}
215 
216 	val |= MII_BMCR_AUTONEG_ENABLE | MII_BMCR_AUTONEG_RESTART;
217 	val &= ~MII_BMCR_ISOLATE;  /* Don't isolate the PHY */
218 
219 	retval = phy_write(phy, MII_BMCR, val);
220 	if (retval < 0) {
221 		goto auto_negotiate_exit;
222 	}
223 
224 	/* Wait for the auto-negotiation process to complete */
225 	do {
226 		if (retries-- == 0U) {
227 			retval = -ETIMEDOUT;
228 			goto auto_negotiate_exit;
229 		}
230 
231 		k_sleep(K_MSEC(100));
232 
233 		retval = phy_read(phy, MII_BMSR, &val);
234 		if (retval < 0) {
235 			goto auto_negotiate_exit;
236 		}
237 	} while (!(val & MII_BMSR_AUTONEG_COMPLETE));
238 
239 	LOG_DBG("PHY auto-negotiate sequence completed");
240 
241 	/* Read abilities of the remote device */
242 	retval = phy_read(phy, MII_ANLPAR, &ability_rcvd);
243 	if (retval < 0) {
244 		goto auto_negotiate_exit;
245 	}
246 
247 	/* Determine the best possible mode of operation */
248 	if ((ability_adv & ability_rcvd) & MII_ADVERTISE_100_FULL) {
249 		*status = ETH_NETWORKCFG_FULLDUPLEX | ETH_NETWORKCFG_SPEED;
250 	} else if ((ability_adv & ability_rcvd) & MII_ADVERTISE_100_HALF) {
251 		*status = ETH_NETWORKCFG_SPEED;
252 	} else if ((ability_adv & ability_rcvd) & MII_ADVERTISE_10_FULL) {
253 		*status = ETH_NETWORKCFG_FULLDUPLEX;
254 	} else {
255 		*status = 0;
256 	}
257 
258 	LOG_DBG("common abilities: speed %s Mb, %s duplex",
259 		*status & ETH_NETWORKCFG_SPEED ? "100" : "10",
260 		*status & ETH_NETWORKCFG_FULLDUPLEX ? "full" : "half");
261 
262 auto_negotiate_exit:
263 	mdio_bus_disable(eth);
264 	return retval;
265 }
266 
phy_gecko_is_linked(const struct phy_gecko_dev * phy)267 bool phy_gecko_is_linked(const struct phy_gecko_dev *phy)
268 {
269 	ETH_TypeDef *const eth = phy->regs;
270 	uint32_t phy_reg;
271 	bool phy_linked = false;
272 
273 	mdio_bus_enable(eth);
274 
275 	if (phy_read(phy, MII_BMSR, &phy_reg) < 0) {
276 		return phy_linked;
277 	}
278 
279 	phy_linked = (phy_reg & MII_BMSR_LINK_STATUS);
280 
281 	mdio_bus_disable(eth);
282 
283 	return phy_linked;
284 }
285