Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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: Lucas Stach <l.stach@pengutronix.de>
Acked-by: David S. Miller <davem@davemloft.net> (for network changes)
Signed-off-by: Shawn Guo <shawnguo@kernel.org>

authored by

Lucas Stach and committed by
Shawn Guo
29380905 ac4bbb45

+58
+3
Documentation/devicetree/bindings/net/fsl-fec.txt
··· 27 27 number to 1. 28 28 - fsl,magic-packet : If present, indicates that the hardware supports waking 29 29 up via magic packet. 30 + - fsl,err006687-workaround-present: If present indicates that the system has 31 + the hardware workaround for ERR006687 applied and does not need a software 32 + workaround. 30 33 31 34 Optional subnodes: 32 35 - mdio : specifies the mdio bus in the FEC, used as a container for phy nodes
+16
arch/arm/mach-imx/cpuidle-imx6q.c
··· 62 62 .safe_state_index = 0, 63 63 }; 64 64 65 + /* 66 + * i.MX6 Q/DL has an erratum (ERR006687) that prevents the FEC from waking the 67 + * CPUs when they are in wait(unclocked) state. As the hardware workaround isn't 68 + * applicable to all boards, disable the deeper idle state when the workaround 69 + * isn't present and the FEC is in use. 70 + */ 71 + void imx6q_cpuidle_fec_irqs_used(void) 72 + { 73 + imx6q_cpuidle_driver.states[1].disabled = true; 74 + } 75 + 76 + void imx6q_cpuidle_fec_irqs_unused(void) 77 + { 78 + imx6q_cpuidle_driver.states[1].disabled = false; 79 + } 80 + 65 81 int __init imx6q_cpuidle_init(void) 66 82 { 67 83 /* Set INT_MEM_CLK_LPM bit to get a reliable WAIT mode support */
+2
drivers/net/ethernet/freescale/fec.h
··· 442 442 #define FEC_QUIRK_SINGLE_MDIO (1 << 11) 443 443 /* Controller supports RACC register */ 444 444 #define FEC_QUIRK_HAS_RACC (1 << 12) 445 + /* Interrupt doesn't wake CPU from deep idle */ 446 + #define FEC_QUIRK_ERR006687 (1 << 13) 445 447 446 448 struct bufdesc_prop { 447 449 int qid;
+12
drivers/net/ethernet/freescale/fec_main.c
··· 60 60 #include <linux/if_vlan.h> 61 61 #include <linux/pinctrl/consumer.h> 62 62 #include <linux/prefetch.h> 63 + #include <soc/imx/cpuidle.h> 63 64 64 65 #include <asm/cacheflush.h> 65 66 ··· 2821 2820 if (ret) 2822 2821 goto err_enet_mii_probe; 2823 2822 2823 + if (fep->quirks & FEC_QUIRK_ERR006687) 2824 + imx6q_cpuidle_fec_irqs_used(); 2825 + 2824 2826 napi_enable(&fep->napi); 2825 2827 phy_start(ndev->phydev); 2826 2828 netif_tx_start_all_queues(ndev); ··· 2858 2854 } 2859 2855 2860 2856 phy_disconnect(ndev->phydev); 2857 + 2858 + if (fep->quirks & FEC_QUIRK_ERR006687) 2859 + imx6q_cpuidle_fec_irqs_unused(); 2861 2860 2862 2861 fec_enet_clk_enable(ndev, false); 2863 2862 pinctrl_pm_select_sleep_state(&fep->pdev->dev); ··· 3300 3293 fep->dev_id = dev_id++; 3301 3294 3302 3295 platform_set_drvdata(pdev, ndev); 3296 + 3297 + if ((of_machine_is_compatible("fsl,imx6q") || 3298 + of_machine_is_compatible("fsl,imx6dl")) && 3299 + !of_property_read_bool(np, "fsl,err006687-workaround-present")) 3300 + fep->quirks |= FEC_QUIRK_ERR006687; 3303 3301 3304 3302 if (of_get_property(np, "fsl,magic-packet", NULL)) 3305 3303 fep->wol_flag |= FEC_WOL_HAS_MAGIC_PACKET;
+25
include/soc/imx/cpuidle.h
··· 1 + /* 2 + * Copyright 2016 Pengutronix, <kernel@pengutronix.de> 3 + * 4 + * This program is free software; you can redistribute it and/or modify it 5 + * under the terms and conditions of the GNU General Public License, 6 + * version 2, as published by the Free Software Foundation. 7 + * 8 + * This program is distributed in the hope it will be useful, but WITHOUT 9 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 10 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 11 + * more details. 12 + */ 13 + 14 + #ifndef __SOC_IMX_CPUIDLE_H__ 15 + #define __SOC_IMX_CPUIDLE_H__ 16 + 17 + #if defined(CONFIG_CPU_IDLE) && defined(CONFIG_SOC_IMX6Q) 18 + void imx6q_cpuidle_fec_irqs_used(void); 19 + void imx6q_cpuidle_fec_irqs_unused(void); 20 + #else 21 + void imx6q_cpuidle_fec_irqs_used(void) { } 22 + void imx6q_cpuidle_fec_irqs_unused(void) { } 23 + #endif 24 + 25 + #endif /* __SOC_IMX_CPUIDLE_H__ */