Skip to content

Commit

Permalink
Merge branch 'fec-fix-refclk-enable-for-SMSC-LAN8710-20'
Browse files Browse the repository at this point in the history
Richard Leitner says:

====================
net: fec: fix refclk enable for SMSC LAN8710/20

This patch series fixes the use of the SMSC LAN8710/20 with a Freescale ETH
when the refclk is generated by the FSL.

This patchset depends on the "phylib: Add device reset GPIO support" patch
submitted by Geert Uytterhoeven/Sergei Shtylyov, which was merged to
net-next as commit bafbdd5 ("phylib: Add device reset GPIO support").

Changes v5:
	- fix reset delay calculation (max_t instead of min_t)

Changes v4:
	- simplify dts parsing
	- simplify reset delay evaluation and execution
	- fec: ensure to only reset once during fec_enet_open()
	- remove dependency notes from commit message
	- add reviews and acks

Changes v3:
	- use phylib to hard-reset the PHY
	- implement reset delays in phylib
	- add new phylib API & flag (PHY_RST_AFTER_CLK_EN) to determine if
	  a PHY is affected

Changes v2:
	- simplify and fix fec_reset_phy function to support multiple calls
	- include: linux: phy: harmonize phy_id{,_mask} type
	- reset the phy instead of not turning the clock on and off
	  (which would have caused a power consumption regression)
====================

Signed-off-by: David S. Miller <davem@davemloft.net>
  • Loading branch information
David S. Miller committed Dec 13, 2017
2 parents 9cca5d2 + 1b0a83a commit f93ea3b
Show file tree
Hide file tree
Showing 8 changed files with 74 additions and 3 deletions.
10 changes: 10 additions & 0 deletions Documentation/devicetree/bindings/net/phy.txt
Original file line number Diff line number Diff line change
Expand Up @@ -55,11 +55,21 @@ Optional Properties:

- reset-gpios: The GPIO phandle and specifier for the PHY reset signal.

- reset-delay-us: Delay after the reset was asserted in microseconds.
If this property is missing the delay will be skipped.

- reset-post-delay-us: Delay after the reset was deasserted in microseconds.
If this property is missing the delay will be skipped.

Example:

ethernet-phy@0 {
compatible = "ethernet-phy-id0141.0e90", "ethernet-phy-ieee802.3-c22";
interrupt-parent = <&PIC>;
interrupts = <35 IRQ_TYPE_EDGE_RISING>;
reg = <0>;

reset-gpios = <&gpio1 4 GPIO_ACTIVE_LOW>;
reset-delay-us = <1000>;
reset-post-delay-us = <2000>;
};
20 changes: 20 additions & 0 deletions drivers/net/ethernet/freescale/fec_main.c
Original file line number Diff line number Diff line change
Expand Up @@ -1862,6 +1862,8 @@ static int fec_enet_clk_enable(struct net_device *ndev, bool enable)
ret = clk_prepare_enable(fep->clk_ref);
if (ret)
goto failed_clk_ref;

