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 <kernel.h>
12 #include <net/mii.h>
13 #include "phy_gecko.h"
14
15 #include <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