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 bafbdd527d
("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>
This commit is contained in:
commit
f93ea3bf15
@ -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 @@ out:
|
||||
}
|
||||
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)
|
||||
{
|
||||
|
Loading…
Reference in New Issue
Block a user