Files
2026-08-24 10:03:24 +08:00

325 lines
7.6 KiB
C

/*
* Copyright (c) 2021-2022 Amlogic, Inc. All rights reserved.
*
* SPDX-License-Identifier: MIT
*/
#include <stdio.h>
#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);
}