Commit 29380905 authored by Lucas Stach's avatar Lucas Stach Committed by Shawn Guo

ARM: imx6: disable deeper idle states when FEC is active w/o HW workaround

The i.MX6 Q/DL has an erratum (ERR006687) that prevents the FEC from
waking the CPUs when they are in wait(unclocked) state. As the hardware
workaround isn't applicable to all boards, disable the deeper idle state
when the workaround isn't present and the FEC is in use.

This allows to safely run a kernel with CPUidle enabled on all i.MX6
boards.
Signed-off-by: default avatarLucas Stach <l.stach@pengutronix.de>
Acked-by: David S. Miller <davem@davemloft.net> (for network changes)
Signed-off-by: default avatarShawn Guo <shawnguo@kernel.org>
parent ac4bbb45
...@@ -27,6 +27,9 @@ Optional properties: ...@@ -27,6 +27,9 @@ Optional properties:
number to 1. number to 1.
- fsl,magic-packet : If present, indicates that the hardware supports waking - fsl,magic-packet : If present, indicates that the hardware supports waking
up via magic packet. up via magic packet.
- fsl,err006687-workaround-present: If present indicates that the system has
the hardware workaround for ERR006687 applied and does not need a software
workaround.
Optional subnodes: Optional subnodes:
- mdio : specifies the mdio bus in the FEC, used as a container for phy nodes - mdio : specifies the mdio bus in the FEC, used as a container for phy nodes
......
...@@ -62,6 +62,22 @@ static struct cpuidle_driver imx6q_cpuidle_driver = { ...@@ -62,6 +62,22 @@ static struct cpuidle_driver imx6q_cpuidle_driver = {
.safe_state_index = 0, .safe_state_index = 0,
}; };
/*
* i.MX6 Q/DL has an erratum (ERR006687) that prevents the FEC from waking the
* CPUs when they are in wait(unclocked) state. As the hardware workaround isn't
* applicable to all boards, disable the deeper idle state when the workaround
* isn't present and the FEC is in use.
*/
void imx6q_cpuidle_fec_irqs_used(void)
{
imx6q_cpuidle_driver.states[1].disabled = true;
}
void imx6q_cpuidle_fec_irqs_unused(void)
{
imx6q_cpuidle_driver.states[1].disabled = false;
}
int __init imx6q_cpuidle_init(void) int __init imx6q_cpuidle_init(void)
{ {
/* Set INT_MEM_CLK_LPM bit to get a reliable WAIT mode support */ /* Set INT_MEM_CLK_LPM bit to get a reliable WAIT mode support */
......
...@@ -442,6 +442,8 @@ struct bufdesc_ex { ...@@ -442,6 +442,8 @@ struct bufdesc_ex {
#define FEC_QUIRK_SINGLE_MDIO (1 << 11) #define FEC_QUIRK_SINGLE_MDIO (1 << 11)
/* Controller supports RACC register */ /* Controller supports RACC register */
#define FEC_QUIRK_HAS_RACC (1 << 12) #define FEC_QUIRK_HAS_RACC (1 << 12)
/* Interrupt doesn't wake CPU from deep idle */
#define FEC_QUIRK_ERR006687 (1 << 13)
struct bufdesc_prop { struct bufdesc_prop {
int qid; int qid;
......
...@@ -60,6 +60,7 @@ ...@@ -60,6 +60,7 @@
#include <linux/if_vlan.h> #include <linux/if_vlan.h>
#include <linux/pinctrl/consumer.h> #include <linux/pinctrl/consumer.h>
#include <linux/prefetch.h> #include <linux/prefetch.h>
#include <soc/imx/cpuidle.h>
#include <asm/cacheflush.h> #include <asm/cacheflush.h>
...@@ -2820,6 +2821,9 @@ fec_enet_open(struct net_device *ndev) ...@@ -2820,6 +2821,9 @@ fec_enet_open(struct net_device *ndev)
if (ret) if (ret)
goto err_enet_mii_probe; goto err_enet_mii_probe;
if (fep->quirks & FEC_QUIRK_ERR006687)
imx6q_cpuidle_fec_irqs_used();
napi_enable(&fep->napi); napi_enable(&fep->napi);
phy_start(ndev->phydev); phy_start(ndev->phydev);
netif_tx_start_all_queues(ndev); netif_tx_start_all_queues(ndev);
...@@ -2855,6 +2859,9 @@ fec_enet_close(struct net_device *ndev) ...@@ -2855,6 +2859,9 @@ fec_enet_close(struct net_device *ndev)
phy_disconnect(ndev->phydev); phy_disconnect(ndev->phydev);
if (fep->quirks & FEC_QUIRK_ERR006687)
imx6q_cpuidle_fec_irqs_unused();
fec_enet_clk_enable(ndev, false); fec_enet_clk_enable(ndev, false);
pinctrl_pm_select_sleep_state(&fep->pdev->dev); pinctrl_pm_select_sleep_state(&fep->pdev->dev);
pm_runtime_mark_last_busy(&fep->pdev->dev); pm_runtime_mark_last_busy(&fep->pdev->dev);
...@@ -3294,6 +3301,11 @@ fec_probe(struct platform_device *pdev) ...@@ -3294,6 +3301,11 @@ fec_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, ndev); platform_set_drvdata(pdev, ndev);
if ((of_machine_is_compatible("fsl,imx6q") ||
of_machine_is_compatible("fsl,imx6dl")) &&
!of_property_read_bool(np, "fsl,err006687-workaround-present"))
fep->quirks |= FEC_QUIRK_ERR006687;
if (of_get_property(np, "fsl,magic-packet", NULL)) if (of_get_property(np, "fsl,magic-packet", NULL))
fep->wol_flag |= FEC_WOL_HAS_MAGIC_PACKET; fep->wol_flag |= FEC_WOL_HAS_MAGIC_PACKET;
......
/*
* Copyright 2016 Pengutronix, <kernel@pengutronix.de>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*/
#ifndef __SOC_IMX_CPUIDLE_H__
#define __SOC_IMX_CPUIDLE_H__
#if defined(CONFIG_CPU_IDLE) && defined(CONFIG_SOC_IMX6Q)
void imx6q_cpuidle_fec_irqs_used(void);
void imx6q_cpuidle_fec_irqs_unused(void);
#else
void imx6q_cpuidle_fec_irqs_used(void) { }
void imx6q_cpuidle_fec_irqs_unused(void) { }
#endif
#endif /* __SOC_IMX_CPUIDLE_H__ */
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