phy_reset_after_clk_enable(ndev->phydev);
} else {
clk_disable_unprepare(fep->clk_ahb);
clk_disable_unprepare(fep->clk_enet_out);
Expand Down Expand Up @@ -2834,6 +2836,7 @@ fec_enet_open(struct net_device *ndev)
{
struct fec_enet_private *fep = netdev_priv(ndev);
int ret;
bool reset_again;

ret = pm_runtime_get_sync(&fep->pdev->dev);
if (ret < 0)
Expand All @@ -2844,6 +2847,17 @@ fec_enet_open(struct net_device *ndev)
if (ret)
goto clk_enable;

/* During the first fec_enet_open call the PHY isn't probed at this
* point. Therefore the phy_reset_after_clk_enable() call within
* fec_enet_clk_enable() fails. As we need this reset in order to be
* sure the PHY is working correctly we check if we need to reset again
* later when the PHY is probed
*/
if (ndev->phydev && ndev->phydev->drv)
reset_again = false;
else
reset_again = true;

/* I should reset the ring buffers here, but I don't yet know
* a simple way to do that.
*/
Expand All @@ -2860,6 +2874,12 @@ fec_enet_open(struct net_device *ndev)
if (ret)
goto err_enet_mii_probe;

/* Call phy_reset_after_clk_enable() again if it failed during
* phy_reset_after_clk_enable() before because the PHY wasn't probed.
*/
if (reset_again)
phy_reset_after_clk_enable(ndev->phydev);

if (fep->quirks & FEC_QUIRK_ERR006687)
imx6q_cpuidle_fec_irqs_used();

Expand Down
13 changes: 11 additions & 2 deletions drivers/net/phy/mdio_device.c
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
#include <linux/slab.h>
#include <linux/string.h>
#include <linux/unistd.h>
#include <linux/delay.h>

void mdio_device_free(struct mdio_device *mdiodev)
{
Expand Down Expand Up @@ -118,8 +119,16 @@ EXPORT_SYMBOL(mdio_device_remove);

void mdio_device_reset(struct mdio_device *mdiodev, int value)
{
if (mdiodev->reset)
gpiod_set_value(mdiodev->reset, value);
unsigned int d;

if (!mdiodev->reset)
return;

gpiod_set_value(mdiodev->reset, value);

d = value ? mdiodev->reset_delay : mdiodev->reset_post_delay;
if (d)
usleep_range(d, d + max_t(unsigned int, d / 10, 100));
}
EXPORT_SYMBOL(mdio_device_reset);

Expand Down
24 changes: 24 additions & 0 deletions drivers/net/phy/phy_device.c
Original file line number Diff line number Diff line change
Expand Up @@ -1218,6 +1218,30 @@ int phy_loopback(struct phy_device *phydev, bool enable)
}
EXPORT_SYMBOL(phy_loopback);

/**
* phy_reset_after_clk_enable - perform a PHY reset if needed
* @phydev: target phy_device struct
*
* Description: Some PHYs are known to need a reset after their refclk was
* enabled. This function evaluates the flags and perform the reset if it's
* needed. Returns < 0 on error, 0 if the phy wasn't reset and 1 if the phy
* was reset.
*/
int phy_reset_after_clk_enable(struct phy_device *phydev)
{
if (!phydev || !phydev->drv)
return -ENODEV;

if (phydev->drv->flags & PHY_RST_AFTER_CLK_EN) {
phy_device_reset(phydev, 1);
phy_device_reset(phydev, 0);
return 1;
}

return 0;
}
EXPORT_SYMBOL(phy_reset_after_clk_enable);

/* Generic PHY support and helper functions */

/**
Expand Down
2 changes: 1 addition & 1 deletion drivers/net/phy/smsc.c
Original file line number Diff line number Diff line change
Expand Up @@ -312,7 +312,7 @@ static struct phy_driver smsc_phy_driver[] = {
.name = "SMSC LAN8710/LAN8720",

.features = PHY_BASIC_FEATURES,
.flags = PHY_HAS_INTERRUPT,
.flags = PHY_HAS_INTERRUPT | PHY_RST_AFTER_CLK_EN,

.probe = smsc_phy_probe,

Expand Down
4 changes: 4 additions & 0 deletions drivers/of/of_mdio.c
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,10 @@ static int of_mdiobus_register_phy(struct mii_bus *mdio,
if (of_property_read_bool(child, "broken-turn-around"))
mdio->phy_ignore_ta_mask |= 1 << addr;

of_property_read_u32(child, "reset-delay-us", &phy->mdio.reset_delay);
of_property_read_u32(child, "reset-post-delay-us",
&phy->mdio.reset_post_delay);

/* Associate the OF node with the device structure so it
* can be looked up later */
of_node_get(child);
Expand Down
2 changes: 2 additions & 0 deletions include/linux/mdio.h
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ struct mdio_device {
int addr;
int flags;
struct gpio_desc *reset;
unsigned int reset_delay;
unsigned int reset_post_delay;
};
#define to_mdio_device(d) container_of(d, struct mdio_device, dev)

Expand Down
2 changes: 2 additions & 0 deletions include/linux/phy.h
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@

#define PHY_HAS_INTERRUPT 0x00000001
#define PHY_IS_INTERNAL 0x00000002
#define PHY_RST_AFTER_CLK_EN 0x00000004
#define MDIO_DEVICE_IS_PHY 0x80000000

/* Interface Mode definitions */
Expand Down Expand Up @@ -853,6 +854,7 @@ int phy_aneg_done(struct phy_device *phydev);

int phy_stop_interrupts(struct phy_device *phydev);
int phy_restart_aneg(struct phy_device *phydev);
int phy_reset_after_clk_enable(struct phy_device *phydev);

static inline void phy_device_reset(struct phy_device *phydev, int value)
{
Expand Down

0 comments on commit f93ea3b

Please sign in to comment.