diff options
author | Lendacky, Thomas <Thomas.Lendacky@amd.com> | 2014-08-05 14:30:44 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2014-08-05 19:47:02 -0400 |
commit | 88131a812b16b45cf999e577ad8d89b41ad606e3 (patch) | |
tree | 19519ab7ec4444ccdd97e05b22a126ee6f511985 /drivers/net | |
parent | f3d0e78d2e733176c2b0d23e7a7d871c7a33c2d6 (diff) |
amd-xgbe: Perform phy connect/disconnect at dev open/stop
A change added to the mdiobus/phy api added a module_get/module_put
during phy connect/disconnect processing. Currently, the driver
performs a phy connect during module probe and a phy disconnect during
module remove. With the addition of the module_get during phy connect
the amd-xgbe module use count is incremented and can no longer be
unloaded.
Move the phy connect/disconnect from the driver probe/remove functions
to the net_device_ops ndo_open/ndo_stop functions. This allows the
module use count to be decremented when the device(s) are brought down
and allows the module to be unloaded.
Signed-off-by: Tom Lendacky <thomas.lendacky@amd.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'drivers/net')
-rw-r--r-- | drivers/net/ethernet/amd/xgbe/xgbe-drv.c | 122 | ||||
-rw-r--r-- | drivers/net/ethernet/amd/xgbe/xgbe-mdio.c | 98 |
2 files changed, 121 insertions, 99 deletions
diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-drv.c b/drivers/net/ethernet/amd/xgbe/xgbe-drv.c index 3bf3c0194ad3..1f5487f4888c 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe-drv.c +++ b/drivers/net/ethernet/amd/xgbe/xgbe-drv.c | |||
@@ -122,6 +122,7 @@ | |||
122 | #include <linux/clk.h> | 122 | #include <linux/clk.h> |
123 | #include <linux/if_ether.h> | 123 | #include <linux/if_ether.h> |
124 | #include <linux/net_tstamp.h> | 124 | #include <linux/net_tstamp.h> |
125 | #include <linux/phy.h> | ||
125 | 126 | ||
126 | #include "xgbe.h" | 127 | #include "xgbe.h" |
127 | #include "xgbe-common.h" | 128 | #include "xgbe-common.h" |
@@ -521,6 +522,114 @@ static void xgbe_free_rx_skbuff(struct xgbe_prv_data *pdata) | |||
521 | DBGPR("<--xgbe_free_rx_skbuff\n"); | 522 | DBGPR("<--xgbe_free_rx_skbuff\n"); |
522 | } | 523 | } |
523 | 524 | ||
525 | static void xgbe_adjust_link(struct net_device *netdev) | ||
526 | { | ||
527 | struct xgbe_prv_data *pdata = netdev_priv(netdev); | ||
528 | struct xgbe_hw_if *hw_if = &pdata->hw_if; | ||
529 | struct phy_device *phydev = pdata->phydev; | ||
530 | int new_state = 0; | ||
531 | |||
532 | if (phydev == NULL) | ||
533 | return; | ||
534 | |||
535 | if (phydev->link) { | ||
536 | /* Flow control support */ | ||
537 | if (pdata->pause_autoneg) { | ||
538 | if (phydev->pause || phydev->asym_pause) { | ||
539 | pdata->tx_pause = 1; | ||
540 | pdata->rx_pause = 1; | ||
541 | } else { | ||
542 | pdata->tx_pause = 0; | ||
543 | pdata->rx_pause = 0; | ||
544 | } | ||
545 | } | ||
546 | |||
547 | if (pdata->tx_pause != pdata->phy_tx_pause) { | ||
548 | hw_if->config_tx_flow_control(pdata); | ||
549 | pdata->phy_tx_pause = pdata->tx_pause; | ||
550 | } | ||
551 | |||
552 | if (pdata->rx_pause != pdata->phy_rx_pause) { | ||
553 | hw_if->config_rx_flow_control(pdata); | ||
554 | pdata->phy_rx_pause = pdata->rx_pause; | ||
555 | } | ||
556 | |||
557 | /* Speed support */ | ||
558 | if (phydev->speed != pdata->phy_speed) { | ||
559 | new_state = 1; | ||
560 | |||
561 | switch (phydev->speed) { | ||
562 | case SPEED_10000: | ||
563 | hw_if->set_xgmii_speed(pdata); | ||
564 | break; | ||
565 | |||
566 | case SPEED_2500: | ||
567 | hw_if->set_gmii_2500_speed(pdata); | ||
568 | break; | ||
569 | |||
570 | case SPEED_1000: | ||
571 | hw_if->set_gmii_speed(pdata); | ||
572 | break; | ||
573 | } | ||
574 | pdata->phy_speed = phydev->speed; | ||
575 | } | ||
576 | |||
577 | if (phydev->link != pdata->phy_link) { | ||
578 | new_state = 1; | ||
579 | pdata->phy_link = 1; | ||
580 | } | ||
581 | } else if (pdata->phy_link) { | ||
582 | new_state = 1; | ||
583 | pdata->phy_link = 0; | ||
584 | pdata->phy_speed = SPEED_UNKNOWN; | ||
585 | } | ||
586 | |||
587 | if (new_state) | ||
588 | phy_print_status(phydev); | ||
589 | } | ||
590 | |||
591 | static int xgbe_phy_init(struct xgbe_prv_data *pdata) | ||
592 | { | ||
593 | struct net_device *netdev = pdata->netdev; | ||
594 | struct phy_device *phydev = pdata->phydev; | ||
595 | int ret; | ||
596 | |||
597 | pdata->phy_link = -1; | ||
598 | pdata->phy_speed = SPEED_UNKNOWN; | ||
599 | pdata->phy_tx_pause = pdata->tx_pause; | ||
600 | pdata->phy_rx_pause = pdata->rx_pause; | ||
601 | |||
602 | ret = phy_connect_direct(netdev, phydev, &xgbe_adjust_link, | ||
603 | pdata->phy_mode); | ||
604 | if (ret) { | ||
605 | netdev_err(netdev, "phy_connect_direct failed\n"); | ||
606 | return ret; | ||
607 | } | ||
608 | |||
609 | if (!phydev->drv || (phydev->drv->phy_id == 0)) { | ||
610 | netdev_err(netdev, "phy_id not valid\n"); | ||
611 | ret = -ENODEV; | ||
612 | goto err_phy_connect; | ||
613 | } | ||
614 | DBGPR(" phy_connect_direct succeeded for PHY %s, link=%d\n", | ||
615 | dev_name(&phydev->dev), phydev->link); | ||
616 | |||
617 | return 0; | ||
618 | |||
619 | err_phy_connect: | ||
620 | phy_disconnect(phydev); | ||
621 | |||
622 | return ret; | ||
623 | } | ||
624 | |||
625 | static void xgbe_phy_exit(struct xgbe_prv_data *pdata) | ||
626 | { | ||
627 | if (!pdata->phydev) | ||
628 | return; | ||
629 | |||
630 | phy_disconnect(pdata->phydev); | ||
631 | } | ||
632 | |||
524 | int xgbe_powerdown(struct net_device *netdev, unsigned int caller) | 633 | int xgbe_powerdown(struct net_device *netdev, unsigned int caller) |
525 | { | 634 | { |
526 | struct xgbe_prv_data *pdata = netdev_priv(netdev); | 635 | struct xgbe_prv_data *pdata = netdev_priv(netdev); |
@@ -986,11 +1095,16 @@ static int xgbe_open(struct net_device *netdev) | |||
986 | 1095 | ||
987 | DBGPR("-->xgbe_open\n"); | 1096 | DBGPR("-->xgbe_open\n"); |
988 | 1097 | ||
1098 | /* Initialize the phy */ | ||
1099 | ret = xgbe_phy_init(pdata); | ||
1100 | if (ret) | ||
1101 | return ret; | ||
1102 | |||
989 | /* Enable the clocks */ | 1103 | /* Enable the clocks */ |
990 | ret = clk_prepare_enable(pdata->sysclk); | 1104 | ret = clk_prepare_enable(pdata->sysclk); |
991 | if (ret) { | 1105 | if (ret) { |
992 | netdev_alert(netdev, "dma clk_prepare_enable failed\n"); | 1106 | netdev_alert(netdev, "dma clk_prepare_enable failed\n"); |
993 | return ret; | 1107 | goto err_phy_init; |
994 | } | 1108 | } |
995 | 1109 | ||
996 | ret = clk_prepare_enable(pdata->ptpclk); | 1110 | ret = clk_prepare_enable(pdata->ptpclk); |
@@ -1047,6 +1161,9 @@ err_ptpclk: | |||
1047 | err_sysclk: | 1161 | err_sysclk: |
1048 | clk_disable_unprepare(pdata->sysclk); | 1162 | clk_disable_unprepare(pdata->sysclk); |
1049 | 1163 | ||
1164 | err_phy_init: | ||
1165 | xgbe_phy_exit(pdata); | ||
1166 | |||
1050 | return ret; | 1167 | return ret; |
1051 | } | 1168 | } |
1052 | 1169 | ||
@@ -1077,6 +1194,9 @@ static int xgbe_close(struct net_device *netdev) | |||
1077 | clk_disable_unprepare(pdata->ptpclk); | 1194 | clk_disable_unprepare(pdata->ptpclk); |
1078 | clk_disable_unprepare(pdata->sysclk); | 1195 | clk_disable_unprepare(pdata->sysclk); |
1079 | 1196 | ||
1197 | /* Release the phy */ | ||
1198 | xgbe_phy_exit(pdata); | ||
1199 | |||
1080 | DBGPR("<--xgbe_close\n"); | 1200 | DBGPR("<--xgbe_close\n"); |
1081 | 1201 | ||
1082 | return 0; | 1202 | return 0; |
diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-mdio.c b/drivers/net/ethernet/amd/xgbe/xgbe-mdio.c index eecd360430a4..6d2221e023f4 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe-mdio.c +++ b/drivers/net/ethernet/amd/xgbe/xgbe-mdio.c | |||
@@ -116,7 +116,6 @@ | |||
116 | 116 | ||
117 | #include <linux/module.h> | 117 | #include <linux/module.h> |
118 | #include <linux/kmod.h> | 118 | #include <linux/kmod.h> |
119 | #include <linux/spinlock.h> | ||
120 | #include <linux/mdio.h> | 119 | #include <linux/mdio.h> |
121 | #include <linux/phy.h> | 120 | #include <linux/phy.h> |
122 | #include <linux/of.h> | 121 | #include <linux/of.h> |
@@ -158,77 +157,6 @@ static int xgbe_mdio_write(struct mii_bus *mii, int prtad, int mmd_reg, | |||
158 | return 0; | 157 | return 0; |
159 | } | 158 | } |
160 | 159 | ||
161 | static void xgbe_adjust_link(struct net_device *netdev) | ||
162 | { | ||
163 | struct xgbe_prv_data *pdata = netdev_priv(netdev); | ||
164 | struct xgbe_hw_if *hw_if = &pdata->hw_if; | ||
165 | struct phy_device *phydev = pdata->phydev; | ||
166 | int new_state = 0; | ||
167 | |||
168 | if (phydev == NULL) | ||
169 | return; | ||
170 | |||
171 | DBGPR_MDIO("-->xgbe_adjust_link: address=%d, newlink=%d, curlink=%d\n", | ||
172 | phydev->addr, phydev->link, pdata->phy_link); | ||
173 | |||
174 | if (phydev->link) { | ||
175 | /* Flow control support */ | ||
176 | if (pdata->pause_autoneg) { | ||
177 | if (phydev->pause || phydev->asym_pause) { | ||
178 | pdata->tx_pause = 1; | ||
179 | pdata->rx_pause = 1; | ||
180 | } else { | ||
181 | pdata->tx_pause = 0; | ||
182 | pdata->rx_pause = 0; | ||
183 | } | ||
184 | } | ||
185 | |||
186 | if (pdata->tx_pause != pdata->phy_tx_pause) { | ||
187 | hw_if->config_tx_flow_control(pdata); | ||
188 | pdata->phy_tx_pause = pdata->tx_pause; | ||
189 | } | ||
190 | |||
191 | if (pdata->rx_pause != pdata->phy_rx_pause) { | ||
192 | hw_if->config_rx_flow_control(pdata); | ||
193 | pdata->phy_rx_pause = pdata->rx_pause; | ||
194 | } | ||
195 | |||
196 | /* Speed support */ | ||
197 | if (phydev->speed != pdata->phy_speed) { | ||
198 | new_state = 1; | ||
199 | |||
200 | switch (phydev->speed) { | ||
201 | case SPEED_10000: | ||
202 | hw_if->set_xgmii_speed(pdata); | ||
203 | break; | ||
204 | |||
205 | case SPEED_2500: | ||
206 | hw_if->set_gmii_2500_speed(pdata); | ||
207 | break; | ||
208 | |||
209 | case SPEED_1000: | ||
210 | hw_if->set_gmii_speed(pdata); | ||
211 | break; | ||
212 | } | ||
213 | pdata->phy_speed = phydev->speed; | ||
214 | } | ||
215 | |||
216 | if (phydev->link != pdata->phy_link) { | ||
217 | new_state = 1; | ||
218 | pdata->phy_link = 1; | ||
219 | } | ||
220 | } else if (pdata->phy_link) { | ||
221 | new_state = 1; | ||
222 | pdata->phy_link = 0; | ||
223 | pdata->phy_speed = SPEED_UNKNOWN; | ||
224 | } | ||
225 | |||
226 | if (new_state) | ||
227 | phy_print_status(phydev); | ||
228 | |||
229 | DBGPR_MDIO("<--xgbe_adjust_link\n"); | ||
230 | } | ||
231 | |||
232 | void xgbe_dump_phy_registers(struct xgbe_prv_data *pdata) | 160 | void xgbe_dump_phy_registers(struct xgbe_prv_data *pdata) |
233 | { | 161 | { |
234 | struct device *dev = pdata->dev; | 162 | struct device *dev = pdata->dev; |
@@ -278,7 +206,6 @@ void xgbe_dump_phy_registers(struct xgbe_prv_data *pdata) | |||
278 | 206 | ||
279 | int xgbe_mdio_register(struct xgbe_prv_data *pdata) | 207 | int xgbe_mdio_register(struct xgbe_prv_data *pdata) |
280 | { | 208 | { |
281 | struct net_device *netdev = pdata->netdev; | ||
282 | struct device_node *phy_node; | 209 | struct device_node *phy_node; |
283 | struct mii_bus *mii; | 210 | struct mii_bus *mii; |
284 | struct phy_device *phydev; | 211 | struct phy_device *phydev; |
@@ -293,7 +220,6 @@ int xgbe_mdio_register(struct xgbe_prv_data *pdata) | |||
293 | return -EINVAL; | 220 | return -EINVAL; |
294 | } | 221 | } |
295 | 222 | ||
296 | /* Register with the MDIO bus */ | ||
297 | mii = mdiobus_alloc(); | 223 | mii = mdiobus_alloc(); |
298 | if (mii == NULL) { | 224 | if (mii == NULL) { |
299 | dev_err(pdata->dev, "mdiobus_alloc failed\n"); | 225 | dev_err(pdata->dev, "mdiobus_alloc failed\n"); |
@@ -348,26 +274,6 @@ int xgbe_mdio_register(struct xgbe_prv_data *pdata) | |||
348 | pdata->mii = mii; | 274 | pdata->mii = mii; |
349 | pdata->mdio_mmd = MDIO_MMD_PCS; | 275 | pdata->mdio_mmd = MDIO_MMD_PCS; |
350 | 276 | ||
351 | pdata->phy_link = -1; | ||
352 | pdata->phy_speed = SPEED_UNKNOWN; | ||
353 | pdata->phy_tx_pause = pdata->tx_pause; | ||
354 | pdata->phy_rx_pause = pdata->rx_pause; | ||
355 | |||
356 | ret = phy_connect_direct(netdev, phydev, &xgbe_adjust_link, | ||
357 | pdata->phy_mode); | ||
358 | if (ret) { | ||
359 | netdev_err(netdev, "phy_connect_direct failed\n"); | ||
360 | goto err_phy_device; | ||
361 | } | ||
362 | |||
363 | if (!phydev->drv || (phydev->drv->phy_id == 0)) { | ||
364 | netdev_err(netdev, "phy_id not valid\n"); | ||
365 | ret = -ENODEV; | ||
366 | goto err_phy_connect; | ||
367 | } | ||
368 | DBGPR(" phy_connect_direct succeeded for PHY %s, link=%d\n", | ||
369 | dev_name(&phydev->dev), phydev->link); | ||
370 | |||
371 | phydev->autoneg = pdata->default_autoneg; | 277 | phydev->autoneg = pdata->default_autoneg; |
372 | if (phydev->autoneg == AUTONEG_DISABLE) { | 278 | if (phydev->autoneg == AUTONEG_DISABLE) { |
373 | phydev->speed = pdata->default_speed; | 279 | phydev->speed = pdata->default_speed; |
@@ -386,9 +292,6 @@ int xgbe_mdio_register(struct xgbe_prv_data *pdata) | |||
386 | 292 | ||
387 | return 0; | 293 | return 0; |
388 | 294 | ||
389 | err_phy_connect: | ||
390 | phy_disconnect(phydev); | ||
391 | |||
392 | err_phy_device: | 295 | err_phy_device: |
393 | phy_device_free(phydev); | 296 | phy_device_free(phydev); |
394 | 297 | ||
@@ -408,7 +311,6 @@ void xgbe_mdio_unregister(struct xgbe_prv_data *pdata) | |||
408 | { | 311 | { |
409 | DBGPR("-->xgbe_mdio_unregister\n"); | 312 | DBGPR("-->xgbe_mdio_unregister\n"); |
410 | 313 | ||
411 | phy_disconnect(pdata->phydev); | ||
412 | pdata->phydev = NULL; | 314 | pdata->phydev = NULL; |
413 | 315 | ||
414 | module_put(pdata->phy_module); | 316 | module_put(pdata->phy_module); |