]>
Commit | Line | Data |
---|---|---|
aa09677c FF |
1 | /* |
2 | * Broadcom GENET MDIO routines | |
3 | * | |
4 | * Copyright (c) 2014 Broadcom Corporation | |
5 | * | |
6 | * This program is free software; you can redistribute it and/or modify | |
7 | * it under the terms of the GNU General Public License version 2 as | |
8 | * published by the Free Software Foundation. | |
aa09677c FF |
9 | */ |
10 | ||
11 | ||
12 | #include <linux/types.h> | |
13 | #include <linux/delay.h> | |
14 | #include <linux/wait.h> | |
15 | #include <linux/mii.h> | |
16 | #include <linux/ethtool.h> | |
17 | #include <linux/bitops.h> | |
18 | #include <linux/netdevice.h> | |
19 | #include <linux/platform_device.h> | |
20 | #include <linux/phy.h> | |
21 | #include <linux/phy_fixed.h> | |
22 | #include <linux/brcmphy.h> | |
23 | #include <linux/of.h> | |
24 | #include <linux/of_net.h> | |
25 | #include <linux/of_mdio.h> | |
26 | ||
27 | #include "bcmgenet.h" | |
28 | ||
29 | /* read a value from the MII */ | |
30 | static int bcmgenet_mii_read(struct mii_bus *bus, int phy_id, int location) | |
31 | { | |
32 | int ret; | |
33 | struct net_device *dev = bus->priv; | |
34 | struct bcmgenet_priv *priv = netdev_priv(dev); | |
35 | u32 reg; | |
36 | ||
37 | bcmgenet_umac_writel(priv, (MDIO_RD | (phy_id << MDIO_PMD_SHIFT) | | |
c91b7f66 | 38 | (location << MDIO_REG_SHIFT)), UMAC_MDIO_CMD); |
aa09677c FF |
39 | /* Start MDIO transaction*/ |
40 | reg = bcmgenet_umac_readl(priv, UMAC_MDIO_CMD); | |
41 | reg |= MDIO_START_BUSY; | |
42 | bcmgenet_umac_writel(priv, reg, UMAC_MDIO_CMD); | |
43 | wait_event_timeout(priv->wq, | |
c91b7f66 FF |
44 | !(bcmgenet_umac_readl(priv, UMAC_MDIO_CMD) |
45 | & MDIO_START_BUSY), | |
46 | HZ / 100); | |
aa09677c FF |
47 | ret = bcmgenet_umac_readl(priv, UMAC_MDIO_CMD); |
48 | ||
49 | if (ret & MDIO_READ_FAIL) | |
50 | return -EIO; | |
51 | ||
52 | return ret & 0xffff; | |
53 | } | |
54 | ||
55 | /* write a value to the MII */ | |
56 | static int bcmgenet_mii_write(struct mii_bus *bus, int phy_id, | |
c91b7f66 | 57 | int location, u16 val) |
aa09677c FF |
58 | { |
59 | struct net_device *dev = bus->priv; | |
60 | struct bcmgenet_priv *priv = netdev_priv(dev); | |
61 | u32 reg; | |
62 | ||
63 | bcmgenet_umac_writel(priv, (MDIO_WR | (phy_id << MDIO_PMD_SHIFT) | | |
c91b7f66 FF |
64 | (location << MDIO_REG_SHIFT) | (0xffff & val)), |
65 | UMAC_MDIO_CMD); | |
aa09677c FF |
66 | reg = bcmgenet_umac_readl(priv, UMAC_MDIO_CMD); |
67 | reg |= MDIO_START_BUSY; | |
68 | bcmgenet_umac_writel(priv, reg, UMAC_MDIO_CMD); | |
69 | wait_event_timeout(priv->wq, | |
c91b7f66 FF |
70 | !(bcmgenet_umac_readl(priv, UMAC_MDIO_CMD) & |
71 | MDIO_START_BUSY), | |
72 | HZ / 100); | |
aa09677c FF |
73 | |
74 | return 0; | |
75 | } | |
76 | ||
77 | /* setup netdev link state when PHY link status change and | |
78 | * update UMAC and RGMII block when link up | |
79 | */ | |
80 | static void bcmgenet_mii_setup(struct net_device *dev) | |
81 | { | |
82 | struct bcmgenet_priv *priv = netdev_priv(dev); | |
83 | struct phy_device *phydev = priv->phydev; | |
84 | u32 reg, cmd_bits = 0; | |
85 | unsigned int status_changed = 0; | |
86 | ||
87 | if (priv->old_link != phydev->link) { | |
88 | status_changed = 1; | |
89 | priv->old_link = phydev->link; | |
90 | } | |
91 | ||
92 | if (phydev->link) { | |
93 | /* program UMAC and RGMII block based on established link | |
94 | * speed, pause, and duplex. | |
95 | * the speed set in umac->cmd tell RGMII block which clock | |
96 | * 25MHz(100Mbps)/125MHz(1Gbps) to use for transmit. | |
97 | * receive clock is provided by PHY. | |
98 | */ | |
99 | reg = bcmgenet_ext_readl(priv, EXT_RGMII_OOB_CTRL); | |
100 | reg &= ~OOB_DISABLE; | |
101 | reg |= RGMII_LINK; | |
102 | bcmgenet_ext_writel(priv, reg, EXT_RGMII_OOB_CTRL); | |
103 | ||
104 | /* speed */ | |
105 | if (phydev->speed == SPEED_1000) | |
106 | cmd_bits = UMAC_SPEED_1000; | |
107 | else if (phydev->speed == SPEED_100) | |
108 | cmd_bits = UMAC_SPEED_100; | |
109 | else | |
110 | cmd_bits = UMAC_SPEED_10; | |
111 | cmd_bits <<= CMD_SPEED_SHIFT; | |
112 | ||
113 | if (priv->old_duplex != phydev->duplex) { | |
114 | status_changed = 1; | |
115 | priv->old_duplex = phydev->duplex; | |
116 | } | |
117 | ||
118 | /* duplex */ | |
119 | if (phydev->duplex != DUPLEX_FULL) | |
120 | cmd_bits |= CMD_HD_EN; | |
121 | ||
122 | if (priv->old_pause != phydev->pause) { | |
123 | status_changed = 1; | |
124 | priv->old_pause = phydev->pause; | |
125 | } | |
126 | ||
127 | /* pause capability */ | |
128 | if (!phydev->pause) | |
129 | cmd_bits |= CMD_RX_PAUSE_IGNORE | CMD_TX_PAUSE_IGNORE; | |
24052408 | 130 | } |
aa09677c | 131 | |
c677ba8b FF |
132 | if (!status_changed) |
133 | return; | |
134 | ||
135 | if (phydev->link) { | |
aa09677c FF |
136 | reg = bcmgenet_umac_readl(priv, UMAC_CMD); |
137 | reg &= ~((CMD_SPEED_MASK << CMD_SPEED_SHIFT) | | |
138 | CMD_HD_EN | | |
139 | CMD_RX_PAUSE_IGNORE | CMD_TX_PAUSE_IGNORE); | |
140 | reg |= cmd_bits; | |
141 | bcmgenet_umac_writel(priv, reg, UMAC_CMD); | |
aa09677c | 142 | |
24052408 | 143 | } |
c677ba8b FF |
144 | |
145 | phy_print_status(phydev); | |
aa09677c FF |
146 | } |
147 | ||
148 | void bcmgenet_mii_reset(struct net_device *dev) | |
149 | { | |
150 | struct bcmgenet_priv *priv = netdev_priv(dev); | |
151 | ||
152 | if (priv->phydev) { | |
153 | phy_init_hw(priv->phydev); | |
154 | phy_start_aneg(priv->phydev); | |
155 | } | |
156 | } | |
157 | ||
158 | static void bcmgenet_ephy_power_up(struct net_device *dev) | |
159 | { | |
160 | struct bcmgenet_priv *priv = netdev_priv(dev); | |
161 | u32 reg = 0; | |
162 | ||
163 | /* EXT_GPHY_CTRL is only valid for GENETv4 and onward */ | |
164 | if (!GENET_IS_V4(priv)) | |
165 | return; | |
166 | ||
167 | reg = bcmgenet_ext_readl(priv, EXT_GPHY_CTRL); | |
168 | reg &= ~(EXT_CFG_IDDQ_BIAS | EXT_CFG_PWR_DOWN); | |
169 | reg |= EXT_GPHY_RESET; | |
170 | bcmgenet_ext_writel(priv, reg, EXT_GPHY_CTRL); | |
171 | mdelay(2); | |
172 | ||
173 | reg &= ~EXT_GPHY_RESET; | |
174 | bcmgenet_ext_writel(priv, reg, EXT_GPHY_CTRL); | |
175 | udelay(20); | |
176 | } | |
177 | ||
178 | static void bcmgenet_internal_phy_setup(struct net_device *dev) | |
179 | { | |
180 | struct bcmgenet_priv *priv = netdev_priv(dev); | |
181 | u32 reg; | |
182 | ||
183 | /* Power up EPHY */ | |
184 | bcmgenet_ephy_power_up(dev); | |
185 | /* enable APD */ | |
186 | reg = bcmgenet_ext_readl(priv, EXT_EXT_PWR_MGMT); | |
187 | reg |= EXT_PWR_DN_EN_LD; | |
188 | bcmgenet_ext_writel(priv, reg, EXT_EXT_PWR_MGMT); | |
189 | bcmgenet_mii_reset(dev); | |
190 | } | |
191 | ||
192 | static void bcmgenet_moca_phy_setup(struct bcmgenet_priv *priv) | |
193 | { | |
194 | u32 reg; | |
195 | ||
196 | /* Speed settings are set in bcmgenet_mii_setup() */ | |
197 | reg = bcmgenet_sys_readl(priv, SYS_PORT_CTRL); | |
198 | reg |= LED_ACT_SOURCE_MAC; | |
199 | bcmgenet_sys_writel(priv, reg, SYS_PORT_CTRL); | |
200 | } | |
201 | ||
202 | int bcmgenet_mii_config(struct net_device *dev) | |
203 | { | |
204 | struct bcmgenet_priv *priv = netdev_priv(dev); | |
205 | struct phy_device *phydev = priv->phydev; | |
206 | struct device *kdev = &priv->pdev->dev; | |
207 | const char *phy_name = NULL; | |
208 | u32 id_mode_dis = 0; | |
209 | u32 port_ctrl; | |
210 | u32 reg; | |
211 | ||
212 | priv->ext_phy = !phy_is_internal(priv->phydev) && | |
213 | (priv->phy_interface != PHY_INTERFACE_MODE_MOCA); | |
214 | ||
215 | if (phy_is_internal(priv->phydev)) | |
216 | priv->phy_interface = PHY_INTERFACE_MODE_NA; | |
217 | ||
218 | switch (priv->phy_interface) { | |
219 | case PHY_INTERFACE_MODE_NA: | |
220 | case PHY_INTERFACE_MODE_MOCA: | |
221 | /* Irrespective of the actually configured PHY speed (100 or | |
222 | * 1000) GENETv4 only has an internal GPHY so we will just end | |
223 | * up masking the Gigabit features from what we support, not | |
224 | * switching to the EPHY | |
225 | */ | |
226 | if (GENET_IS_V4(priv)) | |
227 | port_ctrl = PORT_MODE_INT_GPHY; | |
228 | else | |
229 | port_ctrl = PORT_MODE_INT_EPHY; | |
230 | ||
231 | bcmgenet_sys_writel(priv, port_ctrl, SYS_PORT_CTRL); | |
232 | ||
233 | if (phy_is_internal(priv->phydev)) { | |
234 | phy_name = "internal PHY"; | |
235 | bcmgenet_internal_phy_setup(dev); | |
236 | } else if (priv->phy_interface == PHY_INTERFACE_MODE_MOCA) { | |
237 | phy_name = "MoCA"; | |
238 | bcmgenet_moca_phy_setup(priv); | |
239 | } | |
240 | break; | |
241 | ||
242 | case PHY_INTERFACE_MODE_MII: | |
243 | phy_name = "external MII"; | |
244 | phydev->supported &= PHY_BASIC_FEATURES; | |
245 | bcmgenet_sys_writel(priv, | |
c91b7f66 | 246 | PORT_MODE_EXT_EPHY, SYS_PORT_CTRL); |
aa09677c FF |
247 | break; |
248 | ||
249 | case PHY_INTERFACE_MODE_REVMII: | |
250 | phy_name = "external RvMII"; | |
251 | /* of_mdiobus_register took care of reading the 'max-speed' | |
252 | * PHY property for us, effectively limiting the PHY supported | |
253 | * capabilities, use that knowledge to also configure the | |
254 | * Reverse MII interface correctly. | |
255 | */ | |
256 | if ((priv->phydev->supported & PHY_BASIC_FEATURES) == | |
257 | PHY_BASIC_FEATURES) | |
258 | port_ctrl = PORT_MODE_EXT_RVMII_25; | |
259 | else | |
260 | port_ctrl = PORT_MODE_EXT_RVMII_50; | |
261 | bcmgenet_sys_writel(priv, port_ctrl, SYS_PORT_CTRL); | |
262 | break; | |
263 | ||
264 | case PHY_INTERFACE_MODE_RGMII: | |
265 | /* RGMII_NO_ID: TXC transitions at the same time as TXD | |
266 | * (requires PCB or receiver-side delay) | |
267 | * RGMII: Add 2ns delay on TXC (90 degree shift) | |
268 | * | |
269 | * ID is implicitly disabled for 100Mbps (RG)MII operation. | |
270 | */ | |
271 | id_mode_dis = BIT(16); | |
272 | /* fall through */ | |
273 | case PHY_INTERFACE_MODE_RGMII_TXID: | |
274 | if (id_mode_dis) | |
275 | phy_name = "external RGMII (no delay)"; | |
276 | else | |
277 | phy_name = "external RGMII (TX delay)"; | |
278 | reg = bcmgenet_ext_readl(priv, EXT_RGMII_OOB_CTRL); | |
279 | reg |= RGMII_MODE_EN | id_mode_dis; | |
280 | bcmgenet_ext_writel(priv, reg, EXT_RGMII_OOB_CTRL); | |
281 | bcmgenet_sys_writel(priv, | |
c91b7f66 | 282 | PORT_MODE_EXT_GPHY, SYS_PORT_CTRL); |
aa09677c FF |
283 | break; |
284 | default: | |
285 | dev_err(kdev, "unknown phy mode: %d\n", priv->phy_interface); | |
286 | return -EINVAL; | |
287 | } | |
288 | ||
289 | dev_info(kdev, "configuring instance for %s\n", phy_name); | |
290 | ||
291 | return 0; | |
292 | } | |
293 | ||
294 | static int bcmgenet_mii_probe(struct net_device *dev) | |
295 | { | |
296 | struct bcmgenet_priv *priv = netdev_priv(dev); | |
9abf0c2b | 297 | struct device_node *dn = priv->pdev->dev.of_node; |
aa09677c | 298 | struct phy_device *phydev; |
aa09677c FF |
299 | int ret; |
300 | ||
301 | if (priv->phydev) { | |
302 | pr_info("PHY already attached\n"); | |
303 | return 0; | |
304 | } | |
305 | ||
9abf0c2b FF |
306 | /* In the case of a fixed PHY, the DT node associated |
307 | * to the PHY is the Ethernet MAC DT node. | |
308 | */ | |
9518259f | 309 | if (!priv->phy_dn && of_phy_is_fixed_link(dn)) { |
9abf0c2b FF |
310 | ret = of_phy_register_fixed_link(dn); |
311 | if (ret) | |
312 | return ret; | |
aa09677c | 313 | |
9518259f | 314 | priv->phy_dn = of_node_get(dn); |
9abf0c2b FF |
315 | } |
316 | ||
317 | phydev = of_phy_connect(dev, priv->phy_dn, bcmgenet_mii_setup, 0, | |
318 | priv->phy_interface); | |
aa09677c FF |
319 | if (!phydev) { |
320 | pr_err("could not attach to PHY\n"); | |
321 | return -ENODEV; | |
322 | } | |
323 | ||
324 | priv->old_link = -1; | |
325 | priv->old_duplex = -1; | |
326 | priv->old_pause = -1; | |
327 | priv->phydev = phydev; | |
328 | ||
329 | /* Configure port multiplexer based on what the probed PHY device since | |
330 | * reading the 'max-speed' property determines the maximum supported | |
331 | * PHY speed which is needed for bcmgenet_mii_config() to configure | |
332 | * things appropriately. | |
333 | */ | |
334 | ret = bcmgenet_mii_config(dev); | |
335 | if (ret) { | |
336 | phy_disconnect(priv->phydev); | |
337 | return ret; | |
338 | } | |
339 | ||
aa09677c FF |
340 | phydev->advertising = phydev->supported; |
341 | ||
342 | /* The internal PHY has its link interrupts routed to the | |
343 | * Ethernet MAC ISRs | |
344 | */ | |
345 | if (phy_is_internal(priv->phydev)) | |
346 | priv->mii_bus->irq[phydev->addr] = PHY_IGNORE_INTERRUPT; | |
347 | else | |
348 | priv->mii_bus->irq[phydev->addr] = PHY_POLL; | |
349 | ||
350 | pr_info("attached PHY at address %d [%s]\n", | |
c91b7f66 | 351 | phydev->addr, phydev->drv->name); |
aa09677c FF |
352 | |
353 | return 0; | |
354 | } | |
355 | ||
356 | static int bcmgenet_mii_alloc(struct bcmgenet_priv *priv) | |
357 | { | |
358 | struct mii_bus *bus; | |
359 | ||
360 | if (priv->mii_bus) | |
361 | return 0; | |
362 | ||
363 | priv->mii_bus = mdiobus_alloc(); | |
364 | if (!priv->mii_bus) { | |
365 | pr_err("failed to allocate\n"); | |
366 | return -ENOMEM; | |
367 | } | |
368 | ||
369 | bus = priv->mii_bus; | |
370 | bus->priv = priv->dev; | |
371 | bus->name = "bcmgenet MII bus"; | |
372 | bus->parent = &priv->pdev->dev; | |
373 | bus->read = bcmgenet_mii_read; | |
374 | bus->write = bcmgenet_mii_write; | |
375 | snprintf(bus->id, MII_BUS_ID_SIZE, "%s-%d", | |
c91b7f66 | 376 | priv->pdev->name, priv->pdev->id); |
aa09677c | 377 | |
c489be08 | 378 | bus->irq = kcalloc(PHY_MAX_ADDR, sizeof(int), GFP_KERNEL); |
aa09677c FF |
379 | if (!bus->irq) { |
380 | mdiobus_free(priv->mii_bus); | |
381 | return -ENOMEM; | |
382 | } | |
383 | ||
384 | return 0; | |
385 | } | |
386 | ||
387 | static int bcmgenet_mii_of_init(struct bcmgenet_priv *priv) | |
388 | { | |
389 | struct device_node *dn = priv->pdev->dev.of_node; | |
390 | struct device *kdev = &priv->pdev->dev; | |
391 | struct device_node *mdio_dn; | |
392 | char *compat; | |
393 | int ret; | |
394 | ||
395 | compat = kasprintf(GFP_KERNEL, "brcm,genet-mdio-v%d", priv->version); | |
396 | if (!compat) | |
397 | return -ENOMEM; | |
398 | ||
399 | mdio_dn = of_find_compatible_node(dn, NULL, compat); | |
400 | kfree(compat); | |
401 | if (!mdio_dn) { | |
402 | dev_err(kdev, "unable to find MDIO bus node\n"); | |
403 | return -ENODEV; | |
404 | } | |
405 | ||
406 | ret = of_mdiobus_register(priv->mii_bus, mdio_dn); | |
407 | if (ret) { | |
408 | dev_err(kdev, "failed to register MDIO bus\n"); | |
409 | return ret; | |
410 | } | |
411 | ||
412 | /* Fetch the PHY phandle */ | |
413 | priv->phy_dn = of_parse_phandle(dn, "phy-handle", 0); | |
414 | ||
415 | /* Get the link mode */ | |
416 | priv->phy_interface = of_get_phy_mode(dn); | |
417 | ||
418 | return 0; | |
419 | } | |
420 | ||
421 | int bcmgenet_mii_init(struct net_device *dev) | |
422 | { | |
423 | struct bcmgenet_priv *priv = netdev_priv(dev); | |
424 | int ret; | |
425 | ||
426 | ret = bcmgenet_mii_alloc(priv); | |
427 | if (ret) | |
428 | return ret; | |
429 | ||
430 | ret = bcmgenet_mii_of_init(priv); | |
431 | if (ret) | |
432 | goto out_free; | |
433 | ||
434 | ret = bcmgenet_mii_probe(dev); | |
435 | if (ret) | |
436 | goto out; | |
437 | ||
438 | return 0; | |
439 | ||
440 | out: | |
9518259f | 441 | of_node_put(priv->phy_dn); |
aa09677c FF |
442 | mdiobus_unregister(priv->mii_bus); |
443 | out_free: | |
444 | kfree(priv->mii_bus->irq); | |
445 | mdiobus_free(priv->mii_bus); | |
446 | return ret; | |
447 | } | |
448 | ||
449 | void bcmgenet_mii_exit(struct net_device *dev) | |
450 | { | |
451 | struct bcmgenet_priv *priv = netdev_priv(dev); | |
452 | ||
9518259f | 453 | of_node_put(priv->phy_dn); |
aa09677c FF |
454 | mdiobus_unregister(priv->mii_bus); |
455 | kfree(priv->mii_bus->irq); | |
456 | mdiobus_free(priv->mii_bus); | |
457 | } |