rk3399 ubuntu 休眠唤醒功能
背景
为了降低功耗,需要考虑使用系统的休眠唤醒功能。
休眠方式
rk3399 休眠方式有两种,mem
,freeze
.
内存待机 (mem)
在 mem 模式下,系统将所有设备状态保存到内存中,然后进入低功耗状态。唤醒后,系统从内存中恢复设备状态。内存待机模式通常称为 S3 睡眠模式。
冻结 (freeze)
在 freeze 模式下,所有用户空间任务被冻结,所有设备进入低功耗状态,但 CPU 仍保持活跃。冻结模式更适用于在短时间内节省电能。
我们使用 mem
模式进入休眠,指令如下:
root@firefly:/sys/power# cat state
freeze mem
root@firefly:/sys/power# echo mem > state
USB 唤醒
内核配置
&rockchip_suspend {
status = "okay";
rockchip,sleep-debug-en = <1>;
rockchip,sleep-mode-config = <
(0
| RKPM_SLP_ARMPD
| RKPM_SLP_PERILPPD
| RKPM_SLP_DDR_RET
| RKPM_SLP_PLLPD
| RKPM_SLP_CENTER_PD
| RKPM_SLP_AP_PWROFF
)
>;
rockchip,wakeup-config = <
(0
| RKPM_GPIO_WKUP_EN
| RKPM_USB_WKUP_EN
| RKPM_USB_LINESTATE_WKUP_EN
| RKPM_PWM_WKUP_EN
)
>;
rockchip,pwm-regulator-config = <
(0
| PWM2_REGULATOR_EN
)
>;
rockchip,power-ctrl =
<&gpio1 17 GPIO_ACTIVE_LOW>,
<&gpio1 14 GPIO_ACTIVE_HIGH>;
};
进入休眠
root@firefly:/sys/power# cat state
freeze mem
root@firefly:/sys/power# echo mem > state
INFO: sleep mode config[0xde]:
INFO: AP_PWROFF
INFO: SLP_ARMPD
INFO: SLP_PLLPD
INFO: DDR_RET
INFO: SLP_CENTER_PD
INFO: wakeup source config[0x4884]:
INFO: GPIO interrupt can wakeup system
INFO: USBDEV detect can wakeup system
INFO: PWM interrupt can wakeup system
INFO: PWM CONFIG[0x4]:
INFO: PWM: PWM2D_REGULATOR_EN
INFO: APIOS info[0x0]:
INFO: not config
INFO: GPIO POWER INFO:
INFO: GPIO1_C1
INFO: GPIO1_B6
INFO: PMU_MODE_CONG: 0x1466bf41
INFO: RK3399 the wake up information:
INFO: wake up status: 0x80
INFO: USBDEV detect wakeup
插拔 TypeC USB 唤醒
插拔 type C 线可以唤醒系统,唤醒后日志如下:
INFO: RK3399 the wake up information:
INFO: wake up status: 0x80
INFO: USBDEV detect wakeup
功耗
工作状态:12v 0.4A 4.8W
休眠状态:12v 0.17A 2.03W
以太网唤醒
唤醒流程
以上流程图是realtek技术人员提供的资料,根据这个流程图可以知晓整个休眠唤醒的过程,以及涉及的PHY寄存器配置。
测试脚本
下面是基于RK提供的RTL8211E 测试脚本修改 的RTL8211F 测试脚本,寄存器配置与上述流程图基本吻合。
#!/bin/bash
#***********************************************************************
# 8211F配制
# 可以在不休眠情况下验证功能,如下几点需要手动修改:
# 1、寄存器路径可能不同: busybox find /sys/ -name phy_reg
# 2、网关可能不同: ifconfig 自己确认修改
# 3、有可能MAC地址随机,统一固定成001122334455, magic tool也设置一样
#
#***************************************************************************
#######################################
#Into sleep mode initial WOL
#Reg31:0007
# 30:006E
# 21:1100
# 22:3322
# 23:5544
# 31:0007
# 30:006D
# 22:9FFF
# 21:1000
# 25:0001
# 31:0000
######################################
#MAC address 001122334455
function suspend(){
# set INTB/PMEB pin
echo 31 0x0d40 > /sys/devices/platform/fe300000.ethernet/mdio_bus/stmmac-0/stmmac-0:00/phy_registers
sleep 0.5
echo 22 0x20 > /sys/devices/platform/fe300000.ethernet/mdio_bus/stmmac-0/stmmac-0:00/phy_registers
sleep 0.5
# set mac 4e:78:eb:7b:3a:e2
echo 31 0x0d8c > /sys/devices/platform/fe300000.ethernet/mdio_bus/stmmac-0/stmmac-0:00/phy_registers
sleep 0.5
echo 16 0x784e > /sys/devices/platform/fe300000.ethernet/mdio_bus/stmmac-0/stmmac-0:00/phy_registers
sleep 0.5
echo 17 0x7beb > /sys/devices/platform/fe300000.ethernet/mdio_bus/stmmac-0/stmmac-0:00/phy_registers
sleep 0.5
echo 18 0xe23a > /sys/devices/platform/fe300000.ethernet/mdio_bus/stmmac-0/stmmac-0:00/phy_registers
cat /sys/devices/platform/fe300000.ethernet/mdio_bus/stmmac-0/stmmac-0:00/phy_registers
echo "############################################################################"
# set max packet length
echo 31 0x0d8a > /sys/devices/platform/fe300000.ethernet/mdio_bus/stmmac-0/stmmac-0:00/phy_registers
sleep 0.5
echo 17 0x9fff > /sys/devices/platform/fe300000.ethernet/mdio_bus/stmmac-0/stmmac-0:00/phy_registers
sleep 0.5
# enable wol event
echo 31 0x0d8a > /sys/devices/platform/fe300000.ethernet/mdio_bus/stmmac-0/stmmac-0:00/phy_registers
sleep 0.5
echo 16 0x1000 > /sys/devices/platform/fe300000.ethernet/mdio_bus/stmmac-0/stmmac-0:00/phy_registers
#echo 16 0xffff > /sys/devices/platform/fe300000.ethernet/mdio_bus/stmmac-0/stmmac-0:00/phy_registers
sleep 0.5
# disable rgmii pad
echo 31 0x0d8a > /sys/devices/platform/fe300000.ethernet/mdio_bus/stmmac-0/stmmac-0:00/phy_registers
sleep 0.5
echo 19 0x8002 > /sys/devices/platform/fe300000.ethernet/mdio_bus/stmmac-0/stmmac-0:00/phy_registers
sleep 0.5
echo 31 0xa42 > /sys/devices/platform/fe300000.ethernet/mdio_bus/stmmac-0/stmmac-0:00/phy_registers
sleep 0.5
}
####################
#after wake
#Reg31:0007
# 30:006D
# 21:0000
# 22:9FFF
# 25:0000
# 31:0000
###################
function resume(){
# disable wol event
echo 31 0x0d8a > /sys/devices/platform/fe300000.ethernet/mdio_bus/stmmac-0/stmmac-0:00/phy_registers
sleep 0.5
echo 16 0x0 > /sys/devices/platform/fe300000.ethernet/mdio_bus/stmmac-0/stmmac-0:00/phy_registers
sleep 0.5
# reset wol
echo 31 0x0d8a > /sys/devices/platform/fe300000.ethernet/mdio_bus/stmmac-0/stmmac-0:00/phy_registers
sleep 0.5
echo 17 0x1fff > /sys/devices/platform/fe300000.ethernet/mdio_bus/stmmac-0/stmmac-0:00/phy_registers
sleep 0.5
# enable rgmii pad
echo 31 0x0d8a > /sys/devices/platform/fe300000.ethernet/mdio_bus/stmmac-0/stmmac-0:00/phy_registers
sleep 0.5
echo 19 0x2 > /sys/devices/platform/fe300000.ethernet/mdio_bus/stmmac-0/stmmac-0:00/phy_registers
sleep 0.5
# set INTB pin
echo 31 0x0d40 > /sys/devices/platform/fe300000.ethernet/mdio_bus/stmmac-0/stmmac-0:00/phy_registers
sleep 0.5
echo 22 0x0 > /sys/devices/platform/fe300000.ethernet/mdio_bus/stmmac-0/stmmac-0:00/phy_registers
sleep 0.5
echo 31 0xa42 > /sys/devices/platform/fe300000.ethernet/mdio_bus/stmmac-0/stmmac-0:00/phy_registers
sleep 0.5
}
case "$1" in
start)
echo -n "suspend test:"
suspend
echo "done"
;;
stop)
echo -n "resume test:"
resume
echo "done"
;;
*)echo "usage: $0 start | stop "
;;
esac
exit 0
以上脚本用于测试RTL8211F芯片的唤醒功能,可以在不进休眠的情况下验证magic packet的唤醒功能。
执行 start 后,通过wakeonlan 工具发送magic packet, 然后测量中断引脚(gpio3_16)是否被拉低,如果被拉低说明脚本执行成功,phy芯片支持休眠唤醒,剩下的就是适配RK3399的驱动和dts.
内核适配
DTS
dts 中需要配置以下内容:
- 中断引脚gpio
wolirq-gpio
,根据原理图可知是gpio3_16
- pinctrl 配置
gmac_pmeb_gpios
,其实也是中断引脚的初始化配置 phy_reset
的复位时间调整为下拉50ms,上拉100ms,这个参数非常重要!!!默认值太小会导致唤醒后以太网复位失败,只能手动down,up eth0恢复。- 休眠模式下使能
vcc3v3_s3
电源,确保phy芯片及中断引脚电源域(VCC3V3_LAN) 保持供电,否则phy芯片无法接收魔术包,也就无法唤醒了。至于如何确定保留哪个电源域,需要将原理图以及rk808 pmic
的dts配置对应起来。
下面就是根据以上需求配置后的DTS.
--- a/arch/arm64/boot/dts/rockchip/rk3399-keenon-linux.dts
+++ b/arch/arm64/boot/dts/rockchip/rk3399-keenon-linux.dts
@@ -428,11 +428,13 @@
clock_in_out = "input";
snps,reset-gpio = <&gpio3 15 GPIO_ACTIVE_LOW>;
snps,reset-active-low;
- snps,reset-delays-us = <0 10000 50000>;
+ /* delay time for phy reset cannot be too short, which may cause phy reset fail */
+ snps,reset-delays-us = <0 50000 100000>;
assigned-clocks = <&cru SCLK_RMII_SRC>;
assigned-clock-parents = <&clkin_gmac>;
+ wolirq-gpio = <&gpio3 16 GPIO_ACTIVE_LOW>;
pinctrl-names = "default";
- pinctrl-0 = <&rgmii_pins>;
+ pinctrl-0 = <&rgmii_pins &gmac_pmeb_gpios>;
tx_delay = <0x2d>;
rx_delay = <0x2b>;
status = "okay";
@@ -671,7 +673,7 @@
regulator-boot-on;
regulator-name = "vcc3v3_s3";
regulator-state-mem {
- regulator-off-in-suspend;
+ regulator-on-in-suspend;
};
};
@@ -912,6 +914,12 @@
rockchip,pins = <1 2 RK_FUNC_GPIO &pcfg_pull_up>;
};
};
+
+ gmac-wol {
+ gmac_pmeb_gpios: gmac-pmeb-gpios {
+ rockchip,pins = <3 16 RK_FUNC_GPIO &pcfg_pull_none>;
+ };
+ };
};
&pwm0 {
@@ -1128,3 +1136,35 @@
&target {
temperature = <100000>;
};
+
+&rockchip_suspend {
+ /* disable rockchip_suspend which conflict with RTL8211F WOL feature */
+ status = "disabled";
+ rockchip,sleep-debug-en = <1>;
+ rockchip,sleep-mode-config = <
+ (0
+ | RKPM_SLP_ARMPD
+ | RKPM_SLP_PERILPPD
+ | RKPM_SLP_DDR_RET
+ | RKPM_SLP_PLLPD
+ | RKPM_SLP_CENTER_PD
+ | RKPM_SLP_AP_PWROFF
+ )
+ >;
+ rockchip,wakeup-config = <
+ (0
+ | RKPM_GPIO_WKUP_EN
+ | RKPM_USB_WKUP_EN
+ | RKPM_USB_LINESTATE_WKUP_EN
+ | RKPM_PWM_WKUP_EN
+ )
+ >;
+ rockchip,pwm-regulator-config = <
+ (0
+ | PWM2_REGULATOR_EN
+ )
+ >;
+ rockchip,power-ctrl =
+ <&gpio1 17 GPIO_ACTIVE_LOW>,
+ <&gpio1 14 GPIO_ACTIVE_HIGH>;
+};
说明:最后禁用的
rockchip_suspend
也很关闭,保留是为了以后支持其它唤醒方式,禁掉是为了放在其它唤醒方式与网络唤醒功能产生冲突。经测试,在启用**rockchip_suspend**
功能的情况下,以太网唤醒会失败!
phy & gmac 驱动
以下改动绝大部分源自RK提供的patch,其功能是添加 RTL8211E/RTL8211F phy芯片的网络唤醒功能,包括进入休眠以及退出休眠时的操作,并添加中断引脚的配置和处理函数,确保中断触发后能够唤醒系统。
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
@@ -54,6 +54,7 @@
#include "dwmac-rk-tool.h"
#include <linux/reset.h>
#include <linux/of_mdio.h>
+#include <linux/gpio.h>
#define STMMAC_ALIGN(x) __ALIGN_KERNEL(x, SMP_CACHE_BYTES)
@@ -1793,6 +1794,16 @@ static int stmmac_hw_setup(struct net_device *dev, bool init_ptp)
return 0;
}
+static irqreturn_t wol_io_isr(int irq, void *dev_id)
+{
+ struct net_device *dev = (struct net_device *)dev_id;
+ struct stmmac_priv *priv = netdev_priv(dev);
+
+ printk(KERN_ERR "keenon: enter wol_io_isr\n");
+ wake_lock_timeout(&priv->plat->wol_wake_lock, msecs_to_jiffies(8000));
+ return IRQ_HANDLED;
+}
+
/**
* stmmac_open - open entry point of the driver
* @dev : pointer to the device structure.
@@ -1877,6 +1888,26 @@ static int stmmac_open(struct net_device *dev)
}
}
+ if (priv->plat->wolirq_io > 0) {
+ ret = devm_gpio_request(priv->device, priv->plat->wolirq_io, "gmac_wol_io");
+ if (ret) {
+ pr_err("%s: ERROR: failed to request WOL GPIO %d, err: %d\n",
+ __func__, priv->plat->wolirq_io, ret);
+ goto lpiirq_error;
+ }
+
+ priv->plat->wol_irq = gpio_to_irq(priv->plat->wolirq_io);
+ ret = devm_request_irq(priv->device, priv->plat->wol_irq, wol_io_isr,
+ IRQF_TRIGGER_FALLING, "gmac_wol_io_irq", dev);
+ if (ret) {
+ pr_err("%s: ERROR: request wol io irq fail: %d", __func__, ret);
+ devm_gpio_free(priv->device, priv->plat->wolirq_io);
+ goto lpiirq_error;
+ }
+ disable_irq(priv->plat->wol_irq);
+ enable_irq_wake(priv->plat->wol_irq);
+ }
+
napi_enable(&priv->napi);
netif_start_queue(dev);
@@ -1938,6 +1969,12 @@ static int stmmac_release(struct net_device *dev)
if (priv->lpi_irq > 0)
free_irq(priv->lpi_irq, dev);
+ if (priv->plat->wol_irq > 0)
+ devm_free_irq(priv->device, priv->plat->wol_irq, dev);
+
+ if (priv->plat->wolirq_io > 0)
+ devm_gpio_free(priv->device, priv->plat->wolirq_io);
+
/* Stop TX/RX DMA and clear the descriptors */
priv->hw->dma->stop_tx(priv->ioaddr);
priv->hw->dma->stop_rx(priv->ioaddr);
@@ -3053,6 +3090,8 @@ int stmmac_dvr_probe(struct device *device,
INIT_DELAYED_WORK(&priv->scan_dwork, stmmac_scan_delayline_dwork);
#endif
+ wake_lock_init(&priv->plat->wol_wake_lock, WAKE_LOCK_SUSPEND, "wol_wake_lock");
+
return ret;
error_netdev_register:
@@ -3119,6 +3158,8 @@ int stmmac_suspend(struct device *dev)
struct net_device *ndev = dev_get_drvdata(dev);
struct stmmac_priv *priv = netdev_priv(ndev);
+ enable_irq(priv->plat->wol_irq);
+
if (!ndev || !netif_running(ndev))
return 0;
@@ -3166,6 +3207,8 @@ int stmmac_resume(struct device *dev)
struct net_device *ndev = dev_get_drvdata(dev);
struct stmmac_priv *priv = netdev_priv(ndev);
+ disable_irq(priv->plat->wol_irq);
+
if (!netif_running(ndev))
return 0;
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c
@@ -29,6 +29,7 @@
#include <linux/of_net.h>
#include <linux/of_device.h>
#include <linux/of_mdio.h>
+#include <linux/of_gpio.h>
#include "stmmac.h"
#include "stmmac_platform.h"
@@ -109,6 +110,7 @@ stmmac_probe_config_dt(struct platform_device *pdev, const char **mac)
struct device_node *np = pdev->dev.of_node;
struct plat_stmmacenet_data *plat;
struct stmmac_dma_cfg *dma_cfg;
+ enum of_gpio_flags flags;
plat = devm_kzalloc(&pdev->dev, sizeof(*plat), GFP_KERNEL);
if (!plat)
@@ -117,6 +119,8 @@ stmmac_probe_config_dt(struct platform_device *pdev, const char **mac)
*mac = of_get_mac_address(np);
plat->interface = of_get_phy_mode(np);
+ plat->wolirq_io = of_get_named_gpio_flags(np, "wolirq-gpio", 0, &flags);
+
/* Get max speed of operation from device tree */
if (of_property_read_u32(np, "max-speed", &plat->max_speed))
plat->max_speed = -1;
--- a/drivers/net/phy/phy_device.c
+++ b/drivers/net/phy/phy_device.c
@@ -1232,8 +1232,70 @@ static int gen10g_config_init(struct phy_device *phydev)
int genphy_suspend(struct phy_device *phydev)
{
int value;
+ struct net_device * ndev = phydev->attached_dev;
mutex_lock(&phydev->lock);
+//#if RTL8211E
+#if 0
+ phy_write(phydev, 31, 0x07);
+ phy_write(phydev, 30, 0x6e);
+ phy_write(phydev, 21, ((u16)ndev->dev_addr[1] << 8) + ndev->dev_addr[0]);
+ phy_write(phydev, 22, ((u16)ndev->dev_addr[3] << 8) + ndev->dev_addr[2]);
+ phy_write(phydev, 23, ((u16)ndev->dev_addr[5] << 8) + ndev->dev_addr[4]);
+
+ phy_write(phydev, 31, 0x07);
+ phy_write(phydev, 30, 0x6d);
+ phy_write(phydev, 22, 0x1fff);
+ value = phy_read(phydev, 22);
+
+ phy_write(phydev, 31, 0x07);
+ phy_write(phydev, 30, 0x6d);
+ phy_write(phydev, 21, 0x1000);
+ value = phy_read(phydev, 21);
+
+ phy_write(phydev, 31, 0x07);
+ phy_write(phydev, 30, 0x6d);
+ value = phy_read(phydev, 25);
+ phy_write(phydev, 25, value | 0x1);
+
+ phy_write(phydev, 31, 0x0);
+#endif
+
+//#if RTL8211F
+#if 1
+ //set INTB pin
+ phy_write(phydev, 31, 0x0d40);
+ value = phy_read(phydev, 22);
+ phy_write(phydev, 22, value | BIT(5));
+
+ //set MAC address
+ if(ndev) {
+ phy_write(phydev, 31, 0x0d8c);
+ phy_write(phydev, 16, ((u16)ndev->dev_addr[1] << 8) + ndev->dev_addr[0]);
+ phy_write(phydev, 17, ((u16)ndev->dev_addr[3] << 8) + ndev->dev_addr[2]);
+ phy_write(phydev, 18, ((u16)ndev->dev_addr[5] << 8) + ndev->dev_addr[4]);
+ }
+
+ //set max packet length
+ phy_write(phydev, 31, 0x0d8a);
+ phy_write(phydev, 17, 0x9fff);
+
+ //enable wol event
+ phy_write(phydev, 31, 0x0d8a);
+ phy_write(phydev, 16, 0x1000);
+
+ //disable rgmii pad
+ phy_write(phydev, 31, 0x0d8a);
+ value = phy_read(phydev, 19);
+ phy_write(phydev, 19, value | BIT(15));
+
+ phy_write(phydev, 31, 0xa42);
+#endif
+ mutex_unlock(&phydev->lock);
+
+ return 0;
+/*
+ mutex_lock(&phydev->lock);
value = phy_read(phydev, MII_BMCR);
phy_write(phydev, MII_BMCR, value | BMCR_PDOWN);
@@ -1241,6 +1303,7 @@ int genphy_suspend(struct phy_device *phydev)
mutex_unlock(&phydev->lock);
return 0;
+*/
}
EXPORT_SYMBOL(genphy_suspend);
@@ -1253,6 +1316,59 @@ int genphy_resume(struct phy_device *phydev)
{
int value;
+ if (phydev->suspended) {
+ mutex_lock(&phydev->lock);
+//#if RTL8211E
+#if 0
+ phy_write(phydev, 31, 0x07);
+ phy_write(phydev, 30, 0x6d);
+ phy_write(phydev, 21, 0x0);
+ value = phy_read(phydev, 21);
+
+ phy_write(phydev, 31, 0x07);
+ phy_write(phydev, 30, 0x6d);
+ value = phy_read(phydev, 22);
+ phy_write(phydev, 22, value | BIT(15));
+ value = phy_read(phydev, 22);
+
+ phy_write(phydev, 31, 0x07);
+ phy_write(phydev, 30, 0x6d);
+ value = phy_read(phydev, 25);
+ phy_write(phydev, 25, value & (~(0x1)));
+
+ phy_write(phydev, 31, 0x0);
+#endif
+
+//#if RTL8211F
+#if 1
+ //disable wol event
+ phy_write(phydev, 31, 0x0d8a);
+ phy_write(phydev, 16, 0x0);
+
+ //reset wol
+ phy_write(phydev, 31, 0x0d8a);
+ value = phy_read(phydev, 17);
+ phy_write(phydev, 17, value & (~BIT(15)));
+
+ //enable rgmii pad
+ phy_write(phydev, 31, 0x0d8a);
+ value = phy_read(phydev, 19);
+ phy_write(phydev, 19, value & (~BIT(15)));
+
+ //set INTB pin
+ phy_write(phydev, 31, 0x0d40);
+ value = phy_read(phydev, 22);
+ phy_write(phydev, 22, value & (~BIT(5)));
+
+ phy_write(phydev, 31, 0xa42);
+#endif
+ mutex_unlock(&phydev->lock);
+
+ msleep(100);
+
+ return 0;
+ }
+
mutex_lock(&phydev->lock);
value = phy_read(phydev, MII_BMCR);
diff --git a/include/linux/stmmac.h b/include/linux/stmmac.h
--- a/include/linux/stmmac.h
+++ b/include/linux/stmmac.h
@@ -27,6 +27,7 @@
#define __STMMAC_PLATFORM_DATA
#include <linux/platform_device.h>
+#include <linux/wakelock.h>
#define STMMAC_RX_COE_NONE 0
#define STMMAC_RX_COE_TYPE1 1
@@ -123,5 +124,9 @@ struct plat_stmmacenet_data {
void (*exit)(struct platform_device *pdev, void *priv);
void (*get_eth_addr)(void *priv, unsigned char *addr);
void *bsp_priv;
+ int wolirq_io;
+ int wolirq_io_level;
+ int wol_irq;
+ struct wake_lock wol_wake_lock;
};
#endif
RK提供的patch无法直接实现休眠唤醒功能,其中包括编译错误问题,休眠时kernel crash导致休眠失败问题等。这些问题经过排查分析已经解决了,部分问题的说明见下文。以上就是最终实现网络唤醒的代码。
唤醒工具
根据系统平台的不同,休眠后的唤醒工具可以分为以下3类。
ubuntu
如果是Ubuntu系统,可以使用 wakeonlan指令进行唤醒。
sudo wakeonlan -i 192.168.64.255 4e:78:eb:7b:3a:e2
注意:
- 需要使用广播IP地址
192.168.64.255
,否则唤醒会失败- udp包的端口号没有特殊要求,默认的9或者自定义的5555都可以唤醒
- mac地址必须是phy芯片对应的网口
Android
对于Android系统, 可以使用apk,比如 Wake On Lan_v1.35_apkpure.com.apk
发送唤醒包,或者app内部实现wol数据包的组装和发送。
windows
对于windows系统,可以使用RK提供的 Magic_Package (Wake On Lan).rar
, 或者下载 wakeuppro
软件。
调试方法
通常情况下,系统进入休眠后没有任何串口日志,哪怕异常也看不出,为了调试,可以在休眠前执行以下语句,将日志打印到串口。
echo 7 4 1 7 > /proc/sys/kernel/printk
echo N > /sys/module/printk/parameters/console_suspend
echo 1 > /sys/power/pm_print_times
echo mem > /sys/power/state
# 合一
echo 7 4 1 7 > /proc/sys/kernel/printk && echo N > /sys/module/printk/parameters/console_suspend && echo 1 > /sys/power/pm_print_times
网络唤醒失败问题排查
进入休眠时异常
root@firefly:~# echo 7 4 1 7 > /proc/sys/kernel/printk
root@firefly:~# echo N > /sys/module/printk/parameters/console_suspend
root@firefly:~# echo 1 > /sys/power/pm_print_times
root@firefly:~# cat /sys/power/pm_print_times
1
root@firefly:~# echo mem > /sys/power/state
[ 241.184074] PM: suspend entry 2024-04-15 11:47:06.000579232 UTC
[ 241.184597] PM: Syncing filesystems ... done.
[ 241.194766] Freezing user space processes ... (elapsed 0.004 seconds) done.
[ 241.199524] Freezing remaining freezable tasks ... (elapsed 0.001 seconds) done.
[ 241.203165] calling 1-1.4+ @ 6, parent: 1-1, cb: usb_dev_suspend
[ 241.203283] calling 6-1+ @ 48, parent: usb6, cb: usb_dev_suspend
[ 241.204271] call 1-1.4+ returned 0 after 13 usecs
[ 241.204727] calling 3-1+ @ 6, parent: usb3, cb: usb_dev_suspend
[ 241.205310] calling input1+ @ 1317, parent: gpio-keys, cb: input_dev_suspend
[ 241.205384] calling 5-1+ @ 151, parent: usb5, cb: usb_dev_suspend
[ 241.206485] call input1+ returned 0 after 1 usecs
[ 241.207033] calling fe310000.dwmmc+ @ 1317, parent: platform, cb: pm_generic_suspend
[ 241.207721] call fe310000.dwmmc+ returned 0 after 1 usecs
[ 241.208355] calling usb4+ @ 13725, parent: fe3a0000.usb, cb: usb_dev_suspend
[ 241.208573] calling 1-1+ @ 153, parent: usb1, cb: usb_dev_suspend
[ 241.210830] call 1-1+ returned 0 after 2200 usecs
[ 241.247858] call 3-1+ returned 0 after 41593 usecs
[ 241.258065] call usb4+ returned 0 after 47391 usecs
[ 241.258648] calling fe3a0000.usb+ @ 1317, parent: platform, cb: pm_generic_suspend
[ 241.259369] call fe3a0000.usb+ returned 0 after 12 usecs
[ 241.259965] calling usb3+ @ 6, parent: fe380000.usb, cb: usb_dev_suspend
[ 241.262173] call usb3+ returned 0 after 1576 usecs
[ 241.262910] calling fe380000.usb+ @ 1317, parent: platform, cb: pm_generic_suspend
[ 241.273979] call fe380000.usb+ returned 0 after 10094 usecs
[ 241.301011] call 5-1+ returned 0 after 93377 usecs
[ 241.301601] calling usb5+ @ 13725, parent: xhci-hcd.2.auto, cb: usb_dev_suspend
[ 241.302654] call usb5+ returned 0 after 321 usecs
[ 241.309006] call 6-1+ returned 0 after 103237 usecs
[ 241.309521] calling usb6+ @ 153, parent: xhci-hcd.2.auto, cb: usb_dev_suspend
[ 241.310194] call usb6+ returned 0 after 27 usecs
[ 241.310912] calling xhci-hcd.2.auto+ @ 1317, parent: fe900000.dwc3, cb: platform_pm_suspend
[ 241.311681] call xhci-hcd.2.auto+ returned 0 after 18 usecs
[ 241.312190] calling fe900000.dwc3+ @ 1317, parent: usb1, cb: platform_pm_suspend
[ 241.312860] call fe900000.dwc3+ returned 0 after 8 usecs
[ 241.313333] calling usb1+ @ 1317, parent: platform, cb: pm_generic_suspend
[ 241.313965] call usb1+ returned 0 after 15 usecs
[ 241.314400] calling xhci-hcd.1.auto+ @ 1317, parent: fe800000.dwc3, cb: platform_pm_suspend
[ 241.315145] call xhci-hcd.1.auto+ returned 0 after 0 usecs
[ 241.315640] calling fe800000.dwc3+ @ 1317, parent: usb0, cb: platform_pm_suspend
[ 241.316535] android_work: sent uevent USB_STATE=DISCONNECTED
[ 241.317082] call fe800000.dwc3+ returned 0 after 761 usecs
[ 241.317569] calling usb0+ @ 1317, parent: platform, cb: pm_generic_suspend
[ 241.318188] call usb0+ returned 0 after 1 usecs
[ 241.318635] calling ff9a0000.gpu+ @ 1317, parent: platform, cb: pm_generic_suspend
[ 241.318689] calling mmc1:0001+ @ 153, parent: mmc1, cb: mmc_bus_suspend
[ 241.320126] call ff9a0000.gpu+ returned 0 after 218 usecs
[ 241.320612] calling ff7c0000.phy+ @ 1317, parent: platform, cb: pm_generic_suspend
[ 241.321273] call mmc1:0001+ returned 0 after 2519 usecs
[ 241.321749] call ff7c0000.phy+ returned 0 after 0 usecs
[ 241.322225] calling ff770000.syscon:usb2-phy@e450+ @ 1317, parent: ff770000.syscon, cb: pm_generic_suspend
[ 241.323086] call ff770000.syscon:usb2-phy@e450+ returned 0 after 4 usecs
[ 241.323687] calling snd-soc-dummy+ @ 1317, parent: platform, cb: platform_pm_suspend
[ 241.324379] call snd-soc-dummy+ returned 0 after 0 usecs
[ 241.324870] calling led_ctl+ @ 1317, parent: leds, cb: led_suspend
[ 241.325425] call led_ctl+ returned 0 after 7 usecs
[ 241.325875] calling mmc1::+ @ 1317, parent: fe330000.sdhci, cb: led_suspend
[ 241.326492] call mmc1::+ returned 0 after 0 usecs
[ 241.326936] calling cpufreq-dt+ @ 1317, parent: platform, cb: platform_pm_suspend
[ 241.327599] call cpufreq-dt+ returned 0 after 0 usecs
[ 241.328066] calling input0+ @ 1317, parent: 4-0022, cb: input_dev_suspend
[ 241.328669] call input0+ returned 0 after 1 usecs
[ 241.329101] calling 4-0022+ @ 1317, parent: i2c-4, cb: fusb30x_pm_suspend
[ 241.329710] call 4-0022+ returned 0 after 6 usecs
[ 241.330142] calling rk808-rtc+ @ 1317, parent: 0-001b, cb: platform_pm_suspend
[ 241.330781] call rk808-rtc+ returned 0 after 0 usecs
[ 241.331258] calling rk808-regulator+ @ 1317, parent: 0-001b, cb: platform_pm_suspend
[ 241.331950] call rk808-regulator+ returned 0 after 0 usecs
[ 241.332436] calling rk808-clkout+ @ 1317, parent: 0-001b, cb: platform_pm_suspend
[ 241.333103] call rk808-clkout+ returned 0 after 0 usecs
[ 241.333576] calling 0-001b+ @ 1317, parent: i2c-0, cb: rk808_suspend
[ 241.334149] call 0-001b+ returned 0 after 1 usecs
[ 241.334579] calling rtc0+ @ 1317, parent: 0-0051, cb: rtc_suspend
[ 241.335697] call rtc0+ returned 0 after 544 usecs
[ 241.336195] calling 0-0051+ @ 1317, parent: i2c-0, cb: hym8563_suspend
[ 241.336776] call 0-0051+ returned 0 after 0 usecs
[ 241.337269] calling stmmac-0:01+ @ 1317, parent: stmmac-0, cb: mdio_bus_suspend
[ 241.337347] calling usb2+ @ 153, parent: fe3e0000.usb, cb: usb_dev_suspend
[ 241.337448] calling usb1+ @ 48, parent: fe3c0000.usb, cb: usb_dev_suspend
[ 241.337764] call usb1+ returned 0 after 306 usecs
[ 241.339729] Unable to handle kernel NULL pointer dereference at virtual address 00000318
[ 241.340442] pgd = ffffffc0eb940000
[ 241.340743] [00000318] *pgd=00000000eb968003, *pud=00000000eb968003, *pmd=00000000eefdf003, *pte=0000000000000000
[ 241.341667] Internal error: Oops: 96000007 [#1] SMP
[ 241.342095] Modules linked in:
[ 241.342374] CPU: 3 PID: 1317 Comm: bash Not tainted 4.4.194 #36
[ 241.342893] Hardware name: Rockchip RK3399 Keenon Board (Linux Opensource) (DT)
[ 241.343531] task: ffffffc0edfe4380 task.stack: ffffffc0eba70000
[ 241.344052] PC is at genphy_suspend+0x68/0x180
[ 241.344445] LR is at genphy_suspend+0x68/0x180
[ 241.344835] pc : [<ffffff80085d8fd4>] lr : [<ffffff80085d8fd4>] pstate: 20000145
[ 241.345480] sp : ffffffc0eba739a0
[ 241.345773] x29: ffffffc0eba739a0 x28: ffffff8009237cf8
[ 241.346247] x27: ffffffc0f12f00f0 x26: ffffff8008581c84
[ 241.346720] x25: ffffff8009237000 x24: ffffff800951d688
[ 241.347194] x23: ffffff8008f31183 x22: 000000382c8e9eb5
[ 241.347669] x21: ffffff80085da2b4 x20: 0000000000000000
[ 241.348142] x19: ffffffc0f12f0000 x18: ffffff8089365897
[ 241.348616] x17: 0000000000000000 x16: 0000000000000000
[ 241.349089] x15: 0000000000000000 x14: 0000000000052764
[ 241.349562] x13: 000000000000000a x12: 0000000000000030
[ 241.350037] x11: 00000000fffffffe x10: ffffff800936589f
[ 241.350510] x9 : 0000000005f5e0ff x8 : ffffff800835b114
[ 241.350984] x7 : ffffff800920e688 x6 : 0000000000069d0e
[ 241.351459] x5 : 000000015dc9eef7 x4 : ffffff80085e0f38
[ 241.351932] x3 : 00000000ffff264a x2 : 00000000ffff1a92
[ 241.352405] x1 : 0000000000000000 x0 : 0000000000000000
[ 241.352881]
[ 241.352881] PC: 0xffffff80085d8f54:
...
[ 241.466657]
[ 241.466793] Process bash (pid: 1317, stack limit = 0xffffffc0eba70000)
[ 241.467365] Stack: (0xffffffc0eba739a0 to 0xffffffc0eba74000)
...
[ 241.510453] [<ffffff80085d8fd4>] genphy_suspend+0x68/0x180
[ 241.510936] [<ffffff80085d8dd4>] phy_suspend+0x68/0x78
[ 241.511390] [<ffffff80085da300>] mdio_bus_suspend+0x4c/0x60
[ 241.511883] [<ffffff8008581378>] dpm_run_callback+0xa4/0x178
[ 241.512381] [<ffffff8008581b18>] __device_suspend+0x190/0x2fc
[ 241.512887] [<ffffff80085839d4>] dpm_suspend+0x268/0x2e8
[ 241.513355] [<ffffff8008583ee4>] dpm_suspend_start+0x70/0x74
[ 241.513856] [<ffffff80080ea8c8>] suspend_devices_and_enter+0x68/0x258
[ 241.514421] [<ffffff80080eaeb8>] pm_suspend+0x400/0x548
[ 241.514881] [<ffffff80080e9314>] state_store+0xb8/0xe0
[ 241.515336] [<ffffff80083ba2a4>] kobj_attr_store+0x14/0x24
[ 241.515821] [<ffffff8008216c80>] sysfs_kf_write+0x54/0x74
[ 241.516296] [<ffffff8008215cdc>] kernfs_fop_write+0x120/0x17c
[ 241.516803] [<ffffff80081ab8e8>] __vfs_write+0x48/0xe8
[ 241.517256] [<ffffff80081ac1b4>] vfs_write+0xa8/0x15c
[ 241.517702] [<ffffff80081acb54>] SyS_write+0x5c/0xb0
[ 241.518141] [<ffffff8008082f70>] el0_svc_naked+0x24/0x28
[ 241.518609] Code: 5281b183 f9400660 528003e2 940006c3 (f9418e80)
[ 241.519145] ---[ end trace 89b1acabdee346a2 ]---
前期测试时发现休眠失败,经排查,发现是驱动执行到 genphy_suspend
时出现空指针异常,进一步确认是配置mac地址时,变量ndev
为空,因此后面加了一个空指针判断就好了。
+ //set MAC address
+ if(ndev) {
+ phy_write(phydev, 31, 0x0d8c);
+ phy_write(phydev, 16, ((u16)ndev->dev_addr[1] << 8) + ndev->dev_addr[0]);
+ phy_write(phydev, 17, ((u16)ndev->dev_addr[3] << 8) + ndev->dev_addr[2]);
+ phy_write(phydev, 18, ((u16)ndev->dev_addr[5] << 8) + ndev->dev_addr[4]);
+ }
中断信号无法拉低到0v
这个问题也是很神奇,测试发现发送magic packet后,中断引脚确实被拉低了,但是只是从3.3v拉低到2v,而不是0v,导致中断触发失败。最终发现是这块板子的问题,换了一块就好了,怀疑是这块板子哪里短路或者损坏了。
有中断触发但无法唤醒
当中断信号问题解决后,发现还是无法唤醒,检查中断计数 cat /proc/interrupts
是有新增的。gmac_wol_io_irq
计数值有增加,说明正常触发了中断,但就是无法唤醒。
root@firefly:/# cat /proc/interrupts
CPU0 CPU1 CPU2 CPU3 CPU4 CPU5
14: 0 0 0 0 0 0 GICv3 29 Edge arch_timer
15: 154130 126946 108949 115420 75167 80830 GICv3 30 Edge arch_timer
17: 23495 23295 22069 21663 23800 22967 GICv3 113 Level rk_timer
20: 0 0 0 0 0 0 GICv3 37 Level ff6d0000.dma-controller
21: 0 0 0 0 0 0 GICv3 38 Level ff6d0000.dma-controller
22: 0 0 0 0 0 0 GICv3 39 Level ff6e0000.dma-controller
23: 0 0 0 0 0 0 GICv3 40 Level ff6e0000.dma-controller
24: 27 0 0 0 0 0 GICv3 44 Level eth0
25: 45 0 0 0 0 0 GICv3 96 Level dw-mci
26: 0 0 0 0 0 0 GICv3 97 Level dw-mci
27: 15728 0 0 0 0 0 GICv3 43 Level mmc1
28: 5901 0 0 0 0 0 GICv3 58 Level ehci_hcd:usb5
29: 0 0 0 0 0 0 GICv3 60 Level ohci_hcd:usb6
30: 498 0 0 0 0 0 GICv3 62 Level ehci_hcd:usb1
31: 0 0 0 0 0 0 GICv3 64 Level ohci_hcd:usb2
32: 0 0 0 0 0 0 GICv3 94 Level ff100000.saradc
33: 6252 0 0 0 0 0 GICv3 89 Level ff3c0000.i2c
35: 239 0 0 0 0 0 GICv3 132 Level serial
36: 0 0 0 0 0 0 GICv3 129 Level rockchip_thermal
38: 693 0 0 0 0 0 GICv3 88 Level ff3d0000.i2c
40: 0 0 0 0 0 0 GICv3 148 Level ff660000.rkvdec
42: 0 0 0 0 0 0 GICv3 87 Level rga
44: 12 0 0 0 0 0 GICv3 51 Level ff9a0000.gpu
45: 1 0 0 0 0 0 GICv3 52 Level ff9a0000.gpu
46: 1 0 0 0 0 0 GICv3 53 Level ff9a0000.gpu
57: 0 0 0 0 0 0 gpio0 5 Edge GPIO Key Power
86: 10 0 0 0 0 0 gpio1 2 Level fusb302
105: 0 0 0 0 0 0 gpio1 21 Level rk808
164: 3 1 0 0 0 0 gpio3 16 Edge gmac_wol_io_irq
212: 7 0 0 0 0 0 GICv3 63 Level rockchip_usb2phy
213: 2882 0 0 0 0 0 GICv3 137 Level dwc3
214: 232 0 0 0 0 0 GICv3 142 Level xhci-hcd:usb3
224: 4 0 0 0 0 0 GICv3 59 Level rockchip_usb2phy
225: 3 0 0 0 0 0 GICv3 135 Level rockchip_usb2phy_bvalid
226: 3 0 0 0 0 0 GICv3 138 Level rockchip_usb2phy
IPI0: 107703 97644 97952 92577 76656 83385 Rescheduling interrupts
IPI1: 23 55 35 44 54 58 Function call interrupts
IPI2: 0 0 0 0 0 0 CPU stop interrupts
IPI3: 0 0 0 0 0 0 CPU stop (for crash dump) interrupts
IPI4: 939 1047 913 986 1374 1455 Timer broadcast interrupts
IPI5: 8 6 4 3 0 1 IRQ work interrupts
IPI6: 0 0 0 0 0 0 CPU wake-up interrupts
最终尝试将 rockchip_suspend
禁掉后就好了,至于为什么需要禁掉,目前RK没有给出明确答复,留意下就好。怀疑是两套唤醒机制会有冲突。
唤醒后以太网无法使用
好不容易把唤醒功能实现了,结果发现eth0无法正常使用,ping不通192.168.64.10, 而且会出现NETDEV WATCHDOG
[ 6034.474982] rk_gmac-dwmac fe300000.ethernet eth0: Link is Up - 100Mbps/Full - flow control off
[ 6042.778740] NETDEV WATCHDOG: eth0 (rk_gmac-dwmac): transmit queue 0 timed out
[ 6042.779391] ------------[ cut here ]------------
[ 6042.779795] WARNING: at net/sched/sch_generic.c:306
[ 6042.780221] Modules linked in:
[ 6042.780494]
[ 6042.780631] CPU: 4 PID: 0 Comm: swapper/4 Not tainted 4.4.194 #52
[ 6042.781163] Hardware name: Rockchip RK3399 Keenon Board (Linux Opensource) (DT)
[ 6042.781800] task: ffffffc0f1c5b600 task.stack: ffffffc0f1c74000
[ 6042.782326] PC is at dev_watchdog+0x1e4/0x234
[ 6042.782709] LR is at dev_watchdog+0x1e4/0x234
[ 6042.783090] pc : [<ffffff8008a84f18>] lr : [<ffffff8008a84f18>] pstate: 20000145
[ 6042.783734] sp : ffffffc0f6f2dde0
[ 6042.784024] x29: ffffffc0f6f2dde0 x28: ffffff800932a000
[ 6042.784496] x27: ffffffc0f6f2ded8 x26: 00000000ffffffff
[ 6042.784967] x25: 0000000000000004 x24: ffffffc0f1294880
[ 6042.785438] x23: 0000000000000001 x22: ffffff800932a000
[ 6042.785910] x21: ffffffc0f12c03e0 x20: 0000000000000000
[ 6042.786382] x19: ffffffc0f12c0000 x18: ffffff8089365897
[ 6042.786852] x17: 0000000000000000 x16: 0000000000000000
[ 6042.787323] x15: 0000000000000000 x14: 00000000000be1f4
[ 6042.787795] x13: 000000000000000a x12: 0000000000000030
[ 6042.788266] x11: 00000000fffffffe x10: ffffff800936589f
[ 6042.788738] x9 : 0000000005f5e0ff x8 : ffffff800835b114
[ 6042.789210] x7 : ffffff800920e688 x6 : 0000000000000051
[ 6042.789681] x5 : 0000000000000010 x4 : 0000000000000000
[ 6042.790152] x3 : 0000000000000000 x2 : 0000000000000040
[ 6042.790622] x1 : 0000000000000006 x0 : 0000000000000041
经RK指导,怀疑是唤醒后phy没有正常复位,但是测量信号发现是有进行复位的,不过复位时间很短,后来经过调整复位的上下拉时间解决。
snps,reset-gpio = <&gpio3 15 GPIO_ACTIVE_LOW>;
snps,reset-active-low;
- snps,reset-delays-us = <0 10000 50000>;
+ /* delay time for phy reset cannot be too short, which may cause phy reset fail */
+ snps,reset-delays-us = <0 50000 100000>;
下面这是修改时间后的PHY_RESET信号。
小结
- 网络唤醒流程
- 使用
echo mem > /sys/power/state
进入休眠状态 - 休眠时phy将中断引脚拉高,使能唤醒功能
- 发送
magic packet
给phy芯片,phy芯片接收到后将中断引脚拉低 - 中断引脚下降沿触发中断,进行唤醒操作
- 唤醒后对phy进行复位,并禁止中断,配置phy寄存器
- 使用
- 以太网唤醒功能依赖于PHY芯片,RK3399 两个网口,只有eth0可以用于网络唤醒
- 注意休眠时保证phy芯片及中断引脚gpio的电源保持上电
- 注意rockchip_suspend 与 网络唤醒不能同时使用
- 注意mac地址配置时需要判空处理,否则会出现crash
- 注意示波器测量信号时防止引脚短接引起短路,损坏电路
参考
- RK redmine issue: https://redmine.rock-chips.com/issues/474137
- Realtek WOL App Note: RTL8211F_Series_WOL_App_Note_1.5.pdf
- wol测试脚本:wol_set_f_4e78eb7b3ae2.sh
版权声明:本博客所有文章除特殊声明外,均采用 CC BY-NC 4.0 许可协议。转载请注明出处 litreily的博客!