Commit f93ea3bf authored by David S. Miller's avatar David S. Miller

Merge branch 'fec-fix-refclk-enable-for-SMSC-LAN8710-20'

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 bafbdd52 ("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: default avatarDavid S. Miller <davem@davemloft.net>
parents 9cca5d2f 1b0a83ac
......@@ -55,6 +55,12 @@ 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 {
......@@ -62,4 +68,8 @@ ethernet-phy@0 {
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>;
};
......@@ -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);
......@@ -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)
......@@ -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.
*/
......@@ -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();
......
......@@ -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)
{
......@@ -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);
......
......@@ -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 */
/**
......
......@@ -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,
......
......@@ -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);
......
......@@ -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)
......
......@@ -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 */
......@@ -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)
{
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment