325 lines
7.6 KiB
C
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);
|
|
}
|