Commit 5164172f authored by David S. Miller's avatar David S. Miller

Merge branch 'fec-next'

Fugang Duan says:

====================
net: fec: add Wake-on-LAN support

The patch series enable FEC Wake-on-LAN feature for i.MX6q/dl and i.MX6SX SOCs.
FEC HW support sleep mode, when system in suspend status with FEC all clock gate
off, magic packet can wake up system. For different SOCs, there have special SOC
GPR register to let FEC enter sleep mode or exit sleep mode, add these to platform
callback for driver' call.

Patch#1: add WOL interface supports.
Patch#2: add SOC special sleep of/off operations for driver's sleep callback.
Patch#3: add magic pattern support for devicetree.
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 03366a33 07b4d2dd
......@@ -22,6 +22,8 @@ Optional properties:
- fsl,num-rx-queues : The property is valid for enet-avb IP, which supports
hw multi queues. Should specify the rx queue number, otherwise set rx queue
number to 1.
- fsl,magic-packet : If present, indicates that the hardware supports waking
up via magic packet.
Optional subnodes:
- mdio : specifies the mdio bus in the FEC, used as a container for phy nodes
......
......@@ -67,6 +67,7 @@ &fec {
phy-mode = "rgmii";
interrupts-extended = <&gpio1 6 IRQ_TYPE_LEVEL_HIGH>,
<&intc 0 119 IRQ_TYPE_LEVEL_HIGH>;
fsl,magic-packet;
status = "okay";
};
......
......@@ -160,6 +160,7 @@ &fec {
pinctrl-0 = <&pinctrl_enet>;
phy-mode = "rgmii";
phy-reset-gpios = <&gpio1 25 0>;
fsl,magic-packet;
status = "okay";
};
......
......@@ -31,6 +31,8 @@
#include <linux/micrel_phy.h>
#include <linux/mfd/syscon.h>
#include <linux/mfd/syscon/imx6q-iomuxc-gpr.h>
#include <linux/fec.h>
#include <linux/netdevice.h>
#include <asm/mach/arch.h>
#include <asm/mach/map.h>
#include <asm/system_misc.h>
......@@ -39,6 +41,35 @@
#include "cpuidle.h"
#include "hardware.h"
static struct fec_platform_data fec_pdata;
static void imx6q_fec_sleep_enable(int enabled)
{
struct regmap *gpr;
gpr = syscon_regmap_lookup_by_compatible("fsl,imx6q-iomuxc-gpr");
if (!IS_ERR(gpr)) {
if (enabled)
regmap_update_bits(gpr, IOMUXC_GPR13,
IMX6Q_GPR13_ENET_STOP_REQ,
IMX6Q_GPR13_ENET_STOP_REQ);
else
regmap_update_bits(gpr, IOMUXC_GPR13,
IMX6Q_GPR13_ENET_STOP_REQ, 0);
} else
pr_err("failed to find fsl,imx6q-iomux-gpr regmap\n");
}
static void __init imx6q_enet_plt_init(void)
{
struct device_node *np;
np = of_find_node_by_path("/soc/aips-bus@02100000/ethernet@02188000");
if (np && of_get_property(np, "fsl,magic-packet", NULL))
fec_pdata.sleep_mode_enable = imx6q_fec_sleep_enable;
}
/* For imx6q sabrelite board: set KSZ9021RN RGMII pad skew */
static int ksz9021rn_phy_fixup(struct phy_device *phydev)
{
......@@ -261,6 +292,12 @@ static void __init imx6q_axi_init(void)
}
}
/* Add auxdata to pass platform data */
static const struct of_dev_auxdata imx6q_auxdata_lookup[] __initconst = {
OF_DEV_AUXDATA("fsl,imx6q-fec", 0x02188000, NULL, &fec_pdata),
{ /* sentinel */ }
};
static void __init imx6q_init_machine(void)
{
struct device *parent;
......@@ -274,11 +311,13 @@ static void __init imx6q_init_machine(void)
imx6q_enet_phy_init();
of_platform_populate(NULL, of_default_bus_match_table, NULL, parent);
of_platform_populate(NULL, of_default_bus_match_table,
imx6q_auxdata_lookup, parent);
imx_anatop_init();
cpu_is_imx6q() ? imx6q_pm_init() : imx6dl_pm_init();
imx6q_1588_init();
imx6q_enet_plt_init();
imx6q_axi_init();
}
......
......@@ -12,12 +12,62 @@
#include <linux/regmap.h>
#include <linux/mfd/syscon.h>
#include <linux/mfd/syscon/imx6q-iomuxc-gpr.h>
#include <linux/fec.h>
#include <linux/netdevice.h>
#include <asm/mach/arch.h>
#include <asm/mach/map.h>
#include "common.h"
#include "cpuidle.h"
static struct fec_platform_data fec_pdata[2];
static void imx6sx_fec1_sleep_enable(int enabled)
{
struct regmap *gpr;
gpr = syscon_regmap_lookup_by_compatible("fsl,imx6sx-iomuxc-gpr");
if (!IS_ERR(gpr)) {
if (enabled)
regmap_update_bits(gpr, IOMUXC_GPR4,
IMX6SX_GPR4_FEC_ENET1_STOP_REQ,
IMX6SX_GPR4_FEC_ENET1_STOP_REQ);
else
regmap_update_bits(gpr, IOMUXC_GPR4,
IMX6SX_GPR4_FEC_ENET1_STOP_REQ, 0);
} else
pr_err("failed to find fsl,imx6sx-iomux-gpr regmap\n");
}
static void imx6sx_fec2_sleep_enable(int enabled)
{
struct regmap *gpr;
gpr = syscon_regmap_lookup_by_compatible("fsl,imx6sx-iomuxc-gpr");
if (!IS_ERR(gpr)) {
if (enabled)
regmap_update_bits(gpr, IOMUXC_GPR4,
IMX6SX_GPR4_FEC_ENET2_STOP_REQ,
IMX6SX_GPR4_FEC_ENET2_STOP_REQ);
else
regmap_update_bits(gpr, IOMUXC_GPR4,
IMX6SX_GPR4_FEC_ENET2_STOP_REQ, 0);
} else
pr_err("failed to find fsl,imx6sx-iomux-gpr regmap\n");
}
static void __init imx6sx_enet_plt_init(void)
{
struct device_node *np;
np = of_find_node_by_path("/soc/aips-bus@02100000/ethernet@02188000");
if (np && of_get_property(np, "fsl,magic-packet", NULL))
fec_pdata[0].sleep_mode_enable = imx6sx_fec1_sleep_enable;
np = of_find_node_by_path("/soc/aips-bus@02100000/ethernet@021b4000");
if (np && of_get_property(np, "fsl,magic-packet", NULL))
fec_pdata[1].sleep_mode_enable = imx6sx_fec2_sleep_enable;
}
static int ar8031_phy_fixup(struct phy_device *dev)
{
u16 val;
......
......@@ -357,6 +357,7 @@ struct bufdesc_ex {
#define FEC_ENET_RXB ((uint)0x01000000) /* A buffer was received */
#define FEC_ENET_MII ((uint)0x00800000) /* MII interrupt */
#define FEC_ENET_EBERR ((uint)0x00400000) /* SDMA bus error */
#define FEC_ENET_WAKEUP ((uint)0x00020000) /* Wakeup request */
#define FEC_ENET_TXF (FEC_ENET_TXF_0 | FEC_ENET_TXF_1 | FEC_ENET_TXF_2)
#define FEC_ENET_RXF (FEC_ENET_RXF_0 | FEC_ENET_RXF_1 | FEC_ENET_RXF_2)
#define FEC_ENET_TS_AVAIL ((uint)0x00010000)
......@@ -512,6 +513,7 @@ struct fec_enet_private {
int irq[FEC_IRQ_NUM];
bool bufdesc_ex;
int pause_flag;
int wol_flag;
u32 quirks;
struct napi_struct napi;
......
......@@ -187,6 +187,9 @@ MODULE_PARM_DESC(macaddr, "FEC Ethernet MAC address");
#define FEC_MMFR_RA(v) ((v & 0x1f) << 18)
#define FEC_MMFR_TA (2 << 16)
#define FEC_MMFR_DATA(v) (v & 0xffff)
/* FEC ECR bits definition */
#define FEC_ECR_MAGICEN (1 << 2)
#define FEC_ECR_SLEEP (1 << 3)
#define FEC_MII_TIMEOUT 30000 /* us */
......@@ -195,6 +198,9 @@ MODULE_PARM_DESC(macaddr, "FEC Ethernet MAC address");
#define FEC_PAUSE_FLAG_AUTONEG 0x1
#define FEC_PAUSE_FLAG_ENABLE 0x2
#define FEC_WOL_HAS_MAGIC_PACKET (0x1 << 0)
#define FEC_WOL_FLAG_ENABLE (0x1 << 1)
#define FEC_WOL_FLAG_SLEEP_ON (0x1 << 2)
#define COPYBREAK_DEFAULT 256
......@@ -1089,7 +1095,9 @@ 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;
/* We cannot expect a graceful transmit stop without link !!! */
if (fep->link) {
......@@ -1103,17 +1111,28 @@ fec_stop(struct net_device *ndev)
* For i.MX6SX SOC, enet use AXI bus, we use disable MAC
* instead of reset MAC itself.
*/
if (fep->quirks & FEC_QUIRK_HAS_AVB) {
writel(0, fep->hwp + FEC_ECNTRL);
if (!(fep->wol_flag & FEC_WOL_FLAG_SLEEP_ON)) {
if (fep->quirks & FEC_QUIRK_HAS_AVB) {
writel(0, fep->hwp + FEC_ECNTRL);
} else {
writel(1, fep->hwp + FEC_ECNTRL);
udelay(10);
}
writel(FEC_DEFAULT_IMASK, fep->hwp + FEC_IMASK);
} else {
writel(1, fep->hwp + FEC_ECNTRL);
udelay(10);
writel(FEC_DEFAULT_IMASK | FEC_ENET_WAKEUP, fep->hwp + FEC_IMASK);
val = readl(fep->hwp + FEC_ECNTRL);
val |= (FEC_ECR_MAGICEN | FEC_ECR_SLEEP);
writel(val, fep->hwp + FEC_ECNTRL);
if (pdata && pdata->sleep_mode_enable)
pdata->sleep_mode_enable(true);
}
writel(fep->phy_speed, fep->hwp + FEC_MII_SPEED);
writel(FEC_DEFAULT_IMASK, fep->hwp + FEC_IMASK);
/* We have to keep ENET enabled to have MII interrupt stay working */
if (fep->quirks & FEC_QUIRK_ENET_MAC) {
if (fep->quirks & FEC_QUIRK_ENET_MAC &&
!(fep->wol_flag & FEC_WOL_FLAG_SLEEP_ON)) {
writel(2, fep->hwp + FEC_ECNTRL);
writel(rmii_mode, fep->hwp + FEC_R_CNTRL);
}
......@@ -2427,6 +2446,44 @@ static int fec_enet_set_tunable(struct net_device *netdev,
return ret;
}
static void
fec_enet_get_wol(struct net_device *ndev, struct ethtool_wolinfo *wol)
{
struct fec_enet_private *fep = netdev_priv(ndev);
if (fep->wol_flag & FEC_WOL_HAS_MAGIC_PACKET) {
wol->supported = WAKE_MAGIC;
wol->wolopts = fep->wol_flag & FEC_WOL_FLAG_ENABLE ? WAKE_MAGIC : 0;
} else {
wol->supported = wol->wolopts = 0;
}
}
static int
fec_enet_set_wol(struct net_device *ndev, struct ethtool_wolinfo *wol)
{
struct fec_enet_private *fep = netdev_priv(ndev);
if (!(fep->wol_flag & FEC_WOL_HAS_MAGIC_PACKET))
return -EINVAL;
if (wol->wolopts & ~WAKE_MAGIC)
return -EINVAL;
device_set_wakeup_enable(&ndev->dev, wol->wolopts & WAKE_MAGIC);
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 {
fep->wol_flag &= (~FEC_WOL_FLAG_ENABLE);
if (fep->irq[0] > 0)
disable_irq_wake(fep->irq[0]);
}
return 0;
}
static const struct ethtool_ops fec_enet_ethtool_ops = {
.get_settings = fec_enet_get_settings,
.set_settings = fec_enet_set_settings,
......@@ -2445,6 +2502,8 @@ static const struct ethtool_ops fec_enet_ethtool_ops = {
.get_ts_info = fec_enet_get_ts_info,
.get_tunable = fec_enet_get_tunable,
.set_tunable = fec_enet_set_tunable,
.get_wol = fec_enet_get_wol,
.set_wol = fec_enet_set_wol,
};
static int fec_enet_ioctl(struct net_device *ndev, struct ifreq *rq, int cmd)
......@@ -2705,6 +2764,9 @@ fec_enet_open(struct net_device *ndev)
phy_start(fep->phy_dev);
netif_tx_start_all_queues(ndev);
device_set_wakeup_enable(&ndev->dev, fep->wol_flag &
FEC_WOL_FLAG_ENABLE);
return 0;
err_enet_mii_probe:
......@@ -3153,6 +3215,9 @@ fec_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, ndev);
if (of_get_property(np, "fsl,magic-packet", NULL))
fep->wol_flag |= FEC_WOL_HAS_MAGIC_PACKET;
phy_node = of_parse_phandle(np, "phy-handle", 0);
if (!phy_node && of_phy_is_fixed_link(np)) {
ret = of_phy_register_fixed_link(np);
......@@ -3247,6 +3312,8 @@ fec_probe(struct platform_device *pdev)
0, pdev->name, ndev);
if (ret)
goto failed_irq;
fep->irq[i] = irq;
}
init_completion(&fep->mdio_done);
......@@ -3263,6 +3330,9 @@ fec_probe(struct platform_device *pdev)
if (ret)
goto failed_register;
device_init_wakeup(&ndev->dev, fep->wol_flag &
FEC_WOL_HAS_MAGIC_PACKET);
if (fep->bufdesc_ex && fep->ptp_clock)
netdev_info(ndev, "registered PHC device %d\n", fep->dev_id);
......@@ -3316,6 +3386,8 @@ static int __maybe_unused fec_suspend(struct device *dev)
rtnl_lock();
if (netif_running(ndev)) {
if (fep->wol_flag & FEC_WOL_FLAG_ENABLE)
fep->wol_flag |= FEC_WOL_FLAG_SLEEP_ON;
phy_stop(fep->phy_dev);
napi_disable(&fep->napi);
netif_tx_lock_bh(ndev);
......@@ -3323,11 +3395,12 @@ static int __maybe_unused fec_suspend(struct device *dev)
netif_tx_unlock_bh(ndev);
fec_stop(ndev);
fec_enet_clk_enable(ndev, false);
pinctrl_pm_select_sleep_state(&fep->pdev->dev);
if (!(fep->wol_flag & FEC_WOL_FLAG_ENABLE))
pinctrl_pm_select_sleep_state(&fep->pdev->dev);
}
rtnl_unlock();
if (fep->reg_phy)
if (fep->reg_phy && !(fep->wol_flag & FEC_WOL_FLAG_ENABLE))
regulator_disable(fep->reg_phy);
/* SOC supply clock to phy, when clock is disabled, phy link down
......@@ -3343,9 +3416,11 @@ 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;
if (fep->reg_phy) {
if (fep->reg_phy && !(fep->wol_flag & FEC_WOL_FLAG_ENABLE)) {
ret = regulator_enable(fep->reg_phy);
if (ret)
return ret;
......@@ -3353,12 +3428,21 @@ static int __maybe_unused fec_resume(struct device *dev)
rtnl_lock();
if (netif_running(ndev)) {
pinctrl_pm_select_default_state(&fep->pdev->dev);
ret = fec_enet_clk_enable(ndev, true);
if (ret) {
rtnl_unlock();
goto failed_clk;
}
if (fep->wol_flag & FEC_WOL_FLAG_ENABLE) {
if (pdata && pdata->sleep_mode_enable)
pdata->sleep_mode_enable(false);
val = readl(fep->hwp + FEC_ECNTRL);
val &= ~(FEC_ECR_MAGICEN | FEC_ECR_SLEEP);
writel(val, fep->hwp + FEC_ECNTRL);
fep->wol_flag &= ~FEC_WOL_FLAG_SLEEP_ON;
} else {
pinctrl_pm_select_default_state(&fep->pdev->dev);
}
fec_restart(ndev);
netif_tx_lock_bh(ndev);
netif_device_attach(ndev);
......
......@@ -19,6 +19,7 @@
struct fec_platform_data {
phy_interface_t phy;
unsigned char mac[ETH_ALEN];
void (*sleep_mode_enable)(int enabled);
};
#endif
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