/* * Copyright (c) 2021-2022 Amlogic, Inc. All rights reserved. * * SPDX-License-Identifier: MIT */ #include #include "FreeRTOS.h" #include "suspend.h" #include "interrupt.h" #include "mailbox-api.h" #include "irq.h" #include "eth.h" #include "common.h" #include "mac.h" #include "phy.h" #include "pack_manager.h" #include "eth_mbox.h" #include "magic_packet.h" #include "ipv4.h" #include "mdns_offload.h" #ifdef CONFIG_N200_REVA #include "interrupt_control_pic.h" #endif #include "n200_func.h" #define DRV_NAME_PREFIX "ETH: " #define ETH_WAKEUP_IRQ_NUM IRQ_ETH_PMT_NUM #define ETH_CTRL_FLAG_PHY_TYPE BIT(0) /* 0:External 1:Internal */ #define ETH_CTRL_FLAG_NO_WOL BIT(1) /* 0:Enable 1:Disable */ #define ETH_CTRL_FLAG_WK_MODE BIT(2) /* 0:Old mode 1:New mode */ #define ETH_CTRL_FLAG_NO_CK_LV BIT(3) /* 1:No check INTB_PMEB level */ #define ETH_CTRL_FLAG_CMD_MODE BIT(31) /* 0:No 1:Yes */ #if defined(ETH_T5_APPLY_WORKAROUND) static bool ignoreOnceIrq; #endif static uint32_t ctrlFlag = ETH_CTRL_FLAG_NO_CK_LV; uint8_t ethWakeupReason; uint32_t ethWakeupSrc = BIT(ETH_WAKEUP_MAGIC) | BIT(ETH_WAKEUP_MDNS) | BIT(ETH_WAKEUP_PORT); static bool enableCheck; #if defined(CONFIG_ETHERNET_MAC) static bool macReady; #endif static void prvETHIrqHandler(void) { uint32_t buf[4] = { 0 }; #if defined(ETH_T5_APPLY_WORKAROUND) if (ignoreOnceIrq) { ignoreOnceIrq = false; return; } #endif if (enableCheck) printf(DRV_NAME_PREFIX "I-WOL DIRECT WKUP\n"); buf[0] = ETH_PMT_WAKEUP; STR_Wakeup_src_Queue_Send_FromISR(buf); DisableIrq(ETH_WAKEUP_IRQ_NUM); } #if defined(ETH_WAKEUP_GPIO_NUM) static void prvETHGpioHandler(void) { uint32_t buf[4] = { 0 }; buf[0] = ETH_PHY_GPIO; STR_Wakeup_src_Queue_Send_FromISR(buf); } #endif static void prvETHOldModeInit(void) { if (ctrlFlag & ETH_CTRL_FLAG_PHY_TYPE) { /* Internal Phy */ if (!(ctrlFlag & ETH_CTRL_FLAG_NO_WOL)) { /* Enable WOL */ RegisterIrq(ETH_WAKEUP_IRQ_NUM, 2, prvETHIrqHandler); #if defined(ETH_T5_APPLY_WORKAROUND) && defined(CONFIG_N200_REVA) pic_complete_interrupt(ETH_WAKEUP_IRQ_NUM); #endif printf(DRV_NAME_PREFIX "I-WOL EN\n"); } } else { /* External Phy */ if (!(ctrlFlag & ETH_CTRL_FLAG_NO_WOL)) { #if defined(ETH_WAKEUP_GPIO_NUM) /* Enable WOL */ xPinmuxSet(ETH_WAKEUP_GPIO_NUM, PIN_FUNC0); xGpioSetDir(ETH_WAKEUP_GPIO_NUM, GPIO_DIR_IN); xPinconfSet(ETH_WAKEUP_GPIO_NUM, ETH_WAKEUP_EDGE == IRQF_TRIGGER_FALLING ? PINF_CONFIG_BIAS_PULL_UP : PINF_CONFIG_BIAS_PULL_DOWN); xRequestGpioIRQ(ETH_WAKEUP_GPIO_NUM, prvETHGpioHandler, ETH_WAKEUP_EDGE); printf(DRV_NAME_PREFIX "E-WOL EN\n"); #endif } else { #if defined(ETH_RESET_GPIO_NUM) /* Control reset to put phy into low power mode */ xPinmuxSet(ETH_RESET_GPIO_NUM, PIN_FUNC0); #if defined(ETH_RESET_GPIO_IS_OD_PIN) xGpioSetDir(ETH_RESET_GPIO_NUM, ETH_RESET_ACTIVE_LEVEL ? GPIO_DIR_IN : GPIO_DIR_OUT); #else xGpioSetDir(ETH_RESET_GPIO_NUM, GPIO_DIR_OUT); xGpioSetValue(ETH_RESET_GPIO_NUM, ETH_RESET_ACTIVE_LEVEL); #endif printf(DRV_NAME_PREFIX "E-RST SET\n"); #endif } } } static void prvETHOldModeDeinit(void) { if (ctrlFlag & ETH_CTRL_FLAG_PHY_TYPE) { /* Internal Phy */ if (!(ctrlFlag & ETH_CTRL_FLAG_NO_WOL)) { /* Disable WOL */ #if defined(ETH_T5_APPLY_WORKAROUND) ignoreOnceIrq = true; #endif DisableIrq(ETH_WAKEUP_IRQ_NUM); UnRegisterIrq(ETH_WAKEUP_IRQ_NUM); printf(DRV_NAME_PREFIX "I-WOL DIS\n"); } } else { /* External Phy */ if (!(ctrlFlag & ETH_CTRL_FLAG_NO_WOL)) { #if defined(ETH_WAKEUP_GPIO_NUM) /* Disable WOL */ vFreeGpioIRQ(ETH_WAKEUP_GPIO_NUM); printf(DRV_NAME_PREFIX "E-WOL DIS\n"); #endif } else { #if defined(ETH_RESET_GPIO_NUM) #if defined(ETH_RESET_GPIO_IS_OD_PIN) xGpioSetDir(ETH_RESET_GPIO_NUM, ETH_RESET_ACTIVE_LEVEL ? GPIO_DIR_OUT : GPIO_DIR_IN); #else xGpioSetValue(ETH_RESET_GPIO_NUM, !ETH_RESET_ACTIVE_LEVEL); #endif printf(DRV_NAME_PREFIX "E-RST CLR\n"); #endif } } } static void prvETHNewModeInit(void) { /* Clear past wakeup reason */ ethWakeupReason = 0; /* If all wakeup sources are disabled, no work needs to be done */ if (!ethWakeupSrc) return; #if defined(CONFIG_ETHERNET_MAC) macReady = xMacInit(); if (!macReady) return; vPackManagerInit(); #else return; #endif #if defined(CONFIG_ETHERNET_PHY) vPhyInit(); #endif #if defined(CONFIG_ETHERNET_MAGIC_PACKET) if (ethWakeupSrc & BIT(ETH_WAKEUP_MAGIC)) vMagicPacketInit(); #endif #if defined(CONFIG_NET_IPV4) vIPv4Init(); #endif #if defined(CONFIG_NET_MDNSOFFLOAD) if ((ethWakeupSrc & BIT(ETH_WAKEUP_MDNS)) || (ethWakeupSrc & BIT(ETH_WAKEUP_PORT))) mdns_start(ethWakeupSrc); #endif printf(DRV_NAME_PREFIX "EN\n"); } static void prvETHNewModeDeinit(void) { if (!ethWakeupSrc) return; #if defined(CONFIG_ETHERNET_MAC) if (!macReady) return; #endif #if defined(CONFIG_NET_MDNSOFFLOAD) if (ethWakeupSrc & BIT(ETH_WAKEUP_MDNS)) mdns_stop(); #endif #if defined(CONFIG_NET_IPV4) vIPv4Deinit(); #endif #if defined(CONFIG_ETHERNET_MAGIC_PACKET) if (ethWakeupSrc & BIT(ETH_WAKEUP_MAGIC)) vMagicPacketDeinit(); #endif #if defined(CONFIG_ETHERNET_PHY) vPhyDeinit(); #endif #if defined(CONFIG_ETHERNET_MAC) vPackManagerDeinit(); vMacDeinit(); macReady = false; #else return; #endif printf(DRV_NAME_PREFIX "DIS\n"); } void vETHInit(void) { if (ctrlFlag & ETH_CTRL_FLAG_WK_MODE) prvETHNewModeInit(); else prvETHOldModeInit(); } void vETHDeint(void) { if (ctrlFlag & ETH_CTRL_FLAG_WK_MODE) prvETHNewModeDeinit(); else prvETHOldModeDeinit(); } void vETHEnableIrq(void) { /* Enable interrupt only when using internal phy WOL */ if (ctrlFlag & ETH_CTRL_FLAG_NO_WOL) return; /* The new mode will no longer use interrupt-based wakeup */ if (ctrlFlag & ETH_CTRL_FLAG_WK_MODE) return; if (ctrlFlag & ETH_CTRL_FLAG_PHY_TYPE) { /* Internal phy */ EnableIrq(ETH_WAKEUP_IRQ_NUM); #if !defined(CONFIG_SOC_T5W) && !defined(CONFIG_SOC_T5D) /* * Set the interrupt to level trigger to ensure that the magic packet * received during the suspend flow can also wakeup the system. */ enableCheck = true; eclic_set_level_trig(GetIrqInner(ETH_WAKEUP_IRQ_NUM)); enableCheck = false; #endif } else { /* External phy */ #if defined(ETH_WAKEUP_GPIO_NUM) uint8_t valid_level = ETH_WAKEUP_EDGE == IRQF_TRIGGER_RISING; /* * Process the magic packet received during the suspend flow * and wakeup the system directly. */ if (!(ctrlFlag & ETH_CTRL_FLAG_NO_CK_LV) && (xGpioGetValue(ETH_WAKEUP_GPIO_NUM) == valid_level)) { uint32_t buf[4] = { 0 }; printf(DRV_NAME_PREFIX "E-WOL DIRECT WKUP\n"); buf[0] = ETH_PHY_GPIO; STR_Wakeup_src_Queue_Send(buf); } #endif } } bool xETHIsExternalPhy(void) { return !(ctrlFlag & ETH_CTRL_FLAG_PHY_TYPE); } static void *prvETHSetWol(void *msg) { uint32_t flag = *(uint32_t *)msg; /* Using mbox for command interaction */ if (flag & ETH_CTRL_FLAG_CMD_MODE) return xEthMboxHandler(msg); /* * 0x00000000: External Phy / Enable WOL * 0x00000001: Internal Phy / Enable WOL * 0x00000002: External Phy / Disable WOL * 0x00000004: New mode * 0x00000008: No check INTB_PMEB level */ ctrlFlag = flag; printf(DRV_NAME_PREFIX "FLAG (%08x)\n", ctrlFlag); return NULL; } static const char * const names[] = {"unused", "magic", "mDNS", "port"}; void vETHWakeup(uint8_t reason, bool isr) { uint32_t buf[4] = { 0 }; ethWakeupReason = reason; printf(DRV_NAME_PREFIX "WKUP[%s]\n", reason < ARRAY_SIZE(names) ? names[reason] : "xxx"); buf[0] = ETH_WAKEUP; if (isr) STR_Wakeup_src_Queue_Send_FromISR(buf); else STR_Wakeup_src_Queue_Send(buf); } void vETHMailboxCallback(void) { xInstallRemoteMessageCallbackFeedBack(REE2AO_CHANNEL, MBX_CMD_SET_ETHERNET_WOL, prvETHSetWol, 1); }