MLK-10939-01 net: fec: add stop mode support for dts register set
The current driver support stop mode by calling machine api. The patch add dts support to set gpr register for stop request. After magic pattern comming during system suspend status, system will be waked up, and irq handler will be running, there have enet register access. Since all clocks are disabled in suspend, and clocks are enabled after resume function. But irq handler run before resume function. For imx7d chip, access register need some clocks enabled, otherwise system hang. So the patch also disable wake up irq in the suspend, after resume back enable the irq, which can avoid system hang issue. Signed-off-by: Fugang Duan <B38611@freescale.com> (cherry pick and merge from commit: 8da4f80af0913781a4f9d50917c1dd66180e519d)
This commit is contained in:
committed by
Leonard Crestez
parent
fc014617ae
commit
19b76fd012
@ -30,6 +30,9 @@ Optional properties:
|
||||
- fsl,err006687-workaround-present: If present indicates that the system has
|
||||
the hardware workaround for ERR006687 applied and does not need a software
|
||||
workaround.
|
||||
- fsl,wakeup_irq : The property define the wakeup irq index in enet irq source.
|
||||
- stop-mode : If present, indicates soc need to set gpr bit to request stop
|
||||
mode.
|
||||
|
||||
Optional subnodes:
|
||||
- mdio : specifies the mdio bus in the FEC, used as a container for phy nodes
|
||||
|
||||
@ -471,6 +471,12 @@ struct bufdesc_prop {
|
||||
unsigned char dsize_log2;
|
||||
};
|
||||
|
||||
struct fec_enet_stop_mode {
|
||||
struct regmap *gpr;
|
||||
u8 req_gpr;
|
||||
u8 req_bit;
|
||||
};
|
||||
|
||||
struct fec_enet_priv_tx_q {
|
||||
struct bufdesc_prop bd;
|
||||
unsigned char *tx_bounce[TX_RING_SIZE];
|
||||
@ -545,6 +551,7 @@ struct fec_enet_private {
|
||||
bool bufdesc_ex;
|
||||
int pause_flag;
|
||||
int wol_flag;
|
||||
int wake_irq;
|
||||
u32 quirks;
|
||||
|
||||
struct napi_struct napi;
|
||||
@ -589,6 +596,8 @@ struct fec_enet_private {
|
||||
unsigned int next_counter;
|
||||
|
||||
u64 ethtool_stats[0];
|
||||
|
||||
struct fec_enet_stop_mode gpr;
|
||||
};
|
||||
|
||||
void fec_ptp_init(struct platform_device *pdev);
|
||||
|
||||
@ -61,6 +61,8 @@
|
||||
#include <linux/pinctrl/consumer.h>
|
||||
#include <linux/prefetch.h>
|
||||
#include <soc/imx/cpuidle.h>
|
||||
#include <linux/mfd/syscon.h>
|
||||
#include <linux/regmap.h>
|
||||
|
||||
#include <asm/cacheflush.h>
|
||||
|
||||
@ -1079,11 +1081,30 @@ fec_restart(struct net_device *ndev)
|
||||
|
||||
}
|
||||
|
||||
static int fec_enet_stop_mode(struct fec_enet_private *fep, bool enabled)
|
||||
{
|
||||
struct fec_platform_data *pdata = fep->pdev->dev.platform_data;
|
||||
|
||||
if (fep->gpr.gpr) {
|
||||
if (enabled)
|
||||
regmap_update_bits(fep->gpr.gpr, fep->gpr.req_gpr,
|
||||
1 << fep->gpr.req_bit,
|
||||
1 << fep->gpr.req_bit);
|
||||
else
|
||||
regmap_update_bits(fep->gpr.gpr, fep->gpr.req_gpr,
|
||||
1 << fep->gpr.req_bit,
|
||||
0);
|
||||
} else if (pdata && pdata->sleep_mode_enable) {
|
||||
pdata->sleep_mode_enable(enabled);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
fec_stop(struct net_device *ndev)
|
||||
{
|
||||
struct fec_enet_private *fep = netdev_priv(ndev);
|
||||
struct fec_platform_data *pdata = fep->pdev->dev.platform_data;
|
||||
u32 rmii_mode = readl(fep->hwp + FEC_R_CNTRL) & (1 << 8);
|
||||
u32 val;
|
||||
|
||||
@ -1113,8 +1134,7 @@ fec_stop(struct net_device *ndev)
|
||||
val |= (FEC_ECR_MAGICEN | FEC_ECR_SLEEP);
|
||||
writel(val, fep->hwp + FEC_ECNTRL);
|
||||
|
||||
if (pdata && pdata->sleep_mode_enable)
|
||||
pdata->sleep_mode_enable(true);
|
||||
fec_enet_stop_mode(fep, true);
|
||||
}
|
||||
writel(fep->phy_speed, fep->hwp + FEC_MII_SPEED);
|
||||
|
||||
@ -2578,15 +2598,10 @@ fec_enet_set_wol(struct net_device *ndev, struct ethtool_wolinfo *wol)
|
||||
return -EINVAL;
|
||||
|
||||
device_set_wakeup_enable(&ndev->dev, wol->wolopts & WAKE_MAGIC);
|
||||
if (device_may_wakeup(&ndev->dev)) {
|
||||
if (device_may_wakeup(&ndev->dev))
|
||||
fep->wol_flag |= FEC_WOL_FLAG_ENABLE;
|
||||
if (fep->irq[0] > 0)
|
||||
enable_irq_wake(fep->irq[0]);
|
||||
} else {
|
||||
else
|
||||
fep->wol_flag &= (~FEC_WOL_FLAG_ENABLE);
|
||||
if (fep->irq[0] > 0)
|
||||
disable_irq_wake(fep->irq[0]);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -3368,6 +3383,41 @@ fec_enet_get_queue_num(struct platform_device *pdev, int *num_tx, int *num_rx)
|
||||
|
||||
}
|
||||
|
||||
static void fec_enet_of_parse_stop_mode(struct platform_device *pdev)
|
||||
{
|
||||
struct net_device *dev = platform_get_drvdata(pdev);
|
||||
struct device_node *np = pdev->dev.of_node;
|
||||
struct fec_enet_private *fep = netdev_priv(dev);
|
||||
struct device_node *node;
|
||||
phandle phandle;
|
||||
u32 out_val[3];
|
||||
int ret;
|
||||
|
||||
ret = of_property_read_u32_array(np, "stop-mode", out_val, 3);
|
||||
if (ret) {
|
||||
dev_dbg(&pdev->dev, "no stop-mode property\n");
|
||||
return;
|
||||
}
|
||||
|
||||
phandle = *out_val;
|
||||
node = of_find_node_by_phandle(phandle);
|
||||
if (!node) {
|
||||
dev_dbg(&pdev->dev, "could not find gpr node by phandle\n");
|
||||
return;
|
||||
}
|
||||
|
||||
fep->gpr.gpr = syscon_node_to_regmap(node);
|
||||
if (IS_ERR(fep->gpr.gpr)) {
|
||||
dev_dbg(&pdev->dev, "could not find gpr regmap\n");
|
||||
return;
|
||||
}
|
||||
|
||||
of_node_put(node);
|
||||
|
||||
fep->gpr.req_gpr = out_val[1];
|
||||
fep->gpr.req_bit = out_val[2];
|
||||
}
|
||||
|
||||
static int
|
||||
fec_probe(struct platform_device *pdev)
|
||||
{
|
||||
@ -3430,6 +3480,8 @@ fec_probe(struct platform_device *pdev)
|
||||
!of_property_read_bool(np, "fsl,err006687-workaround-present"))
|
||||
fep->quirks |= FEC_QUIRK_ERR006687;
|
||||
|
||||
fec_enet_of_parse_stop_mode(pdev);
|
||||
|
||||
if (of_get_property(np, "fsl,magic-packet", NULL))
|
||||
fep->wol_flag |= FEC_WOL_HAS_MAGIC_PACKET;
|
||||
|
||||
@ -3541,6 +3593,12 @@ fec_probe(struct platform_device *pdev)
|
||||
fep->irq[i] = irq;
|
||||
}
|
||||
|
||||
ret = of_property_read_u32(np, "fsl,wakeup_irq", &irq);
|
||||
if (!ret && irq < FEC_IRQ_NUM)
|
||||
fep->wake_irq = fep->irq[irq];
|
||||
else
|
||||
fep->wake_irq = fep->irq[0];
|
||||
|
||||
init_completion(&fep->mdio_done);
|
||||
ret = fec_enet_mii_init(pdev);
|
||||
if (ret)
|
||||
@ -3628,10 +3686,13 @@ static int __maybe_unused fec_suspend(struct device *dev)
|
||||
netif_device_detach(ndev);
|
||||
netif_tx_unlock_bh(ndev);
|
||||
fec_stop(ndev);
|
||||
fec_enet_clk_enable(ndev, false);
|
||||
if (!(fep->wol_flag & FEC_WOL_FLAG_ENABLE))
|
||||
if (!(fep->wol_flag & FEC_WOL_FLAG_ENABLE)) {
|
||||
pinctrl_pm_select_sleep_state(&fep->pdev->dev);
|
||||
pinctrl_pm_select_sleep_state(&fep->pdev->dev);
|
||||
} else {
|
||||
disable_irq(fep->wake_irq);
|
||||
enable_irq_wake(fep->wake_irq);
|
||||
}
|
||||
fec_enet_clk_enable(ndev, false);
|
||||
} else if (fep->mii_bus_share && !ndev->phydev) {
|
||||
fec_enet_clk_enable(ndev, false);
|
||||
pinctrl_pm_select_sleep_state(&fep->pdev->dev);
|
||||
@ -3654,7 +3715,6 @@ static int __maybe_unused fec_resume(struct device *dev)
|
||||
{
|
||||
struct net_device *ndev = dev_get_drvdata(dev);
|
||||
struct fec_enet_private *fep = netdev_priv(ndev);
|
||||
struct fec_platform_data *pdata = fep->pdev->dev.platform_data;
|
||||
int ret;
|
||||
int val;
|
||||
|
||||
@ -3671,9 +3731,11 @@ static int __maybe_unused fec_resume(struct device *dev)
|
||||
rtnl_unlock();
|
||||
goto failed_clk;
|
||||
}
|
||||
|
||||
if (fep->wol_flag & FEC_WOL_FLAG_ENABLE) {
|
||||
if (pdata && pdata->sleep_mode_enable)
|
||||
pdata->sleep_mode_enable(false);
|
||||
disable_irq_wake(fep->wake_irq);
|
||||
fec_enet_stop_mode(fep, false);
|
||||
enable_irq(fep->wake_irq);
|
||||
val = readl(fep->hwp + FEC_ECNTRL);
|
||||
val &= ~(FEC_ECR_MAGICEN | FEC_ECR_SLEEP);
|
||||
writel(val, fep->hwp + FEC_ECNTRL);
|
||||
|
||||
Reference in New Issue
Block a user