add missing phy drivers to hal/phy

This commit is contained in:
Rustam Adilov 2025-12-31 12:37:44 +05:00
parent ff2b2b9623
commit c8b4dd54e7
No known key found for this signature in database
GPG key ID: 29B04ACBBA88C32C
31 changed files with 21560 additions and 0 deletions

View file

@ -0,0 +1,10 @@
# SPDX-License-Identifier: GPL-2.0-only
#
# Copyright (c) 2023 Realtek Semiconductor Corp. All rights reserved.
#
config RTK_MSSDK_PHY
tristate "Realtek MSSDK PHYs"
help
Currently supports the RTL8261N,RTL8264B PHYs.

View file

@ -0,0 +1,28 @@
# SPDX-License-Identifier: GPL-2.0-only
#
# Copyright (c) 2023 Realtek Semiconductor Corp. All rights reserved.
#
ccflags-y := -DRTK_PHYDRV_IN_LINUX
#ccflags-y += -DDEBUG
obj-$(CONFIG_RTK_MSSDK_PHY) += rtk-ms-phy.o
rtk-ms-phy-objs := rtk_phy.o
rtk-ms-phy-objs += rtk_phy_rtl8224.o
rtk-ms-phy-objs += rtk_osal.o
# files from SDK
rtk-ms-phy-objs += phy_patch.o
rtk-ms-phy-objs += phy_rtl826xb_patch.o
rtk-ms-phy-objs += phy_rtl8224_patch.o
# rtk phylib
rtk-ms-phy-objs += rtk_phylib.o
rtk-ms-phy-objs += rtk_phylib_rtl826xb.o
rtk-ms-phy-objs += rtk_phylib_rtl8224.o
ifdef CONFIG_MACSEC
rtk-ms-phy-objs += rtk_macsec.o
rtk-ms-phy-objs += rtk_phylib_macsec.o
endif

View file

@ -0,0 +1,25 @@
/*
* SPDX-License-Identifier: GPL-2.0-only
*
* Copyright (c) 2023 Realtek Semiconductor Corp. All rights reserved.
*/
the driver is based on linux 5.10.146
1. Copy the folder "drivers/net/phy/rtk" to your linux in the same path
2. Modify Kconfig: linux/drivers/net/phy/Kconfig for add menuconfig
if PHYLIB
......
source "drivers/net/phy/rtk/Kconfig"
endif # PHYLIB
3. Modify Makefile: linux/drivers/net/phy/Makefile for build code
obj-$(CONFIG_RTK_MSSDK_PHY) += rtk/
4. enable kernel config:
CONFIG_RTK_MSSDK_PHY=y

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,165 @@
/*
* SPDX-License-Identifier: GPL-2.0-only
*
* Copyright (c) 2023 Realtek Semiconductor Corp. All rights reserved.
*/
#ifndef __COMMON_ERROR_H__
#define __COMMON_ERROR_H__
/*
* Include Files
*/
#if defined(RTK_PHYDRV_IN_LINUX)
#include "type.h"
#else
#include <common/type.h>
#endif
/*
* Data Type Declaration
*/
typedef enum rt_error_common_e
{
RT_ERR_FAILED = -1, /* General Error */
/* 0x0000xxxx for common error code */
RT_ERR_OK = 0, /* 0x00000000, OK */
RT_ERR_INPUT = 0xF001, /* 0x0000F001, invalid input parameter */
RT_ERR_UNIT_ID, /* 0x0000F002, invalid unit id */
RT_ERR_PORT_ID, /* 0x0000F003, invalid port id */
RT_ERR_PORT_MASK, /* 0x0000F004, invalid port mask */
RT_ERR_PORT_LINKDOWN, /* 0x0000F005, link down port status */
RT_ERR_ENTRY_INDEX, /* 0x0000F006, invalid entry index */
RT_ERR_NULL_POINTER, /* 0x0000F007, input parameter is null pointer */
RT_ERR_QUEUE_ID, /* 0x0000F008, invalid queue id */
RT_ERR_QUEUE_NUM, /* 0x0000F009, invalid queue number */
RT_ERR_BUSYWAIT_TIMEOUT, /* 0x0000F00a, busy watting time out */
RT_ERR_MAC, /* 0x0000F00b, invalid mac address */
RT_ERR_OUT_OF_RANGE, /* 0x0000F00c, input parameter out of range */
RT_ERR_CHIP_NOT_SUPPORTED, /* 0x0000F00d, functions not supported by this chip model */
RT_ERR_SMI, /* 0x0000F00e, SMI error */
RT_ERR_NOT_INIT, /* 0x0000F00f, The module is not initial */
RT_ERR_CHIP_NOT_FOUND, /* 0x0000F010, The chip can not found */
RT_ERR_NOT_ALLOWED, /* 0x0000F011, actions not allowed by the function */
RT_ERR_DRIVER_NOT_FOUND, /* 0x0000F012, The driver can not found */
RT_ERR_SEM_LOCK_FAILED, /* 0x0000F013, Failed to lock semaphore */
RT_ERR_SEM_UNLOCK_FAILED, /* 0x0000F014, Failed to unlock semaphore */
RT_ERR_THREAD_EXIST, /* 0x0000F015, Thread exist */
RT_ERR_THREAD_CREATE_FAILED, /* 0x0000F016, Thread create fail */
RT_ERR_FWD_ACTION, /* 0x0000F017, Invalid forwarding Action */
RT_ERR_IPV4_ADDRESS, /* 0x0000F018, Invalid IPv4 address */
RT_ERR_IPV6_ADDRESS, /* 0x0000F019, Invalid IPv6 address */
RT_ERR_PRIORITY, /* 0x0000F01a, Invalid Priority value */
RT_ERR_FID, /* 0x0000F01b, invalid fid */
RT_ERR_ENTRY_NOTFOUND, /* 0x0000F01c, specified entry not found */
RT_ERR_DROP_PRECEDENCE, /* 0x0000F01d, invalid drop precedence */
RT_ERR_NOT_FINISH, /* 0x0000F01e, Action not finish, still need to wait */
RT_ERR_TIMEOUT, /* 0x0000F01f, Time out */
RT_ERR_REG_ARRAY_INDEX_1, /* 0x0000F020, invalid index 1 of register array */
RT_ERR_REG_ARRAY_INDEX_2, /* 0x0000F021, invalid index 2 of register array */
RT_ERR_ETHER_TYPE, /* 0x0000F022, invalid ether type */
RT_ERR_MBUF_PKT_NOT_AVAILABLE, /* 0x0000F023, mbuf->packet is not available */
RT_ERR_QOS_INVLD_RSN, /* 0x0000F024, invalid pkt to CPU reason */
RT_ERR_CB_FUNCTION_EXIST, /* 0x0000F025, Callback function exist */
RT_ERR_CB_FUNCTION_FULL, /* 0x0000F026, Callback function number is full */
RT_ERR_CB_FUNCTION_NOT_FOUND, /* 0x0000F027, Callback function can not found */
RT_ERR_TBL_FULL, /* 0x0000F028, The table is full */
RT_ERR_TRUNK_ID, /* 0x0000F029, invalid trunk id */
RT_ERR_TYPE, /* 0x0000F02a, invalid type */
RT_ERR_ENTRY_EXIST, /* 0x0000F02b, entry exists */
RT_ERR_CHIP_UNDEFINED_VALUE, /* 0x0000F02c, chip returned an undefined value */
RT_ERR_EXCEEDS_CAPACITY, /* 0x0000F02d, exceeds the capacity of hardware */
RT_ERR_ENTRY_REFERRED, /* 0x0000F02e, entry is still being referred */
RT_ERR_OPER_DENIED, /* 0x0000F02f, operation denied */
RT_ERR_PORT_NOT_SUPPORTED, /* 0x0000F030, functions not supported by this port */
RT_ERR_SOCKET, /* 0x0000F031, socket error */
RT_ERR_MEM_ALLOC, /* 0x0000F032, insufficient memory resource */
RT_ERR_ABORT, /* 0x0000F033, operation aborted */
RT_ERR_DEV_ID, /* 0x0000F034, invalid device id */
RT_ERR_DRIVER_NOT_SUPPORTED, /* 0x0000F035, functions not supported by this driver */
RT_ERR_NOT_SUPPORTED, /* 0x0000F036, functions not supported */
RT_ERR_SER, /* 0x0000F037, ECC or parity error */
RT_ERR_MEM_NOT_ALIGN, /* 0x0000F038, memory address is not aligned */
RT_ERR_SEM_FAKELOCK_OK, /* 0x0000F039, attach thread lock a semaphore which was already locked */
RT_ERR_CHECK_FAILED, /* 0x0000F03a, check result is failed */
RT_ERR_COMMON_END = 0xFFFF /* The symbol is the latest symbol of common error */
} rt_error_common_t;
/*
* Macro Definition
*/
#define RT_PARAM_CHK(expr, errCode)\
do {\
if ((int32)(expr)) {\
return errCode; \
}\
} while (0)
#define RT_PARAM_CHK_EHDL(expr, errCode, err_hdl)\
do {\
if ((int32)(expr)) {\
{err_hdl}\
return errCode; \
}\
} while (0)
#define RT_INIT_CHK(state)\
do {\
if (INIT_COMPLETED != (state)) {\
return RT_ERR_NOT_INIT;\
}\
} while (0)
#define RT_INIT_REENTRY_CHK(state)\
do {\
if (INIT_COMPLETED == (state)) {\
osal_printf(" %s had already been initialized!\n", __FUNCTION__);\
return RT_ERR_OK;\
}\
} while (0)
#define RT_INIT_REENTRY_CHK_NO_WARNING(state)\
do {\
if (INIT_COMPLETED == (state)) {\
return RT_ERR_OK;\
}\
} while (0)
#define RT_ERR_CHK(op, ret)\
do {\
if ((ret = (op)) != RT_ERR_OK)\
return ret;\
} while(0)
#define RT_ERR_HDL(op, errHandle, ret)\
do {\
if ((ret = (op)) != RT_ERR_OK)\
goto errHandle;\
} while(0)
#define RT_ERR_CHK_EHDL(op, ret, err_hdl)\
do {\
if ((ret = (op)) != RT_ERR_OK)\
{\
{err_hdl}\
return ret;\
}\
} while(0)
#define RT_NULL_HDL(pointer, err_label)\
do {\
if (NULL == (pointer)) {\
goto err_label;\
}\
} while (0)
#define RT_ERR_VOID_CHK(op, ret)\
do {\
if ((ret = (op)) != RT_ERR_OK) {\
osal_printf("Fail in %s %d, ret %x!\n", __FUNCTION__, __LINE__, ret);\
return ;}\
} while(0)
#endif /* __COMMON_ERROR_H__ */

View file

@ -0,0 +1,179 @@
/*
* SPDX-License-Identifier: GPL-2.0-only
*
* Copyright (c) 2023 Realtek Semiconductor Corp. All rights reserved.
*/
/*
* Include Files
*/
#if defined(RTK_PHYDRV_IN_LINUX)
#include "rtk_osal.h"
#else
#include <common/rt_type.h>
#include <common/rt_error.h>
#include <common/debug/rt_log.h>
#include <hal/common/halctrl.h>
#include <hal/phy/phy_patch.h>
#endif
/*
* Function Declaration
*/
uint8 phy_patch_op_translate(uint8 patch_mode, uint8 patch_op, uint8 compare_op)
{
if (patch_mode != PHY_PATCH_MODE_CMP)
{
return patch_op;
}
else
{
switch (compare_op)
{
case RTK_PATCH_CMP_WS:
return RTK_PATCH_OP_SKIP;
case RTK_PATCH_CMP_W:
case RTK_PATCH_CMP_WC:
case RTK_PATCH_CMP_SWC:
default:
return RTK_PATCH_OP_TO_CMP(patch_op, compare_op);
}
}
}
int32 phy_patch_op(rt_phy_patch_db_t *pPhy_patchDb, uint32 unit, rtk_port_t port, uint8 portOffset, uint8 patch_op, uint16 portmask, uint16 pagemmd, uint16 addr, uint8 msb, uint8 lsb, uint16 data, uint8 patch_mode)
{
rtk_hwpatch_t op;
op.patch_op = patch_op;
op.portmask = portmask;
op.pagemmd = pagemmd;
op.addr = addr;
op.msb = msb;
op.lsb = lsb;
op.data = data;
op.compare_op = RTK_PATCH_CMP_W;
return pPhy_patchDb->fPatch_op(unit, port, portOffset, &op, patch_mode);
}
static int32 _phy_patch_process(uint32 unit, rtk_port_t port, uint8 portOffset, rtk_hwpatch_t *pPatch, int32 size, uint8 patch_mode)
{
int32 i = 0;
int32 ret = 0;
int32 chk_ret = RT_ERR_OK;
int32 n;
rtk_hwpatch_t *patch = pPatch;
rt_phy_patch_db_t *pPatchDb = NULL;
PHYPATCH_DB_GET(unit, port, pPatchDb);
if (size <= 0)
{
return RT_ERR_OK;
}
n = size / sizeof(rtk_hwpatch_t);
for (i = 0; i < n; i++)
{
ret = pPatchDb->fPatch_op(unit, port, portOffset, &patch[i], patch_mode);
if ((ret != RT_ERR_ABORT) && (ret != RT_ERR_OK))
{
if ((ret == RT_ERR_CHECK_FAILED) && (patch_mode == PHY_PATCH_MODE_CMP))
{
osal_printf("PATCH CHECK: Failed entry:%u|%u|0x%X|0x%X|%u|%u|0x%X\n",
i + 1, patch[i].patch_op, patch[i].pagemmd, patch[i].addr, patch[i].msb, patch[i].lsb, patch[i].data);
chk_ret = RT_ERR_CHECK_FAILED;
continue;
}
else
{
RT_LOG(LOG_MAJOR_ERR, (MOD_HAL | MOD_PHY), "U%u P%u %s failed! %u[%u][0x%X][0x%X][0x%X] ret=0x%X\n", unit, port, __FUNCTION__,
i+1, patch[i].patch_op, patch[i].pagemmd, patch[i].addr, patch[i].data, ret);
return ret;
}
}
}
return (chk_ret == RT_ERR_CHECK_FAILED) ? chk_ret : RT_ERR_OK;
}
/* Function Name:
* phy_patch
* Description:
* apply initial patch data to PHY
* Input:
* unit - unit id
* port - port id
* portOffset - the index offset of port based the base port in the PHY chip
* Output:
* None
* Return:
* RT_ERR_OK
* RT_ERR_FAILED
* RT_ERR_CHECK_FAILED
* RT_ERR_NOT_SUPPORTED
* Note:
* None
*/
int32 phy_patch(uint32 unit, rtk_port_t port, uint8 portOffset, uint8 patch_mode)
{
int32 ret = RT_ERR_OK;
int32 chk_ret = RT_ERR_OK;
uint32 i = 0;
uint8 patch_type = 0;
rt_phy_patch_db_t *pPatchDb = NULL;
rtk_hwpatch_seq_t *table = NULL;
PHYPATCH_DB_GET(unit, port, pPatchDb);
if ((pPatchDb == NULL) || (pPatchDb->fPatch_op == NULL) || (pPatchDb->fPatch_flow == NULL))
{
RT_LOG(LOG_MAJOR_ERR, (MOD_HAL | MOD_PHY), "U%u P%u phy_patch, db is NULL\n", unit, port);
return RT_ERR_DRIVER_NOT_SUPPORTED;
}
if (patch_mode == PHY_PATCH_MODE_CMP)
{
table = pPatchDb->cmp_table;
}
else
{
table = pPatchDb->seq_table;
}
RT_LOG(LOG_INFO, (MOD_HAL | MOD_PHY), "phy_patch: U%u P%u portOffset:%u patch_mode:%u\n", unit, port, portOffset, patch_mode);
for (i = 0; i < RTK_PATCH_SEQ_MAX; i++)
{
patch_type = table[i].patch_type;
RT_LOG(LOG_INFO, (MOD_HAL | MOD_PHY), "phy_patch: table[%u] patch_type:%u\n", i, patch_type);
if (RTK_PATCH_TYPE_IS_DATA(patch_type))
{
ret = _phy_patch_process(unit, port, portOffset, table[i].patch.data.conf, table[i].patch.data.size, patch_mode);
if (ret == RT_ERR_CHECK_FAILED)
chk_ret = ret;
else if (ret != RT_ERR_OK)
{
RT_LOG(LOG_MAJOR_ERR, (MOD_HAL | MOD_PHY), "U%u P%u patch_mode:%u id:%u patch-%u failed. ret:0x%X\n", unit, port, patch_mode, i, patch_type, ret);
return ret;
}
}
else if (RTK_PATCH_TYPE_IS_FLOW(patch_type))
{
RT_ERR_CHK_EHDL(pPatchDb->fPatch_flow(unit, port, portOffset, table[i].patch.flow_id, patch_mode),
ret, RT_LOG(LOG_MAJOR_ERR, (MOD_HAL | MOD_PHY), "U%u P%u patch_mode:%u id:%u patch-%u failed. ret:0x%X\n", unit, port, patch_mode, i, patch_type, ret););
}
else
{
break;
}
}
return (chk_ret == RT_ERR_CHECK_FAILED) ? chk_ret : RT_ERR_OK;
}

View file

@ -0,0 +1,173 @@
/*
* SPDX-License-Identifier: GPL-2.0-only
*
* Copyright (c) 2023 Realtek Semiconductor Corp. All rights reserved.
*/
#ifndef __HAL_PHY_PATCH_H__
#define __HAL_PHY_PATCH_H__
/*
* Include Files
*/
#if defined(RTK_PHYDRV_IN_LINUX)
#include "rtk_phylib_def.h"
#else
#include <common/rt_type.h>
#include <common/rt_autoconf.h>
#endif
/*
* Symbol Definition
*/
#define PHYPATCH_PHYCTRL_IN_HALCTRL 0 /* 3.6.x: 1 ,4.0.x: 1, 4.1.x+: 0 */
#define PHYPATCH_FMAILY_IN_HWP 0 /* 3.6.x: 1 ,4.0.x: 0, 4.1.x+: 0 */
#define PHY_PATCH_MODE_BCAST_DEFAULT PHY_PATCH_MODE_BCAST /* 3.6.x: PHY_PATCH_MODE_BCAST_BUS ,4.0.x+: PHY_PATCH_MODE_BCAST */
#define PHY_PATCH_MODE_NORMAL 0
#define PHY_PATCH_MODE_CMP 1
#define PHY_PATCH_MODE_BCAST 2
#define PHY_PATCH_MODE_BCAST_BUS 3
#define RTK_PATCH_CMP_W 0 /* write */
#define RTK_PATCH_CMP_WC 1 /* compare */
#define RTK_PATCH_CMP_SWC 2 /* sram compare */
#define RTK_PATCH_CMP_WS 3 /* skip */
#define RTK_PATCH_OP_SECTION_SIZE 50
#define RTK_PATCH_OP_TO_CMP(_op, _cmp) (_op + (RTK_PATCH_OP_SECTION_SIZE * _cmp))
/* 0~49 normal op */
#define RTK_PATCH_OP_PHY 0
#define RTK_PATCH_OP_PHYOCP 1
#define RTK_PATCH_OP_TOP 2
#define RTK_PATCH_OP_TOPOCP 3
#define RTK_PATCH_OP_PSDS0 4
#define RTK_PATCH_OP_PSDS1 5
#define RTK_PATCH_OP_MSDS 6
#define RTK_PATCH_OP_MAC 7
/* 50~99 normal op for compare */
#define RTK_PATCH_OP_CMP_PHY RTK_PATCH_OP_TO_CMP(RTK_PATCH_OP_PHY , RTK_PATCH_CMP_WC)
#define RTK_PATCH_OP_CMP_PHYOCP RTK_PATCH_OP_TO_CMP(RTK_PATCH_OP_PHYOCP , RTK_PATCH_CMP_WC)
#define RTK_PATCH_OP_CMP_TOP RTK_PATCH_OP_TO_CMP(RTK_PATCH_OP_TOP , RTK_PATCH_CMP_WC)
#define RTK_PATCH_OP_CMP_TOPOCP RTK_PATCH_OP_TO_CMP(RTK_PATCH_OP_TOPOCP , RTK_PATCH_CMP_WC)
#define RTK_PATCH_OP_CMP_PSDS0 RTK_PATCH_OP_TO_CMP(RTK_PATCH_OP_PSDS0 , RTK_PATCH_CMP_WC)
#define RTK_PATCH_OP_CMP_PSDS1 RTK_PATCH_OP_TO_CMP(RTK_PATCH_OP_PSDS1 , RTK_PATCH_CMP_WC)
#define RTK_PATCH_OP_CMP_MSDS RTK_PATCH_OP_TO_CMP(RTK_PATCH_OP_MSDS , RTK_PATCH_CMP_WC)
#define RTK_PATCH_OP_CMP_MAC RTK_PATCH_OP_TO_CMP(RTK_PATCH_OP_MAC , RTK_PATCH_CMP_WC)
/* 100~149 normal op for sram compare */
#define RTK_PATCH_OP_CMP_SRAM_PHY RTK_PATCH_OP_TO_CMP(RTK_PATCH_OP_PHY , RTK_PATCH_CMP_SWC)
#define RTK_PATCH_OP_CMP_SRAM_PHYOCP RTK_PATCH_OP_TO_CMP(RTK_PATCH_OP_PHYOCP , RTK_PATCH_CMP_SWC)
#define RTK_PATCH_OP_CMP_SRAM_TOP RTK_PATCH_OP_TO_CMP(RTK_PATCH_OP_TOP , RTK_PATCH_CMP_SWC)
#define RTK_PATCH_OP_CMP_SRAM_TOPOCP RTK_PATCH_OP_TO_CMP(RTK_PATCH_OP_TOPOCP , RTK_PATCH_CMP_SWC)
#define RTK_PATCH_OP_CMP_SRAM_PSDS0 RTK_PATCH_OP_TO_CMP(RTK_PATCH_OP_PSDS0 , RTK_PATCH_CMP_SWC)
#define RTK_PATCH_OP_CMP_SRAM_PSDS1 RTK_PATCH_OP_TO_CMP(RTK_PATCH_OP_PSDS1 , RTK_PATCH_CMP_SWC)
#define RTK_PATCH_OP_CMP_SRAM_MSDS RTK_PATCH_OP_TO_CMP(RTK_PATCH_OP_MSDS , RTK_PATCH_CMP_SWC)
#define RTK_PATCH_OP_CMP_SRAM_MAC RTK_PATCH_OP_TO_CMP(RTK_PATCH_OP_MAC , RTK_PATCH_CMP_SWC)
/* 200~255 control op */
#define RTK_PATCH_OP_DELAY_MS 200
#define RTK_PATCH_OP_SKIP 255
/*
patch type PHY_PATCH_TYPE_NONE => empty
patch type: PHY_PATCH_TYPE_TOP ~ (PHY_PATCH_TYPE_END-1) => data array
patch type: PHY_PATCH_TYPE_END ~ (PHY_PATCH_TYPE_END + RTK_PATCH_TYPE_FLOW_MAX) => flow
*/
#define RTK_PATCH_TYPE_IS_DATA(_patch_type) (_patch_type > PHY_PATCH_TYPE_NONE && _patch_type < PHY_PATCH_TYPE_END)
#define RTK_PATCH_TYPE_IS_FLOW(_patch_type) (_patch_type >= PHY_PATCH_TYPE_END && _patch_type <= (PHY_PATCH_TYPE_END + RTK_PATCH_TYPE_FLOWID_MAX))
/*
* Macro Definition
*/
#if PHYPATCH_PHYCTRL_IN_HALCTRL
#define PHYPATCH_DB_GET(_unit, _port, _pPatchDb) \
do {\
hal_control_t *pHalCtrl = NULL;\
if ((pHalCtrl = hal_ctrlInfo_get(_unit)) == NULL)\
return RT_ERR_FAILED;\
_pPatchDb = (pHalCtrl->pPhy_ctrl[_port]->pPhy_patchDb);\
} while(0)
#else
#if defined(RTK_PHYDRV_IN_LINUX)
#else
#include <hal/phy/phydef.h>
#include <hal/phy/phy_probe.h>
#endif
#define PHYPATCH_DB_GET(_unit, _port, _pPatchDb) \
do {\
rt_phyctrl_t *pPhyCtrl = NULL;\
if ((pPhyCtrl = phy_phyctrl_get(_unit, _port)) == NULL)\
return RT_ERR_FAILED;\
_pPatchDb = (pPhyCtrl->pPhy_patchDb);\
} while(0)
#endif
#if PHYPATCH_FMAILY_IN_HWP
#define PHYPATCH_IS_RTKSDS(_unit) (HWP_9300_FAMILY_ID(_unit) || HWP_9310_FAMILY_ID(_unit))
#else
#define PHYPATCH_IS_RTKSDS(_unit) (RTK_9300_FAMILY_ID(_unit) || RTK_9310_FAMILY_ID(_unit) || RTK_9311B_FAMILY_ID(_unit) || RTK_9330_FAMILY_ID(_unit))
#endif
#define PHYPATCH_TABLE_ASSIGN(_pPatchDb, _table, _idx, _patch_type, _para) \
do {\
if (RTK_PATCH_TYPE_IS_DATA(_patch_type)) {\
_pPatchDb->_table[_idx].patch_type = _patch_type;\
_pPatchDb->_table[_idx].patch.data.conf = _para;\
_pPatchDb->_table[_idx].patch.data.size = sizeof(_para);\
}\
else if (RTK_PATCH_TYPE_IS_FLOW(_patch_type)) {\
_pPatchDb->_table[_idx].patch_type = _patch_type;\
_pPatchDb->_table[_idx].patch.flow_id = _patch_type;\
}\
else {\
_pPatchDb->_table[_idx].patch_type = PHY_PATCH_TYPE_NONE;\
}\
} while(0)
#define PHYPATCH_SEQ_TABLE_ASSIGN(_pPatchDb, _idx, _patch_type, _para) PHYPATCH_TABLE_ASSIGN(_pPatchDb, seq_table, _idx, _patch_type, _para)
#define PHYPATCH_CMP_TABLE_ASSIGN(_pPatchDb, _idx, _patch_type, _para) PHYPATCH_TABLE_ASSIGN(_pPatchDb, cmp_table, _idx, _patch_type, _para)
#define PHYPATCH_COMPARE(_mmdpage, _reg, _msb, _lsb, _exp, _real, _mask) \
do {\
uint32 _rData = REG32_FIELD_GET(_real, _lsb, _mask);\
if (_exp != _rData) {\
osal_printf("PATCH CHECK: %u(0x%X).%u(0x%X)[%u:%u] = 0x%X (!= 0x%X)\n", _mmdpage, _mmdpage, _reg, _reg, _msb, _lsb, _rData, _exp);\
return RT_ERR_CHECK_FAILED;\
}\
} while (0)
/*
* Function Declaration
*/
extern uint8 phy_patch_op_translate(uint8 patch_mode, uint8 patch_op, uint8 compare_op);
extern int32 phy_patch_op(rt_phy_patch_db_t *pPhy_patchDb, uint32 unit, rtk_port_t port, uint8 portOffset,
uint8 patch_op, uint16 portmask, uint16 pagemmd, uint16 addr, uint8 msb, uint8 lsb, uint16 data,
uint8 patch_mode);
/* Function Name:
* phy_patch
* Description:
* apply initial patch data to PHY
* Input:
* unit - unit id
* port - port id
* portOffset - the index offset of port based the base port in the PHY chip
* Output:
* None
* Return:
* RT_ERR_OK
* RT_ERR_FAILED
* RT_ERR_CHECK_FAILED
* RT_ERR_NOT_SUPPORTED
* Note:
* None
*/
extern int32 phy_patch(uint32 unit, rtk_port_t port, uint8 portOffset, uint8 patch_mode);
#endif /* __HAL_PHY_PATCH_H__ */

View file

@ -0,0 +1,641 @@
/*
* Copyright (C) 2009-2022 Realtek Semiconductor Corp.
* All Rights Reserved.
*
* This program is the proprietary software of Realtek Semiconductor
* Corporation and/or its licensors, and only be used, duplicated,
* modified or distributed under the authorized license from Realtek.
*
* ANY USE OF THE SOFTWARE OTHER THAN AS AUTHORIZED UNDER
* THIS LICENSE OR COPYRIGHT LAW IS PROHIBITED.
*
* $Revision: $
* $Date: $
*
* Purpose : PHY 8224 HW patch APIs.
*
* Feature : PHY 8224 HW patch APIs
*
*/
/*
* Include Files
*/
#if defined(RTK_PHYDRV_IN_LINUX)
#include "phy_rtl8224_patch.h"
#include "construct/conf_rtl8224.c"
#include "rtk_phylib_rtl8224.h"
#else
#include <common/rt_type.h>
#include <common/rt_error.h>
#include <common/debug/rt_log.h>
#include <soc/type.h>
#include <hal/common/halctrl.h>
#include <hal/mac/miim_common_drv.h>
#include <hal/phy/phy_construct.h>
#include <osal/time.h>
#include <hal/phy/construct/conftypes.h>
#if defined(CONFIG_SDK_RTL8224)
#include <hal/phy/phy_rtl8224.h>
#include <hal/phy/construct/conf_rtl8224.c>
#endif
#endif
/*
* Symbol Definition
*/
#define PHY_PATCH_OP_NORMAL 0
#define PHY_PATCH_OP_BCAST 1
#define PHY_PATCH_OP_BCAST_SAME_CHIP 2
#define PHY_PATCH_WAIT_TIMEOUT 10000000
#define PHY_PATCH_LOG LOG_INFO
/*
* Data Declaration
*/
rtk_phy_hwpatch_t *patch_fwpr_conf;
rtk_phy_hwpatch_t *patch_fwlm_conf;
rtk_phy_hwpatch_t *patch_afe_conf;
rtk_phy_hwpatch_t *patch_top_conf;
rtk_phy_hwpatch_t *patch_sds_conf;
int32 patch_fwpr_conf_size;
int32 patch_fwlm_conf_size;
int32 patch_afe_conf_size;
int32 patch_top_conf_size;
int32 patch_sds_conf_size;
/*
* Macro Declaration
*/
/*
* Function Declaration
*/
uint32
_phy_rtl8224_patch_mask(uint8 msb, uint8 lsb)
{
uint32 val = 0;
uint8 i = 0;
for (i = lsb; i <= msb; i++)
{
val |= (1 << i);
}
return val;
}
int32
_phy_rtl8224_patch_mask_get(uint8 msb, uint8 lsb, uint32 *mask)
{
if ((msb > 15) || (lsb > 15) || (msb < lsb))
{
return RT_ERR_FAILED;
}
*mask = _phy_rtl8224_patch_mask(msb, lsb);
return RT_ERR_OK;
}
int32
_phy_rtl8224_patch_wait(uint32 unit, rtk_port_t port, uint32 mmdAddr, uint32 mmdReg, uint32 data, uint32 mask, uint8 bcast_op)
{
int32 ret = RT_ERR_OK;
uint32 rData = 0;
uint32 cnt = 0;
WAIT_COMPLETE_VAR()
WAIT_COMPLETE(PHY_PATCH_WAIT_TIMEOUT)
{
if ((ret = phy_common_general_reg_mmd_get(unit, port, mmdAddr, mmdReg, &rData)) != RT_ERR_OK)
return ret;
++cnt;
if ((rData & mask) == data)
break;
osal_time_mdelay(1);
}
if (WAIT_COMPLETE_IS_TIMEOUT())
{
PR_ERR("U%u 8224 patch wait[%u,0x%X,0x%X,0x%X]:0x%X cnt:%u\n", unit, mmdAddr, mmdReg, data, mask, rData, cnt);
return RT_ERR_TIMEOUT;
}
return RT_ERR_OK;
}
int32
_phy_rtl8224_patch_wait_not_equal(uint32 unit, rtk_port_t port, uint32 mmdAddr, uint32 mmdReg, uint32 data, uint32 mask, uint8 bcast_op)
{
int32 ret = RT_ERR_OK;
uint32 rData = 0;
uint32 cnt = 0;
WAIT_COMPLETE_VAR()
WAIT_COMPLETE(PHY_PATCH_WAIT_TIMEOUT)
{
if ((ret = phy_common_general_reg_mmd_get(unit, port, mmdAddr, mmdReg, &rData)) != RT_ERR_OK)
return ret;
++cnt;
if ((rData & mask) != data)
break;
osal_time_mdelay(1);
}
if(WAIT_COMPLETE_IS_TIMEOUT())
{
RT_LOG(LOG_MAJOR_ERR, (MOD_HAL | MOD_PHY), "U%u P%u 8224 patch wait[%u,0x%X,0x%X,0x%X]:0x%X cnt:%u\n", unit, port, mmdAddr, mmdReg, data, mask, rData, cnt);
return RT_ERR_TIMEOUT;
}
return RT_ERR_OK;
}
int32
_phy_rtl8224_patch_top_get(uint32 unit, rtk_port_t port, uint32 topPage, uint32 topReg, uint32 *pData)
{
int32 ret = RT_ERR_OK;
uint32 rData = 0;
if ((ret = phy_common_general_reg_mmd_get(unit, port, PHY_MMD_VEND1, topReg, &rData)) != RT_ERR_OK)
return ret;
*pData = rData;
return RT_ERR_OK;
}
int32
_phy_rtl8224_patch_top_set(uint32 unit, rtk_port_t port, uint32 topPage, uint32 topReg, uint32 wData)
{
int32 ret = RT_ERR_OK;
if ((ret = phy_common_general_reg_mmd_set(unit, port, PHY_MMD_VEND1, topReg, wData)) != RT_ERR_OK)
return ret;
return RT_ERR_OK;
}
int32
_phy_rtl8224_patch_sds_get(uint32 unit, rtk_port_t port, uint32 sdsPage, uint32 sdsReg, uint32 *pData)
{
int32 ret = RT_ERR_OK;
uint32 rData = 0;
uint32 sdsAddr = (0x1 << 15) | (0x0 << 14) | (sdsReg << 7) | (sdsPage << 1);
if ((ret = _phy_rtl8224_patch_top_set(unit, port, 0, 0x3F8, sdsAddr)) != RT_ERR_OK)
return ret;
if ((ret = _phy_rtl8224_patch_top_get(unit, port, 0, 0x3FC, &rData)) != RT_ERR_OK)
return ret;
*pData = rData;
return _phy_rtl8224_patch_wait(unit, port, PHY_MMD_VEND1, 0x3F8, 0, BIT_15, PHY_PATCH_OP_NORMAL);
}
int32
_phy_rtl8224_patch_sds_set(uint32 unit, rtk_port_t port, uint32 sdsPage, uint32 sdsReg, uint32 wData, uint8 bcast)
{
int32 ret = RT_ERR_OK;
uint32 sdsAddr = (0x1 << 15) | (0x1 << 14) | (sdsReg << 7) | (sdsPage << 1);
if ((ret = _phy_rtl8224_patch_top_set(unit, port, 0, 0x400, wData)) != RT_ERR_OK)
return ret;
if ((ret = _phy_rtl8224_patch_top_set(unit, port, 0, 0x3F8, sdsAddr)) != RT_ERR_OK)
return ret;
return _phy_rtl8224_patch_wait(unit, port, PHY_MMD_VEND1, 0x3F8, 0, BIT_15, bcast);
}
int32
phy_rtl8224_patch_process_op(uint32 unit, rtk_port_t port, uint8 portOffset, rtk_phy_hwpatch_t *op, uint8 bcast)
{
int32 ret = RT_ERR_OK;
uint32 mask = 0;
uint32 rData = 0;
uint32 wData = 0;
if ((op->portmask & (1 << portOffset)) == 0)
{
return RT_ERR_ABORT;
}
ret = _phy_rtl8224_patch_mask_get(op->msb, op->lsb, &mask);
if (ret != RT_ERR_OK)
return ret;
switch (op->patch_op)
{
case RTK_HWPATCH_OP_PHY:
if ((op->msb != 15) || (op->lsb != 0))
{
if ((ret = phy_common_general_reg_mmd_get(unit, port, PHY_MMD_VEND2, op->addr, &rData)) != RT_ERR_OK)
{
PR_ERR("\n[port = %d] addr = 0x%08x ERROR!!!!!\n",port->mdio.addr, op->addr);
return ret;
}
}
wData = REG32_FIELD_SET(rData, op->data, op->lsb, mask);
if ((ret = phy_common_general_reg_mmd_set(unit, port, PHY_MMD_VEND2, op->addr, wData)) != RT_ERR_OK)
return ret;
break;
case RTK_HWPATCH_OP_MMD:
if ((op->msb != 15) || (op->lsb != 0))
{
if ((ret = phy_common_general_reg_mmd_get(unit, port, PHY_MMD_VEND1, op->addr, &rData)) != RT_ERR_OK)
return ret;
}
wData = REG32_FIELD_SET(rData, op->data, op->lsb, mask);
if ((ret = phy_common_general_reg_mmd_set(unit, port, PHY_MMD_VEND1, op->addr, wData)) != RT_ERR_OK)
return ret;
break;
case RTK_HWPATCH_OP_TOP:
if ((op->msb != 15) || (op->lsb != 0))
{
if ((ret = _phy_rtl8224_patch_top_get(unit, port, PHY_MMD_VEND1, op->addr, &rData)) != RT_ERR_OK)
return ret;
}
wData = REG32_FIELD_SET(rData, op->data, op->lsb, mask);
if ((ret = _phy_rtl8224_patch_top_set(unit, port, PHY_MMD_VEND1, op->addr, wData)) != RT_ERR_OK)
return ret;
break;
case RTK_HWPATCH_OP_SDS:
if ((op->msb != 15) || (op->lsb != 0))
{
if ((ret = _phy_rtl8224_patch_sds_get(unit, port, op->pagemmd, op->addr, &rData)) != RT_ERR_OK)
return ret;
}
wData = REG32_FIELD_SET(rData, op->data, op->lsb, mask);
if ((ret = _phy_rtl8224_patch_sds_set(unit, port, op->pagemmd, op->addr, wData, bcast)) != RT_ERR_OK)
return ret;
break;
case RTK_HWPATCH_OP_PHYW:
case RTK_HWPATCH_OP_ALGO:
case RTK_HWPATCH_OP_DATARAM:
case RTK_HWPATCH_OP_UNKNOWN:
default:
return RT_ERR_INPUT;
}
return RT_ERR_OK;
}
int32
phy_rtl8224_patch_op(uint32 unit, rtk_port_t port, uint8 portOffset, uint8 patch_op, uint8 portmask, uint16 pagemmd, uint16 addr, uint8 msb, uint8 lsb, uint16 data)
{
rtk_phy_hwpatch_t op;
op.patch_op = patch_op;
op.portmask = portmask;
op.pagemmd = pagemmd;
op.addr = addr;
op.msb = msb;
op.lsb = lsb;
op.data = data;
return phy_rtl8224_patch_process_op(unit, port, portOffset, &op, PHY_PATCH_OP_NORMAL);
}
int32
_phy_rtl8224_patch_process(uint32 unit, rtk_port_t port, uint8 portOffset, rtk_phy_hwpatch_t *pPatch, int32 size, uint32 *cnt, uint8 bcast)
{
int32 ret = RT_ERR_OK;
int32 i = 0;
int32 n = 0;
rtk_phy_hwpatch_t *patch = pPatch;
if (size <= 0)
{
*cnt = 0;
return RT_ERR_OK;
}
n = size/sizeof(rtk_phy_hwpatch_t);
for (i = 0; i < n; i++)
{
ret = phy_rtl8224_patch_process_op(unit, port, portOffset, &patch[i], bcast);
if ((ret != RT_ERR_ABORT) && (ret != RT_ERR_OK))
{
RT_LOG(LOG_MAJOR_ERR, (MOD_HAL | MOD_PHY), "U%u P%u %s failed! i=%u ret=0x%X\n", unit, port, __FUNCTION__, i, ret);
return ret;
}
}
*cnt = i;
return RT_ERR_OK;
}
int32
_phy_rtl8224_patch_assign(uint32 unit, rtk_port_t port, uint32 chipVer)
{
if(chipVer == PHY_RTL8224_VER_A)
{
patch_fwpr_conf = rtl8224_a_patch_fwpr_conf;
patch_fwlm_conf = rtl8224_a_patch_fwlm_conf;
patch_afe_conf = rtl8224_a_patch_afe_conf;
patch_top_conf = rtl8224_a_patch_top_conf;
patch_sds_conf = rtl8224_a_patch_sds_conf;
patch_fwpr_conf_size = sizeof(rtl8224_a_patch_fwpr_conf);
patch_fwlm_conf_size= sizeof(rtl8224_a_patch_fwlm_conf);
patch_afe_conf_size= sizeof(rtl8224_a_patch_afe_conf);
patch_top_conf_size= sizeof(rtl8224_a_patch_top_conf);
patch_sds_conf_size= sizeof(rtl8224_a_patch_sds_conf);
}
else if(chipVer == PHY_RTL8224_VER_B)
{
patch_fwpr_conf = rtl8224_b_patch_fwpr_conf;
patch_fwlm_conf = rtl8224_b_patch_fwlm_conf;
patch_afe_conf = rtl8224_b_patch_afe_conf;
patch_top_conf = rtl8224_b_patch_top_conf;
patch_sds_conf = rtl8224_b_patch_sds_conf;
patch_fwpr_conf_size = sizeof(rtl8224_b_patch_fwpr_conf);
patch_fwlm_conf_size= sizeof(rtl8224_b_patch_fwlm_conf);
patch_afe_conf_size= sizeof(rtl8224_b_patch_afe_conf);
patch_top_conf_size= sizeof(rtl8224_b_patch_top_conf);
patch_sds_conf_size= sizeof(rtl8224_b_patch_sds_conf);
}else{
patch_fwpr_conf = rtl8224_patch_fwpr_conf;
patch_fwlm_conf = rtl8224_patch_fwlm_conf;
patch_afe_conf = rtl8224_patch_afe_conf;
patch_top_conf = rtl8224_patch_top_conf;
patch_sds_conf = rtl8224_patch_sds_conf;
patch_fwpr_conf_size = sizeof(rtl8224_patch_fwpr_conf);
patch_fwlm_conf_size= sizeof(rtl8224_patch_fwlm_conf);
patch_afe_conf_size= sizeof(rtl8224_patch_afe_conf);
patch_top_conf_size= sizeof(rtl8224_patch_top_conf);
patch_sds_conf_size= sizeof(rtl8224_patch_sds_conf);
}
return RT_ERR_OK;
}
int32
_phy_rtl8224_patch_version_assign(uint32 unit, rtk_port_t port, uint8 portOffset, uint32 chipVer)
{
int32 ret = RT_ERR_OK;
//writephy_ocp $phynum 0xA438 $currentVersion
if((chipVer == PHY_RTL8224_VER_A) || (chipVer == PHY_RTL8224_VER_B))
{
ret = phy_rtl8224_patch_op(unit, port, portOffset, RTK_HWPATCH_OP_PHY, 0xff, 0x00, 0xA438, 15, 0, RTL8224_AB_FW_VER);
}
else
{
ret = phy_rtl8224_patch_op(unit, port, portOffset, RTK_HWPATCH_OP_PHY, 0xff, 0x00, 0xA438, 15, 0, RTL8224_FW_VER);
}
return ret;
}
int32
_phy_rtl8224_patch(uint32 unit, rtk_port_t port, uint8 portOffset, uint8 bcast)
{
int32 ret = RT_ERR_OK;
uint32 cnt = 0;
uint32 chipVer = 5;
phy_8224_chipVer_get(unit, port, &chipVer);
_phy_rtl8224_patch_assign(unit, port, chipVer);
//Patch Request
//PP_PHYReg w $PHYID 0xB820 bit.4 0x1 patch request
if ((ret = phy_rtl8224_patch_op(unit, port, portOffset, RTK_HWPATCH_OP_PHY, 0xff, 0x00, 0xb820, 4, 4, 0x1)) != RT_ERR_OK)
return ret;
//PP_PHYReg_bit r $PHYID 0xB800 6 6 wait 1
if ((ret = _phy_rtl8224_patch_wait(unit, port, PHY_MMD_VEND2, 0xB800, BIT_6, BIT_6, bcast)) != RT_ERR_OK)
return ret;
if ((ret = _phy_rtl8224_patch_process(unit, port, portOffset, patch_fwpr_conf, patch_fwpr_conf_size, &cnt, bcast))!= RT_ERR_OK)
{
RT_LOG(LOG_MAJOR_ERR, (MOD_HAL | MOD_PHY), "U%u P%u 8224 fwpr patch failed. ret:0x%X\n", unit, port, ret);
return ret;
}
RT_LOG(PHY_PATCH_LOG, (MOD_HAL | MOD_PHY), "U%u P%u 8224 fwpr patch done. ret:0x%X cnt:%d\n", unit, port, ret, cnt);
//PP_PHYReg w $PHYID 0xB820 0x0000
if ((ret = phy_rtl8224_patch_op(unit, port, portOffset, RTK_HWPATCH_OP_PHY, 0xff, 0x00, 0xb820, 4, 4, 0x0)) != RT_ERR_OK)
return ret;
//PP_PHYReg_bit r $PHYID 0xB800 6 6 wait 0 (Release patch request & wait patch_rdy = 0)
if ((ret = _phy_rtl8224_patch_wait(unit, port, PHY_MMD_VEND2, 0xB800, 0, BIT_6, bcast)) != RT_ERR_OK)
return ret;
//Lock Main
//writephybits_ocp $phynum 0xa4a0 10 10 0x1
if ((ret = phy_rtl8224_patch_op(unit, port, portOffset, RTK_HWPATCH_OP_PHY, 0xff, 0x00, 0xA4A0, 10, 10, 0x1)) != RT_ERR_OK)
return ret;
//[PP_PHYReg_bit r $PHYID 0xa600 7 0]
if ((ret = _phy_rtl8224_patch_wait(unit, port, PHY_MMD_VEND2, 0xa600, 0x1, 0xFF, bcast)) != RT_ERR_OK)
return ret;
if ((ret = _phy_rtl8224_patch_process(unit, port, portOffset, patch_fwlm_conf, patch_fwlm_conf_size, &cnt, bcast))!= RT_ERR_OK)
{
RT_LOG(LOG_MAJOR_ERR, (MOD_HAL | MOD_PHY), "U%u P%u 8224 fwlm patch failed. ret:0x%X\n", unit, port, ret);
return ret;
}
RT_LOG(PHY_PATCH_LOG, (MOD_HAL | MOD_PHY), "U%u P%u 8224 fwlm patch done. ret:0x%X cnt:%d\n", unit, port, ret, cnt);
if ((ret = _phy_rtl8224_patch_process(unit, port, portOffset, patch_afe_conf, patch_afe_conf_size, &cnt, bcast)) != RT_ERR_OK)
{
RT_LOG(LOG_MAJOR_ERR, (MOD_HAL | MOD_PHY), "U%u P%u 8224 afe patch failed. ret:0x%X\n", unit, port, ret);
return ret;
}
RT_LOG(PHY_PATCH_LOG, (MOD_HAL | MOD_PHY), "U%u P%u 8224 afe patch done. ret:0x%X cnt:%d\n", unit, port, ret, cnt);
//Patch PHY
if(chipVer == PHY_RTL8224_VER_A){
//writephy_ocp $phynum 0xa5d0 0x0
if ((ret = phy_rtl8224_patch_op(unit, port, portOffset, RTK_HWPATCH_OP_PHY, 0xff, 0x00, 0xa5d0, 15, 0, 0x0)) != RT_ERR_OK)
return ret;
//writephybits_ocp $phynum 0xa430 2 0 0x0
if ((ret = phy_rtl8224_patch_op(unit, port, portOffset, RTK_HWPATCH_OP_PHY, 0xff, 0x00, 0xa430, 2, 0, 0x0)) != RT_ERR_OK)
return ret;
}
//writephy_ocp $phynum 0xa47E 7 6 0x1
if ((ret = phy_rtl8224_patch_op(unit, port, portOffset, RTK_HWPATCH_OP_PHY, 0xff, 0x00, 0xa47E, 7, 6, 0x1)) != RT_ERR_OK)
return ret;
//Release Lock Main
//writephybits_ocp $phynum 0xa4a0 10 10 0x0
if ((ret = phy_rtl8224_patch_op(unit, port, portOffset, RTK_HWPATCH_OP_PHY, 0xff, 0x00, 0xA4A0, 10, 10, 0x0)) != RT_ERR_OK)
return ret;
//[PP_PHYReg_bit r $PHYID 0xa600 7 0]
if ((ret = _phy_rtl8224_patch_wait_not_equal(unit, port, PHY_MMD_VEND2, 0xa600, 0x1, 0xFF, bcast)) != RT_ERR_OK)
return ret;
if ((ret = _phy_rtl8224_patch_process(unit, port, portOffset, patch_top_conf, patch_top_conf_size, &cnt, bcast))!= RT_ERR_OK)
{
RT_LOG(LOG_MAJOR_ERR, (MOD_HAL | MOD_PHY), "U%u P%u 8224 top patch failed. ret:0x%X\n", unit, port, ret);
return ret;
}
RT_LOG(PHY_PATCH_LOG, (MOD_HAL | MOD_PHY), "U%u P%u 8224 top patch done. ret:0x%X cnt:%d\n", unit, port, ret, cnt);
if ((ret = _phy_rtl8224_patch_process(unit, port, portOffset, patch_sds_conf, patch_sds_conf_size, &cnt, bcast))!= RT_ERR_OK)
{
RT_LOG(LOG_MAJOR_ERR, (MOD_HAL | MOD_PHY), "U%u P%u 8224 sds patch failed. ret:0x%X\n", unit, port, ret);
return ret;
}
RT_LOG(PHY_PATCH_LOG, (MOD_HAL | MOD_PHY), "U%u P%u 8224 sds patch done. ret:0x%X cnt:%d\n", unit, port, ret, cnt);
//writephy_ocp $phynum 0xA436 0x801E
if ((ret = phy_rtl8224_patch_op(unit, port, portOffset, RTK_HWPATCH_OP_PHY, 0xff, 0x00, 0xA436, 15, 0, 0x801E)) != RT_ERR_OK)
return ret;
ret = _phy_rtl8224_patch_version_assign(unit, port, portOffset, chipVer);
return ret;
}
/* Function Name:
* phy_rtl8224_sdsReg_get
* Description:
* Get SerDes Register
* Input:
* unit - unit id
* baseport - base port id on the PHY chip
* sdsPage - the SerDes page
* sdsReg - the SerDes register
* Output:
* pData - return register value
* Return:
* RT_ERR_OK
* RT_ERR_FAILED
* RT_ERR_NOT_SUPPORTED
* RT_ERR_ABORT
* Note:
* None
*/
int32
phy_rtl8224_sdsReg_get(uint32 unit, rtk_port_t baseport, uint32 sdsPage, uint32 sdsReg, uint32 *pData)
{
_phy_rtl8224_patch_sds_get(unit, baseport, sdsPage, sdsReg, pData);
return RT_ERR_OK;
}
/* Function Name:
* phy_rtl8224_sdsReg_set
* Description:
* Set SerDes Register
* Input:
* unit - unit id
* baseport - base port id on the PHY chip
* sdsPage - the SerDes page
* sdsReg - the SerDes register
* wData - the write data
* Output:
* None
* Return:
* RT_ERR_OK
* RT_ERR_FAILED
* RT_ERR_NOT_SUPPORTED
* RT_ERR_ABORT
* Note:
* None
*/
int32
phy_rtl8224_sdsReg_set(uint32 unit, rtk_port_t baseport, uint32 sdsPage, uint32 sdsReg, uint32 wData)
{
return _phy_rtl8224_patch_sds_set(unit, baseport, sdsPage, sdsReg, wData, 0);
}
/* Function Name:
* phy_rtl8224_patch
* Description:
* Apply initial patch data to PHY
* Input:
* unit - unit id
* baseport - base port id on the PHY chip
* portOffset - the index offset base on baseport for the port to patch
* Output:
* None
* Return:
* RT_ERR_OK
* RT_ERR_FAILED
* RT_ERR_NOT_SUPPORTED
* RT_ERR_ABORT
* Note:
* None
*/
int32
phy_rtl8224_patch(uint32 unit, rtk_port_t port, uint8 portOffset)
{
int32 ret = RT_ERR_OK;
uint32 phyData = 0;
if ((ret = _phy_rtl8224_patch(unit, port, portOffset, PHY_PATCH_OP_NORMAL)) != RT_ERR_OK)
return ret;
/*Disable 100M/1G EEE advertise ability */
phyData = 0;
if ((ret = phy_common_general_reg_mmd_set(unit, port, PHY_MMD_VEND2, 0xa5d0, phyData)) != RT_ERR_OK)
return ret;
/*Disable 1G lite ability */
if ((ret = phy_common_general_reg_mmd_get(unit, port, PHY_MMD_VEND2, 0xa428, &phyData)) != RT_ERR_OK)
return ret;
phyData &= ~(BIT_9);
if ((ret = phy_common_general_reg_mmd_set(unit, port, PHY_MMD_VEND2, 0xa428, phyData)) != RT_ERR_OK)
return ret;
if (PHYPATCH_IS_RTKSDS(unit))
{
PR_INFO("\nDisable SerDes AN with Realtek switch");
/* Disable USXGMII AN */
if ((ret = phy_8224_sdsReg_get(unit, port, 7, 17, &phyData)) != RT_ERR_OK)
return ret;
phyData &= ~(uint32)(0xf);
if ((ret = phy_8224_sdsReg_set(unit, port, 7, 17, phyData)) != RT_ERR_OK)
return ret;
}
if (!PHYPATCH_IS_RTKSDS(unit))
{
if ((ret = phy_8224_sdsOpCode_set(unit, port, 0x3)) != RT_ERR_OK)
return ret;
if ((ret = phy_8224_sdsAmPeriod_set(unit, port, 0xa4)) != RT_ERR_OK)
return ret;
}
else
{
PR_INFO("\nConnect to Realtek switch");
if ((ret = phy_8224_sdsOpCode_set(unit, port, 0xaa)) != RT_ERR_OK)
return ret;
if ((ret = phy_8224_sdsAmPeriod_set(unit, port, 0x5078)) != RT_ERR_OK)
return ret;
if ((ret = phy_8224_sdsAllAm_set(unit, port, 0x0)) != RT_ERR_OK)
return ret;
if ((ret = phy_8224_sdsExtra_set(unit, port)) != RT_ERR_OK)
return ret;
}
return ret;
}

View file

@ -0,0 +1,128 @@
/*
* Copyright (C) 2009-2022 Realtek Semiconductor Corp.
* All Rights Reserved.
*
* This program is the proprietary software of Realtek Semiconductor
* Corporation and/or its licensors, and only be used, duplicated,
* modified or distributed under the authorized license from Realtek.
*
* ANY USE OF THE SOFTWARE OTHER THAN AS AUTHORIZED UNDER
* THIS LICENSE OR COPYRIGHT LAW IS PROHIBITED.
*
* $Revision: $
* $Date: $
*
* Purpose : PHY 8224 HW patch APIs.
*
* Feature : PHY 8224 HW patch APIs
*
*/
#ifndef __HAL_PHY_PHY_RTL8224_PATCH_H__
#define __HAL_PHY_PHY_RTL8224_PATCH_H__
/*
* Include Files
*/
#if defined(RTK_PHYDRV_IN_LINUX)
#include "rtk_osal.h"
#include "rtk_phylib_def.h"
#else
#include <common/rt_type.h>
#include <rtk/port.h>
#endif
/* Function Name:
* phy_rtl8224_sdsReg_get
* Description:
* Get SerDes Register
* Input:
* unit - unit id
* baseport - base port id on the PHY chip
* sdsPage - the SerDes page
* sdsReg - the SerDes register
* Output:
* pData - return register value
* Return:
* RT_ERR_OK
* RT_ERR_FAILED
* RT_ERR_NOT_SUPPORTED
* RT_ERR_ABORT
* Note:
* None
*/
extern int32
phy_rtl8224_sdsReg_get(uint32 unit, rtk_port_t baseport, uint32 sdsPage, uint32 sdsReg, uint32 *pData);
/* Function Name:
* phy_rtl8224_sdsReg_set
* Description:
* Set SerDes Register
* Input:
* unit - unit id
* baseport - base port id on the PHY chip
* sdsPage - the SerDes page
* sdsReg - the SerDes register
* wData - the write data
* Output:
* None
* Return:
* RT_ERR_OK
* RT_ERR_FAILED
* RT_ERR_NOT_SUPPORTED
* RT_ERR_ABORT
* Note:
* None
*/
extern int32
phy_rtl8224_sdsReg_set(uint32 unit, rtk_port_t baseport, uint32 sdsPage, uint32 sdsReg, uint32 wData);
/* Function Name:
* phy_rtl8224_patch
* Description:
* Apply patch data to PHY
* Input:
* unit - unit id
* baseport - base port id on the PHY chip
* portOffset - the index offset base on baseport for the port to patch
* Output:
* None
* Return:
* RT_ERR_OK
* RT_ERR_FAILED
* RT_ERR_NOT_SUPPORTED
* RT_ERR_ABORT
* Note:
* None
*/
extern int32
phy_rtl8224_patch(uint32 unit, rtk_port_t baseport, uint8 portOffset);
/* Function Name:
* phy_rtl8224_broadcast_patch
* Description:
* Apply patch data to PHY
* Input:
* unit - unit id
* baseport - base port id on the PHY chip
* portOffset - the index offset base on baseport for the port to patch
* perChip - 1 for per-chip mode, 0 for per-bus mode
* Output:
* None
* Return:
* RT_ERR_OK
* RT_ERR_FAILED
* RT_ERR_NOT_SUPPORTED
* RT_ERR_ABORT
* Note:
* None
*/
extern int32
phy_rtl8224_broadcast_patch(uint32 unit, uint8 port, uint8 portOffset, uint8 perChip);
#endif /* __HAL_PHY_PHY_RTL8224_PATCH_H__ */

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,63 @@
/*
* SPDX-License-Identifier: GPL-2.0-only
*
* Copyright (c) 2023 Realtek Semiconductor Corp. All rights reserved.
*/
#ifndef __HAL_PHY_PHY_RTL826XB_PATCH_H__
#define __HAL_PHY_PHY_RTL826XB_PATCH_H__
/*
* Include Files
*/
#if defined(RTK_PHYDRV_IN_LINUX)
#include "rtk_osal.h"
#include "rtk_phylib_def.h"
#else
#include <common/rt_type.h>
#include <rtk/port.h>
#endif
/* Function Name:
* phy_rtl826xb_patch
* Description:
* apply patch data to PHY
* Input:
* unit - unit id
* baseport - base port id on the PHY chip
* portOffset - the index offset base on baseport for the port to patch
* Output:
* None
* Return:
* RT_ERR_OK
* RT_ERR_FAILED
* RT_ERR_NOT_SUPPORTED
* RT_ERR_ABORT
* Note:
* None
*/
extern int32 phy_rtl826xb_patch(uint32 unit, rtk_port_t baseport, uint8 portOffset);
/* Function Name:
* phy_rtl826xb_broadcast_patch
* Description:
* apply patch data to PHY
* Input:
* unit - unit id
* baseport - base port id on the PHY chip
* portOffset - the index offset base on baseport for the port to patch
* perChip - 1 for per-chip mode, 0 for per-bus mode
* Output:
* None
* Return:
* RT_ERR_OK
* RT_ERR_FAILED
* RT_ERR_NOT_SUPPORTED
* RT_ERR_ABORT
* Note:
* None
*/
extern int32 phy_rtl826xb_broadcast_patch(uint32 unit, rtk_port_t port, uint8 portOffset, uint8 perChip);
extern int32 phy_rtl826xb_patch_db_init(uint32 unit, rtk_port_t port, rt_phy_patch_db_t **pPhy_patchDb);
#endif /* __HAL_PHY_PHY_RTL826XB_PATCH_H__ */

View file

@ -0,0 +1,885 @@
/*
* SPDX-License-Identifier: GPL-2.0-only
*
* Copyright (c) 2023 Realtek Semiconductor Corp. All rights reserved.
*/
#include <linux/version.h>
#include <linux/phy.h>
#include <crypto/aes.h>
#include <net/macsec.h>
#include "rtk_phylib_macsec.h"
#include "rtk_phylib_rtl826xb.h"
#include "rtk_phylib.h"
#include "rtk_phy.h"
static int rtk_macsec_del_txsa(struct macsec_context *ctx);
static int rtk_macsec_del_rxsa(struct macsec_context *ctx);
static int __rtk_macsec_clear_txsc(struct macsec_context *ctx, uint32 sc_id)
{
int32 ret = 0;
RTK_PHYLIB_ERR_CHK(rtk_phylib_macsec_sc_del(ctx->phydev, RTK_MACSEC_DIR_EGRESS, sc_id));
return ret;
}
static int __rtk_macsec_clear_rxsc(struct macsec_context *ctx, uint32 sc_id)
{
int32 ret = 0;
RTK_PHYLIB_ERR_CHK(rtk_phylib_macsec_sc_del(ctx->phydev, RTK_MACSEC_DIR_INGRESS, sc_id));
return ret;
}
static int rtk_macsec_dev_open(struct macsec_context *ctx)
{
#if (LINUX_VERSION_CODE < KERNEL_VERSION(6, 1, 0))
if (ctx->prepare)
return 0;
#endif
return rtk_phylib_macsec_enable_set(ctx->phydev, 1);
}
static int rtk_macsec_dev_stop(struct macsec_context *ctx)
{
#if (LINUX_VERSION_CODE < KERNEL_VERSION(6, 1, 0))
if (ctx->prepare)
return 0;
#endif
return rtk_phylib_macsec_enable_set(ctx->phydev, 0);
}
static int rtk_macsec_add_secy(struct macsec_context *ctx)
{
int32 ret = 0;
struct rtk_phy_priv *priv = ctx->phydev->priv;
rtk_macsec_port_info_t *macsec_db = priv->macsec;
#if (LINUX_VERSION_CODE < KERNEL_VERSION(6, 1, 0))
if (ctx->prepare)
return 0;
#endif
if(MACSEC_SC_IS_USED(macsec_db, RTK_MACSEC_DIR_EGRESS, 0))
{
PR_ERR("[%s]MACSEC_SC_IS_USED\n",__FUNCTION__);
return -EEXIST;
}
/* create TX SC */
{
uint32 sc_id = 0;
rtk_macsec_sc_t sc;
memset(&sc, 0x0, sizeof(rtk_macsec_sc_t));
switch (ctx->secy->key_len)
{
case 16:
sc.tx.cipher_suite = (ctx->secy->xpn) ? RTK_MACSEC_CIPHER_GCM_ASE_XPN_128 : RTK_MACSEC_CIPHER_GCM_ASE_128;
break;
case 32:
sc.tx.cipher_suite = (ctx->secy->xpn) ? RTK_MACSEC_CIPHER_GCM_ASE_XPN_256 : RTK_MACSEC_CIPHER_GCM_ASE_256;
break;
default:
PR_ERR("Not support key_len %d\n", ctx->secy->key_len);
return -EINVAL;
}
RTK_PHYLIB_VAL_TO_BYTE_ARRAY(ctx->secy->sci, 8, sc.tx.sci, 0, 8);
PR_DBG("[%s]secy->sci: 0x%llX\n", __FUNCTION__, ctx->secy->sci);
sc.tx.flow_match = RTK_MACSEC_MATCH_NON_CTRL;
sc.tx.protect_frame = ctx->secy->protect_frames;
sc.tx.include_sci = ctx->secy->tx_sc.send_sci;
sc.tx.use_es = ctx->secy->tx_sc.end_station;
sc.tx.use_scb = ctx->secy->tx_sc.scb;
sc.tx.conf_protect = ctx->secy->tx_sc.encrypt;
RTK_PHYLIB_ERR_CHK(rtk_phylib_macsec_sc_create(ctx->phydev, RTK_MACSEC_DIR_EGRESS, &sc, &sc_id, (ctx->secy->tx_sc.active) ? 1 : 0));
}
return ret;
}
static int rtk_macsec_del_secy(struct macsec_context *ctx)
{
int32 ret = 0;
struct rtk_phy_priv *priv = ctx->phydev->priv;
rtk_macsec_port_info_t *macsec_db = priv->macsec;
uint32 i = 0;
#if (LINUX_VERSION_CODE < KERNEL_VERSION(6, 1, 0))
if (ctx->prepare)
return 0;
#endif
for (i = 0; i < MACSEC_SC_MAX(macsec_db); i++)
{
if(MACSEC_SC_IS_USED(macsec_db, RTK_MACSEC_DIR_EGRESS, i))
{
PR_DBG("[%s] clear TX SC %u\n", __FUNCTION__, i);
RTK_PHYLIB_ERR_CHK(__rtk_macsec_clear_txsc(ctx, i));
}
if(MACSEC_SC_IS_USED(macsec_db, RTK_MACSEC_DIR_INGRESS, i))
{
PR_DBG("[%s] clear RX SC %u\n", __FUNCTION__, i);
RTK_PHYLIB_ERR_CHK(__rtk_macsec_clear_rxsc(ctx, i));
}
}
memset(&(macsec_db->port_stats), 0x0, sizeof(rtk_macsec_sc_t));
return ret;
}
static int rtk_macsec_upd_secy(struct macsec_context *ctx)
{
int32 ret = 0;
struct rtk_phy_priv *priv = ctx->phydev->priv;
rtk_macsec_port_info_t *macsec_db = priv->macsec;
rtk_macsec_cipher_t cipher_suite;
#if (LINUX_VERSION_CODE < KERNEL_VERSION(6, 1, 0))
if (ctx->prepare)
return 0;
#endif
if(MACSEC_SC_IS_CLEAR(macsec_db, RTK_MACSEC_DIR_EGRESS, 0))
{
PR_ERR("[%s]MACSEC_SC_IS_CLEAR\n",__FUNCTION__);
return -ENOENT;
}
/* create TX SC */
{
uint32 sc_id = 0;
rtk_macsec_sc_t sc;
rtk_macsec_sc_t cur_sc;
RTK_PHYLIB_ERR_CHK(rtk_phylib_macsec_sc_get(ctx->phydev, RTK_MACSEC_DIR_EGRESS, 0, &sc));
memcpy(&cur_sc, &sc, sizeof(rtk_macsec_sc_t));
switch (ctx->secy->key_len)
{
case 16:
cipher_suite = (ctx->secy->xpn) ? RTK_MACSEC_CIPHER_GCM_ASE_XPN_128 : RTK_MACSEC_CIPHER_GCM_ASE_128;
break;
case 32:
cipher_suite = (ctx->secy->xpn) ? RTK_MACSEC_CIPHER_GCM_ASE_XPN_256 : RTK_MACSEC_CIPHER_GCM_ASE_256;
break;
default:
PR_ERR("Not support key_len %d\n", ctx->secy->key_len);
return -EINVAL;
}
RTK_PHYLIB_VAL_TO_BYTE_ARRAY(ctx->secy->sci, 8, sc.tx.sci, 0, 8);
#ifdef MACSEC_DBG_PRINT
PR_DBG("[%s]secy->sci: 0x%llX\n", __FUNCTION__, ctx->secy->sci);
PR_DBG("cipher_suite %u => %u\n", sc.tx.cipher_suite, cipher_suite);
PR_DBG("flow_match %u => %u\n", sc.tx.flow_match, RTK_MACSEC_MATCH_NON_CTRL);
PR_DBG("protect_frame %u => %u\n", sc.tx.protect_frame, ctx->secy->protect_frames);
PR_DBG("include_sci %u => %u\n", sc.tx.include_sci, ctx->secy->tx_sc.send_sci);
PR_DBG("use_es %u => %u\n", sc.tx.use_es, ctx->secy->tx_sc.end_station);
PR_DBG("use_scb %u => %u\n", sc.tx.use_scb, ctx->secy->tx_sc.scb);
PR_DBG("conf_protect %u => %u\n", sc.tx.conf_protect, ctx->secy->tx_sc.encrypt);
#endif
sc.tx.cipher_suite = cipher_suite;
sc.tx.flow_match = RTK_MACSEC_MATCH_NON_CTRL;
sc.tx.protect_frame = ctx->secy->protect_frames;
sc.tx.include_sci = ctx->secy->tx_sc.send_sci;
sc.tx.use_es = ctx->secy->tx_sc.end_station;
sc.tx.use_scb = ctx->secy->tx_sc.scb;
sc.tx.conf_protect = ctx->secy->tx_sc.encrypt;
if(memcmp(&cur_sc, &sc, sizeof(rtk_macsec_sc_t)) != 0)
RTK_PHYLIB_ERR_CHK(rtk_phylib_macsec_sc_update(ctx->phydev, RTK_MACSEC_DIR_EGRESS, &sc, &sc_id, (ctx->secy->tx_sc.active) ? 1 : 0));
}
return ret;
}
static int rtk_macsec_add_rxsc(struct macsec_context *ctx)
{
int32 ret = 0;
uint32 sc_id = 0;
rtk_macsec_sc_t sc;
memset(&sc, 0x0, sizeof(rtk_macsec_sc_t));
#if (LINUX_VERSION_CODE < KERNEL_VERSION(6, 1, 0))
if (ctx->prepare)
return 0;
#endif
ret = rtk_phylib_macsec_sci_to_scid(ctx->phydev, RTK_MACSEC_DIR_INGRESS, ctx->rx_sc->sci, &sc_id);
if(ret != RTK_PHYLIB_ERR_ENTRY_NOTFOUND) //sc is existed
{
PR_DBG("[%s] ret:%d sc_id:%d is existed \n", __FUNCTION__, ret, sc_id);
return -EEXIST;
}
PR_DBG("[%s]rx_sc->sci: 0x%llX\n", __FUNCTION__, ctx->rx_sc->sci);
switch (ctx->secy->key_len)
{
case 16:
sc.rx.cipher_suite = (ctx->secy->xpn) ? RTK_MACSEC_CIPHER_GCM_ASE_XPN_128 : RTK_MACSEC_CIPHER_GCM_ASE_128;
break;
case 32:
sc.rx.cipher_suite = (ctx->secy->xpn) ? RTK_MACSEC_CIPHER_GCM_ASE_XPN_256 : RTK_MACSEC_CIPHER_GCM_ASE_256;
break;
default:
PR_ERR("Not support key_len %d\n", ctx->secy->key_len);
return -EINVAL;
}
PR_DBG("[%s]rx_sc->sci: 0x%llX\n", __FUNCTION__, ctx->rx_sc->sci);
RTK_PHYLIB_VAL_TO_BYTE_ARRAY(ctx->rx_sc->sci, 8, sc.rx.sci, 0, 8);
sc.rx.flow_match = RTK_MACSEC_MATCH_SCI;
switch (ctx->secy->validate_frames)
{
case MACSEC_VALIDATE_DISABLED:
sc.rx.validate_frames = RTK_MACSEC_VALIDATE_DISABLE;
break;
case MACSEC_VALIDATE_CHECK:
sc.rx.validate_frames = RTK_MACSEC_VALIDATE_CHECK;
break;
case MACSEC_VALIDATE_STRICT:
sc.rx.validate_frames = RTK_MACSEC_VALIDATE_STRICT;
break;
default:
PR_ERR("Not support validate_frames %d\n", ctx->secy->validate_frames);
return -EINVAL;
}
sc.rx.replay_protect = ctx->secy->replay_protect;
sc.rx.replay_window = ctx->secy->replay_window;
RTK_PHYLIB_ERR_CHK(rtk_phylib_macsec_sc_create(ctx->phydev, RTK_MACSEC_DIR_INGRESS, &sc, &sc_id, (ctx->rx_sc->active) ? 1 : 0));
return ret;
}
static int rtk_macsec_del_rxsc(struct macsec_context *ctx)
{
int32 ret = 0;
uint32 sc_id = 0;
#if (LINUX_VERSION_CODE < KERNEL_VERSION(6, 1, 0))
if (ctx->prepare)
return 0;
#endif
RTK_PHYLIB_ERR_CHK(rtk_phylib_macsec_sci_to_scid(ctx->phydev, RTK_MACSEC_DIR_INGRESS, ctx->rx_sc->sci, &sc_id));
RTK_PHYLIB_ERR_CHK(__rtk_macsec_clear_rxsc(ctx, sc_id));
return ret;
}
static int rtk_macsec_upd_rxsc(struct macsec_context *ctx)
{
int32 ret = 0;
uint32 sc_id = 0;
rtk_macsec_sc_t sc;
rtk_macsec_sc_t cur_sc;
#if (LINUX_VERSION_CODE < KERNEL_VERSION(6, 1, 0))
if (ctx->prepare)
return 0;
#endif
ret = rtk_phylib_macsec_sci_to_scid(ctx->phydev, RTK_MACSEC_DIR_INGRESS, ctx->rx_sc->sci, &sc_id);
if(ret == RTK_PHYLIB_ERR_ENTRY_NOTFOUND) //sc is not existed
{
PR_DBG("[%s] ret:%d sc_id:%d is not existed \n", __FUNCTION__, ret, sc_id);
return -ENOENT;
}
PR_DBG("[%s]rx_sc->sci: 0x%llX\n", __FUNCTION__, ctx->rx_sc->sci);
RTK_PHYLIB_ERR_CHK(rtk_phylib_macsec_sc_get(ctx->phydev, RTK_MACSEC_DIR_INGRESS, sc_id, &sc));
memcpy(&cur_sc, &sc, sizeof(rtk_macsec_sc_t));
switch (ctx->secy->key_len)
{
case 16:
sc.rx.cipher_suite = (ctx->secy->xpn) ? RTK_MACSEC_CIPHER_GCM_ASE_XPN_128 : RTK_MACSEC_CIPHER_GCM_ASE_128;
break;
case 32:
sc.rx.cipher_suite = (ctx->secy->xpn) ? RTK_MACSEC_CIPHER_GCM_ASE_XPN_256 : RTK_MACSEC_CIPHER_GCM_ASE_256;
break;
default:
PR_ERR("Not support key_len %d\n", ctx->secy->key_len);
return -EINVAL;
}
PR_DBG("[%s]rx_sc->sci: 0x%llX\n", __FUNCTION__, ctx->rx_sc->sci);
RTK_PHYLIB_VAL_TO_BYTE_ARRAY(ctx->rx_sc->sci, 8, sc.rx.sci, 0, 8);
sc.rx.flow_match = RTK_MACSEC_MATCH_SCI;
switch (ctx->secy->validate_frames)
{
case MACSEC_VALIDATE_DISABLED:
sc.rx.validate_frames = RTK_MACSEC_VALIDATE_DISABLE;
break;
case MACSEC_VALIDATE_CHECK:
sc.rx.validate_frames = RTK_MACSEC_VALIDATE_CHECK;
break;
case MACSEC_VALIDATE_STRICT:
sc.rx.validate_frames = RTK_MACSEC_VALIDATE_STRICT;
break;
default:
PR_ERR("Not support validate_frames %d\n", ctx->secy->validate_frames);
return -EINVAL;
}
sc.rx.replay_protect = ctx->secy->replay_protect;
sc.rx.replay_window = ctx->secy->replay_window;
if(memcmp(&cur_sc, &sc, sizeof(rtk_macsec_sc_t)) != 0)
RTK_PHYLIB_ERR_CHK(rtk_phylib_macsec_sc_update(ctx->phydev, RTK_MACSEC_DIR_INGRESS, &sc, &sc_id, (ctx->rx_sc->active) ? 1 : 0));
return ret;
}
static int __rtk_macsec_set_rxsa(struct macsec_context *ctx, bool update)
{
int32 ret = 0;
uint32 sc_id = 0;
rtk_macsec_sa_t sa;
memset(&sa, 0x0, sizeof(rtk_macsec_sa_t));
RTK_PHYLIB_ERR_CHK(rtk_phylib_macsec_sci_to_scid(ctx->phydev, RTK_MACSEC_DIR_INGRESS, ctx->sa.rx_sa->sc->sci, &sc_id));
if (update)
{
RTK_PHYLIB_ERR_CHK(rtk_phylib_macsec_sa_get(ctx->phydev, RTK_MACSEC_DIR_INGRESS, sc_id, ctx->sa.assoc_num, &sa));
}
else
{
sa.key_bytes = ctx->secy->key_len;
memcpy(sa.key, ctx->sa.key, sa.key_bytes);
if(ctx->secy->xpn)
{
memcpy(sa.salt, ctx->sa.rx_sa->key.salt.bytes, MACSEC_SALT_LEN);
RTK_PHYLIB_VAL_TO_BYTE_ARRAY(ctx->sa.rx_sa->ssci, 4, sa.ssci, 0, 4);
}
}
sa.pn = ctx->sa.tx_sa->next_pn_halves.lower;
sa.pn_h = ctx->sa.tx_sa->next_pn_halves.upper;
#ifdef MACSEC_DBG_PRINT
{
PR_DBG("[%s,%d] update:%u \n", __FUNCTION__, __LINE__, update);
PR_DBG(" KEY 0-15 = 0x%02X%02X%02X%02X%02X%02X%02X%02X %02X%02X%02X%02X%02X%02X%02X%02X\n",
sa.key[0], sa.key[1], sa.key[2], sa.key[3],
sa.key[4], sa.key[5], sa.key[6], sa.key[7],
sa.key[8], sa.key[9], sa.key[10],sa.key[11],
sa.key[12],sa.key[13],sa.key[14],sa.key[15]);
if (ctx->secy->key_len == 32)
{
PR_DBG(" KEY16-31 = 0x%02X%02X%02X%02X%02X%02X%02X%02X %02X%02X%02X%02X%02X%02X%02X%02X\n",
sa.key[16],sa.key[17],sa.key[18],sa.key[19],
sa.key[20],sa.key[21],sa.key[22],sa.key[23],
sa.key[24],sa.key[25],sa.key[26],sa.key[27],
sa.key[28],sa.key[29],sa.key[30],sa.key[31]);
}
PR_DBG(" ctx->sa.rx_sa->active: %u\n", ctx->sa.rx_sa->active);
PR_DBG(" ctx->secy->xpn: %u\n", ctx->secy->xpn);
PR_DBG(" sa.pn: 0x%X, sa.pn_h: 0x%X\n", sa.pn, sa.pn_h);
if(ctx->secy->xpn)
{
PR_DBG(" ctx->sa.tx_sa->key.salt = 0x%02X%02X%02X%02X %02X%02X%02X%02X %02X%02X%02X%02X\n",
sa.salt[0], sa.salt[1], sa.salt[2], sa.salt[3],
sa.salt[4], sa.salt[5], sa.salt[6], sa.salt[7],
sa.salt[8], sa.salt[9], sa.salt[10], sa.salt[11]);
}
}
#endif
RTK_PHYLIB_ERR_CHK(rtk_phylib_macsec_sa_create(ctx->phydev, RTK_MACSEC_DIR_INGRESS, sc_id, ctx->sa.assoc_num, &sa));
if (ctx->sa.rx_sa->active)
{
RTK_PHYLIB_ERR_CHK(rtk_phylib_macsec_sa_activate(ctx->phydev, RTK_MACSEC_DIR_INGRESS, sc_id, ctx->sa.assoc_num));
}
else
{
RTK_PHYLIB_ERR_CHK(rtk_phylib_macsec_rxsa_disable(ctx->phydev, sc_id, ctx->sa.assoc_num));
}
return ret;
}
static int rtk_macsec_add_rxsa(struct macsec_context *ctx)
{
int32 ret = 0;
uint32 sc_id = 0, sa_id = 0;
struct rtk_phy_priv *priv = ctx->phydev->priv;
rtk_macsec_port_info_t *macsec_db = priv->macsec;
#if (LINUX_VERSION_CODE < KERNEL_VERSION(6, 1, 0))
if (ctx->prepare)
return 0;
#endif
RTK_PHYLIB_ERR_CHK(__rtk_macsec_set_rxsa(ctx, false));
RTK_PHYLIB_ERR_CHK(rtk_phylib_macsec_sci_to_scid(ctx->phydev, RTK_MACSEC_DIR_INGRESS, ctx->sa.rx_sa->sc->sci, &sc_id));
sa_id = PHY_MACSEC_HW_SA_ID(sc_id, ctx->sa.assoc_num);
macsec_db->rxsa_stats[sa_id] = kzalloc(sizeof(rtk_macsec_rxsa_stats_t), GFP_KERNEL);
if (macsec_db->rxsa_stats[sa_id] == NULL)
{
rtk_macsec_del_rxsa(ctx);
return -ENOMEM;
}
return ret;
}
static int rtk_macsec_del_rxsa(struct macsec_context *ctx)
{
int32 ret = 0;
uint32 sc_id = 0, sa_id = 0;
struct rtk_phy_priv *priv = ctx->phydev->priv;
rtk_macsec_port_info_t *macsec_db = priv->macsec;
#if (LINUX_VERSION_CODE < KERNEL_VERSION(6, 1, 0))
if (ctx->prepare)
return 0;
#endif
RTK_PHYLIB_ERR_CHK(rtk_phylib_macsec_sci_to_scid(ctx->phydev, RTK_MACSEC_DIR_INGRESS, ctx->sa.rx_sa->sc->sci, &sc_id));
RTK_PHYLIB_ERR_CHK(rtk_phylib_macsec_sa_del(ctx->phydev, RTK_MACSEC_DIR_INGRESS, sc_id, ctx->sa.assoc_num));
sa_id = PHY_MACSEC_HW_SA_ID(sc_id, ctx->sa.assoc_num);
if (macsec_db->rxsa_stats[sa_id] != NULL)
{
kfree(macsec_db->rxsa_stats[sa_id]);
macsec_db->rxsa_stats[sa_id] = NULL;
}
return ret;
}
static int rtk_macsec_upd_rxsa(struct macsec_context *ctx)
{
int32 ret = 0;
#if (LINUX_VERSION_CODE < KERNEL_VERSION(6, 1, 0))
if (ctx->prepare)
return 0;
#endif
RTK_PHYLIB_ERR_CHK(__rtk_macsec_set_rxsa(ctx, true));
return ret;
}
static int __rtk_macsec_set_txsa(struct macsec_context *ctx, bool update)
{
int32 ret = 0;
uint32 sc_id = 0;
rtk_macsec_sa_t sa;
memset(&sa, 0x0, sizeof(rtk_macsec_sa_t));
RTK_PHYLIB_ERR_CHK(rtk_phylib_macsec_sci_to_scid(ctx->phydev, RTK_MACSEC_DIR_EGRESS, ctx->secy->sci, &sc_id));
if (update)
{
RTK_PHYLIB_ERR_CHK(rtk_phylib_macsec_sa_get(ctx->phydev, RTK_MACSEC_DIR_EGRESS, sc_id, ctx->sa.assoc_num, &sa));
}
else
{
sa.key_bytes = ctx->secy->key_len;
memcpy(sa.key, ctx->sa.key, sa.key_bytes);
if(ctx->secy->xpn)
{
memcpy(sa.salt, ctx->sa.tx_sa->key.salt.bytes, MACSEC_SALT_LEN);
RTK_PHYLIB_VAL_TO_BYTE_ARRAY(ctx->sa.tx_sa->ssci, 4, sa.ssci, 0, 4);
}
}
sa.pn = ctx->sa.tx_sa->next_pn_halves.lower;
sa.pn_h = ctx->sa.tx_sa->next_pn_halves.upper;
#ifdef MACSEC_DBG_PRINT
{
PR_DBG("[%s,%d] update:%u \n", __FUNCTION__, __LINE__, update);
PR_DBG(" KEY 0-15 = 0x%02X%02X%02X%02X%02X%02X%02X%02X %02X%02X%02X%02X%02X%02X%02X%02X\n",
sa.key[0], sa.key[1], sa.key[2], sa.key[3],
sa.key[4], sa.key[5], sa.key[6], sa.key[7],
sa.key[8], sa.key[9], sa.key[10],sa.key[11],
sa.key[12],sa.key[13],sa.key[14],sa.key[15]);
if (ctx->secy->key_len == 32)
{
PR_DBG(" KEY16-31 = 0x%02X%02X%02X%02X%02X%02X%02X%02X %02X%02X%02X%02X%02X%02X%02X%02X\n",
sa.key[16],sa.key[17],sa.key[18],sa.key[19],
sa.key[20],sa.key[21],sa.key[22],sa.key[23],
sa.key[24],sa.key[25],sa.key[26],sa.key[27],
sa.key[28],sa.key[29],sa.key[30],sa.key[31]);
}
PR_DBG(" ctx->sa.tx_sa->active: %u\n", ctx->sa.tx_sa->active);
PR_DBG(" ctx->secy->xpn: %u\n", ctx->secy->xpn);
PR_DBG(" sa.pn: 0x%X, sa.pn_h: 0x%X\n", sa.pn, sa.pn_h);
if(ctx->secy->xpn)
{
PR_DBG(" ctx->sa.tx_sa->key.salt = 0x%02X%02X%02X%02X %02X%02X%02X%02X %02X%02X%02X%02X\n",
sa.salt[0], sa.salt[1], sa.salt[2], sa.salt[3],
sa.salt[4], sa.salt[5], sa.salt[6], sa.salt[7],
sa.salt[8], sa.salt[9], sa.salt[10], sa.salt[11]);
}
}
#endif
RTK_PHYLIB_ERR_CHK(rtk_phylib_macsec_sa_create(ctx->phydev, RTK_MACSEC_DIR_EGRESS, sc_id, ctx->sa.assoc_num, &sa));
if (ctx->sa.tx_sa->active)
{
RTK_PHYLIB_ERR_CHK(rtk_phylib_macsec_sa_activate(ctx->phydev, RTK_MACSEC_DIR_EGRESS, sc_id, ctx->sa.assoc_num));
}
else
{
RTK_PHYLIB_ERR_CHK(rtk_phylib_macsec_txsa_disable(ctx->phydev, sc_id));
}
return ret;
}
static int rtk_macsec_add_txsa(struct macsec_context *ctx)
{
int32 ret = 0;
uint32 sc_id = 0, sa_id = 0;
struct rtk_phy_priv *priv = ctx->phydev->priv;
rtk_macsec_port_info_t *macsec_db = priv->macsec;
#if (LINUX_VERSION_CODE < KERNEL_VERSION(6, 1, 0))
if (ctx->prepare)
return 0;
#endif
RTK_PHYLIB_ERR_CHK(__rtk_macsec_set_txsa(ctx, false));
RTK_PHYLIB_ERR_CHK(rtk_phylib_macsec_sci_to_scid(ctx->phydev, RTK_MACSEC_DIR_EGRESS, ctx->secy->sci, &sc_id));
sa_id = PHY_MACSEC_HW_SA_ID(sc_id, ctx->sa.assoc_num);
macsec_db->txsa_stats[sa_id] = kzalloc(sizeof(rtk_macsec_txsa_stats_t), GFP_KERNEL);
if (macsec_db->txsa_stats[sa_id] == NULL)
{
rtk_macsec_del_txsa(ctx);
return -ENOMEM;
}
return ret;
}
static int rtk_macsec_del_txsa(struct macsec_context *ctx)
{
int32 ret = 0;
uint32 sc_id = 0, sa_id = 0;
struct rtk_phy_priv *priv = ctx->phydev->priv;
rtk_macsec_port_info_t *macsec_db = priv->macsec;
#if (LINUX_VERSION_CODE < KERNEL_VERSION(6, 1, 0))
if (ctx->prepare)
return 0;
#endif
RTK_PHYLIB_ERR_CHK(rtk_phylib_macsec_sci_to_scid(ctx->phydev, RTK_MACSEC_DIR_EGRESS, ctx->secy->sci, &sc_id));
RTK_PHYLIB_ERR_CHK(rtk_phylib_macsec_sa_del(ctx->phydev, RTK_MACSEC_DIR_EGRESS, sc_id, ctx->sa.assoc_num));
sa_id = PHY_MACSEC_HW_SA_ID(sc_id, ctx->sa.assoc_num);
if (macsec_db->txsa_stats[sa_id] != NULL)
{
kfree(macsec_db->txsa_stats[sa_id]);
macsec_db->txsa_stats[sa_id] = NULL;
}
return ret;
}
static int rtk_macsec_upd_txsa(struct macsec_context *ctx)
{
int32 ret = 0;
#if (LINUX_VERSION_CODE < KERNEL_VERSION(6, 1, 0))
if (ctx->prepare)
return 0;
#endif
RTK_PHYLIB_ERR_CHK(__rtk_macsec_set_txsa(ctx, true));
return ret;
}
static int rtk_macsec_get_dev_stats(struct macsec_context *ctx)
{
int32 ret = 0;
uint64 cnt = 0;
uint32 sc_id = 0, an = 0;
#if (LINUX_VERSION_CODE < KERNEL_VERSION(6, 1, 0))
if (ctx->prepare)
return 0;
#endif
RTK_PHYLIB_ERR_CHK(rtk_phylib_macsec_stat_port_get(ctx->phydev, RTK_MACSEC_STAT_OutPktsUntagged, &cnt));
ctx->stats.dev_stats->OutPktsUntagged = cnt;
RTK_PHYLIB_ERR_CHK(rtk_phylib_macsec_stat_port_get(ctx->phydev, RTK_MACSEC_STAT_InPktsUntagged, &cnt));
ctx->stats.dev_stats->InPktsUntagged = cnt;
RTK_PHYLIB_ERR_CHK(rtk_phylib_macsec_stat_port_get(ctx->phydev, RTK_MACSEC_STAT_InPktsNoTag, &cnt));
ctx->stats.dev_stats->InPktsNoTag = cnt;
RTK_PHYLIB_ERR_CHK(rtk_phylib_macsec_stat_port_get(ctx->phydev, RTK_MACSEC_STAT_InPktsBadTag, &cnt));
ctx->stats.dev_stats->InPktsBadTag = cnt;
RTK_PHYLIB_ERR_CHK(rtk_phylib_macsec_stat_port_get(ctx->phydev, RTK_MACSEC_STAT_InPktsUnknownSCI, &cnt));
ctx->stats.dev_stats->InPktsUnknownSCI = cnt;
RTK_PHYLIB_ERR_CHK(rtk_phylib_macsec_stat_port_get(ctx->phydev, RTK_MACSEC_STAT_InPktsNoSCI, &cnt));
ctx->stats.dev_stats->InPktsNoSCI = cnt;
ctx->stats.dev_stats->InPktsOverrun = 0; /* not support */
/* accumulate over each egress SA */
RTK_PHYLIB_ERR_CHK(rtk_phylib_macsec_sci_to_scid(ctx->phydev, RTK_MACSEC_DIR_EGRESS, ctx->secy->sci, &sc_id));
for (an = 0; an < MACSEC_NUM_AN; an++)
{
if (0 == rtk_phylib_macsec_stat_txsa_get(ctx->phydev, sc_id, an, RTK_MACSEC_TXSA_STAT_OutPktsTooLong, &cnt))
{
ctx->stats.dev_stats->OutPktsTooLong += cnt;
}
}
return ret;
}
static int rtk_macsec_get_txsc_stats(struct macsec_context *ctx)
{
int32 ret = 0;
uint64 cnt = 0;
uint32 sc_id = 0, an = 0;
#if (LINUX_VERSION_CODE < KERNEL_VERSION(6, 1, 0))
if (ctx->prepare)
return 0;
#endif
RTK_PHYLIB_ERR_CHK(rtk_phylib_macsec_sci_to_scid(ctx->phydev, RTK_MACSEC_DIR_EGRESS, ctx->secy->sci, &sc_id));
/* accumulate over each egress SA */
for (an = 0; an < MACSEC_NUM_AN; an++)
{
if (0 == rtk_phylib_macsec_stat_txsa_get(ctx->phydev, sc_id, an, RTK_MACSEC_TXSA_STAT_OutPktsProtectedEncrypted, &cnt))
{
if (ctx->secy->tx_sc.encrypt)
{
ctx->stats.tx_sc_stats->OutPktsEncrypted += cnt;
}
else
{
ctx->stats.tx_sc_stats->OutPktsProtected += cnt;
}
}
if (0 == rtk_phylib_macsec_stat_txsa_get(ctx->phydev, sc_id, an, RTK_MACSEC_TXSA_STAT_OutOctetsProtectedEncrypted, &cnt))
{
if (ctx->secy->tx_sc.encrypt)
{
ctx->stats.tx_sc_stats->OutOctetsEncrypted += cnt;
}
else
{
ctx->stats.tx_sc_stats->OutOctetsProtected += cnt;
}
}
}
return ret;
}
static int rtk_macsec_get_rxsc_stats(struct macsec_context *ctx)
{
int32 ret = 0;
uint64 cnt = 0;
uint32 sc_id = 0, an = 0;
#if (LINUX_VERSION_CODE < KERNEL_VERSION(6, 1, 0))
if (ctx->prepare)
return 0;
#endif
RTK_PHYLIB_ERR_CHK(rtk_phylib_macsec_sci_to_scid(ctx->phydev, RTK_MACSEC_DIR_INGRESS, ctx->rx_sc->sci, &sc_id));
/* accumulate over each ingress SA */
for (an = 0; an < MACSEC_NUM_AN; an++)
{
if (0 == rtk_phylib_macsec_stat_rxsa_get(ctx->phydev, sc_id, an, RTK_MACSEC_RXSA_STAT_InOctetsDecryptedValidated, &cnt))
{
ctx->stats.rx_sc_stats->InOctetsValidated += cnt;
ctx->stats.rx_sc_stats->InOctetsDecrypted = ctx->stats.rx_sc_stats->InOctetsValidated;
}
if (0 == rtk_phylib_macsec_stat_rxsa_get(ctx->phydev, sc_id, an, RTK_MACSEC_RXSA_STAT_InPktsUnchecked, &cnt))
{
ctx->stats.rx_sc_stats->InPktsUnchecked += cnt;
}
if (0 == rtk_phylib_macsec_stat_rxsa_get(ctx->phydev, sc_id, an, RTK_MACSEC_RXSA_STAT_InPktsDelayed, &cnt))
{
ctx->stats.rx_sc_stats->InPktsDelayed += cnt;
}
if (0 == rtk_phylib_macsec_stat_rxsa_get(ctx->phydev, sc_id, an, RTK_MACSEC_RXSA_STAT_InPktsOK, &cnt))
{
ctx->stats.rx_sc_stats->InPktsOK += cnt;
}
if (0 == rtk_phylib_macsec_stat_rxsa_get(ctx->phydev, sc_id, an, RTK_MACSEC_RXSA_STAT_InPktsInvalid, &cnt))
{
ctx->stats.rx_sc_stats->InPktsInvalid += cnt;
}
if (0 == rtk_phylib_macsec_stat_rxsa_get(ctx->phydev, sc_id, an, RTK_MACSEC_RXSA_STAT_InPktsLate, &cnt))
{
ctx->stats.rx_sc_stats->InPktsLate += cnt;
}
if (0 == rtk_phylib_macsec_stat_rxsa_get(ctx->phydev, sc_id, an, RTK_MACSEC_RXSA_STAT_InPktsNotValid, &cnt))
{
ctx->stats.rx_sc_stats->InPktsNotValid += cnt;
}
if (0 == rtk_phylib_macsec_stat_rxsa_get(ctx->phydev, sc_id, an, RTK_MACSEC_RXSA_STAT_InPktsNotUsingSA, &cnt))
{
ctx->stats.rx_sc_stats->InPktsNotUsingSA += cnt;
}
if (0 == rtk_phylib_macsec_stat_rxsa_get(ctx->phydev, sc_id, an, RTK_MACSEC_RXSA_STAT_InPktsUnusedSA, &cnt))
{
ctx->stats.rx_sc_stats->InPktsUnusedSA += cnt;
}
}
return ret;
}
static int rtk_macsec_get_txsa_stats(struct macsec_context *ctx)
{
int32 ret = 0;
uint64 cnt = 0;
uint32 sc_id = 0;
rtk_macsec_sa_t sa;
#if (LINUX_VERSION_CODE < KERNEL_VERSION(6, 1, 0))
if (ctx->prepare)
return 0;
#endif
RTK_PHYLIB_ERR_CHK(rtk_phylib_macsec_sci_to_scid(ctx->phydev, RTK_MACSEC_DIR_EGRESS, ctx->secy->sci, &sc_id));
RTK_PHYLIB_ERR_CHK(rtk_phylib_macsec_stat_txsa_get(ctx->phydev, sc_id, ctx->sa.assoc_num, RTK_MACSEC_TXSA_STAT_OutPktsProtectedEncrypted, &cnt));
if (ctx->secy->tx_sc.encrypt)
{
ctx->stats.tx_sa_stats->OutPktsEncrypted = (uint32)cnt;
}
else
{
ctx->stats.tx_sa_stats->OutPktsProtected = (uint32)cnt;
}
RTK_PHYLIB_ERR_CHK(rtk_phylib_macsec_sa_get(ctx->phydev, RTK_MACSEC_DIR_EGRESS, sc_id, ctx->sa.assoc_num, &sa));
ctx->sa.tx_sa->next_pn_halves.lower = sa.pn;
ctx->sa.tx_sa->next_pn_halves.upper = sa.pn_h;
return ret;
}
static int rtk_macsec_get_rxsa_stats(struct macsec_context *ctx)
{
int32 ret = 0;
uint64 cnt = 0;
uint32 sc_id = 0;
rtk_macsec_sa_t sa;
#if (LINUX_VERSION_CODE < KERNEL_VERSION(6, 1, 0))
if (ctx->prepare)
return 0;
#endif
RTK_PHYLIB_ERR_CHK(rtk_phylib_macsec_sci_to_scid(ctx->phydev, RTK_MACSEC_DIR_INGRESS, ctx->rx_sc->sci, &sc_id));
RTK_PHYLIB_ERR_CHK(rtk_phylib_macsec_stat_rxsa_get(ctx->phydev, sc_id, ctx->sa.assoc_num, RTK_MACSEC_RXSA_STAT_InPktsOK, &cnt));
ctx->stats.rx_sa_stats->InPktsOK = (uint32)cnt;
RTK_PHYLIB_ERR_CHK(rtk_phylib_macsec_stat_rxsa_get(ctx->phydev, sc_id, ctx->sa.assoc_num, RTK_MACSEC_RXSA_STAT_InPktsInvalid, &cnt));
ctx->stats.rx_sa_stats->InPktsInvalid = (uint32)cnt;
RTK_PHYLIB_ERR_CHK(rtk_phylib_macsec_stat_rxsa_get(ctx->phydev, sc_id, ctx->sa.assoc_num, RTK_MACSEC_RXSA_STAT_InPktsNotValid, &cnt));
ctx->stats.rx_sa_stats->InPktsNotValid = (uint32)cnt;
RTK_PHYLIB_ERR_CHK(rtk_phylib_macsec_stat_rxsa_get(ctx->phydev, sc_id, ctx->sa.assoc_num, RTK_MACSEC_RXSA_STAT_InPktsNotUsingSA, &cnt));
ctx->stats.rx_sa_stats->InPktsNotUsingSA = (uint32)cnt;
RTK_PHYLIB_ERR_CHK(rtk_phylib_macsec_stat_rxsa_get(ctx->phydev, sc_id, ctx->sa.assoc_num, RTK_MACSEC_RXSA_STAT_InPktsUnusedSA, &cnt));
ctx->stats.rx_sa_stats->InPktsUnusedSA = (uint32)cnt;
RTK_PHYLIB_ERR_CHK(rtk_phylib_macsec_sa_get(ctx->phydev, RTK_MACSEC_DIR_INGRESS, sc_id, ctx->sa.assoc_num, &sa));
ctx->sa.rx_sa->next_pn_halves.lower = sa.pn;
ctx->sa.rx_sa->next_pn_halves.upper = sa.pn_h;
return ret;
}
static const struct macsec_ops rtk_macsec_ops = {
/* Device wide */
.mdo_dev_open = rtk_macsec_dev_open,
.mdo_dev_stop = rtk_macsec_dev_stop,
/* SecY */
.mdo_add_secy = rtk_macsec_add_secy,
.mdo_upd_secy = rtk_macsec_upd_secy,
.mdo_del_secy = rtk_macsec_del_secy,
/* Security channels */
.mdo_add_rxsc = rtk_macsec_add_rxsc,
.mdo_upd_rxsc = rtk_macsec_upd_rxsc,
.mdo_del_rxsc = rtk_macsec_del_rxsc,
/* Security associations */
.mdo_add_rxsa = rtk_macsec_add_rxsa,
.mdo_upd_rxsa = rtk_macsec_upd_rxsa,
.mdo_del_rxsa = rtk_macsec_del_rxsa,
.mdo_add_txsa = rtk_macsec_add_txsa,
.mdo_upd_txsa = rtk_macsec_upd_txsa,
.mdo_del_txsa = rtk_macsec_del_txsa,
/* Statistics */
.mdo_get_dev_stats = rtk_macsec_get_dev_stats,
.mdo_get_tx_sc_stats = rtk_macsec_get_txsc_stats,
.mdo_get_rx_sc_stats = rtk_macsec_get_rxsc_stats,
.mdo_get_tx_sa_stats = rtk_macsec_get_txsa_stats,
.mdo_get_rx_sa_stats = rtk_macsec_get_rxsa_stats,
};
int rtk_macsec_init(struct phy_device *phydev)
{
int32 ret = 0;
struct rtk_phy_priv *priv = phydev->priv;
priv->macsec = kzalloc(sizeof(*(priv->macsec)), GFP_KERNEL);
if (!priv->macsec)
return -ENOMEM;
memset(priv->macsec, 0, sizeof(*(priv->macsec)));
switch (phydev->drv->phy_id)
{
case REALTEK_PHY_ID_RTL8261N:
case REALTEK_PHY_ID_RTL8264B:
phydev->macsec_ops = &rtk_macsec_ops;
priv->macsec->max_sa_num = 64;
priv->macsec->max_sc_num = 64/4;
ret = rtk_phylib_826xb_macsec_init(phydev);
if (ret != 0)
{
phydev_err(phydev, "[%s]rtk_phylib_826xb_macsec_init failed!! 0x%X\n", __FUNCTION__, ret);
return ret;
}
break;
default:
PR_ERR("[%s]phy_id: 0x%X not support!\n", __FUNCTION__, phydev->drv->phy_id);
kfree(priv->macsec);
priv->macsec = NULL;
return -EOPNOTSUPP;
}
RTK_PHYLIB_ERR_CHK(rtk_phylib_macsec_init(phydev));
return 0;
}

View file

@ -0,0 +1,58 @@
/*
* SPDX-License-Identifier: GPL-2.0-only
*
* Copyright (c) 2023 Realtek Semiconductor Corp. All rights reserved.
*/
#include "type.h"
#include "error.h"
#include "rtk_phylib_def.h"
#include <linux/version.h>
#include <linux/jiffies.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/sched.h>
#include <linux/wait.h>
#include <linux/sched/signal.h>
#include <linux/phy.h>
#ifndef CONFIG_OPENWRT_SDK
int32
osal_time_usecs_get(osal_usecs_t *pUsec)
{
struct timespec64 ts;
RT_PARAM_CHK((NULL == pUsec), RT_ERR_NULL_POINTER);
ktime_get_ts64(&ts);
*pUsec = (osal_usecs_t)((ts.tv_sec * USEC_PER_SEC) + (ts.tv_nsec / NSEC_PER_USEC));
return RT_ERR_OK;
}
void *
osal_alloc(uint32 size)
{
void *p;
p = kmalloc((size_t)size, GFP_ATOMIC);
return p;
}
#endif
int32
phy_common_general_reg_mmd_get(uint32 unit, rtk_port_t port, uint32 mmdAddr, uint32 mmdReg, uint32 *pData)
{
int32 rData = 0;
rData = phy_read_mmd(port, mmdAddr, mmdReg);
if (rData < 0)
return RT_ERR_FAILED;
*pData = (uint32)rData;
return RT_ERR_OK;
}
int32
phy_common_general_reg_mmd_set(uint32 unit, rtk_port_t port, uint32 mmdAddr, uint32 mmdReg, uint32 data)
{
int ret = phy_write_mmd(port, mmdAddr, mmdReg, data);
return (ret < 0) ? RT_ERR_FAILED : RT_ERR_OK;
}

View file

@ -0,0 +1,100 @@
/*
* SPDX-License-Identifier: GPL-2.0-only
*
* Copyright (c) 2023 Realtek Semiconductor Corp. All rights reserved.
*/
#ifndef __RTK_PHY_OSAL_H
#define __RTK_PHY_OSAL_H
#include <linux/kernel.h>
#include <linux/phy.h>
#include "type.h"
#include "error.h"
#include "phy_patch.h"
#include "rtk_phylib.h"
#ifdef PHYPATCH_DB_GET
#undef PHYPATCH_DB_GET
#endif
#define PHYPATCH_DB_GET(_unit, _pPhy_device, _pPatchDb) \
do { \
struct rtk_phy_priv *_pPriv = (_pPhy_device)->priv; \
rt_phy_patch_db_t *_pDb = _pPriv->patch; _pPatchDb = _pDb; \
/*printk("[PHYPATCH_DB_GET] ? [%s]\n", (_pDb != NULL) ? "E":"N");*/ \
} while(0)
#define HWP_9300_FAMILY_ID(_unit) 1
#define HWP_9310_FAMILY_ID(_unit) 0
#define RTK_9300_FAMILY_ID(_unit) 1
#define RTK_9310_FAMILY_ID(_unit) 0
#define RTK_9311B_FAMILY_ID(_unit) 0
#define RTK_9330_FAMILY_ID(_unit) 0
#ifndef WAIT_COMPLETE_VAR
#define WAIT_COMPLETE_VAR() \
osal_usecs_t _t, _now, _t_wait=0, _timeout; \
int32 _chkCnt=0;
#define WAIT_COMPLETE(_timeout_us) \
_timeout = _timeout_us; \
for(osal_time_usecs_get(&_t),osal_time_usecs_get(&_now),_t_wait=0,_chkCnt=0 ; \
(_t_wait <= _timeout); \
osal_time_usecs_get(&_now), _chkCnt++, _t_wait += ((_now >= _t) ? (_now - _t) : (0xFFFFFFFF - _t + _now)),_t = _now \
)
#define WAIT_COMPLETE_IS_TIMEOUT() (_t_wait > _timeout)
#endif
/* OSAL */
#include <linux/slab.h>
int32 osal_time_usecs_get(osal_usecs_t *pUsec);
void *osal_alloc(uint32 size);
#define osal_time_mdelay mdelay
#include <linux/ctype.h> /* for Kernel Space */
#include <linux/kernel.h>
#include <linux/string.h>
#define osal_strlen strlen
#define osal_strcmp strcmp
#define osal_strcpy strcpy
#define osal_strncpy strncpy
#define osal_strcat strcat
#define osal_strchr strchr
#define osal_memset memset
#define osal_memcpy memcpy
#define osal_memcmp memcmp
#define osal_strdup strdup
#define osal_strncmp strncmp
#define osal_strstr strstr
#define osal_strtok strtok
#define osal_strtok_r strtok_r
#define osal_toupper toupper
#define osal_printf printk
/* HWP */
#define HWP_PORT_SMI(unit, port) 0
#define HWP_PHY_MODEL_BY_PORT(unit, port) 0
#define HWP_PHY_ADDR(unit, port) 0
#define HWP_PHY_BASE_MACID(unit, p) 0
#define HWP_PORT_TRAVS_EXCEPT_CPU(unit, p) if (bcast_phyad < 0x1F && p != NULL)
/* RT_LOG */
//#define RT_LOG(level, module, fmt, args...) do { printk("RT_LOG:"fmt, ## args); } while(0)
#define RT_LOG(level, module, fmt, args...) do {} while(0)
#define RT_ERR(error_code, module, fmt, args...) do {} while(0)
#define RT_INIT_ERR(error_code, module, fmt, args...) do {} while(0)
#define RT_INIT_MSG(fmt, args...) do {} while(0)
#define phy_826xb_ctrl_set(unit, p, RTK_PHY_CTRL_MIIM_BCAST_PHYAD, bcast_phyad) 0
#define phy_8224_ctrl_set(unit, p, RTK_PHY_CTRL_MIIM_BCAST_PHYAD, bcast_phyad) 0
/* reg access */
int32 phy_common_general_reg_mmd_get(uint32 unit, rtk_port_t port, uint32 mmdAddr, uint32 mmdReg, uint32 *pData);
int32 phy_common_general_reg_mmd_set(uint32 unit, rtk_port_t port, uint32 mmdAddr, uint32 mmdReg, uint32 data);
#endif /* __RTK_PHY_OSAL_H */

View file

@ -0,0 +1,613 @@
/*
* SPDX-License-Identifier: GPL-2.0-only
*
* Copyright (c) 2023 Realtek Semiconductor Corp. All rights reserved.
*/
#include <linux/module.h>
#include <linux/phy.h>
#include <linux/ethtool.h>
#include <linux/ethtool_netlink.h>
#include <linux/netdevice.h>
#include "phy_rtl826xb_patch.h"
#include "rtk_phylib_rtl826xb.h"
#include "rtk_phylib_rtl8224.h"
#include "rtk_phylib_macsec.h"
#include "rtk_phylib.h"
#include "rtk_phy.h"
#include "rtk_phy_rtl8224.h"
static int rtk_phy_cable_test_report_trans(rtk_rtct_channel_result_t *result)
{
if(result->cable_status == 0)
return ETHTOOL_A_CABLE_RESULT_CODE_OK;
if(result->cable_status & RTK_PHYLIB_CABLE_STATUS_INTER_PAIR_SHORT)
return ETHTOOL_A_CABLE_RESULT_CODE_SAME_SHORT;
if(result->cable_status & RTK_PHYLIB_CABLE_STATUS_SHORT)
return ETHTOOL_A_CABLE_RESULT_CODE_CROSS_SHORT;
if(result->cable_status & RTK_PHYLIB_CABLE_STATUS_OPEN)
return ETHTOOL_A_CABLE_RESULT_CODE_OPEN;
if(result->cable_status & RTK_PHYLIB_CABLE_STATUS_CROSS)
return ETHTOOL_A_CABLE_RESULT_CODE_CROSS_SHORT;
return ETHTOOL_A_CABLE_RESULT_CODE_UNSPEC;
}
static int rtl826xb_get_features(struct phy_device *phydev)
{
int ret;
struct rtk_phy_priv *priv = phydev->priv;
ret = genphy_c45_pma_read_abilities(phydev);
if (ret)
return ret;
linkmode_or(phydev->supported, phydev->supported, PHY_BASIC_FEATURES);
linkmode_set_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT,
phydev->supported);
linkmode_set_bit(ETHTOOL_LINK_MODE_5000baseT_Full_BIT,
phydev->supported);
/* not support 10M modes */
linkmode_clear_bit(ETHTOOL_LINK_MODE_10baseT_Half_BIT,
phydev->supported);
linkmode_clear_bit(ETHTOOL_LINK_MODE_10baseT_Full_BIT,
phydev->supported);
switch (priv->phytype)
{
case RTK_PHYLIB_RTL8251L:
case RTK_PHYLIB_RTL8254B:
linkmode_clear_bit(ETHTOOL_LINK_MODE_10000baseT_Full_BIT,
phydev->supported);
break;
default:
break;
}
return 0;
}
static int rtl826xb_probe(struct phy_device *phydev)
{
struct rtk_phy_priv *priv = NULL;
int data = 0;
priv = devm_kzalloc(&phydev->mdio.dev, sizeof(struct rtk_phy_priv), GFP_KERNEL);
if (!priv)
{
return -ENOMEM;
}
memset(priv, 0, sizeof(struct rtk_phy_priv));
if (phy_rtl826xb_patch_db_init(0, phydev, &(priv->patch)) != RT_ERR_OK)
return -ENOMEM;
if (phydev->drv->phy_id == REALTEK_PHY_ID_RTL8261N)
{
data = phy_read_mmd(phydev, 30, 0x103);
if (data < 0)
return data;
if (data == 0x8251)
{
priv->phytype = RTK_PHYLIB_RTL8251L;
}
else
{
priv->phytype = RTK_PHYLIB_RTL8261N;
}
}
else if (phydev->drv->phy_id == REALTEK_PHY_ID_RTL8264B)
{
data = phy_read_mmd(phydev, 30, 0x103);
if (data < 0)
return data;
if (data == 0x8254)
{
priv->phytype = RTK_PHYLIB_RTL8254B;
}
else
{
priv->phytype = RTK_PHYLIB_RTL8264B;
}
}
priv->isBasePort = (phydev->drv->phy_id == REALTEK_PHY_ID_RTL8261N) ? (1) : (((phydev->mdio.addr % 4) == 0) ? (1) : (0));
phydev->priv = priv;
return 0;
}
static int rtkphy_config_init(struct phy_device *phydev)
{
int ret = 0;
switch (phydev->drv->phy_id)
{
case REALTEK_PHY_ID_RTL8261N:
case REALTEK_PHY_ID_RTL8264B:
phydev_info(phydev, "%s:%u [RTL8261N/RTL826XB] phy_id: 0x%X PHYAD:%d\n", __FUNCTION__, __LINE__, phydev->drv->phy_id, phydev->mdio.addr);
#if 1 /* toggle reset */
phy_write_mmd(phydev, 30, 0x145, 0x0001);
phy_write_mmd(phydev, 30, 0x145, 0x0000);
mdelay(30);
#endif
ret = phy_patch(0, phydev, 0, PHY_PATCH_MODE_NORMAL);
if (ret)
{
phydev_err(phydev, "%s:%u [RTL8261N/RTL826XB] patch failed!! 0x%X\n", __FUNCTION__, __LINE__, ret);
return ret;
}
ret = rtk_phylib_826xb_intr_init(phydev);
if (ret)
{
phydev_err(phydev, "%s:%u [RTL8261N/RTL826XB] rtk_phylib_826xb_intr_init failed!! 0x%X\n", __FUNCTION__, __LINE__, ret);
return ret;
}
ret = rtk_macsec_init(phydev);
if (ret)
{
phydev_err(phydev, "%s:%u [RTL8261N/RTL826XB] rtk_macsec_init failed!! 0x%X\n", __FUNCTION__, __LINE__, ret);
return ret;
}
ret = rtk_phylib_826xb_sds_write(phydev, 6, 3, 15, 0, 0x88C6);
if (ret)
{
phydev_err(phydev, "%s:%u [RTL8261N/RTL826XB] SerDes init failed!! 0x%X\n", __FUNCTION__, __LINE__, ret);
return ret;
}
#if 0 /* Debug: patch check */
ret = phy_patch(0, phydev, 0, PHY_PATCH_MODE_CMP);
if (ret)
{
phydev_err(phydev, "%s:%u [RTL8261N/RTL826XB] phy_patch failed!! 0x%X\n", __FUNCTION__, __LINE__, ret);
return ret;
}
printk("[%s,%u] patch chk %s\n", __FUNCTION__, __LINE__, (ret == 0) ? "PASS" : "FAIL");
#endif
#if 0 /* Debug: USXGMII*/
{
uint32 data = 0;
rtk_phylib_826xb_sds_read(phydev, 0x07, 0x10, 15, 0, &data);
printk("[%s,%u] SDS 0x07, 0x10 : 0x%X\n", __FUNCTION__, __LINE__, data);
rtk_phylib_826xb_sds_read(phydev, 0x06, 0x12, 15, 0, &data);
printk("[%s,%u] SDS 0x06, 0x12 : 0x%X\n", __FUNCTION__, __LINE__, data);
}
{
u16 sdspage = 0x5, sdsreg = 0x0;
u16 regData = (sdspage & 0x3f) | ((sdsreg & 0x1f) << 6) | BIT(15);
u16 readData = 0;
phy_write_mmd(phydev, 30, 323, regData);
do
{
udelay(10);
readData = phy_read_mmd(phydev, 30, 323);
} while ((readData & BIT(15)) != 0);
readData = phy_read_mmd(phydev, 30, 322);
printk("[%s,%d] sds link [%s] (0x%X)\n", __FUNCTION__, __LINE__, (readData & BIT(12)) ? "UP" : "DOWN", readData);
}
#endif
break;
default:
phydev_err(phydev, "%s:%u Unknow phy_id: 0x%X\n", __FUNCTION__, __LINE__, phydev->drv->phy_id);
return -EPERM;
}
return ret;
}
static int rtkphy_c45_suspend(struct phy_device *phydev)
{
int ret = 0;
ret = rtk_phylib_c45_power_low(phydev);
phydev->speed = SPEED_UNKNOWN;
phydev->duplex = DUPLEX_UNKNOWN;
phydev->pause = 0;
phydev->asym_pause = 0;
return ret;
}
static int rtkphy_c45_resume(struct phy_device *phydev)
{
return rtk_phylib_c45_power_normal(phydev);
}
static int rtkphy_c45_config_aneg(struct phy_device *phydev)
{
bool changed = false;
u16 reg = 0;
int ret = 0;
phydev->mdix_ctrl = ETH_TP_MDI_AUTO;
if (phydev->autoneg == AUTONEG_DISABLE)
{
if (phydev->speed != SPEED_100)
{
return -EPERM;
}
return genphy_c45_pma_setup_forced(phydev);
}
ret = genphy_c45_an_config_aneg(phydev);
if (ret < 0)
return ret;
if (ret > 0)
changed = true;
reg = 0;
if (linkmode_test_bit(ETHTOOL_LINK_MODE_1000baseT_Full_BIT,
phydev->advertising))
reg |= BIT(9);
if (linkmode_test_bit(ETHTOOL_LINK_MODE_1000baseT_Half_BIT,
phydev->advertising))
reg |= BIT(8);
ret = phy_modify_mmd_changed(phydev, MDIO_MMD_VEND2, 0xA412,
BIT(9) | BIT(8) , reg);
if (ret < 0)
return ret;
if (ret > 0)
changed = true;
return genphy_c45_check_and_restart_aneg(phydev, changed);
}
static int rtkphy_c45_aneg_done(struct phy_device *phydev)
{
return genphy_c45_aneg_done(phydev);
}
static int rtkphy_c45_read_status(struct phy_device *phydev)
{
int ret = 0, status = 0;
phydev->speed = SPEED_UNKNOWN;
phydev->duplex = DUPLEX_UNKNOWN;
phydev->pause = 0;
phydev->asym_pause = 0;
ret = genphy_c45_read_link(phydev);
if (ret)
return ret;
if (phydev->autoneg == AUTONEG_ENABLE)
{
linkmode_clear_bit(ETHTOOL_LINK_MODE_1000baseT_Full_BIT,
phydev->lp_advertising);
ret = genphy_c45_read_lpa(phydev);
if (ret)
return ret;
status = phy_read_mmd(phydev, 31, 0xA414);
if (status < 0)
return status;
linkmode_mod_bit(ETHTOOL_LINK_MODE_1000baseT_Full_BIT,
phydev->lp_advertising, status & BIT(11));
phy_resolve_aneg_linkmode(phydev);
}
else
{
ret = genphy_c45_read_pma(phydev);
}
/* mdix*/
status = phy_read_mmd(phydev, MDIO_MMD_PMAPMD, MDIO_PMA_10GBT_SWAPPOL);
if (status < 0)
return status;
switch (status & 0x3)
{
case MDIO_PMA_10GBT_SWAPPOL_ABNX | MDIO_PMA_10GBT_SWAPPOL_CDNX:
phydev->mdix = ETH_TP_MDI;
break;
case 0:
phydev->mdix = ETH_TP_MDI_X;
break;
default:
phydev->mdix = ETH_TP_MDI_INVALID;
break;
}
return ret;
}
static int rtkphy_c45_pcs_loopback(struct phy_device *phydev, bool enable)
{
return rtk_phylib_c45_pcs_loopback(phydev, (enable == true) ? 1 : 0);
}
static int rtl826xb_cable_test_start(struct phy_device *phydev)
{
return rtk_phylib_826xb_cable_test_start(phydev);
}
static int rtl826xb_cable_test_get_status(struct phy_device *phydev, bool *finished)
{
uint32 finish_read = 0;
int32 ret = 0;
uint32 pair = 0;
rtk_rtct_channel_result_t reslut;
*finished = false;
RTK_PHYLIB_ERR_CHK(rtk_phylib_826xb_cable_test_finished_get(phydev, &finish_read));
*finished = (finish_read == 1) ? true : false;
if (finish_read == 1)
{
for (pair = 0; pair < 4; pair++)
{
RTK_PHYLIB_ERR_CHK(rtk_phylib_826xb_cable_test_result_get(phydev, pair, &reslut));
ethnl_cable_test_result(phydev, pair, rtk_phy_cable_test_report_trans(&reslut));
if(reslut.cable_status != RTK_PHYLIB_CABLE_STATUS_NORMAL)
ethnl_cable_test_fault_length(phydev, pair, reslut.length_cm);
}
}
return ret;
}
static int rtl826xb_config_intr(struct phy_device *phydev)
{
int32 ret = 0;
RTK_PHYLIB_ERR_CHK(rtk_phylib_826xb_intr_enable(phydev, (phydev->interrupts == PHY_INTERRUPT_ENABLED)? 1 : 0));
return ret;
}
static int rtl826xb_ack_intr(struct phy_device *phydev)
{
int32 ret = 0;
uint32 status = 0;
RTK_PHYLIB_ERR_CHK(rtk_phylib_826xb_intr_read_clear(phydev, &status));
if (status & RTK_PHY_INTR_WOL)
{
rtk_phylib_826xb_wol_reset(phydev);
}
return ret;
}
static irqreturn_t rtl826xb_handle_intr(struct phy_device *phydev)
{
irqreturn_t ret;
uint32 status = 0;
RTK_PHYLIB_ERR_CHK(rtk_phylib_826xb_intr_read_clear(phydev, &status));
if (status & RTK_PHY_INTR_LINK_CHANGE)
{
pr_debug("[%s,%d] RTK_PHY_INTR_LINK_CHANGE\n", __FUNCTION__, __LINE__);
phy_mac_interrupt(phydev);
}
if (status & RTK_PHY_INTR_WOL)
{
pr_debug("[%s,%d] RTK_PHY_INTR_WOL\n", __FUNCTION__, __LINE__);
rtk_phylib_826xb_wol_reset(phydev);
}
return IRQ_HANDLED;
}
static int rtl826xb_get_tunable(struct phy_device *phydev, struct ethtool_tunable *tuna, void *data)
{
int32 ret = 0;
uint32 val = 0;
switch (tuna->id)
{
case ETHTOOL_PHY_EDPD:
RTK_PHYLIB_ERR_CHK(rtk_phylib_826xb_link_down_power_saving_get(phydev, &val));
*(u16 *)data = (val == 0) ? ETHTOOL_PHY_EDPD_DISABLE : ETHTOOL_PHY_EDPD_DFLT_TX_MSECS;
return 0;
default:
return -EOPNOTSUPP;
}
}
static int rtl826xb_set_tunable(struct phy_device *phydev, struct ethtool_tunable *tuna, const void *data)
{
int32 ret = 0;
uint32 val = 0;
switch (tuna->id)
{
case ETHTOOL_PHY_EDPD:
switch (*(const u16 *)data)
{
case ETHTOOL_PHY_EDPD_DFLT_TX_MSECS:
val = 1;
break;
case ETHTOOL_PHY_EDPD_DISABLE:
val = 0;
break;
default:
return -EINVAL;
}
RTK_PHYLIB_ERR_CHK(rtk_phylib_826xb_link_down_power_saving_set(phydev, val));
return 0;
default:
return -EOPNOTSUPP;
}
}
static int rtl826xb_set_wol(struct phy_device *phydev,
struct ethtool_wolinfo *wol)
{
int32 ret = 0;
uint8 *mac_addr = NULL;
uint32 rtk_wol_opts = 0;
struct net_device *ndev = phydev->attached_dev;
if (!ndev)
return RTK_PHYLIB_ERR_FAILED;
if (wol->wolopts & ~( WAKE_PHY | WAKE_MAGIC | WAKE_UCAST | WAKE_BCAST | WAKE_MCAST))
return -EOPNOTSUPP;
if (wol->wolopts & (WAKE_MAGIC | WAKE_UCAST))
{
mac_addr = (uint8 *) ndev->dev_addr;
RTK_PHYLIB_ERR_CHK(rtk_phylib_826xb_wol_unicast_addr_set(phydev, mac_addr));
}
if (wol->wolopts & WAKE_MCAST)
{
RTK_PHYLIB_ERR_CHK(rtk_phylib_826xb_wol_multicast_mask_reset(phydev));
if (!netdev_mc_empty(ndev))
{
struct netdev_hw_addr *ha;
netdev_for_each_mc_addr(ha, ndev)
{
pr_info("[%s,%d] mac: %pM \n", __FUNCTION__, __LINE__, ha->addr);
RTK_PHYLIB_ERR_CHK(rtk_phylib_826xb_wol_multicast_mask_add(phydev, rtk_phylib_826xb_wol_multicast_mac2offset(ha->addr)));
}
}
}
if (wol->wolopts & WAKE_PHY)
rtk_wol_opts |= RTK_WOL_OPT_LINK;
if (wol->wolopts & WAKE_MAGIC)
rtk_wol_opts |= RTK_WOL_OPT_MAGIC;
if (wol->wolopts & WAKE_UCAST)
rtk_wol_opts |= RTK_WOL_OPT_UCAST;
if (wol->wolopts & WAKE_BCAST)
rtk_wol_opts |= RTK_WOL_OPT_BCAST;
if (wol->wolopts & WAKE_MCAST)
rtk_wol_opts |= RTK_WOL_OPT_MCAST;
RTK_PHYLIB_ERR_CHK(rtk_phylib_826xb_wol_set(phydev, rtk_wol_opts));
return 0;
}
static void rtl826xb_get_wol(struct phy_device *phydev,
struct ethtool_wolinfo *wol)
{
int32 ret = 0;
uint32 rtk_wol_opts = 0;
wol->supported = WAKE_PHY | WAKE_MAGIC | WAKE_UCAST | WAKE_BCAST | WAKE_MCAST;
wol->wolopts = 0;
ret = rtk_phylib_826xb_wol_get(phydev, &rtk_wol_opts);
if (ret != 0)
return;
if (rtk_wol_opts & RTK_WOL_OPT_LINK)
wol->wolopts |= WAKE_PHY;
if (rtk_wol_opts & RTK_WOL_OPT_MAGIC)
wol->wolopts |= WAKE_MAGIC;
if (rtk_wol_opts & RTK_WOL_OPT_UCAST)
wol->wolopts |= WAKE_UCAST;
if (rtk_wol_opts & RTK_WOL_OPT_MCAST)
wol->wolopts |= WAKE_MCAST;
if (rtk_wol_opts & RTK_WOL_OPT_BCAST)
wol->wolopts |= WAKE_BCAST;
}
static struct phy_driver rtk_phy_drivers[] = {
{
PHY_ID_MATCH_EXACT(REALTEK_PHY_ID_RTL8261N),
.name = "Realtek RTL8261N/8261BE/8251L",
.get_features = rtl826xb_get_features,
.config_init = rtkphy_config_init,
.probe = rtl826xb_probe,
.suspend = rtkphy_c45_suspend,
.resume = rtkphy_c45_resume,
.config_aneg = rtkphy_c45_config_aneg,
.aneg_done = rtkphy_c45_aneg_done,
.read_status = rtkphy_c45_read_status,
.set_loopback = rtkphy_c45_pcs_loopback,
.cable_test_start = rtl826xb_cable_test_start,
.cable_test_get_status = rtl826xb_cable_test_get_status,
.config_intr = rtl826xb_config_intr,
.ack_interrupt = rtl826xb_ack_intr,
.handle_interrupt = rtl826xb_handle_intr,
.get_tunable = rtl826xb_get_tunable,
.set_tunable = rtl826xb_set_tunable,
.set_wol = rtl826xb_set_wol,
.get_wol = rtl826xb_get_wol,
},
{
PHY_ID_MATCH_EXACT(REALTEK_PHY_ID_RTL8264B),
.name = "Realtek RTL8264B/8254B",
.get_features = rtl826xb_get_features,
.config_init = rtkphy_config_init,
.probe = rtl826xb_probe,
.suspend = rtkphy_c45_suspend,
.resume = rtkphy_c45_resume,
.config_aneg = rtkphy_c45_config_aneg,
.aneg_done = rtkphy_c45_aneg_done,
.read_status = rtkphy_c45_read_status,
.set_loopback = rtkphy_c45_pcs_loopback,
.cable_test_start = rtl826xb_cable_test_start,
.cable_test_get_status = rtl826xb_cable_test_get_status,
.config_intr = rtl826xb_config_intr,
.ack_interrupt = rtl826xb_ack_intr,
.handle_interrupt = rtl826xb_handle_intr,
.get_tunable = rtl826xb_get_tunable,
.set_tunable = rtl826xb_set_tunable,
.set_wol = rtl826xb_set_wol,
.get_wol = rtl826xb_get_wol,
},
{
PHY_ID_MATCH_EXACT(REALTEK_PHY_ID_RTL8224),
.name = "Realtek RTL8224/8224N",
.get_features = rtl8224_get_features,
.config_init = rtl8224_config_init,
.probe = rtl8224_probe,
.suspend = rtkphy_c45_suspend,
.resume = rtkphy_c45_resume,
.config_aneg = rtkphy_c45_config_aneg,
.aneg_done = rtkphy_c45_aneg_done,
.read_status = rtkphy_c45_read_status,
.set_loopback = rtkphy_c45_pcs_loopback,
.cable_test_start = rtl8224_cable_test_start,
.cable_test_get_status = rtl8224_cable_test_get_status,
.get_tunable = rtl8224_get_tunable,
.set_tunable = rtl8224_set_tunable,
.config_intr = rtl8224_config_intr,
.ack_interrupt = rtl8224_ack_intr,
.handle_interrupt = rtl8224_handle_intr,
},
};
module_phy_driver(rtk_phy_drivers);
static struct mdio_device_id __maybe_unused rtk_phy_tbl[] = {
{ PHY_ID_MATCH_EXACT(REALTEK_PHY_ID_RTL8261N) },
{ PHY_ID_MATCH_EXACT(REALTEK_PHY_ID_RTL8264B) },
{ PHY_ID_MATCH_EXACT(REALTEK_PHY_ID_RTL8224) },
{ },
};
MODULE_DEVICE_TABLE(mdio, rtk_phy_tbl);
MODULE_AUTHOR("Realtek");
MODULE_DESCRIPTION("Realtek PHY drivers");
MODULE_LICENSE("GPL");

View file

@ -0,0 +1,22 @@
/*
* SPDX-License-Identifier: GPL-2.0-only
*
* Copyright (c) 2023 Realtek Semiconductor Corp. All rights reserved.
*/
#define REALTEK_PHY_ID_RTL8261N 0x001CCAF3
#define REALTEK_PHY_ID_RTL8264B 0x001CC813
#define REALTEK_PHY_ID_RTL8224 0x001CCAD0
#if IS_ENABLED(CONFIG_MACSEC)
int rtk_macsec_init(struct phy_device *phydev);
#else
static inline int rtk_macsec_init(struct phy_device *phydev)
{
return 0;
}
#endif

View file

@ -0,0 +1,573 @@
#include <linux/phy.h>
#include <linux/ethtool.h>
#include <linux/ethtool_netlink.h>
#include "rtk_phylib.h"
#include "rtk_phy.h"
#include "rtk_phylib_rtl8224.h"
#include "rtk_phy_rtl8224.h"
#include "phy_rtl8224_patch.h"
#include "error.h"
#include "rtk_osal.h"
//#define RTL8224_INTERNAL_INFO 1
#ifdef RTL8224_INTERNAL_INFO
#define RTL8224_INTERNAL_PORT_CHECK(port)\
do{\
printk("\n[%s] addr(%d)\n",__FUNCTION__,port->mdio.addr);\
}while(0)
#else
#define RTL8224_INTERNAL_PORT_CHECK(port)\
do{\
}while(0)
#endif
static int _phy_8224_cable_test_report_trans(uint32 pair, rtk_rtctResult_t *result)
{
switch(pair)
{
case 0:
if(result->un.ge_result.channelAShort)
return ETHTOOL_A_CABLE_RESULT_CODE_CROSS_SHORT;
if(result->un.ge_result.channelAOpen)
return ETHTOOL_A_CABLE_RESULT_CODE_OPEN;
if(result->un.ge_result.channelAMismatch)
return ETHTOOL_A_CABLE_RESULT_CODE_CROSS_SHORT;
if(result->un.ge_result.channelAPairBusy)
return ETHTOOL_A_CABLE_RESULT_CODE_SAME_SHORT;
break;
case 1:
if(result->un.ge_result.channelBShort)
return ETHTOOL_A_CABLE_RESULT_CODE_CROSS_SHORT;
if(result->un.ge_result.channelBOpen)
return ETHTOOL_A_CABLE_RESULT_CODE_OPEN;
if(result->un.ge_result.channelBMismatch)
return ETHTOOL_A_CABLE_RESULT_CODE_CROSS_SHORT;
if(result->un.ge_result.channelBPairBusy)
return ETHTOOL_A_CABLE_RESULT_CODE_SAME_SHORT;
break;
case 2:
if(result->un.ge_result.channelCShort)
return ETHTOOL_A_CABLE_RESULT_CODE_CROSS_SHORT;
if(result->un.ge_result.channelCOpen)
return ETHTOOL_A_CABLE_RESULT_CODE_OPEN;
if(result->un.ge_result.channelCMismatch)
return ETHTOOL_A_CABLE_RESULT_CODE_CROSS_SHORT;
if(result->un.ge_result.channelCPairBusy)
return ETHTOOL_A_CABLE_RESULT_CODE_SAME_SHORT;
break;
case 3:
if(result->un.ge_result.channelDShort)
return ETHTOOL_A_CABLE_RESULT_CODE_CROSS_SHORT;
if(result->un.ge_result.channelDOpen)
return ETHTOOL_A_CABLE_RESULT_CODE_OPEN;
if(result->un.ge_result.channelDMismatch)
return ETHTOOL_A_CABLE_RESULT_CODE_CROSS_SHORT;
if(result->un.ge_result.channelDPairBusy)
return ETHTOOL_A_CABLE_RESULT_CODE_SAME_SHORT;
break;
default:
return ETHTOOL_A_CABLE_RESULT_CODE_UNSPEC;
}
return ETHTOOL_A_CABLE_RESULT_CODE_OK;
}
static int _phy_8224_cable_test_length_get(uint32 pair, rtk_rtctResult_t *result, uint32 *length_cm)
{
switch(pair)
{
case 0:
if(result->un.ge_result.channelAShort)
return -1;
if(result->un.ge_result.channelAOpen)
return -1;
if(result->un.ge_result.channelAMismatch)
return -1;
if(result->un.ge_result.channelAPairBusy)
return -1;
*length_cm = result->un.ge_result.channelALen;
break;
case 1:
if(result->un.ge_result.channelBShort)
return -1;
if(result->un.ge_result.channelBOpen)
return -1;
if(result->un.ge_result.channelBMismatch)
return -1;
if(result->un.ge_result.channelBPairBusy)
return -1;
*length_cm = result->un.ge_result.channelBLen;
break;
case 2:
if(result->un.ge_result.channelCShort)
return -1;
if(result->un.ge_result.channelCOpen)
return -1;
if(result->un.ge_result.channelCMismatch)
return -1;
if(result->un.ge_result.channelCPairBusy)
return -1;
*length_cm = result->un.ge_result.channelCLen;
break;
case 3:
if(result->un.ge_result.channelDShort)
return -1;
if(result->un.ge_result.channelDOpen)
return -1;
if(result->un.ge_result.channelDMismatch)
return -1;
if(result->un.ge_result.channelDPairBusy)
return -1;
*length_cm = result->un.ge_result.channelDLen;
break;
default:
return -1;
}
return 0;
}
static int _phy_8224_dbCompleted_check(struct phy_device *phydev)
{
struct rtl8224_priv_info *priv = (struct rtl8224_priv_info *)phydev->priv;
struct phy_device *base_phydev = NULL;
struct rtl8224_priv_info *base_priv = NULL;
struct phy_device *tmp_phydev = NULL;
struct rtl8224_priv_info *tmp_priv = NULL;
int phy_addr, loop, ret = 0;
base_phydev = priv->basePort;
base_priv = (struct rtl8224_priv_info *)base_phydev->priv;
phy_addr = base_phydev->mdio.addr;
for(loop = 0; loop < RTL8224_PORT_NUM; loop++)
{
tmp_phydev = mdiobus_get_phy(base_phydev->mdio.bus, (phy_addr + loop));
tmp_priv = (struct rtl8224_priv_info *)tmp_phydev->priv;
if((loop == 0) && (tmp_phydev != base_phydev))
{
PR_ERR("[%d][loop %d] base port is WRONG!!!",__LINE__,loop);
ret = -1;
goto db_not_ready;
}
if(tmp_phydev != base_priv->memberPort[loop])
{
PR_ERR("[%d][loop %d] member port is WRONG!!!",__LINE__,loop);
ret = -2;
goto db_not_ready;
}
if(tmp_priv->port_offset != loop)
{
PR_ERR("[%d][loop %d] port offset is WRONG!!!",__LINE__,loop);
ret = -3;
goto db_not_ready;
}
if((tmp_priv->is_basePort != 0) && (loop != 0))
{
PR_ERR("[%d][loop %d] is_basePort is WRONG!!!",__LINE__,loop);
ret = -4;
goto db_not_ready;
}
}
return ret;
db_not_ready:
PR_ERR("[%d][loop %d] dbCompleted is NOT Ready!!!",__LINE__,loop);
return ret;
}
/* updated base port db*/
static struct phy_device *phy_8224_basePort_db_updated(struct phy_device *phydev, int *reg_data1, int *reg_data2)
{
struct rtl8224_priv_info *phy_priv = (struct rtl8224_priv_info *)phydev->priv;
*reg_data1 = phy_read_mmd(phydev, 30, 0x4);
if (*reg_data1 < 0)
goto bus_read_error;
*reg_data2 = phy_read_mmd(phydev, 30, 0x5);
if (*reg_data2 < 0)
goto bus_read_error;
if(*reg_data2 == 0x8224) /* base port match */
{
phy_priv->basePort = phydev;
phy_priv->is_basePort = 1;
phy_priv->port_offset = 0;
phy_priv->memberPort[RTL8224_BASE_PORT_OFFSET]= phydev;
if(*reg_data1 == 0x0)
phy_priv->phytype = RTK_PHYLIB_RTL8224;
else if(*reg_data1 == 0x7000)
phy_priv->phytype = RTK_PHYLIB_RTL8224N;
else
goto basePort_match_error;
}
else
{
phy_priv->is_basePort = 0;
return NULL;
}
return phydev;
bus_read_error:
PR_ERR("\n[%s] read error!\n",__FUNCTION__);
return NULL;
basePort_match_error:
PR_ERR("\n[%s] base port match error!\n",__FUNCTION__);
return NULL;
}
/* updated member port db*/
int phy_8224_member_db_updated(struct phy_device *phydev)
{
struct rtl8224_priv_info *phy_priv = (struct rtl8224_priv_info *)phydev->priv;
struct phy_device *tmp_phydev = NULL;
struct rtl8224_priv_info *temp_priv;
int index = RTL8224_PORT_NUM;
int port_offset = 0;
int phy_addr = phydev->mdio.addr;
/* base port's phy address is the first always, */
/* and 4 phy addresses are continuous */
while(index > 0)
{
phy_addr--;
index --;
tmp_phydev = mdiobus_get_phy(phydev->mdio.bus, phy_addr); /* get other port's phy device by phy address*/
temp_priv = (struct rtl8224_priv_info *)tmp_phydev->priv;
if(tmp_phydev == NULL)
{
/* to find next one */
continue;
}
else
{
if(temp_priv->is_basePort == 1)
{
/* Record base port*/
phy_priv->basePort = tmp_phydev;
/* Record member port*/
phy_priv->memberPort[RTL8224_BASE_PORT_OFFSET] = tmp_phydev;
/* port offset */
port_offset = (phydev->mdio.addr - tmp_phydev->mdio.addr);
phy_priv->memberPort[port_offset] = phydev;
phy_priv->port_offset = port_offset;
/* updated base port's member port db */
temp_priv->memberPort[port_offset] = phydev;
return 0;
}
}
}
PR_ERR("\n[%s](PHY addr %d) FAILED!!!\n",__FUNCTION__,phydev->mdio.addr);
return -1;
}
int rtl8224_probe(struct phy_device *phydev)
{
struct rtl8224_priv_info *priv = NULL;
int reg_data1 = 0, reg_data2 = 0, db_ret = 0, tmp_base = 1;
struct phy_device *tmp_phydev = NULL;
struct rtl8224_priv_info *tmp_priv;
RTL8224_INTERNAL_PORT_CHECK(phydev);
priv = kmalloc(sizeof(struct rtl8224_priv_info), GFP_KERNEL);
if (!priv)
{
return -ENOMEM;
}
memset(priv, 0, sizeof(struct rtl8224_priv_info));
phydev->priv = priv;
if (phydev->drv->phy_id == REALTEK_PHY_ID_RTL8224)
{
tmp_phydev = phy_8224_basePort_db_updated(phydev, &reg_data1, &reg_data2);
/* updated member port db */
if(tmp_phydev == NULL)
{
tmp_base = 0;
db_ret = phy_8224_member_db_updated(phydev);
if(db_ret)
goto match_error;
}
}
else
{
goto match_error;
}
tmp_priv = (struct rtl8224_priv_info *)phydev->priv;
phydev->priv = priv;
return 0;
match_error:
PR_ERR("[%s] probe miss MATCH!!!\n",__FUNCTION__);
devm_kfree(&phydev->mdio.dev, priv);
return -ENXIO;
}
int rtl8224_get_features(struct phy_device *phydev)
{
int ret;
RTL8224_INTERNAL_PORT_CHECK(phydev);
ret = genphy_c45_pma_read_abilities(phydev);
if (ret)
return ret;
linkmode_or(phydev->supported, phydev->supported, PHY_BASIC_FEATURES);
linkmode_set_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT,
phydev->supported);
/* not support 10M modes */
linkmode_clear_bit(ETHTOOL_LINK_MODE_10baseT_Half_BIT,
phydev->supported);
linkmode_clear_bit(ETHTOOL_LINK_MODE_10baseT_Full_BIT,
phydev->supported);
return 0;
}
int rtl8224_config_init(struct phy_device *phydev)
{
struct rtl8224_priv_info *priv = (struct rtl8224_priv_info *)phydev->priv;
struct rtl8224_priv_info *tmp_priv = NULL;
int ret = 0, loop = 0;
int32 unit = 0;
rtk_port_t port;
RTL8224_INTERNAL_PORT_CHECK(phydev);
ret = _phy_8224_dbCompleted_check(phydev);
if(ret)
PR_ERR("\n PHY data base is NOT completed\n");
if(priv->is_basePort == 1)
{
PR_INFO("\n Base port (phy_addr %d), execute patch...\n",phydev->mdio.addr);
for (loop = 0; loop < RTL8224_PORT_NUM; loop++)
{
if ((port = _phy_8224_get_phydevice_by_offset(unit, phydev, loop)) == NULL)
{
PR_ERR("\n Offset(%d) PHY device cannot get\n",loop);
return -1;
}
tmp_priv = (struct rtl8224_priv_info *)port->priv;
phy_rtl8224_patch(unit, port, loop);
_phy_8224_mdi_reverse_set(port, RTL8224_MDI_PIN_SWAP);
if ((RTL8224_MDI_PAIR_SWAP) != 0)
{
_phy_8224_tx_polarity_swap_set(port, RTL8224_MDI_PAIR_SWAP);
}
_phy_8224_interrupt_init(unit, port);
}
PR_INFO("\n Base port (phy_addr %d), path completed!\n",phydev->mdio.addr);
}
else
{
PR_INFO("\n(phy_addr %d) is member port\n", phydev->mdio.addr);
}
return ret;
}
int
rtl8224_cable_test_start(struct phy_device *phydev)
{
int32 ret = RT_ERR_OK;
uint32 unit = 0;
uint32 phyData = 0, speed = 0, duplex = 0;
uint32 tryTime = 1000;
rtk_enable_t ena;
rtk_port_t port = phydev;
RTL8224_INTERNAL_PORT_CHECK(phydev);
if ((ret = _phy_8224_common_c45_enable_get(unit, port, &ena)) != RT_ERR_OK)
return ret;
if (ena == DISABLED)
{
return RT_ERR_OPER_DENIED;
}
/* Check the port is link up or not?
* Due to bit 2 is LL(latching low), need to read twice to get current status
*/
if ((ret = phy_common_general_reg_mmd_get(unit, port, PHY_MMD_PMAPMD, 0x1, &phyData)) != RT_ERR_OK)
return ret;
if ((ret = phy_common_general_reg_mmd_get(unit, port, PHY_MMD_PMAPMD, 0x1, &phyData)) != RT_ERR_OK)
return ret;
_phy_8224_common_c45_speedDuplexStatusResReg_get(unit, port, &speed, &duplex);
if (phyData & BIT_2)
{
if (speed == PORT_SPEED_10M)
{
return RT_ERR_PORT_NOT_SUPPORTED;
}
return RT_ERR_OK;
}
else
{
if ((ret = phy_common_general_reg_mmd_get(unit, port, PHY_MMD_VEND2, 0xA400, &phyData)) != RT_ERR_OK)
return ret;
phyData |= (BIT_9); //[9]=1
if ((ret = phy_common_general_reg_mmd_set(unit, port, PHY_MMD_VEND2, 0xA400, phyData)) != RT_ERR_OK)
return ret;
osal_time_mdelay(500);
if ((ret = phy_common_general_reg_mmd_get(unit, port, PHY_MMD_VEND2, 0xA422, &phyData)) != RT_ERR_OK)
return ret;
phyData &= (~BIT_15); //[15]=0
phyData |= (BIT_1); //[1]=1
if ((ret = phy_common_general_reg_mmd_set(unit, port, PHY_MMD_VEND2, 0xA422, phyData)) != RT_ERR_OK)
return ret;
phyData |= (BIT_4 | BIT_5 | BIT_6 | BIT_7); //[7:4]=0xf
if ((ret = phy_common_general_reg_mmd_set(unit, port, PHY_MMD_VEND2, 0xA422, phyData)) != RT_ERR_OK)
return ret;
phyData |= (BIT_0); //[0]=0x1
if ((ret = phy_common_general_reg_mmd_set(unit, port, PHY_MMD_VEND2, 0xA422, phyData)) != RT_ERR_OK)
return ret;
if ((ret = phy_common_general_reg_mmd_get(unit, port, PHY_MMD_VEND2, 0xA422, &phyData)) != RT_ERR_OK)
return ret;
while((phyData & BIT_15) == 0)
{
osal_time_mdelay(10);
if ((ret = phy_common_general_reg_mmd_get(unit, port, PHY_MMD_VEND2, 0xA422, &phyData)) != RT_ERR_OK)
return ret;
tryTime--;
if(tryTime == 0)
return RT_ERR_NOT_FINISH;
}
}
return ret;
}
int rtl8224_cable_test_get_status(struct phy_device *phydev, bool *finished)
{
int32 ret = 0;
uint32 pair = 0, unit = 0, length_cm;
rtk_rtctResult_t result;
RTL8224_INTERNAL_PORT_CHECK(phydev);
*finished = false;
ret = _phy_8224_rtctResult_get(unit, phydev, &result);
if(ret == 0)
*finished = true;
for (pair = 0; pair < 4; pair++)
{
ethnl_cable_test_result(phydev, pair, _phy_8224_cable_test_report_trans(pair, &result));
if(_phy_8224_cable_test_length_get(pair, &result, &length_cm))
ethnl_cable_test_fault_length(phydev, pair, length_cm);
}
return 0;
}
int rtl8224_get_tunable(struct phy_device *phydev, struct ethtool_tunable *tuna, void *data)
{
int32 ret = 0;
rtk_enable_t val = 0;
uint32 unit = 0;
RTL8224_INTERNAL_PORT_CHECK(phydev);
switch (tuna->id)
{
case ETHTOOL_PHY_EDPD:
RTK_PHYLIB_ERR_CHK(phy_8224_linkDownPowerSavingEnable_get(unit, phydev, &val));
*(u16 *)data = (val == DISABLED) ? ETHTOOL_PHY_EDPD_DISABLE : ETHTOOL_PHY_EDPD_DFLT_TX_MSECS;
return 0;
default:
return -EOPNOTSUPP;
}
}
int rtl8224_set_tunable(struct phy_device *phydev, struct ethtool_tunable *tuna, const void *data)
{
int32 ret = 0;
rtk_enable_t val = DISABLED;
uint32 unit = 0;
RTL8224_INTERNAL_PORT_CHECK(phydev);
switch (tuna->id)
{
case ETHTOOL_PHY_EDPD:
switch (*(const u16 *)data)
{
case ETHTOOL_PHY_EDPD_DISABLE:
val = DISABLED;
break;
default:
val = ENABLED;
break;
}
RTK_PHYLIB_ERR_CHK(phy_8224_linkDownPowerSavingEnable_set(unit, phydev, val));
return 0;
default:
return -EOPNOTSUPP;
}
}
int rtl8224_config_intr(struct phy_device *phydev)
{
int32 ret = 0;
RTL8224_INTERNAL_PORT_CHECK(phydev);
RTK_PHYLIB_ERR_CHK(_phy_8224_intr_enable(phydev, (phydev->interrupts == PHY_INTERRUPT_ENABLED)? 1 : 0));
return ret;
}
int rtl8224_ack_intr(struct phy_device *phydev)
{
int32 ret = 0;
uint32 status = 0;
RTL8224_INTERNAL_PORT_CHECK(phydev);
RTK_PHYLIB_ERR_CHK(_phy_8224_intr_read_clear(phydev, &status));
return ret;
}
irqreturn_t rtl8224_handle_intr(struct phy_device *phydev)
{
irqreturn_t ret;
uint32 status = 0;
RTL8224_INTERNAL_PORT_CHECK(phydev);
RTK_PHYLIB_ERR_CHK(_phy_8224_intr_read_clear(phydev, &status));
if (status & BIT_4)
{
PR_INFO("[%s,%d] RTK_PHY_INTR_LINK_CHANGE\n", __FUNCTION__, __LINE__);
phy_mac_interrupt(phydev);
}
return IRQ_HANDLED;
}

View file

@ -0,0 +1,13 @@
extern int rtl8224_probe(struct phy_device *phydev);
extern int rtl8224_get_features(struct phy_device *phydev);
extern int rtl8224_config_init(struct phy_device *phydev);
extern int rtl8224_cable_test_start(struct phy_device *phydev);
extern int rtl8224_cable_test_get_status(struct phy_device *phydev, bool *finished);
extern int rtl8224_get_tunable(struct phy_device *phydev, struct ethtool_tunable *tuna, void *data);
extern int rtl8224_set_tunable(struct phy_device *phydev, struct ethtool_tunable *tuna, const void *data);
extern int rtl8224_config_intr(struct phy_device *phydev);
extern int rtl8224_ack_intr(struct phy_device *phydev);
extern irqreturn_t rtl8224_handle_intr(struct phy_device *phydev);

View file

@ -0,0 +1,117 @@
/*
* SPDX-License-Identifier: GPL-2.0-only
*
* Copyright (c) 2023 Realtek Semiconductor Corp. All rights reserved.
*/
#include "rtk_phylib.h"
#include <linux/phy.h>
/* OSAL */
void rtk_phylib_mdelay(uint32 msec)
{
#if defined(RTK_PHYDRV_IN_LINUX)
mdelay(msec);
#else
osal_time_mdelay(msec);
#endif
}
void rtk_phylib_udelay(uint32 usec)
{
#if defined(RTK_PHYDRV_IN_LINUX)
if (1000 <= usec)
{
mdelay(usec/1000);
usec = usec % 1000;
}
udelay(usec);
#else
osal_time_udelay(usec);
#endif
}
int32 rtk_phylib_time_usecs_get(uint32 *pUsec)
{
struct timespec64 ts;
if(NULL == pUsec)
return RTK_PHYLIB_ERR_INPUT;
ktime_get_ts64(&ts);
*pUsec = ((ts.tv_sec * USEC_PER_SEC) + (ts.tv_nsec / NSEC_PER_USEC));
return 0;
}
/* Register Access APIs */
int32 rtk_phylib_mmd_write(rtk_phydev *phydev, uint32 mmd, uint32 reg, uint8 msb, uint8 lsb, uint32 data)
{
int32 ret = 0;
uint32 mask = 0;
mask = UINT32_BITS_MASK(msb,lsb);
#if defined(RTK_PHYDRV_IN_LINUX)
ret = phy_modify_mmd(phydev, mmd, reg, mask, (data << lsb));
#else
{
uint32 rData = 0, wData = 0;
if ((msb != 15) || (lsb != 0))
{
if ((ret = phy_common_general_reg_mmd_get(phydev->unit, phydev->port, page, reg, &rData)) != RT_ERR_OK)
return ret;
}
wData = REG32_FIELD_SET(rData, data, lsb, mask);
ret = phy_common_general_reg_mmd_set(phydev->unit, phydev->port, page, reg, wData);
}
#endif
return ret;
}
int32 rtk_phylib_mmd_read(rtk_phydev *phydev, uint32 mmd, uint32 reg, uint8 msb, uint8 lsb, uint32 *pData)
{
int32 ret = 0;
uint32 rData = 0;
uint32 mask = 0;
mask = UINT32_BITS_MASK(msb,lsb);
#if defined(RTK_PHYDRV_IN_LINUX)
rData = phy_read_mmd(phydev, mmd, reg);
#else
{
ret = phy_common_general_reg_mmd_get(phydev->unit, phydev->port, page, reg, &rData);
}
#endif
*pData = REG32_FIELD_GET(rData, lsb, mask);
return ret;
}
/* Function Driver */
int32 rtk_phylib_c45_power_normal(rtk_phydev *phydev)
{
int32 ret = 0;
RTK_PHYLIB_ERR_CHK(rtk_phylib_mmd_write(phydev, 1, 0, 11, 11, 0));
return 0;
}
int32 rtk_phylib_c45_power_low(rtk_phydev *phydev)
{
int32 ret = 0;
RTK_PHYLIB_ERR_CHK(rtk_phylib_mmd_write(phydev, 1, 0, 11, 11, 1));
return 0;
}
int32 rtk_phylib_c45_pcs_loopback(rtk_phydev *phydev, uint32 enable)
{
int32 ret = 0;
RTK_PHYLIB_ERR_CHK(rtk_phylib_mmd_write(phydev, 3, 0, 14, 14, (enable == 0) ? 0 : 1));
return 0;
}

View file

@ -0,0 +1,389 @@
/*
* SPDX-License-Identifier: GPL-2.0-only
*
* Copyright (c) 2023 Realtek Semiconductor Corp. All rights reserved.
*/
#ifndef __RTK_PHYLIB_H
#define __RTK_PHYLIB_H
#if defined(RTK_PHYDRV_IN_LINUX)
#include <linux/phy.h>
#include "type.h"
#include "rtk_phylib_def.h"
#else
//#include SDK headers
#endif
#if defined(DEBUG)
#define MACSEC_DBG_PRINT 1
#endif
#if defined(RTK_PHYDRV_IN_LINUX)
#define PR_INFO(_fmt, _args...) pr_info(_fmt, ##_args)
#define PR_DBG(_fmt, _args...) pr_debug(_fmt, ##_args)
#define PR_ERR(_fmt, _args...) pr_err("ERROR: "_fmt, ##_args)
#define RTK_PHYLIB_ERR_FAILED (-EPERM)
#define RTK_PHYLIB_ERR_INPUT (-EINVAL)
#define RTK_PHYLIB_ERR_EXCEEDS_CAPACITY (-ENOSPC)
#define RTK_PHYLIB_ERR_TIMEOUT (-ETIME)
#define RTK_PHYLIB_ERR_ENTRY_NOTFOUND (-ENODATA)
#define RTK_PHYLIB_ERR_OPER_DENIED (-EACCES)
#else
#define PR_INFO(_fmt, _args...) RT_LOG(LOG_INFO, (MOD_HAL|MOD_PHY), _fmt, ##_args)
#define PR_DBG(_fmt, _args...) RT_LOG(LOG_DEBUG, (MOD_HAL|MOD_PHY), _fmt, ##_args)
#define PR_ERR(_fmt, _args...) RT_LOG(LOG_MAJOR_ERR, (MOD_HAL|MOD_PHY), _fmt, ##_args)
#define RTK_PHYLIB_ERR_FAILED (RT_ERR_FAILED)
#define RTK_PHYLIB_ERR_INPUT (RT_ERR_INPUT)
#define RTK_PHYLIB_ERR_EXCEEDS_CAPACITY (RT_ERR_EXCEEDS_CAPACITY)
#define RTK_PHYLIB_ERR_TIMEOUT (RT_ERR_BUSYWAIT_TIMEOUT)
#define RTK_PHYLIB_ERR_ENTRY_NOTFOUND (RT_ERR_ENTRY_NOTFOUND)
#define RTK_PHYLIB_ERR_OPER_DENIED (RT_ERR_OPER_DENIED)
#endif
typedef enum rtk_phylib_phy_e
{
RTK_PHYLIB_NONE,
RTK_PHYLIB_RTL8261N,
RTK_PHYLIB_RTL8264B,
RTK_PHYLIB_RTL8251L,
RTK_PHYLIB_RTL8254B,
RTK_PHYLIB_RTL8224,
RTK_PHYLIB_RTL8224N,
RTK_PHYLIB_END
} rtk_phylib_phy_t;
typedef enum rtk_port_duplex_e
{
PORT_HALF_DUPLEX = 0,
PORT_FULL_DUPLEX,
PORT_DUPLEX_END
} rtk_port_duplex_t;
typedef enum rtk_port_speed_e
{
PORT_SPEED_10M = 0,
PORT_SPEED_100M,
PORT_SPEED_500M,
PORT_SPEED_1000M,
PORT_SPEED_2_5G_LITE,
PORT_SPEED_2G,
PORT_SPEED_2_5G,
PORT_SPEED_5G_LITE,
PORT_SPEED_5G,
PORT_SPEED_10G_LITE,
PORT_SPEED_10G,
PORT_SPEED_25G,
PORT_SPEED_40G,
PORT_SPEED_50G,
PORT_SPEED_100G,
PORT_SPEED_END
} rtk_port_speed_t;
typedef enum rtk_port_lite_mode_e
{
PORT_LITE_1G,
PORT_LITE_2P5G,
PORT_LITE_5G,
PORT_LITE_10G,
PORT_LITE_END
} rtk_port_lite_mode_t;
/* RTCT result Status of one channel */
typedef struct rtk_rtctChannelStatus_s
{
uint32 channelShort;
uint32 channelOpen;
uint32 channelLowMismatch;
uint32 channelHighMismatch;
uint32 channelCrossoverA;
uint32 channelCrossoverB;
uint32 channelCrossoverC;
uint32 channelCrossoverD;
uint32 channelLen;
} rtk_rtctChannelStatus_t;
/* result of RTCT test */
typedef struct rtk_rtctResult_s
{
rtk_port_speed_t linkType;
union
{
struct fe_result_s
{
uint32 isRxShort;
uint32 isTxShort;
uint32 isRxOpen;
uint32 isTxOpen;
uint32 isRxMismatch;
uint32 isTxMismatch;
uint32 isRxLinedriver;
uint32 isTxLinedriver;
uint32 rxLen;
uint32 txLen;
} fe_result;
struct ge_result_s
{
uint32 channelAShort;
uint32 channelBShort;
uint32 channelCShort;
uint32 channelDShort;
uint32 channelAOpen;
uint32 channelBOpen;
uint32 channelCOpen;
uint32 channelDOpen;
uint32 channelAMismatch;
uint32 channelBMismatch;
uint32 channelCMismatch;
uint32 channelDMismatch;
uint32 channelALinedriver;
uint32 channelBLinedriver;
uint32 channelCLinedriver;
uint32 channelDLinedriver;
uint32 channelAHiImpedance;
uint32 channelBHiImpedance;
uint32 channelCHiImpedance;
uint32 channelDHiImpedance;
uint32 channelACross;
uint32 channelBCross;
uint32 channelCCross;
uint32 channelDCross;
uint32 channelAPartialCross;
uint32 channelBPartialCross;
uint32 channelCPartialCross;
uint32 channelDPartialCross;
/* when paire is busy, RTCT shall re-do by calling start API again. */
uint32 channelAPairBusy;
uint32 channelBPairBusy;
uint32 channelCPairBusy;
uint32 channelDPairBusy;
uint32 channelALen;
uint32 channelBLen;
uint32 channelCLen;
uint32 channelDLen;
} ge_result;
struct channels_result_s
{
rtk_rtctChannelStatus_t a;
rtk_rtctChannelStatus_t b;
rtk_rtctChannelStatus_t c;
rtk_rtctChannelStatus_t d;
} channels_result;
} un;
} rtk_rtctResult_t;
#if defined(RTK_PHYDRV_IN_LINUX)
typedef struct phy_device rtk_phydev;
#else
struct rtk_phy_dev_s
{
uint32 unit;
rtk_port_t port;
struct rtk_phy_priv *priv;
};
typedef struct rtk_phy_dev_s rtk_phydev;
#endif
#define RTK_PHYLIB_ERR_CHK(op)\
do {\
if ((ret = (op)) != 0)\
return ret;\
} while(0)
#define RTK_PHYLIB_VAL_TO_BYTE_ARRAY(_val, _valbytes, _array, _start, _bytes)\
do{\
uint32 _i = 0;\
for (_i = 0; _i < _bytes; _i++)\
_array[(_bytes-1)-_i] = (_val >> (8* (_valbytes - _i - 1)));\
}while(0)
#define RTK_PHYLIB_BYTE_ARRAY_TO_VAL(_val, _array, _start, _bytes)\
do{\
uint32 _i = 0;\
_val = 0;\
for (_i = 0; _i < _bytes; _i++)\
_val = (_val << 8) | _array[(_bytes-1)-_i];\
}while(0)
/* RTCT */
#define RTK_PHYLIB_CABLE_STATUS_NORMAL (0)
#define RTK_PHYLIB_CABLE_STATUS_UNKNOWN (1u << 0)
#define RTK_PHYLIB_CABLE_STATUS_SHORT (1u << 1)
#define RTK_PHYLIB_CABLE_STATUS_OPEN (1u << 2)
#define RTK_PHYLIB_CABLE_STATUS_MISMATCH (1u << 3)
#define RTK_PHYLIB_CABLE_STATUS_CROSS (1u << 4)
#define RTK_PHYLIB_CABLE_STATUS_INTER_PAIR_SHORT (1u << 5)
typedef struct rtk_rtct_channel_result_s
{
uint32 cable_status;
uint32 length_cm;
} rtk_rtct_channel_result_t;
/* MACsec */
typedef struct rtk_macsec_sa_info_s
{
uint8 ssci[4];
} rtk_macsec_sa_info_t;
#define MACSEC_SA_IS_USED(macsec_port_info_ptr, dir, sa_id) (macsec_port_info_ptr->sa_used[dir][sa_id])
#define MACSEC_SC_IS_USED(macsec_port_info_ptr, dir, sc_id) (macsec_port_info_ptr->sc_used[dir][sc_id])
#define MACSEC_SA_IS_CLEAR(macsec_port_info_ptr, dir, sa_id) (!MACSEC_SA_IS_USED(macsec_port_info_ptr, dir, sa_id))
#define MACSEC_SC_IS_CLEAR(macsec_port_info_ptr, dir, sc_id) (!MACSEC_SC_IS_USED(macsec_port_info_ptr, dir, sc_id))
#define MACSEC_SA_SET_USED(macsec_port_info_ptr, dir, sa_id) do { macsec_port_info_ptr->sa_used[dir][sa_id] = 1; }while(0)
#define MACSEC_SC_SET_USED(macsec_port_info_ptr, dir, sc_id) do { macsec_port_info_ptr->sc_used[dir][sc_id] = 1; }while(0)
#define MACSEC_SA_UNSET_USED(macsec_port_info_ptr, dir, sa_id) do { macsec_port_info_ptr->sa_used[dir][sa_id] = 0; }while(0)
#define MACSEC_SC_UNSET_USED(macsec_port_info_ptr, dir, sc_id) do { macsec_port_info_ptr->sc_used[dir][sc_id] = 0; }while(0)
#define MACSEC_SA_MAX(macsec_port_info_ptr) macsec_port_info_ptr->max_sa_num
#define MACSEC_SC_MAX(macsec_port_info_ptr) macsec_port_info_ptr->max_sc_num
#define MACSEC_SC_CS(macsec_port_info_ptr, dir, sc_id) macsec_port_info_ptr->cipher_suite[dir][sc_id]
#define MACSEC_SC_MATCH(macsec_port_info_ptr, dir, sc_id) macsec_port_info_ptr->flow_match[dir][sc_id]
#define MACSEC_SA_SSCI(macsec_port_info_ptr, sa_id) macsec_port_info_ptr->sa_info[sa_id].ssci
typedef struct rtk_macsec_port_stats_s
{
uint64 InPktsUntagged;
uint64 InPktsNoTag;
uint64 InPktsBadTag;
uint64 InPktsUnknownSCI;
uint64 InPktsNoSCI;
uint64 OutPktsUntagged;
}rtk_macsec_port_stats_t;
typedef struct rtk_macsec_txsa_stats_s
{
uint64 OutPktsTooLong;
uint64 OutOctetsProtectedEncrypted;
uint64 OutPktsProtectedEncrypted;
}rtk_macsec_txsa_stats_t;
typedef struct rtk_macsec_rxsa_stats_s
{
uint64 InPktsUnusedSA;
uint64 InPktsNotUsingSA;
uint64 InPktsUnchecked;
uint64 InPktsDelayed;
uint64 InPktsLate;
uint64 InPktsOK;
uint64 InPktsInvalid;
uint64 InPktsNotValid;
uint64 InOctetsDecryptedValidated;
}rtk_macsec_rxsa_stats_t;
typedef struct rtk_macsec_port_info_s
{
int32 (*macsec_reg_get)(rtk_phydev *phydev, rtk_macsec_dir_t dir, uint32 reg, uint8 msb, uint8 lsb, uint32 *pData);
int32 (*macsec_reg_set)(rtk_phydev *phydev, rtk_macsec_dir_t dir, uint32 reg, uint8 msb, uint8 lsb, uint32 data);
uint16 sa_gen_seq;
uint32 max_sa_num;
uint32 max_sc_num;
rtk_macsec_cipher_t cipher_suite[RTK_MACSEC_DIR_END][RTK_MAX_MACSEC_SC_PER_PORT];
uint32 flow_match[RTK_MACSEC_DIR_END][RTK_MAX_MACSEC_SC_PER_PORT];
uint8 sc_used[RTK_MACSEC_DIR_END][RTK_MAX_MACSEC_SC_PER_PORT];
uint8 sa_used[RTK_MACSEC_DIR_END][RTK_MAX_MACSEC_SA_PER_PORT];
rtk_macsec_sa_info_t sa_info[RTK_MAX_MACSEC_SA_PER_PORT];
uint64 sci[RTK_MACSEC_DIR_END][RTK_MAX_MACSEC_SC_PER_PORT];
rtk_macsec_port_stats_t port_stats;
rtk_macsec_txsa_stats_t *txsa_stats[RTK_MAX_MACSEC_SA_PER_PORT];
rtk_macsec_rxsa_stats_t *rxsa_stats[RTK_MAX_MACSEC_SA_PER_PORT];
} rtk_macsec_port_info_t;
struct rtk_phy_priv {
rtk_phylib_phy_t phytype;
uint8 isBasePort;
rt_phy_patch_db_t *patch;
rtk_macsec_port_info_t *macsec;
};
/* OSAL */
void rtk_phylib_mdelay(uint32 msec);
void rtk_phylib_udelay(uint32 usec);
#define rtk_phylib_strlen strlen
#define rtk_phylib_strcmp strcmp
#define rtk_phylib_strcpy strcpy
#define rtk_phylib_strncpy strncpy
#define rtk_phylib_strcat strcat
#define rtk_phylib_strchr strchr
#define rtk_phylib_memset memset
#define rtk_phylib_memcpy memcpy
#define rtk_phylib_memcmp memcmp
#define rtk_phylib_strdup strdup
#define rtk_phylib_strncmp strncmp
#define rtk_phylib_strstr strstr
#define rtk_phylib_strtok strtok
#define rtk_phylib_strtok_r strtok_r
#define rtk_phylib_toupper toupper
int rtk_phylib_time_usecs_get(uint32 *pUsec);
#ifndef WAIT_COMPLETE_VAR
#define WAIT_COMPLETE_VAR() \
uint32 _t, _now, _t_wait=0, _timeout; \
int32 _chkCnt=0;
#define WAIT_COMPLETE(_timeout_us) \
_timeout = _timeout_us; \
for(rtk_phylib_time_usecs_get(&_t),rtk_phylib_time_usecs_get(&_now),_t_wait=0,_chkCnt=0 ; \
(_t_wait <= _timeout); \
rtk_phylib_time_usecs_get(&_now), _chkCnt++, _t_wait += ((_now >= _t) ? (_now - _t) : (0xFFFFFFFF - _t + _now)),_t = _now \
)
#define WAIT_COMPLETE_IS_TIMEOUT() (_t_wait > _timeout)
#endif
/* Register Access APIs */
int32 rtk_phylib_mmd_write(rtk_phydev *phydev, uint32 mmd, uint32 reg, uint8 msb, uint8 lsb, uint32 data);
int32 rtk_phylib_mmd_read(rtk_phydev *phydev, uint32 mmd, uint32 reg, uint8 msb, uint8 lsb, uint32 *pData);
/* Function Driver */
int32 rtk_phylib_c45_power_normal(rtk_phydev *phydev);
int32 rtk_phylib_c45_power_low(rtk_phydev *phydev);
int32 rtk_phylib_c45_pcs_loopback(rtk_phydev *phydev, uint32 enable);
/* MACsec*/
int32 rtk_phylib_macsec_init(rtk_phydev *phydev);
int32 rtk_phylib_macsec_enable_get(rtk_phydev *phydev, uint32 *pEna);
int32 rtk_phylib_macsec_enable_set(rtk_phydev *phydev, uint32 ena);
int32 rtk_phylib_macsec_sc_create(rtk_phydev *phydev, rtk_macsec_dir_t dir, rtk_macsec_sc_t *pSc, uint32 *pSc_id, uint8 active);
int32 rtk_phylib_macsec_sc_update(rtk_phydev *phydev, rtk_macsec_dir_t dir, rtk_macsec_sc_t *pSc, uint32 *pSc_id, uint8 active);
int32 rtk_phylib_macsec_sc_del(rtk_phydev *phydev, rtk_macsec_dir_t dir, uint32 sc_id);
int32 rtk_phylib_macsec_sci_to_scid(rtk_phydev *phydev, rtk_macsec_dir_t dir, uint64 sci, uint32 *sc_id);
int32 rtk_phylib_macsec_sc_status_get(rtk_phydev *phydev, rtk_macsec_dir_t dir,uint32 sc_id, rtk_macsec_sc_status_t *pSc_status);
int32 rtk_phylib_macsec_sc_get(rtk_phydev *phydev, rtk_macsec_dir_t dir, uint32 sc_id, rtk_macsec_sc_t *pSc);
int32 rtk_phylib_macsec_sa_activate(rtk_phydev *phydev, rtk_macsec_dir_t dir, uint32 sc_id, rtk_macsec_an_t an);
int32 rtk_phylib_macsec_rxsa_disable(rtk_phydev *phydev, uint32 rxsc_id, rtk_macsec_an_t an);
int32 rtk_phylib_macsec_txsa_disable(rtk_phydev *phydev, uint32 txsc_id);
int32 rtk_phylib_macsec_sa_create(rtk_phydev *phydev, rtk_macsec_dir_t dir, uint32 sc_id, rtk_macsec_an_t an, rtk_macsec_sa_t *pSa);
int32 rtk_phylib_macsec_sa_get(rtk_phydev *phydev, rtk_macsec_dir_t dir, uint32 sc_id, rtk_macsec_an_t an, rtk_macsec_sa_t *pSa);
int32 rtk_phylib_macsec_sa_del(rtk_phydev *phydev, rtk_macsec_dir_t dir, uint32 sc_id, rtk_macsec_an_t an);
int32 rtk_phylib_macsec_stat_port_get(rtk_phydev *phydev, rtk_macsec_stat_t stat, uint64 *pCnt);
int32 rtk_phylib_macsec_stat_txsa_get(rtk_phydev *phydev, uint32 sc_id, rtk_macsec_an_t an, rtk_macsec_txsa_stat_t stat, uint64 *pCnt);
int32 rtk_phylib_macsec_stat_rxsa_get(rtk_phydev *phydev, uint32 sc_id, rtk_macsec_an_t an, rtk_macsec_rxsa_stat_t stat, uint64 *pCnt);
#endif /* __RTK_PHYLIB_H */

View file

@ -0,0 +1,421 @@
/*
* SPDX-License-Identifier: GPL-2.0-only
*
* Copyright (c) 2023 Realtek Semiconductor Corp. All rights reserved.
*/
#ifndef __RTK_PHYLIB_DEF_H
#define __RTK_PHYLIB_DEF_H
#include "type.h"
#define PHY_C22_MMD_PAGE 0x0A41
#define PHY_C22_MMD_DEV_REG 13
#define PHY_C22_MMD_ADD_REG 14
/* MDIO Manageable Device(MDD) address*/
#define PHY_MMD_PMAPMD 1
#define PHY_MMD_PCS 3
#define PHY_MMD_AN 7
#define PHY_MMD_VEND1 30 /* Vendor specific 1 */
#define PHY_MMD_VEND2 31 /* Vendor specific 2 */
#define BIT_0 0x00000001U
#define BIT_1 0x00000002U
#define BIT_2 0x00000004U
#define BIT_3 0x00000008U
#define BIT_4 0x00000010U
#define BIT_5 0x00000020U
#define BIT_6 0x00000040U
#define BIT_7 0x00000080U
#define BIT_8 0x00000100U
#define BIT_9 0x00000200U
#define BIT_10 0x00000400U
#define BIT_11 0x00000800U
#define BIT_12 0x00001000U
#define BIT_13 0x00002000U
#define BIT_14 0x00004000U
#define BIT_15 0x00008000U
#define BIT_16 0x00010000U
#define BIT_17 0x00020000U
#define BIT_18 0x00040000U
#define BIT_19 0x00080000U
#define BIT_20 0x00100000U
#define BIT_21 0x00200000U
#define BIT_22 0x00400000U
#define BIT_23 0x00800000U
#define BIT_24 0x01000000U
#define BIT_25 0x02000000U
#define BIT_26 0x04000000U
#define BIT_27 0x08000000U
#define BIT_28 0x10000000U
#define BIT_29 0x20000000U
#define BIT_30 0x40000000U
#define BIT_31 0x80000000U
#define MASK_1_BITS (BIT_1 - 1)
#define MASK_2_BITS (BIT_2 - 1)
#define MASK_3_BITS (BIT_3 - 1)
#define MASK_4_BITS (BIT_4 - 1)
#define MASK_5_BITS (BIT_5 - 1)
#define MASK_6_BITS (BIT_6 - 1)
#define MASK_7_BITS (BIT_7 - 1)
#define MASK_8_BITS (BIT_8 - 1)
#define MASK_9_BITS (BIT_9 - 1)
#define MASK_10_BITS (BIT_10 - 1)
#define MASK_11_BITS (BIT_11 - 1)
#define MASK_12_BITS (BIT_12 - 1)
#define MASK_13_BITS (BIT_13 - 1)
#define MASK_14_BITS (BIT_14 - 1)
#define MASK_15_BITS (BIT_15 - 1)
#define MASK_16_BITS (BIT_16 - 1)
#define MASK_17_BITS (BIT_17 - 1)
#define MASK_18_BITS (BIT_18 - 1)
#define MASK_19_BITS (BIT_19 - 1)
#define MASK_20_BITS (BIT_20 - 1)
#define MASK_21_BITS (BIT_21 - 1)
#define MASK_22_BITS (BIT_22 - 1)
#define MASK_23_BITS (BIT_23 - 1)
#define MASK_24_BITS (BIT_24 - 1)
#define MASK_25_BITS (BIT_25 - 1)
#define MASK_26_BITS (BIT_26 - 1)
#define MASK_27_BITS (BIT_27 - 1)
#define MASK_28_BITS (BIT_28 - 1)
#define MASK_29_BITS (BIT_29 - 1)
#define MASK_30_BITS (BIT_30 - 1)
#define MASK_31_BITS (BIT_31 - 1)
#define REG32_FIELD_SET(_data, _val, _fOffset, _fMask) ((_data & ~(_fMask)) | ((_val << (_fOffset)) & (_fMask)))
#define REG32_FIELD_GET(_data, _fOffset, _fMask) ((_data & (_fMask)) >> (_fOffset))
#define UINT32_BITS_MASK(_mBit, _lBit) ((0xFFFFFFFF >> (31 - _mBit)) ^ ((1 << _lBit) - 1))
typedef struct phy_device * rtk_port_t;
#if 1 /* ss\sdk\include\hal\phy\phydef.h */
/* unified patch format */
typedef enum rtk_phypatch_type_e
{
PHY_PATCH_TYPE_NONE = 0,
PHY_PATCH_TYPE_TOP = 1,
PHY_PATCH_TYPE_SDS,
PHY_PATCH_TYPE_AFE,
PHY_PATCH_TYPE_UC,
PHY_PATCH_TYPE_UC2,
PHY_PATCH_TYPE_NCTL0,
PHY_PATCH_TYPE_NCTL1,
PHY_PATCH_TYPE_NCTL2,
PHY_PATCH_TYPE_ALGXG,
PHY_PATCH_TYPE_ALG1G,
PHY_PATCH_TYPE_NORMAL,
PHY_PATCH_TYPE_DATARAM,
PHY_PATCH_TYPE_RTCT,
PHY_PATCH_TYPE_END
} rtk_phypatch_type_t;
#define RTK_PATCH_TYPE_FLOW(_id) (PHY_PATCH_TYPE_END + _id)
#define RTK_PATCH_TYPE_FLOWID_MAX PHY_PATCH_TYPE_END
#define RTK_PATCH_SEQ_MAX ( PHY_PATCH_TYPE_END + RTK_PATCH_TYPE_FLOWID_MAX -1)
/* Interrupt */
/* PHY Interrupt Status */
#define RTK_PHY_INTR_NEXT_PAGE_RECV (BIT_0)
#define RTK_PHY_INTR_AN_COMPLETE (BIT_1)
#define RTK_PHY_INTR_LINK_CHANGE (BIT_2)
#define RTK_PHY_INTR_ALDPS_STATE_CHANGE (BIT_3)
#define RTK_PHY_INTR_RLFD (BIT_4)
#define RTK_PHY_INTR_TM_LOW (BIT_5)
#define RTK_PHY_INTR_TM_HIGH (BIT_6)
#define RTK_PHY_INTR_FATAL_ERROR (BIT_7)
#define RTK_PHY_INTR_MACSEC (BIT_8)
#define RTK_PHY_INTR_PTP1588 (BIT_9)
#define RTK_PHY_INTR_WOL (BIT_10)
typedef struct rtk_hwpatch_s
{
uint8 patch_op;
uint8 portmask;
uint16 pagemmd;
uint16 addr;
uint8 msb;
uint8 lsb;
uint16 data;
uint8 compare_op;
uint16 sram_p;
uint16 sram_rr;
uint16 sram_rw;
uint16 sram_a;
} rtk_hwpatch_t;
typedef struct rtk_hwpatch_data_s
{
rtk_hwpatch_t *conf;
uint32 size;
} rtk_hwpatch_data_t;
typedef struct rtk_hwpatch_seq_s
{
uint8 patch_type;
union
{
rtk_hwpatch_data_t data;
uint8 flow_id;
} patch;
} rtk_hwpatch_seq_t;
typedef struct rt_phy_patch_db_s
{
/* patch operation */
int32 (*fPatch_op)(uint32 unit, rtk_port_t port, uint8 portOffset, rtk_hwpatch_t *pPatch_data, uint8 patch_mode);
int32 (*fPatch_flow)(uint32 unit, rtk_port_t port, uint8 portOffset, uint8 patch_flow, uint8 patch_mode);
/* patch data */
rtk_hwpatch_seq_t seq_table[RTK_PATCH_SEQ_MAX];
rtk_hwpatch_seq_t cmp_table[RTK_PATCH_SEQ_MAX];
} rt_phy_patch_db_t;
#endif
#define RTK_HWPATCH_OP_UNKNOWN 0
#define RTK_HWPATCH_OP_SDS 1
#define RTK_HWPATCH_OP_PHY 2
#define RTK_HWPATCH_OP_PHYW 3
#define RTK_HWPATCH_OP_ALGO 4
#define RTK_HWPATCH_OP_TOP 5
#define RTK_HWPATCH_OP_MMD 6
#define RTK_HWPATCH_OP_DATARAM 7
typedef struct rtk_phy_hwpatch_s
{
uint8 patch_op;
uint8 portmask;
uint16 pagemmd;
uint16 addr;
uint8 msb;
uint8 lsb;
uint16 data;
} rtk_phy_hwpatch_t;
/* cable type for cable test */
typedef enum {
RTK_RTCT_CABLE_COMMON,
RTK_RTCT_CABLE_CAT5E,
RTK_RTCT_CABLE_CAT6A,
} rtk_rtct_cable_type_t;
/* MACSec */
#ifndef RTK_MAX_MACSEC_SA_PER_PORT
#define RTK_MAX_MACSEC_SA_PER_PORT 64 /* max number of Secure Association by a port*/
#define RTK_MAX_MACSEC_SC_PER_PORT RTK_MAX_MACSEC_SA_PER_PORT/4 /* max number of Secure Channel by a port (4 AN per SC) */
#endif
#define RTK_MACSEC_MAX_KEY_LEN 32
typedef enum rtk_macsec_reg_e
{
RTK_MACSEC_DIR_EGRESS = 0,
RTK_MACSEC_DIR_INGRESS,
RTK_MACSEC_DIR_END,
} rtk_macsec_dir_t;
typedef enum rtk_macsec_an_e
{
RTK_MACSEC_AN0 = 0,
RTK_MACSEC_AN1,
RTK_MACSEC_AN2,
RTK_MACSEC_AN3,
RTK_MACSEC_AN_MAX,
} rtk_macsec_an_t ;
typedef enum rtk_macsec_flow_e
{
RTK_MACSEC_FLOW_BYPASS = 0,
RTK_MACSEC_FLOW_DROP,
RTK_MACSEC_FLOW_INGRESS,
RTK_MACSEC_FLOW_EGRESS,
} rtk_macsec_flow_type_t;
typedef enum rtk_macsec_validate_e
{
RTK_MACSEC_VALIDATE_STRICT = 0,
RTK_MACSEC_VALIDATE_CHECK,
RTK_MACSEC_VALIDATE_DISABLE,
} rtk_macsec_validate_t;
typedef enum rtk_macsec_cipher_e
{
RTK_MACSEC_CIPHER_GCM_ASE_128 = 0,
RTK_MACSEC_CIPHER_GCM_ASE_256,
RTK_MACSEC_CIPHER_GCM_ASE_XPN_128,
RTK_MACSEC_CIPHER_GCM_ASE_XPN_256,
RTK_MACSEC_CIPHER_MAX,
} rtk_macsec_cipher_t;
typedef enum rtk_macsec_stat_e
{
RTK_MACSEC_STAT_InPktsUntagged = 0,
RTK_MACSEC_STAT_InPktsNoTag,
RTK_MACSEC_STAT_InPktsBadTag,
RTK_MACSEC_STAT_InPktsUnknownSCI,
RTK_MACSEC_STAT_InPktsNoSCI,
RTK_MACSEC_STAT_OutPktsUntagged,
RTK_MACSEC_STAT_MAX,
} rtk_macsec_stat_t;
typedef enum rtk_macsec_txsa_stat_e
{
RTK_MACSEC_TXSA_STAT_OutPktsTooLong = 0,
RTK_MACSEC_TXSA_STAT_OutOctetsProtectedEncrypted,
RTK_MACSEC_TXSA_STAT_OutPktsProtectedEncrypted,
RTK_MACSEC_TXSA_STAT_MAX,
} rtk_macsec_txsa_stat_t;
typedef enum rtk_macsec_rxsa_stat_e
{
RTK_MACSEC_RXSA_STAT_InPktsUnusedSA = 0,
RTK_MACSEC_RXSA_STAT_InPktsNotUsingSA,
RTK_MACSEC_RXSA_STAT_InPktsUnchecked,
RTK_MACSEC_RXSA_STAT_InPktsDelayed,
RTK_MACSEC_RXSA_STAT_InPktsLate,
RTK_MACSEC_RXSA_STAT_InPktsOK,
RTK_MACSEC_RXSA_STAT_InPktsInvalid,
RTK_MACSEC_RXSA_STAT_InPktsNotValid,
RTK_MACSEC_RXSA_STAT_InOctetsDecryptedValidated,
RTK_MACSEC_RXSA_STAT_MAX,
} rtk_macsec_rxsa_stat_t;
typedef enum rtk_macsec_match_tx_e
{
RTK_MACSEC_MATCH_NON_CTRL = 0, /* match all non-control and untagged packets */
RTK_MACSEC_MATCH_MAC_DA, /* match all non-control and untagged packets with specific MAC DA */
} rtk_macsec_match_tx_t;
typedef struct rtk_macsec_txsc_s
{
/* 8-byte SCI([0:5] = MAC address, [6:7] = port index) for this secure channel */
uint8 sci[8];
/* cipher suite for this SC */
rtk_macsec_cipher_t cipher_suite;
/* packet flow type to match this SC */
rtk_macsec_match_tx_t flow_match;
rtk_mac_t mac_da; /* the target address for RTK_MACSEC_MATCH_MAC_DA */
uint8 protect_frame; /* 1 = enable frame protection */
uint8 include_sci; /* 1 = include explicit SCI in packet */
uint8 use_es; /* 1 = set ES (End Station) bit in TCI field */
uint8 use_scb; /* 1 = set SCB (Single Copy Broadcast) bit in TCI field */
uint8 conf_protect; /* 1 = enable confidentiality protection, */
}rtk_macsec_txsc_t;
typedef enum rtk_macsec_match_rx_e
{
RTK_MACSEC_MATCH_SCI = 0,
RTK_MACSEC_MATCH_MAC_SA, //for pkt without SCI field/TCI.SC=0,
} rtk_macsec_match_rx_t;
typedef struct rtk_macsec_rxsc_s
{
/* 8-byte SCI([0:5] = MAC address, [6:7] = port index) for this secure channel */
uint8 sci[8];
/* cipher suite for this SC */
rtk_macsec_cipher_t cipher_suite;
/* packet flow type to match this SC */
rtk_macsec_match_rx_t flow_match;
rtk_mac_t mac_sa; /* the target address for RTK_MACSEC_MATCH_MAC_SA */
/* frame validation level */
rtk_macsec_validate_t validate_frames;
/* replay protection */
uint8 replay_protect; /* 1 = enable replay protection */
uint32 replay_window; /* the window size for replay protection, range for PN: 0 ~ 2^32 - 1, for XPN: 0 ~ 2^30 */
}rtk_macsec_rxsc_t;
typedef union rtk_macsec_sc_u
{
rtk_macsec_txsc_t tx;
rtk_macsec_rxsc_t rx;
}
rtk_macsec_sc_t;
typedef struct rtk_macsec_txsc_status_s
{
uint32 hw_flow_index;
uint32 hw_sa_index;
uint8 sa_inUse;
uint32 hw_flow_data;
uint8 hw_sc_flow_status;
rtk_macsec_an_t running_an;
}
rtk_macsec_txsc_status_t;
typedef struct rtk_macsec_rxsc_status_s
{
uint32 hw_flow_base;
uint32 hw_sa_index[RTK_MACSEC_AN_MAX];
uint8 sa_inUse[RTK_MACSEC_AN_MAX];
uint32 hw_flow_data[RTK_MACSEC_AN_MAX];
uint8 hw_sc_flow_status[RTK_MACSEC_AN_MAX];
}
rtk_macsec_rxsc_status_t;
typedef union rtk_macsec_sc_status_u
{
rtk_macsec_txsc_status_t tx;
rtk_macsec_rxsc_status_t rx;
}
rtk_macsec_sc_status_t;
typedef struct rtk_macsec_sa_s
{
uint8 key[RTK_MACSEC_MAX_KEY_LEN]; // MACsec Key.
uint32 key_bytes; // Size of the MACsec key in bytes (16 for AES128, 32 for AES256).
uint32 pn; // PN (32-bit) or lower 32-bit of XPN (64-bit)
uint32 pn_h; // higher 32-bit of XPN (64-bit)
uint8 salt[12]; // 12-byte salt (for XPN).
uint8 ssci[4]; // 4-byte SSCI value (for XPN).
} rtk_macsec_sa_t;
#define RTK_MACSEC_INTR_EGRESS_PN_THRESHOLD 0x00000001
#define RTK_MACSEC_INTR_EGRESS_PN_ROLLOVER 0x00000002
typedef struct rtk_macsec_intr_status_s
{
/* a bitmap of RTK_MACSEC_INTR_* to present occured event */
uint32 status;
/* When read 1b, the corresponding MACsec egress SA is about to expire due to
the packet number crossing the rtk_macsec_port_cfg_t.pn_intr_threshold or xpn_intr_threshold*/
uint8 egress_pn_thr_an_bmap[RTK_MAX_MACSEC_SC_PER_PORT]; //bitmap of AN3~0.
/* When read 1b, the corresponding MACsec egress SA has expired due to
the packet number reaching the maximum allowed value. */
uint8 egress_pn_exp_an_bmap[RTK_MAX_MACSEC_SC_PER_PORT]; //bitmap of AN3~0.
}
rtk_macsec_intr_status_t;
typedef enum rtk_wol_opt_e
{
RTK_WOL_OPT_LINK = (0x1U << 0),
RTK_WOL_OPT_MAGIC = (0x1U << 1),
RTK_WOL_OPT_UCAST = (0x1U << 2),
RTK_WOL_OPT_MCAST = (0x1U << 3),
RTK_WOL_OPT_BCAST = (0x1U << 4),
} rtk_wol_opt_t;
/* value for RTK_PHY_CTRL_MDI_POLARITY_SWAP */
#define RTK_PHY_CTRL_MDI_POLARITY_SWAP_CH_A 0b0001
#define RTK_PHY_CTRL_MDI_POLARITY_SWAP_CH_B 0b0010
#define RTK_PHY_CTRL_MDI_POLARITY_SWAP_CH_C 0b0100
#define RTK_PHY_CTRL_MDI_POLARITY_SWAP_CH_D 0b1000
#endif /* __RTK_PHYLIB_DEF_H */

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,329 @@
/*
* SPDX-License-Identifier: GPL-2.0-only
*
* Copyright (c) 2023 Realtek Semiconductor Corp. All rights reserved.
*/
#ifndef __RTK_PHYLIB_MACSEC_H
#define __RTK_PHYLIB_MACSEC_H
#if defined(RTK_PHYDRV_IN_LINUX)
#include "type.h"
#include "rtk_phylib_def.h"
#endif
#define PHY_MACSEC_DEV_EGRESS 0
#define PHY_MACSEC_DEV_INGRESS 1
#define MACSEC_REG_OFFS 4
#define PHY_MACSEC_HW_SA_ID(sc_id, an) (sc_id * 4 + an)
#define PHY_MACSEC_HW_FLOW_ID(sc_id) (sc_id * 4)
#define PHY_MACSEC_HW_SA_TO_AN(sa_id) (sa_id % 4)
#define PHY_MACSEC_MAX_SA_EN_SIZE 24 //32-bit words
#define PHY_MACSEC_MAX_SA_IN_SIZE 20
#define PHY_MACSEC_MAX_SA_SIZE PHY_MACSEC_MAX_SA_EN_SIZE
#define MACSEC_XFORM_REC_BASE (0x0000)
#define MACSEC_XFORM_REC_SIZE(dir) ((RTK_MACSEC_DIR_EGRESS == dir) ? PHY_MACSEC_MAX_SA_EN_SIZE : PHY_MACSEC_MAX_SA_IN_SIZE )
#define MACSEC_REG_XFORM_REC(n, dir) (MACSEC_XFORM_REC_BASE + MACSEC_XFORM_REC_SIZE(dir) * \
MACSEC_REG_OFFS * (n % 128))
#define MACSEC_REG_XFORM_REC_OFFS(n, dir, off) (MACSEC_REG_XFORM_REC(n, dir) + off * MACSEC_REG_OFFS)
#define MACSEC_REG_SAM_MAC_SA_MATCH_LO(n) (0x4000 + 16 * MACSEC_REG_OFFS * (n % 128))
#define MACSEC_REG_SAM_MAC_SA_MATCH_HI(n) (0x4004 + 16 * MACSEC_REG_OFFS * (n % 128))
#define MACSEC_REG_SAM_MAC_DA_MATCH_LO(n) (0x4008 + 16 * MACSEC_REG_OFFS * (n % 128))
#define MACSEC_REG_SAM_MAC_DA_MATCH_HI(n) (0x400C + 16 * MACSEC_REG_OFFS * (n % 128))
#define MACSEC_REG_SAM_MISC_MATCH(n) (0x4010 + 16 * MACSEC_REG_OFFS * (n % 128))
#define MACSEC_REG_SAM_SCI_MATCH_LO(n) (0x4014 + 16 * MACSEC_REG_OFFS * (n % 128))
#define MACSEC_REG_SAM_SCI_MATCH_HI(n) (0x4018 + 16 * MACSEC_REG_OFFS * (n % 128))
#define MACSEC_REG_SAM_MASK(n) (0x401C + 16 * MACSEC_REG_OFFS * (n % 128))
#define MACSEC_REG_SAM_EXT_MATCH(n) (0x4020 + 16 * MACSEC_REG_OFFS * (n % 128))
#define MACSEC_REG_SAM_ENTRY_ENABLE(n) (0x6000 + (n * MACSEC_REG_OFFS))
#define MACSEC_REG_SAM_ENTRY_TOGGLE(n) (0x6040 + (n * MACSEC_REG_OFFS))
#define MACSEC_REG_SAM_ENTRY_SET(n) (0x6080 + (n * MACSEC_REG_OFFS))
#define MACSEC_REG_SAM_ENTRY_CLEAR(n) (0x60C0 + (n * MACSEC_REG_OFFS))
#define MACSEC_REG_SAM_ENTRY_ENABLE_CTRL (0x6100)
#define MACSEC_REG_SAM_IN_FLIGHT (0x6104)
#define MACSEC_REG_SAM_FLOW_CTRL(n) (0x7000 + MACSEC_REG_OFFS * (n % 128))
#define MACSEC_REG_SAM_IN_FLIGHT (0x6104)
#define MACSEC_REG_COUNT_CTRL (0xC810)
#define MACSEC_REG_IG_CC_CONTROL (0xE840)
#define MACSEC_REG_COUNT_SECFAIL1 (0xF124)
#define MACSEC_REG_CTX_CTRL (0xF408)
#define MACSEC_REG_CTX_UPD_CTRL (0xF430)
#define MACSEC_REG_SAM_CP_TAG (0x7900)
#define MACSEC_REG_SAM_NM_PARAMS (0x7940)
#define MACSEC_REG_SAM_NM_FLOW_NCP (0x7944)
#define MACSEC_REG_SAM_NM_FLOW_CP (0x7948)
#define MACSEC_REG_MISC_CONTROL (0x797C)
// Mask last byte received of MAC source address
#define MACSEC_SA_MATCH_MASK_MAC_SA_0 BIT_0
#define MACSEC_SA_MATCH_MASK_MAC_SA_1 BIT_1
#define MACSEC_SA_MATCH_MASK_MAC_SA_2 BIT_2
#define MACSEC_SA_MATCH_MASK_MAC_SA_3 BIT_3
#define MACSEC_SA_MATCH_MASK_MAC_SA_4 BIT_4
// Mask first byte received of MAC source address
#define MACSEC_SA_MATCH_MASK_MAC_SA_5 BIT_5
#define MACSEC_SA_MATCH_MASK_MAC_SA_FULL (BIT_0 | BIT_1 | BIT_2 | BIT_3 | BIT_4 | BIT_5)
// Mask last byte received of MAC destination address
#define MACSEC_SA_MATCH_MASK_MAC_DA_0 BIT_6
#define MACSEC_SA_MATCH_MASK_MAC_DA_1 BIT_7
#define MACSEC_SA_MATCH_MASK_MAC_DA_2 BIT_8
#define MACSEC_SA_MATCH_MASK_MAC_DA_3 BIT_9
#define MACSEC_SA_MATCH_MASK_MAC_DA_4 BIT_10
// Mask first byte received of MAC destination address
#define MACSEC_SA_MATCH_MASK_MAC_DA_5 BIT_11
#define MACSEC_SA_MATCH_MASK_MAC_DA_FULL (BIT_6 | BIT_7 | BIT_8 | BIT_9 | BIT_10 | BIT_11)
#define MACSEC_SA_MATCH_MASK_MAC_ETYPE BIT_12
#define MACSEC_SA_MATCH_MASK_VLAN_VALID BIT_13
#define MACSEC_SA_MATCH_MASK_QINQ_FOUND BIT_14
#define MACSEC_SA_MATCH_MASK_STAG_VALID BIT_15
#define MACSEC_SA_MATCH_MASK_QTAG_VALID BIT_16
#define MACSEC_SA_MATCH_MASK_VLAN_UP BIT_17
#define MACSEC_SA_MATCH_MASK_VLAN_ID BIT_18
#define MACSEC_SA_MATCH_MASK_SRC_PORT BIT_19
#define MACSEC_SA_MATCH_MASK_CTRL_PKT BIT_20
// For ingress only
#define MACSEC_SA_MATCH_MASK_MACSEC_SCI BIT_23
#define MACSEC_SA_MATCH_MASK_MACSEC_TCI_AN_SC (BIT_24 | BIT_25 | BIT_29)
#define MACSEC_SAB_CW0_MACSEC_EG32 0x9241e066
#define MACSEC_SAB_CW0_MACSEC_IG32 0xd241e06f
#define MACSEC_SAB_CW0_MACSEC_EG64 0xa241e066
#define MACSEC_SAB_CW0_MACSEC_IG64 0xe241a0ef
#define MACSEC_SAB_CW0_AES128 0x000a0000
#define MACSEC_SAB_CW0_AES256 0x000e0000
#define RTK_MACSEC_PORT_COMMON 0
#define RTK_MACSEC_PORT_RESERVED 1
#define RTK_MACSEC_PORT_CONTROLLED 2
#define RTK_MACSEC_PORT_UNCONTROLLED 3
typedef struct phy_macsec_flow_action_e_s
{
// 1 - enable frame protection,
// 0 - bypass frame through device
uint8 protect_frame;
// 1 - SA is in use, packets classified for it can be transformed
// 0 - SA not in use, packets classified for it can not be transformed
uint8 sa_in_use;
// 1 - inserts explicit SCI in the packet,
// 0 - use implicit SCI (not transferred)
uint8 include_sci;
// 1 - enable ES bit in the generated SecTAG
// 0 - disable ES bit in the generated SecTAG
uint8 use_es;
// 1 - enable SCB bit in the generated SecTAG
// 0 - disable SCB bit in the generated SecTAG
uint8 use_scb;
// Number of VLAN tags to bypass for egress processing.
// Valid values: 0, 1 and 2.
// This feature is only available on HW4.1 and possibly later versions.
uint8 tag_bypass_size;
// 1 - Does not update sa_in_use flag
// 0 - Update sa_in_use flag
uint8 sa_index_update_by_hw;
// The number of bytes (in the range of 0-127) that are authenticated but
// not encrypted following the SecTAG in the encrypted packet. Values
// 65-127 are reserved in HW < 4.0 and should not be used there.
uint8 confidentiality_offset;
// 1 - enable confidentiality protection
// 0 - disable confidentiality protection
uint8 conf_protect;
} phy_macsec_flow_action_e_t;
typedef struct phy_macsec_flow_action_i_s
{
// 1 - enable replay protection
// 0 - disable replay protection
uint8 replay_protect;
// true - SA is in use, packets classified for it can be transformed
// false - SA not in use, packets classified for it can not be transformed
uint8 sa_in_use;
// MACsec frame validation level
rtk_macsec_validate_t validate_frames;
// The number of bytes (in the range of 0-127) that are authenticated but
// not encrypted following the SecTAG in the encrypted packet.
uint8 confidentiality_offset;
} phy_macsec_flow_action_i_t;
typedef struct phy_macsec_flow_action_bd_s
{
// 1 - enable statistics counting for the associated SA
// 0 - disable statistics counting for the associated SA
uint8 sa_in_use;
} phy_macsec_flow_action_bd_t;
typedef struct phy_macsec_flow_action_s
{
uint32 sa_index;
rtk_macsec_flow_type_t flow_type;
union
{
phy_macsec_flow_action_e_t egress;
phy_macsec_flow_action_i_t ingress;
phy_macsec_flow_action_bd_t bypass_drop;
} params;
uint8 dest_port;
} phy_macsec_flow_action_t;
typedef struct
{
// index for flow control rule entry
uint32 flow_index;
// Packet field values to match
// MAC source address
uint8 mac_sa[6];
// MAC destination address
uint8 mac_da[6];
// EtherType
uint16 etherType;
// SCI, for ingress only
uint8 sci[8];
// Parsed VLAN ID compare value
uint16 vlan_id;
// Parsed VLAN valid flag compare value
uint8 fVLANValid;
// Parsed QinQ found flag compare value
uint8 fQinQFound;
// Parsed STAG valid flag compare value
uint8 fSTagValid;
// Parsed QTAG valid flag compare value
uint8 fQTagFound;
// Parsed VLAN User Priority compare value
uint8 vlanUserPriority;
// Packet is a control packet (as pre-decoded) compare value
uint8 fControlPacket;
// true - allow packets without a MACsec tag to match
uint8 fUntagged;
// true - allow packets with a standard and valid MACsec tag to match
uint8 fTagged;
// true - allow packets with an invalid MACsec tag to match
uint8 fBadTag;
// true - allow packets with a MACsec tag indicating KaY handling
// to be done to match
uint8 fKayTag;
// Source port compare value
uint8 sourcePort;
// Priority of this entry for determining the actual transform used
// on a match when multiple entries match, 0 = lowest, 15 = highest.
// In case of identical priorities, the lowest numbered entry takes
// precedence.
uint8 matchPriority;
// MACsec TCI/AN byte compare value, bits are individually masked for
// comparing. The TCI bits are in bits [7:2] while the AN bits reside
// in bits [1:0]. the macsec_TCI_AN field should only be set to
// non-zero for an actual MACsec packet.
uint8 macsec_TCI_AN;
// Match mask for the SA flow rules, see MACSEC_SA_MATCH_MASK_*
uint32 matchMask;
// Parsed inner VLAN ID compare value
uint16 vlanIdInner;
// Parsed inner VLAN UP compare value
uint8 vlanUpInner;
} phy_macsec_flow_match_t;
typedef struct {
uint8 key_offs;
uint8 hkey_offs;
uint8 seq_offs;
uint8 mask_offs;
uint8 ctx_salt_offs;
uint8 iv_offs;
uint8 upd_ctrl_offs;
} phy_macsec_sa_offset_t;
#define RTK_PHY_MACSEC_SA_FLAG_XPN 0x00000001U //Extended Packet Numbering
typedef struct
{
uint32 context_id; //keep 0 for create
rtk_macsec_dir_t direction;
uint32 flow_index; //the flow entry index apply to this SA
uint32 flags; // a bitmap of RTK_PHY_MACSEC_SA_FLAG_*
uint8 an; // 2-bit AN inserted in SecTAG (egress).
uint8 sci[8]; // 8-byte SCI.([0:5] = MAC address, [6:7] = port index)
uint8 key[RTK_MACSEC_MAX_KEY_LEN]; // MACsec Key.
uint32 key_bytes; // Size of the MACsec key in bytes (16 for AES128, 32 for AES256).
uint8 salt[12]; // 12-byte salt (64-bit sequence numbers).
uint8 ssci[4]; // 4-byte SSCI value (64-bit sequence numbers).
uint32 seq; // sequence number.
uint32 seq_h; // High part of sequence number (64-bit sequence numbers)
uint32 replay_window; // Size of the replay window, 0 for strict ordering (ingress).
/* update ctrl */
uint32 next_sa_index; // SA index of the next chained SA (egress).
uint8 sa_expired_irq; // 1 if SA expired IRQ is to be generated.
uint8 next_sa_valid; // SA Index field is a valid SA.
uint8 update_en; // Set to true if the SA must be updated.
} phy_macsec_sa_params_t;
typedef void (*phy_macsec_aes_cb)(
const uint8 * const In_p,
uint8 * const Out_p,
const uint8 * const Key_p,
const unsigned int KeyByteCount);
#endif /* __RTK_PHYLIB_MACSEC_H */

View file

@ -0,0 +1,896 @@
/*
* Copyright (C) 2022 Realtek Semiconductor Corp.
* All Rights Reserved.
*
* This program is the proprietary software of Realtek Semiconductor
* Corporation and/or its licensors, and only be used, duplicated,
* modified or distributed under the authorized license from Realtek.
*
* ANY USE OF THE SOFTWARE OTHER THAN AS AUTHORIZED UNDER
* THIS LICENSE OR COPYRIGHT LAW IS PROHIBITED.
*
* $Revision: $
* $Date: $
*
* Purpose : PHY 8224 Driver APIs.
*
* Feature : PHY 8224 Driver APIs
*
*/
/*
* Include Files
*/
#include "rtk_phylib.h"
#include "rtk_phy.h"
#include "rtk_phylib_rtl8224.h"
#include "phy_rtl8224_patch.h"
#include "error.h"
#include "rtk_osal.h"
/*
* Symbol Definition
*/
#define RTL8224_SWREG_ADDR(_addr) ((_addr) & (0xffff))
rtk_port_t _phy_8224_base_port_get(uint32 unit, rtk_port_t port)
{
struct rtl8224_priv_info *priv = (struct rtl8224_priv_info *)port->priv;
return priv->basePort;
}
/*
* Function Declaration
*/
uint32
_phy_8224_mask(uint8 msb, uint8 lsb)
{
uint32 val = 0;
uint8 i = 0;
for (i = lsb; i <= msb; i++)
{
val |= (1 << i);
}
return val;
}
int32
_phy_8224_mask_get(uint8 msb, uint8 lsb, uint32 *mask)
{
if ((msb > 31) || (lsb > 31) || (msb < lsb))
{
return RT_ERR_FAILED;
}
*mask = _phy_8224_mask(msb, lsb);
return RT_ERR_OK;
}
static int32 _phy_8224_intReg_get(uint32 unit, rtk_port_t port, uint32 reg_addr, uint8 msb, uint8 lsb, uint32 *pData)
{
int32 ret = RT_ERR_OK;
rtk_port_t base_port = 0;
uint32 addr;
uint32 l_data = 0;
uint32 h_data = 0;
uint32 reg_data = 0;
uint32 mask = 0;
if ((msb > 31) || (lsb > 31) || (msb < lsb))
{
return RT_ERR_FAILED;
}
base_port = _phy_8224_base_port_get(unit, port);
/* Reg [15:0]*/
addr = RTL8224_SWREG_ADDR(reg_addr);
ret = phy_common_general_reg_mmd_get(unit, base_port, PHY_MMD_VEND1, addr, &l_data);
if(ret != RT_ERR_OK)
return ret;
/* Reg [31:16]*/
ret = phy_common_general_reg_mmd_get(unit, base_port, PHY_MMD_VEND1, (addr+1), &h_data);
if(ret != RT_ERR_OK)
return ret;
ret = _phy_8224_mask_get(msb, lsb, &mask);
if(ret != RT_ERR_OK)
return ret;
reg_data = ((h_data << 16) | (l_data));
*pData = (((reg_data) & (mask)) >> lsb);
return ret;
}
static int32 _phy_8224_intReg_set(uint32 unit, rtk_port_t port, uint32 reg_addr, uint8 msb, uint8 lsb, uint32 data)
{
int32 ret = RT_ERR_OK;
rtk_port_t base_port = 0;
uint32 addr;
uint32 l_data = 0;
uint32 h_data = 0;
uint32 reg_data = 0;
uint32 mask = 0;
if ((msb > 31) || (lsb > 31) || (msb < lsb))
{
return RT_ERR_FAILED;
}
base_port = _phy_8224_base_port_get(unit, port);
/* Reg [15:0]*/
addr = RTL8224_SWREG_ADDR(reg_addr);
ret = phy_common_general_reg_mmd_get(unit, base_port, PHY_MMD_VEND1, addr, &l_data);
if(ret != RT_ERR_OK)
return ret;
/* Reg [31:16]*/
ret = phy_common_general_reg_mmd_get(unit, base_port, PHY_MMD_VEND1, (addr+1), &h_data);
if(ret != RT_ERR_OK)
return ret;
ret = _phy_8224_mask_get(msb, lsb, &mask);
if(ret != RT_ERR_OK)
return ret;
reg_data = ((h_data << 16) | (l_data));
reg_data &= ~(mask);
reg_data |= (((data) << lsb) & mask);
l_data = (reg_data & 0xffff);
h_data = ((reg_data >> 16)& 0xffff);
ret = phy_common_general_reg_mmd_set(unit, base_port, PHY_MMD_VEND1, addr, l_data);
if(ret != RT_ERR_OK)
return ret;
ret = phy_common_general_reg_mmd_set(unit, base_port, PHY_MMD_VEND1, (addr+1), h_data);
if(ret != RT_ERR_OK)
return ret;
return RT_ERR_OK;
}
static int32 _phy_8224_intRegBit_set(uint32 unit, rtk_port_t port, uint32 reg_addr, uint8 bit, uint32 data)
{
if (bit > 31)
{
return RT_ERR_FAILED;
}
return _phy_8224_intReg_set(unit, port, reg_addr, bit, bit, data);
}
static int32 _phy_8224_sram_get(uint32 unit, rtk_port_t port, uint32 sramAddr, uint32 *pData)
{
int32 ret;
ret = phy_common_general_reg_mmd_set(unit, port, PHY_MMD_VEND2, 0xa436, sramAddr);
if(ret != RT_ERR_OK)
return ret;
ret = phy_common_general_reg_mmd_get(unit, port, PHY_MMD_VEND2, 0xa438, pData);
if(ret != RT_ERR_OK)
return ret;
return ret;
}
int32
_phy_8224_interrupt_init(uint32 unit, rtk_port_t port)
{
int32 ret = RT_ERR_OK;
uint32 phyData;
rtk_port_t base_port;
uint8 port_offset;
base_port = _phy_8224_base_port_get(unit, port);
if ((ret = phy_8224_get_port_offset(base_port, port, &port_offset)) != RT_ERR_OK)
return ret;
/* Clean GPHY Interrupt Pending bits*/
/* Read and Clear*/
if ((ret = phy_common_general_reg_mmd_get(unit, port, PHY_MMD_VEND2, 0xA43A, &phyData)) != RT_ERR_OK)
return ret;
/* Clean ISR_EXT_GPHY */
/* Write 1 to clear */
if ((ret = _phy_8224_intRegBit_set(unit, port, RTL8224_ISR_EXT_GPHY, port_offset, 1)) != RT_ERR_OK)
return ret;
if (port == base_port)
{
/* Disable IMR_EXT_GPHY */
if ((ret = _phy_8224_intReg_set(unit, port, RTL8224_IMR_EXT_GPHY, RTL8224_IMR_EXT_GPHY_3_0_MSB, RTL8224_IMR_EXT_GPHY_3_0_OFFSET, 0)) != RT_ERR_OK)
return ret;
/* Disable Thermal/PTP 1588/MACsec IMR */
if ((ret = _phy_8224_intReg_set(unit, port, 0xbb005f78, RTL8224_INTER_REG_MSB, RTL8224_INTER_REG_LSB, 0)) != RT_ERR_OK)
return ret;
if ((ret = _phy_8224_intReg_set(unit, port, 0xbb005f68, RTL8224_INTER_REG_MSB, RTL8224_INTER_REG_LSB, 0)) != RT_ERR_OK)
return ret;
/* Disable RLFD Interrupt pin output */
if ((ret = _phy_8224_intReg_set(unit, port, 0xbb005f64, RTL8224_INTER_REG_MSB, RTL8224_INTER_REG_LSB, 0)) != RT_ERR_OK)
return ret;
if ((ret = _phy_8224_intReg_set(unit, port, 0xbb005f40, RTL8224_INTER_REG_MSB, RTL8224_INTER_REG_LSB, 0)) != RT_ERR_OK)
return ret;
/* Enable RLFD IMR */
if ((ret = _phy_8224_intReg_set(unit, port, RTL8224_RLFD_CTRL_REG, RTL8224_RLFD_SEL_OFFSET, RTL8224_RLFD_EN_OFFSET, 1)) != RT_ERR_OK)
return ret;
if ((ret = _phy_8224_intReg_set(unit, port, RTL8224_CFG_PHY_POLL_CMD1_REG, RTL8224_CMD_RD_EN_3_0_MSB, RTL8224_CMD_RD_EN_3_0_LSB, 7)) != RT_ERR_OK)
return ret;
/* Enable Global IMR for interrupt GPIO output, high level trigger*/
if ((ret = _phy_8224_intReg_set(unit, port, RTL8224_ISR_SW_INT_MODE, RTL8224_SWITCH_IE_OFFSET, RTL8224_SWITCH_IE_OFFSET, 1)) != RT_ERR_OK)
return ret;
if ((ret = _phy_8224_intReg_set(unit, port, RTL8224_ISR_SW_INT_MODE, RTL8224_SWITCH_INT_MODE_MSB, RTL8224_SWITCH_INT_MODE_OFFSET, PHY_8224_INT_MODE_LOW_LEVEL)) != RT_ERR_OK)
return ret;
}
/*All ports*/
if ((ret = phy_common_general_reg_mmd_get(unit, port, PHY_MMD_VEND2, 0xA442, &phyData)) != RT_ERR_OK)
return ret;
phyData |= (0x1 << PHY_8224_RLFD_ENABLE_OFFSET);
if ((ret = phy_common_general_reg_mmd_set(unit, port, PHY_MMD_VEND2, 0xA442, phyData)) != RT_ERR_OK)
return ret;
return ret;
}
int32
_phy_8224_rtctChannelLen_get(uint32 unit, rtk_port_t port, uint32 channel, uint32 *pChLen)
{
int32 ret = RT_ERR_OK;
uint32 channel_l, channel_h;
uint32 cable_len;
switch (channel)
{
case 0:
if ((ret = _phy_8224_sram_get(unit, port, (0x802c - 0x4), &channel_h)) != RT_ERR_OK)
return ret;
if ((ret = _phy_8224_sram_get(unit, port, (0x802d - 0x4), &channel_l)) != RT_ERR_OK)
return ret;
break;
case 1:
if ((ret = _phy_8224_sram_get(unit, port, (0x8030 - 0x4), &channel_h)) != RT_ERR_OK)
return ret;
if ((ret = _phy_8224_sram_get(unit, port, (0x8031 - 0x4), &channel_l)) != RT_ERR_OK)
return ret;
break;
case 2:
if ((ret = _phy_8224_sram_get(unit, port, (0x8034 - 0x4), &channel_h)) != RT_ERR_OK)
return ret;
if ((ret = _phy_8224_sram_get(unit, port, (0x8035 - 0x4), &channel_l)) != RT_ERR_OK)
return ret;
break;
case 3:
if ((ret = _phy_8224_sram_get(unit, port, (0x8038 - 0x4), &channel_h)) != RT_ERR_OK)
return ret;
if ((ret = _phy_8224_sram_get(unit, port, (0x8039 - 0x4), &channel_l)) != RT_ERR_OK)
return ret;
break;
default:
return RT_ERR_OUT_OF_RANGE;
break;
}
cable_len = ((channel_h & 0xff00)+((channel_l >> 8) & 0xff));
if (cable_len >= RTL8224_RTCT_LEN_OFFSET)
cable_len = cable_len - RTL8224_RTCT_LEN_OFFSET;
else
cable_len = 0;
cable_len = (cable_len*10)/RTL8224_RTCT_LEN_CABLE_FACTOR;
*pChLen = cable_len;
return ret;
}
int32
_phy_8224_rtctStatus_convert(uint32 phyData,
uint32 *pShort, uint32 *pOpen,
uint32 *pMismatch, uint32 *pPairBusy)
{
if (phyData & BIT_6)
{
if (phyData & BIT_5)
{
/* normal */
return RT_ERR_OK;
}
else if (phyData & BIT_3)
{
*pOpen = 1;
}
else if (phyData & BIT_4)
{
*pShort = 1;
}
else if (phyData & BIT_0)
{
*pPairBusy = 1;
}
else if (phyData & BIT_7) /* Interpair short */
{
*pShort = 2;
}
}
else
{
return RT_ERR_PHY_RTCT_NOT_FINISH;
}
return RT_ERR_OK;
}
int32
_phy_8224_common_c45_enable_get(uint32 unit, rtk_port_t port, rtk_enable_t *pEnable)
{
int32 ret;
uint32 phyData = 0;
if ((ret = phy_common_general_reg_mmd_get(unit, port, PHY_MMD_PMAPMD, 0, &phyData)) != RT_ERR_OK)
return ret;
*pEnable = (phyData & BIT_11) ? (DISABLED) : (ENABLED);
return ret;
}
int32
_phy_8224_common_c45_speedDuplexStatusResReg_get(uint32 unit, rtk_port_t port, rtk_port_speed_t *pSpeed, rtk_port_duplex_t *pDuplex)
{
int32 ret = RT_ERR_FAILED;
uint32 phyData, spd;
*pSpeed = PORT_SPEED_10M;
*pDuplex = PORT_HALF_DUPLEX;
if ((ret = phy_common_general_reg_mmd_get(unit, port, PHY_MMD_VEND2, 0xA434, &phyData)) != RT_ERR_OK)
return ret;
/* [10:9,5:4] */
spd = ((phyData & (0x3 << 9)) >> (9 - 2)) | ((phyData & (0x3 << 4)) >> 4);
switch (spd)
{
case 0x0:
*pSpeed = PORT_SPEED_10M;
break;
case 0x1:
*pSpeed = PORT_SPEED_100M;
break;
case 0x2:
*pSpeed = PORT_SPEED_1000M;
break;
case 0x3:
*pSpeed = PORT_SPEED_500M;
break;
case 0x4:
*pSpeed = PORT_SPEED_10G;
break;
case 0x5:
*pSpeed = PORT_SPEED_2_5G;
break;
case 0x6:
*pSpeed = PORT_SPEED_5G;
break;
case 0x7:
*pSpeed = PORT_SPEED_2_5G_LITE;
break;
case 0x8:
*pSpeed = PORT_SPEED_5G_LITE;
break;
case 0x9:
*pSpeed = PORT_SPEED_10G_LITE;
break;
default:
*pSpeed = PORT_SPEED_10M;
break;
}
*pDuplex = (phyData & 0x8)? PORT_FULL_DUPLEX : PORT_HALF_DUPLEX;
return RT_ERR_OK;
}
int32
_phy_8224_tx_polarity_swap_set(rtk_port_t port, uint32 value)
{
struct rtl8224_priv_info *priv = (struct rtl8224_priv_info *)port->priv;
int32 ret = RT_ERR_OK;
uint32 phyData, msb_bit, lsb_bit;
uint32 unit = 0;
rtk_port_t base_port;
uint8 port_offset = 0;
base_port = priv->basePort;
if ((ret = phy_8224_get_port_offset(base_port, port, &port_offset)) != RT_ERR_OK)
return ret;
lsb_bit = (port_offset*RTL8224_PHY_TX_POLARITY_SWAP_OFFSET);
msb_bit = lsb_bit + (RTL8224_PHY_TX_POLARITY_SWAP_OFFSET - 1);
if ((ret = _phy_8224_intReg_get(unit, port, RTL8224_PHY_TX_POLARITY_SWAP, msb_bit, lsb_bit, &phyData)) != RT_ERR_OK)
return ret;
phyData &= (~(BIT_0 | BIT_1 | BIT_2 | BIT_3));
phyData |= (value & RTK_PHY_CTRL_MDI_POLARITY_SWAP_CH_A)? (BIT_0) : (0);
phyData |= (value & RTK_PHY_CTRL_MDI_POLARITY_SWAP_CH_B)? (BIT_1) : (0);
phyData |= (value & RTK_PHY_CTRL_MDI_POLARITY_SWAP_CH_C)? (BIT_2) : (0);
phyData |= (value & RTK_PHY_CTRL_MDI_POLARITY_SWAP_CH_D)? (BIT_3) : (0);
if ((ret = _phy_8224_intReg_set(unit, port, RTL8224_PHY_TX_POLARITY_SWAP, msb_bit, lsb_bit, phyData)) != RT_ERR_OK)
return ret;
return ret;
}
int32
_phy_8224_mdi_reverse_set(rtk_port_t port, uint32 value)
{
struct rtl8224_priv_info *priv = (struct rtl8224_priv_info *)port->priv;
int32 ret = RT_ERR_OK;
uint32 phyData;
uint32 unit = 0;
rtk_port_t base_port;
uint8 port_offset = 0;
base_port = priv->basePort;
if ((ret = phy_8224_get_port_offset(base_port, port, &port_offset)) != RT_ERR_OK)
return ret;
if ((ret = _phy_8224_intReg_get(unit, port, RTL8224_PHY_MDI_REVERSE, port_offset, port_offset, &phyData)) != RT_ERR_OK)
return ret;
/* PortOffset 0&1 --> 0: Reverse, 1: Normal */
/* PortOffset 2&3 --> 0: Normal, 1: Reverse */
if(value != 0) /*Need to reverse*/
{
switch(port_offset)
{
case 0:
case 1:
phyData = 0;
break;
case 2:
case 3:
phyData = 1;
break;
default:
return RT_ERR_FAILED;
}
}else{
switch(port_offset)
{
case 0:
case 1:
phyData = 1;
break;
case 2:
case 3:
phyData = 0;
break;
default:
return RT_ERR_FAILED;
}
}
if ((ret = _phy_8224_intReg_set(unit, port, RTL8224_PHY_MDI_REVERSE, port_offset, port_offset, phyData)) != RT_ERR_OK)
return ret;
return ret;
}
rtk_port_t
_phy_8224_get_phydevice_by_offset(int32 unit, rtk_port_t baseId, uint8 offset)
{
struct rtl8224_priv_info *priv = (struct rtl8224_priv_info *)baseId->priv;
if(priv->memberPort[offset] != NULL)
{
return priv->memberPort[offset];
}else{
return NULL;
}
}
static int32 _phy_8224_sdsRegField_set(uint32 unit, rtk_port_t port, uint32 sdsPage, uint32 sdsReg, uint8 msb, uint8 lsb, uint32 data)
{
struct rtl8224_priv_info *priv = (struct rtl8224_priv_info *)port->priv;
int32 ret;
rtk_port_t base_port;
uint32 reg_data = 0;
uint32 mask = 0;
if ((msb > 15) || (lsb > 15) || (msb < lsb))
{
return RT_ERR_FAILED;
}
base_port = priv->basePort;
phy_rtl8224_sdsReg_get(unit, base_port, sdsPage, sdsReg, &reg_data);
ret = _phy_8224_mask_get(msb, lsb, &mask);
if(ret != RT_ERR_OK)
return ret;
reg_data &= ~(mask);
reg_data |= (((data) << lsb) & mask);
phy_rtl8224_sdsReg_set(unit, base_port, sdsPage, sdsReg, reg_data);
return RT_ERR_OK;
}
int32
_phy_8224_rtctResult_get(uint32 unit, rtk_port_t port, rtk_rtctResult_t *pRtctResult)
{
int32 ret = RT_ERR_OK;
uint32 phyData = 0, speed = 0, duplex = 0;
uint32 channel_status = 0;
uint32 channel_length;
osal_memset(pRtctResult, 0, sizeof(rtk_rtctResult_t));
/* Check the port is link up or not?
* Due to bit 2 is LL(latching low), need to read twice to get current status
*/
if ((ret = phy_common_general_reg_mmd_get(unit, port, PHY_MMD_PMAPMD, 0x1, &phyData)) != RT_ERR_OK)
return ret;
if ((ret = phy_common_general_reg_mmd_get(unit, port, PHY_MMD_PMAPMD, 0x1, &phyData)) != RT_ERR_OK)
return ret;
_phy_8224_common_c45_speedDuplexStatusResReg_get(unit, port, &speed, &duplex);
if (phyData & BIT_2)
{
if (speed == PORT_SPEED_10M)
{
return RT_ERR_PORT_NOT_SUPPORTED;
}
/* If the port link is up,
* return cable length from Channel Estimation
*/
osal_memset(pRtctResult, 0, sizeof(rtk_rtctResult_t));
if (speed == PORT_SPEED_2_5G) /* 2.5GM */
{
pRtctResult->linkType = PORT_SPEED_2_5G;
/* The channel A length is store in UC 0x8ff5 [15:8]*/
if ((ret = phy_common_general_reg_mmd_set(unit, port, PHY_MMD_VEND2, 0xb87c, 0x8ff5)) != RT_ERR_OK)
return ret;
if ((ret = phy_common_general_reg_mmd_get(unit, port, PHY_MMD_VEND2, 0xb87e, &phyData)) != RT_ERR_OK)
return ret;
pRtctResult->un.ge_result.channelALen = ((phyData & 0xFF00) >> 8)*100;
/* The channel B length is store in UC 0x8ff6 [15:8]*/
if ((ret = phy_common_general_reg_mmd_set(unit, port, PHY_MMD_VEND2, 0xb87c, 0x8ff6)) != RT_ERR_OK)
return ret;
if ((ret = phy_common_general_reg_mmd_get(unit, port, PHY_MMD_VEND2, 0xb87e, &phyData)) != RT_ERR_OK)
return ret;
pRtctResult->un.ge_result.channelBLen = ((phyData & 0xFF00) >> 8)*100;
/* The channel C length is store in UC 0x8ff7 [15:8]*/
if ((ret = phy_common_general_reg_mmd_set(unit, port, PHY_MMD_VEND2, 0xb87c, 0x8ff7)) != RT_ERR_OK)
return ret;
if ((ret = phy_common_general_reg_mmd_get(unit, port, PHY_MMD_VEND2, 0xb87e, &phyData)) != RT_ERR_OK)
return ret;
pRtctResult->un.ge_result.channelCLen = ((phyData & 0xFF00) >> 8)*100;
/* The channel D length is store in UC 0x8ff8 [15:8]*/
if ((ret = phy_common_general_reg_mmd_set(unit, port, PHY_MMD_VEND2, 0xb87c, 0x8ff8)) != RT_ERR_OK)
return ret;
if ((ret = phy_common_general_reg_mmd_get(unit, port, PHY_MMD_VEND2, 0xb87e, &phyData)) != RT_ERR_OK)
return ret;
pRtctResult->un.ge_result.channelDLen = ((phyData & 0xFF00) >> 8)*100;
}
else if (speed == PORT_SPEED_1000M) /* 1000M */
{
/* The Length is store in [7:0], and the unit is meter*/
if ((ret = phy_common_general_reg_mmd_get(unit, port, PHY_MMD_VEND2, 0xA880, &phyData)) != RT_ERR_OK)
return ret;
pRtctResult->linkType = PORT_SPEED_1000M;
pRtctResult->un.ge_result.channelALen = (phyData & 0x00FF)*100;
pRtctResult->un.ge_result.channelBLen = (phyData & 0x00FF)*100;
pRtctResult->un.ge_result.channelCLen = (phyData & 0x00FF)*100;
pRtctResult->un.ge_result.channelDLen = (phyData & 0x00FF)*100;
}
else /* 100M */
{
/* The Length is store in [7:0], and the unit is meter*/
if ((ret = phy_common_general_reg_mmd_get(unit, port, PHY_MMD_VEND2, 0xA880, &phyData)) != RT_ERR_OK)
return ret;
pRtctResult->linkType = PORT_SPEED_100M;
pRtctResult->un.fe_result.rxLen = (phyData & 0x00FF)*100;
pRtctResult->un.fe_result.txLen = (phyData & 0x00FF)*100;
}
}
else
{
if ((ret = phy_common_general_reg_mmd_get(unit, port, PHY_MMD_VEND2, 0xA422, &phyData)) != RT_ERR_OK)
return ret;
if ((phyData & BIT_15) == 0)
return RT_ERR_PHY_RTCT_NOT_FINISH;
pRtctResult->linkType = PORT_SPEED_1000M;
if ((ret = _phy_8224_rtctChannelLen_get(unit, port, 0, &channel_length)) != RT_ERR_OK)
return ret;
pRtctResult->un.ge_result.channelALen = (channel_length*100); /* RTCT length unit is cm */
RT_LOG(LOG_DEBUG, (MOD_HAL|MOD_PORT), "%s[CHANNEL A]: Length 0x%d\n", __FUNCTION__, channel_length);
if ((ret = _phy_8224_rtctChannelLen_get(unit, port, 1, &channel_length)) != RT_ERR_OK)
return ret;
pRtctResult->un.ge_result.channelBLen = (channel_length*100); /* RTCT length unit is cm */
RT_LOG(LOG_DEBUG, (MOD_HAL|MOD_PORT), "%s[CHANNEL B]: Length 0x%d\n", __FUNCTION__, channel_length);
if ((ret = _phy_8224_rtctChannelLen_get(unit, port, 2, &channel_length)) != RT_ERR_OK)
return ret;
pRtctResult->un.ge_result.channelCLen = (channel_length*100); /* RTCT length unit is cm */
RT_LOG(LOG_DEBUG, (MOD_HAL|MOD_PORT), "%s[CHANNEL C]: Length 0x%d\n", __FUNCTION__, channel_length);
if ((ret = _phy_8224_rtctChannelLen_get(unit, port, 3, &channel_length)) != RT_ERR_OK)
return ret;
pRtctResult->un.ge_result.channelDLen = (channel_length*100); /* RTCT length unit is cm */
RT_LOG(LOG_DEBUG, (MOD_HAL|MOD_PORT), "%s[CHANNEL D]: Length 0x%d\n", __FUNCTION__, channel_length);
if ((ret = _phy_8224_sram_get(unit, port, 0x8026, &channel_status)) != RT_ERR_OK)
return ret;
if ((ret = _phy_8224_rtctStatus_convert(channel_status,
&pRtctResult->un.ge_result.channelAShort, &pRtctResult->un.ge_result.channelAOpen,
&pRtctResult->un.ge_result.channelAMismatch, &pRtctResult->un.ge_result.channelAPairBusy)) != RT_ERR_OK)
return ret;
RT_LOG(LOG_DEBUG, (MOD_HAL|MOD_PORT), "%s[CHANNEL A]: Status 0x%04X\n", __FUNCTION__, channel_status);
if ((ret = _phy_8224_sram_get(unit, port, 0x802A, &channel_status)) != RT_ERR_OK)
return ret;
if ((ret = _phy_8224_rtctStatus_convert(channel_status,
&pRtctResult->un.ge_result.channelBShort, &pRtctResult->un.ge_result.channelBOpen,
&pRtctResult->un.ge_result.channelBMismatch, &pRtctResult->un.ge_result.channelBPairBusy)) != RT_ERR_OK)
return ret;
RT_LOG(LOG_DEBUG, (MOD_HAL|MOD_PORT), "%s[CHANNEL B]: Status 0x%04X\n", __FUNCTION__, channel_status);
if ((ret = _phy_8224_sram_get(unit, port, 0x802E, &channel_status)) != RT_ERR_OK)
return ret;
if ((ret = _phy_8224_rtctStatus_convert(channel_status,
&pRtctResult->un.ge_result.channelCShort, &pRtctResult->un.ge_result.channelCOpen,
&pRtctResult->un.ge_result.channelCMismatch, &pRtctResult->un.ge_result.channelCPairBusy)) != RT_ERR_OK)
return ret;
RT_LOG(LOG_DEBUG, (MOD_HAL|MOD_PORT), "%s[CHANNEL C]: Status 0x%04X\n", __FUNCTION__, channel_status);
if ((ret = _phy_8224_sram_get(unit, port, 0x8032, &channel_status)) != RT_ERR_OK)
return ret;
if ((ret = _phy_8224_rtctStatus_convert(channel_status,
&pRtctResult->un.ge_result.channelDShort, &pRtctResult->un.ge_result.channelDOpen,
&pRtctResult->un.ge_result.channelDMismatch, &pRtctResult->un.ge_result.channelDPairBusy)) != RT_ERR_OK)
return ret;
RT_LOG(LOG_DEBUG, (MOD_HAL|MOD_PORT), "%s[CHANNEL D]: Status 0x%04X\n", __FUNCTION__, channel_status);
}
return ret;
}
int32
_phy_8224_intr_enable(rtk_port_t port, uint32 enable)
{
int32 ret = RT_ERR_OK;
uint32 phyData, bit_idx;
rtk_port_t base_port = 0;
uint8 port_offset = 0;
uint32 unit = 0;
base_port = _phy_8224_base_port_get(unit, port);
if ((ret = phy_8224_get_port_offset(base_port, port, &port_offset)) != RT_ERR_OK)
return ret;
if (enable == DISABLED)
phyData = 0;
else
phyData = 1;
bit_idx = (RTL8224_IMR_EXT_GPHY_3_0_OFFSET + port_offset);
ret = _phy_8224_intRegBit_set(unit, port, RTL8224_IMR_EXT_GPHY, bit_idx, phyData);
return ret;
}
int32
_phy_8224_intr_read_clear(rtk_port_t port, uint32 *pStatus)
{
int32 ret = RT_ERR_OK;
uint32 phyData = 0;
rtk_port_t base_port;
uint8 port_offset;
uint32 unit = 0;
base_port = _phy_8224_base_port_get(unit, port);
if ((ret = phy_8224_get_port_offset(base_port, port, &port_offset)) != RT_ERR_OK)
return ret;
/* Read and Clear */
if ((ret = phy_common_general_reg_mmd_get(unit, port, PHY_MMD_VEND2, 0xA43A, &phyData)) != RT_ERR_OK)
return ret;
/* Clean ISR_EXT_GPHY */
/* Write 1 to clear */
if ((ret = _phy_8224_intRegBit_set(unit, port, RTL8224_ISR_EXT_GPHY, port_offset, 1)) != RT_ERR_OK)
return ret;
*pStatus = phyData;
return ret;
}
int32
phy_8224_sdsReg_set(uint32 unit, rtk_port_t port, uint32 sdsPage, uint32 sdsReg, uint32 data)
{
struct rtl8224_priv_info *priv = (struct rtl8224_priv_info *)port->priv;
rtk_port_t base_port;
base_port = priv->basePort;
return phy_rtl8224_sdsReg_set(unit, base_port, sdsPage, sdsReg, data);
}
int32
phy_8224_sdsOpCode_set(uint32 unit, rtk_port_t port, uint32 opCode)
{
struct rtl8224_priv_info *priv = (struct rtl8224_priv_info *)port->priv;
rtk_port_t base_port;
base_port = priv->basePort;
return _phy_8224_sdsRegField_set(unit, base_port, PHY_8224_NWAY_OPCODE_PAGE, PHY_8224_NWAY_OPCODE_REG, PHY_8224_NWAY_OPCODE_HIGH_BIT, PHY_8224_NWAY_OPCODE_LOW_BIT, opCode);
}
int32
phy_8224_sdsAmPeriod_set(uint32 unit, rtk_port_t port, uint32 amPeriod)
{
struct rtl8224_priv_info *priv = (struct rtl8224_priv_info *)port->priv;
rtk_port_t base_port;
base_port = priv->basePort;
return _phy_8224_sdsRegField_set(unit, base_port, PHY_8224_AM_PERIOD_PAGE, PHY_8224_AM_PERIOD_REG, PHY_8224_AM_PERIOD_HIGH_BIT, PHY_8224_AM_PERIOD_LOW_BIT, amPeriod);
}
int32
phy_8224_sdsAllAm_set(uint32 unit, rtk_port_t port, uint32 value)
{
struct rtl8224_priv_info *priv = (struct rtl8224_priv_info *)port->priv;
rtk_port_t base_port;
base_port = priv->basePort;
/* AM0 */
_phy_8224_sdsRegField_set(unit, base_port, PHY_8224_AM_PAGE, PHY_8224_AM_CFG0_REG, PHY_8224_AM0_M0_HIGH_BIT, PHY_8224_AM0_M0_LOW_BIT, value);
_phy_8224_sdsRegField_set(unit, base_port, PHY_8224_AM_PAGE, PHY_8224_AM_CFG0_REG, PHY_8224_AM0_M1_HIGH_BIT, PHY_8224_AM0_M1_LOW_BIT, value);
_phy_8224_sdsRegField_set(unit, base_port, PHY_8224_AM_PAGE, PHY_8224_AM_CFG1_REG, PHY_8224_AM0_M2_HIGH_BIT, PHY_8224_AM0_M2_LOW_BIT, value);
/* AM1 */
_phy_8224_sdsRegField_set(unit, base_port, PHY_8224_AM_PAGE, PHY_8224_AM_CFG1_REG, PHY_8224_AM1_M0_HIGH_BIT, PHY_8224_AM1_M0_LOW_BIT, value);
_phy_8224_sdsRegField_set(unit, base_port, PHY_8224_AM_PAGE, PHY_8224_AM_CFG2_REG, PHY_8224_AM1_M1_HIGH_BIT, PHY_8224_AM1_M1_LOW_BIT, value);
_phy_8224_sdsRegField_set(unit, base_port, PHY_8224_AM_PAGE, PHY_8224_AM_CFG2_REG, PHY_8224_AM1_M2_HIGH_BIT, PHY_8224_AM1_M2_LOW_BIT, value);
/* AM2 */
_phy_8224_sdsRegField_set(unit, base_port, PHY_8224_AM_PAGE, PHY_8224_AM_CFG3_REG, PHY_8224_AM2_M0_HIGH_BIT, PHY_8224_AM2_M0_LOW_BIT, value);
_phy_8224_sdsRegField_set(unit, base_port, PHY_8224_AM_PAGE, PHY_8224_AM_CFG3_REG, PHY_8224_AM2_M1_HIGH_BIT, PHY_8224_AM2_M1_LOW_BIT, value);
_phy_8224_sdsRegField_set(unit, base_port, PHY_8224_AM_PAGE, PHY_8224_AM_CFG4_REG, PHY_8224_AM2_M2_HIGH_BIT, PHY_8224_AM2_M2_LOW_BIT, value);
/* AM3 */
_phy_8224_sdsRegField_set(unit, base_port, PHY_8224_AM_PAGE, PHY_8224_AM_CFG4_REG, PHY_8224_AM3_M0_HIGH_BIT, PHY_8224_AM3_M0_LOW_BIT, value);
_phy_8224_sdsRegField_set(unit, base_port, PHY_8224_AM_PAGE, PHY_8224_AM_CFG5_REG, PHY_8224_AM3_M1_HIGH_BIT, PHY_8224_AM3_M1_LOW_BIT, value);
_phy_8224_sdsRegField_set(unit, base_port, PHY_8224_AM_PAGE, PHY_8224_AM_CFG5_REG, PHY_8224_AM3_M2_HIGH_BIT, PHY_8224_AM3_M2_LOW_BIT, value);
return RT_ERR_OK;
}
int32
phy_8224_sdsExtra_set(uint32 unit, rtk_port_t port)
{
struct rtl8224_priv_info *priv = (struct rtl8224_priv_info *)port->priv;
rtk_port_t base_port;
base_port = priv->basePort;
_phy_8224_sdsRegField_set(unit, base_port, PHY_8224_AM_PERIOD_PAGE, PHY_8224_TGR_PRO_0_REG17, PHY_8224_USXG_V2P1P2_RXOFF_HIGH_BIT, PHY_8224_USXG_V2P1P2_RXOFF_LOW_BIT, 0x1);
_phy_8224_sdsRegField_set(unit, base_port, PHY_8224_NWAY_OPCODE_PAGE, PHY_8224_TGR_PRO_1_REG19, PHY_8224_USXG_RXDV_OFF_HIGH_BIT, PHY_8224_USXG_RXDV_OFF_LOW_BIT, 0x1);
return RT_ERR_OK;
}
/* Function Name:
* phy_8224_chipVer_get
* Description:
* Set PHY registers.
* Input:
* unit - unit id
* port - port id
* Output:
* pVer - chip version
* Return:
* RT_ERR_OK - OK
* RT_ERR_FAILED - Failed
* Note:
* NA
*/
int32
phy_8224_chipVer_get(uint32 unit, rtk_port_t port, uint32 *pVer)
{
int32 ret;
uint32 regData;
if ((ret = _phy_8224_intReg_set(unit, port, 0xbb00000c, 19, 16, 0xa)) != RT_ERR_OK)
return ret;
if ((ret = _phy_8224_intReg_get(unit, port, 0xbb00000c, 31, 28, &regData)) != RT_ERR_OK)
return ret;
*pVer = regData;
if ((ret = _phy_8224_intReg_set(unit, port, 0xbb00000c, 19, 16, 0x0)) != RT_ERR_OK)
return ret;
return RT_ERR_OK;
}
int32
phy_8224_linkDownPowerSavingEnable_set(uint32 unit, rtk_port_t port, rtk_enable_t enable)
{
int32 ret = 0;
uint32 phyData = 0;
if ((ret = phy_common_general_reg_mmd_get(unit, port, PHY_MMD_VEND2, 0xA430, &phyData)) != RT_ERR_OK)
return ret;
phyData &= (~BIT_2);
phyData |= (enable == DISABLED) ? (0) : (BIT_2);
if ((ret = phy_common_general_reg_mmd_set(unit, port, PHY_MMD_VEND2, 0xA430, phyData)) != RT_ERR_OK)
return ret;
return RT_ERR_OK;
}
int32
phy_8224_linkDownPowerSavingEnable_get(uint32 unit, rtk_port_t port, rtk_enable_t *pEnable)
{
int32 ret = 0;
uint32 phyData = 0;
if ((ret = phy_common_general_reg_mmd_get(unit, port, PHY_MMD_VEND2, 0xA430, &phyData)) != RT_ERR_OK)
return ret;
*pEnable = (phyData & BIT_2) ? (ENABLED) : (DISABLED);
return RT_ERR_OK;
}
int
phy_8224_get_port_offset(rtk_port_t baseId, rtk_port_t port, uint8 *offset)
{
*offset = (uint8)((port->mdio.addr) - (baseId->mdio.addr));
if(*offset >= RTL8224_PORT_NUM)
return -1;
else
return 0;
}
int32
phy_8224_sdsReg_get(uint32 unit, rtk_port_t port, uint32 sdsPage, uint32 sdsReg, uint32 *pData)
{
struct rtl8224_priv_info *priv = (struct rtl8224_priv_info *)port->priv;
rtk_port_t base_port;
base_port = priv->basePort;
return phy_rtl8224_sdsReg_get(unit, base_port, sdsPage, sdsReg, pData);
}

View file

@ -0,0 +1,231 @@
/*
* Copyright (C) 2022 Realtek Semiconductor Corp.
* All Rights Reserved.
*
* This program is the proprietary software of Realtek Semiconductor
* Corporation and/or its licensors, and only be used, duplicated,
* modified or distributed under the authorized license from Realtek.
*
* ANY USE OF THE SOFTWARE OTHER THAN AS AUTHORIZED UNDER
* THIS LICENSE OR COPYRIGHT LAW IS PROHIBITED.
*
* $Revision: $
* $Date: $
*
* Purpose : PHY 8224 Driver APIs.
*
* Feature : PHY 8224 Driver APIs.
*
*/
#ifndef __HAL_PHY_PHY_RTL8224_H__
#define __HAL_PHY_PHY_RTL8224_H__
/*
* Include Files
*/
#include "rtk_phylib.h"
/*
* Symbol Definition
*/
/*
* Data Declaration
*/
//extern rt_phyInfo_t phy_8224_info;
/*
* Macro Declaration
*/
#define RTL8224_PORT_NUM (4)
#define RTL8224_PER_PORT_MAX_LED (4)
#define RTL8224_LED_PAD_MAX_INDEX (29)
#define RTL8224_INTER_REG_LSB (0)
#define RTL8224_INTER_REG_MSB (31)
#define RTL8224_PHY_MDI_REVERSE 0xbb000a90
#define RTL8224_MDI_REVERSE_OFFSET (0)
#define RTL8224_PHY_TX_POLARITY_SWAP 0xbb000a94
#define RTL8224_PHY_TX_POLARITY_SWAP_OFFSET (4)
#define RTL8224_CFG_PHY_POLL_CMD1_REG (0xbb000ad8)
#define RTL8224_CMD_RD_EN_3_0_MSB (3)
#define RTL8224_CMD_RD_EN_3_0_LSB (0)
/* Internal Register */
/* Global interrupt control */
#define PHY_8224_INT_MODE_HIGH_LEVEL (0)
#define PHY_8224_INT_MODE_LOW_LEVEL (1)
#define PHY_8224_INT_MODE_POS_EDGE (2)
#define PHY_8224_INT_MODE_NEG_EDGE (3)
#define RTL8224_ISR_SW_INT_MODE 0xbb005f84
#define RTL8224_SWITCH_IE_OFFSET (6)
#define RTL8224_SWITCH_INT_MODE_OFFSET (1)
#define RTL8224_SWITCH_INT_MODE_MSB (2)
/* GPHY interrupt control */
#define RTL8224_IMR_EXT_GPHY 0xbb005f5c
#define RTL8224_IMR_EXT_GPHY_3_0_OFFSET (0)
#define RTL8224_IMR_EXT_GPHY_3_0_MSB (3)
#define RTL8224_ISR_EXT_GPHY 0xbb005fb0
#define RTL8224_ISR_EXT_GPHY_OFFSET (0)
#define RTL8224_ISR_EXT_GPHY_MSB (3)
#define RTL8224_IMR_EXT_WOL 0xbb005f68
#define RTL8224_IMR_EXT_PHYWOL_PORT_3_0_OFFSET (9)
#define RTL8224_IMR_EXT_PHYWOL_PORT_3_0_MSB (12)
#define RTL8224_IMR_INT_RLFD 0xbb005f40
#define RTL8224_IMR_INT_RLFD_PORT_8_0_OFFSET (0)
#define RTL8224_IMR_EXT_RLFD 0xbb005f64
#define RTL8224_IMR_EXT_RLFD_PORT_8_0_OFFSET (0)
#define RTL8224_IMR_EXT_RLFD_PORT_SHIFT (4)
#define RTL8224_GPHY_INTR_STATUS_REG (0xbb00a43a)
#define GPHY_AN_ERROR_BIT (0)
#define GPHY_AN_NEXT_PAGE_RECEIVE_BIT (2)
#define GPHY_LINK_CHG_BIT (4)
#define GPHY_ALDPS_STS_CHG_BIT (9)
#define GPHY_FATEL_ERROR_BIT (11)
#define RTL8224_ISR_INT_TM_RLFD_REG (0xbb005f94)
#define RTL8224_ISR_INT_RLFD_PORT_8_0_OFFSET (0)
#define RTL8224_ISR_INT_RLFD_PORT_8_0_MSB (8)
#define RTL8224_ISR_EXT_TM_RLFD_REG (0xbb005fb8)
#define RTL8224_ISR_EXT_RLFD_PORT_8_0_OFFSET (0)
#define RTL8224_ISR_EXT_RLFD_PORT_8_0_MSB (8)
#define RTL8224_ISR_EXT_RLFD_PORT_SHIFT (4)
#define RTL8224_RLFD_CTRL_REG (0xbb006458)
#define RTL8224_RLFD_SEL_OFFSET (10)
#define RTL8224_RLFD_EN_OFFSET (9)
#define PHY_RTL8224_VER_A (0)
#define PHY_RTL8224_VER_B (1)
#define PHY_RTL8224_VER_C (2)
#define PHY_8224_NWAY_OPCODE_PAGE (0x7)
#define PHY_8224_NWAY_OPCODE_REG (16)
#define PHY_8224_NWAY_OPCODE_HIGH_BIT (7)
#define PHY_8224_NWAY_OPCODE_LOW_BIT (0)
#define PHY_8224_NWAY_OPCODE_PAGE (0x7)
#define PHY_8224_TGR_PRO_1_REG19 (19)
#define PHY_8224_USXG_RXDV_OFF_HIGH_BIT (12)
#define PHY_8224_USXG_RXDV_OFF_LOW_BIT (12)
#define PHY_8224_AM_PERIOD_PAGE (0x6)
#define PHY_8224_TGR_PRO_0_REG17 (17)
#define PHY_8224_USXG_V2P1P2_RXOFF_HIGH_BIT (13)
#define PHY_8224_USXG_V2P1P2_RXOFF_LOW_BIT (13)
#define PHY_8224_AM_PERIOD_PAGE (0x6)
#define PHY_8224_AM_PERIOD_REG (18)
#define PHY_8224_AM_PERIOD_HIGH_BIT (15)
#define PHY_8224_AM_PERIOD_LOW_BIT (0)
#define PHY_8224_AM_PAGE (0x6)
#define PHY_8224_AM_CFG0_REG (19)
#define PHY_8224_AM0_M0_HIGH_BIT (7)
#define PHY_8224_AM0_M0_LOW_BIT (0)
#define PHY_8224_AM0_M1_HIGH_BIT (15)
#define PHY_8224_AM0_M1_LOW_BIT (8)
#define PHY_8224_AM_PAGE (0x6)
#define PHY_8224_AM_CFG1_REG (20)
#define PHY_8224_AM0_M2_HIGH_BIT (7)
#define PHY_8224_AM0_M2_LOW_BIT (0)
#define PHY_8224_AM1_M0_HIGH_BIT (15)
#define PHY_8224_AM1_M0_LOW_BIT (8)
#define PHY_8224_AM_PAGE (0x6)
#define PHY_8224_AM_CFG2_REG (21)
#define PHY_8224_AM1_M1_HIGH_BIT (7)
#define PHY_8224_AM1_M1_LOW_BIT (0)
#define PHY_8224_AM1_M2_HIGH_BIT (15)
#define PHY_8224_AM1_M2_LOW_BIT (8)
#define PHY_8224_AM_PAGE (0x6)
#define PHY_8224_AM_CFG3_REG (22)
#define PHY_8224_AM2_M0_HIGH_BIT (7)
#define PHY_8224_AM2_M0_LOW_BIT (0)
#define PHY_8224_AM2_M1_HIGH_BIT (15)
#define PHY_8224_AM2_M1_LOW_BIT (8)
#define PHY_8224_AM_PAGE (0x6)
#define PHY_8224_AM_CFG4_REG (23)
#define PHY_8224_AM2_M2_HIGH_BIT (7)
#define PHY_8224_AM2_M2_LOW_BIT (0)
#define PHY_8224_AM3_M0_HIGH_BIT (15)
#define PHY_8224_AM3_M0_LOW_BIT (8)
#define PHY_8224_AM_PAGE (0x6)
#define PHY_8224_AM_CFG5_REG (24)
#define PHY_8224_AM3_M1_HIGH_BIT (7)
#define PHY_8224_AM3_M1_LOW_BIT (0)
#define PHY_8224_AM3_M2_HIGH_BIT (15)
#define PHY_8224_AM3_M2_LOW_BIT (8)
#define PHY_8224_NWAY_AN_PAGE (0x7)
#define PHY_8224_NWAY_AN_REG (17)
#define PHY_8224_QHSG_AN_CH3_EN_BIT (3)
#define PHY_8224_QHSG_AN_CH2_EN_BIT (2)
#define PHY_8224_QHSG_AN_CH1_EN_BIT (1)
#define PHY_8224_QHSG_AN_CH0_EN_BIT (0)
#define RTL8224_RTCT_LEN_OFFSET (620)
#define RTL8224_RTCT_LEN_CABLE_FACTOR (780)
/* tapbin def bitmask */
#define RTL8224_TAPBIN_DEF_OFFSET (8)
#define RTL8224_TAPBIN_DEF_BITMASK (0x00000F00)
#define PHY_8224_RLFD_ENABLE_OFFSET (15)
#define INVALID_BASE_PORT_PHY_ADDRESS (255)
#define RTL8224_BASE_PORT_OFFSET (0)
#define RTL8224_MDI_PIN_SWAP (1)
#define RTL8224_MDI_PAIR_SWAP (0)
#define RT_ERR_PHY_RTCT_NOT_FINISH (-2)
struct rtl8224_priv_info
{
rtk_phylib_phy_t phytype;
struct phy_device * basePort;
struct phy_device * memberPort[RTL8224_PORT_NUM];
int32 is_basePort;
int32 port_offset;
};
extern int32 phy_8224_chipVer_get(uint32 unit, rtk_port_t port, uint32 *pVer);
extern int32 phy_8224_sdsReg_get(uint32 unit, rtk_port_t port, uint32 sdsPage, uint32 sdsReg, uint32 *pData);
extern int32 phy_8224_sdsReg_set(uint32 unit, rtk_port_t port, uint32 sdsPage, uint32 sdsReg, uint32 data);
extern int32 phy_8224_sdsOpCode_set(uint32 unit, rtk_port_t port, uint32 opCode);
extern int32 phy_8224_sdsAmPeriod_set(uint32 unit, rtk_port_t port, uint32 amPeriod);
extern int32 phy_8224_sdsAllAm_set(uint32 unit, rtk_port_t port, uint32 value);
extern int32 phy_8224_sdsExtra_set(uint32 unit, rtk_port_t port);
extern int phy_8224_get_port_offset(rtk_port_t baseId, rtk_port_t port, uint8 *offset);
extern int32 phy_8224_linkDownPowerSavingEnable_set(uint32 unit, rtk_port_t port, rtk_enable_t enable);
extern int32 phy_8224_linkDownPowerSavingEnable_get(uint32 unit, rtk_port_t port, rtk_enable_t *pEnable);
extern rtk_port_t _phy_8224_get_phydevice_by_offset(int32 unit, rtk_port_t baseId, uint8 offset);
extern int32 _phy_8224_tx_polarity_swap_set(rtk_port_t port, uint32 value);
extern int32 _phy_8224_mdi_reverse_set(rtk_port_t port, uint32 value);
extern int32 _phy_8224_common_c45_enable_get(uint32 unit, rtk_port_t port, rtk_enable_t *pEnable);
extern int32 _phy_8224_common_c45_speedDuplexStatusResReg_get(uint32 unit, rtk_port_t port, rtk_port_speed_t *pSpeed, rtk_port_duplex_t *pDuplex);
extern int32 _phy_8224_intr_enable(rtk_port_t port, uint32 enable);
extern int32 _phy_8224_intr_read_clear(rtk_port_t port, uint32 *pStatus);
extern int32 _phy_8224_interrupt_init(uint32 unit, rtk_port_t port);
extern int32 _phy_8224_rtctResult_get(uint32 unit, rtk_port_t port, rtk_rtctResult_t *pRtctResult);
#endif /* __HAL_PHY_PHY_RTL8224_H__ */

View file

@ -0,0 +1,572 @@
/*
* SPDX-License-Identifier: GPL-2.0-only
*
* Copyright (c) 2023 Realtek Semiconductor Corp. All rights reserved.
*/
#include "rtk_phylib_rtl826xb.h"
/* Indirect Register Access APIs */
int rtk_phylib_826xb_sds_read(rtk_phydev *phydev, uint32 page, uint32 reg, uint8 msb, uint8 lsb, uint32 *pData)
{
int32 ret = 0;
uint32 rData = 0;
uint32 op = (page & 0x3f) | ((reg & 0x1f) << 6) | (0x8000);
uint32 i = 0;
uint32 mask = 0;
mask = UINT32_BITS_MASK(msb,lsb);
RTK_PHYLIB_ERR_CHK(rtk_phylib_mmd_write(phydev, 30, 323, 15, 0, op));
for (i = 0; i < 10; i++)
{
RTK_PHYLIB_ERR_CHK(rtk_phylib_mmd_read(phydev, 30, 323, 15, 15, &rData));
if (rData == 0)
{
break;
}
rtk_phylib_udelay(10);
}
if (i == 10)
{
return -1;
}
RTK_PHYLIB_ERR_CHK(rtk_phylib_mmd_read(phydev, 30, 322, 15, 0, &rData));
*pData = REG32_FIELD_GET(rData, lsb, mask);
return ret;
}
int rtk_phylib_826xb_sds_write(rtk_phydev *phydev, uint32 page, uint32 reg, uint8 msb, uint8 lsb, uint32 data)
{
int32 ret = 0;
uint32 wData = 0, rData = 0;
uint32 op = (page & 0x3f) | ((reg & 0x1f) << 6) | (0x8800);
uint32 mask = 0;
mask = UINT32_BITS_MASK(msb,lsb);
RTK_PHYLIB_ERR_CHK(rtk_phylib_826xb_sds_read(phydev, page, reg, 15, 0, &rData));
wData = REG32_FIELD_SET(rData, data, lsb, mask);
RTK_PHYLIB_ERR_CHK(rtk_phylib_mmd_write(phydev, 30, 321, 15, 0, wData));
RTK_PHYLIB_ERR_CHK(rtk_phylib_mmd_write(phydev, 30, 323, 15, 0, op));
return ret;
}
int rtk_phylib_826xb_indirect_read(rtk_phydev *phydev, uint32 indr_addr, uint32 *pData)
{
int32 ret = 0;
uint32 rData;
RTK_PHYLIB_ERR_CHK(rtk_phylib_mmd_write(phydev, 31, 0xA436, 15, 0, indr_addr));
RTK_PHYLIB_ERR_CHK(rtk_phylib_mmd_read(phydev, 31, 0xA438, 15, 0, &rData));
*pData = rData;
return ret;
}
int rtk_phylib_826xb_sram_read(rtk_phydev *phydev, uint32 indr_addr, uint8 msb, uint8 lsb, uint32 *pData)
{
int32 ret = 0;
uint32 mask = 0, rData = 0;
mask = UINT32_BITS_MASK(msb,lsb);
RTK_PHYLIB_ERR_CHK(rtk_phylib_826xb_indirect_read(phydev, indr_addr, &rData));
*pData = REG32_FIELD_GET(rData, lsb, mask);
return ret;
}
/* MACsec */
int rtk_phylib_826xb_macsec_read(rtk_phydev *phydev, rtk_macsec_dir_t dir, uint32 reg, uint8 msb, uint8 lsb, uint32 *pData)
{
int32 ret = 0;
uint32 data_h = 0, data_l = 0;
uint32 rData = 0;
uint32 mask = 0;
uint32 data_e = 0;
WAIT_COMPLETE_VAR();
mask = UINT32_BITS_MASK(msb,lsb);
switch(dir)
{
case RTK_MACSEC_DIR_EGRESS:
RTK_PHYLIB_ERR_CHK(rtk_phylib_mmd_write(phydev, 30, 0x02FB, 15, 0, reg));
RTK_PHYLIB_ERR_CHK(rtk_phylib_mmd_write(phydev, 30, 0x02FC, 15, 0, 0x10));
WAIT_COMPLETE(10000000)
{
RTK_PHYLIB_ERR_CHK(rtk_phylib_mmd_read(phydev, 30, 0x02FC, 15, 0, &data_e));
if ((data_e & 0x10) == 0x0)
{
#ifdef MACSEC_DBG_PRINT
if (_t_wait != 0)
PR_DBG("[%s-%u] _t_wait: %u\n", __FUNCTION__, dir, _t_wait);
#endif
break;
}
}
if (WAIT_COMPLETE_IS_TIMEOUT())
{
PR_ERR("[%s-%u] timeout!\n", __FUNCTION__, dir);
return RTK_PHYLIB_ERR_TIMEOUT;
}
RTK_PHYLIB_ERR_CHK(rtk_phylib_mmd_read(phydev, 30, 0x02F8, 15, 0, &data_h));
RTK_PHYLIB_ERR_CHK(rtk_phylib_mmd_read(phydev, 30, 0x02F9, 15, 0, &data_l));
break;
case RTK_MACSEC_DIR_INGRESS:
RTK_PHYLIB_ERR_CHK(rtk_phylib_mmd_write(phydev, 31, 0xA6EA, 1, 1, 0x1));
RTK_PHYLIB_ERR_CHK(rtk_phylib_mmd_write(phydev, 30, 0x0300, 15, 0, reg));
RTK_PHYLIB_ERR_CHK(rtk_phylib_mmd_write(phydev, 30, 0x0301, 15, 0, 0x10));
WAIT_COMPLETE(10000000)
{
RTK_PHYLIB_ERR_CHK(rtk_phylib_mmd_read(phydev, 30, 0x0301, 15, 0, &data_e));
if ((data_e & 0x10) == 0x0)
{
#ifdef MACSEC_DBG_PRINT
if (_t_wait != 0)
PR_DBG("[%s-%u] _t_wait: %u\n", __FUNCTION__, dir, _t_wait);
#endif
break;
}
}
if (WAIT_COMPLETE_IS_TIMEOUT())
{
PR_ERR("[%s-%u] timeout!\n", __FUNCTION__, dir);
RTK_PHYLIB_ERR_CHK(rtk_phylib_mmd_write(phydev, 31, 0xA6EA, 1, 1, 0x0));
return RTK_PHYLIB_ERR_TIMEOUT;
}
RTK_PHYLIB_ERR_CHK(rtk_phylib_mmd_read(phydev, 30, 0x02FD, 15, 0, &data_h));
RTK_PHYLIB_ERR_CHK(rtk_phylib_mmd_read(phydev, 30, 0x02FE, 15, 0, &data_l));
RTK_PHYLIB_ERR_CHK(rtk_phylib_mmd_write(phydev, 31, 0xA6EA, 1, 1, 0x0));
break;
default:
return -1;
}
rData = (data_h << 16) + data_l;
*pData = REG32_FIELD_GET(rData, lsb, mask);
return ret;
}
int rtk_phylib_826xb_macsec_write(rtk_phydev *phydev, rtk_macsec_dir_t dir, uint32 reg, uint8 msb, uint8 lsb, uint32 data)
{
int32 ret = 0;
uint32 data_l = data & 0xFFFF;
uint32 data_h = (data >> 16) & 0xFFFF;
uint32 data_e = 0;
WAIT_COMPLETE_VAR();
switch(dir)
{
case RTK_MACSEC_DIR_EGRESS:
RTK_PHYLIB_ERR_CHK(rtk_phylib_mmd_write(phydev, 30, 0x02F8 , 15, 0, data_h));
RTK_PHYLIB_ERR_CHK(rtk_phylib_mmd_write(phydev, 30, 0x02F9 , 15, 0, data_l));
RTK_PHYLIB_ERR_CHK(rtk_phylib_mmd_write(phydev, 30, 0x02FB , 15, 0, reg));
RTK_PHYLIB_ERR_CHK(rtk_phylib_mmd_write(phydev, 30, 0x02FC, 15, 0, 0x1));
WAIT_COMPLETE(10000000)
{
RTK_PHYLIB_ERR_CHK(rtk_phylib_mmd_read(phydev, 30, 0x02FC, 15, 0, &data_e));
if ((data_e & 0x1) == 0x0)
{
#ifdef MACSEC_DBG_PRINT
if (_t_wait != 0)
PR_DBG("[%s-%u] _t_wait: %u\n", __FUNCTION__, dir, _t_wait);
#endif
break;
}
}
if (WAIT_COMPLETE_IS_TIMEOUT())
{
PR_ERR("[%s-%u] timeout!\n", __FUNCTION__, dir);
return RTK_PHYLIB_ERR_TIMEOUT;
}
break;
case RTK_MACSEC_DIR_INGRESS:
RTK_PHYLIB_ERR_CHK(rtk_phylib_mmd_write(phydev, 31, 0xA6EA, 1, 1, 0x1));
RTK_PHYLIB_ERR_CHK(rtk_phylib_mmd_write(phydev, 30, 0x02FD, 15, 0, data_h));
RTK_PHYLIB_ERR_CHK(rtk_phylib_mmd_write(phydev, 30, 0x02FE, 15, 0, data_l));
RTK_PHYLIB_ERR_CHK(rtk_phylib_mmd_write(phydev, 30, 0x0300, 15, 0, reg));
RTK_PHYLIB_ERR_CHK(rtk_phylib_mmd_write(phydev, 30, 0x0301, 15, 0, 0x1));
WAIT_COMPLETE(10000000)
{
RTK_PHYLIB_ERR_CHK(rtk_phylib_mmd_read(phydev, 30, 0x0301, 15, 0, &data_e));
if ((data_e & 0x1) == 0x0)
{
#ifdef MACSEC_DBG_PRINT
if (_t_wait != 0)
PR_DBG("[%s-%u] _t_wait: %u\n", __FUNCTION__, dir, _t_wait);
#endif
break;
}
}
if (WAIT_COMPLETE_IS_TIMEOUT())
{
PR_ERR("[%s-%u] timeout!\n", __FUNCTION__, dir);
RTK_PHYLIB_ERR_CHK(rtk_phylib_mmd_write(phydev, 31, 0xA6EA, 1, 1, 0x0));
return RTK_PHYLIB_ERR_TIMEOUT;
}
RTK_PHYLIB_ERR_CHK(rtk_phylib_mmd_write(phydev, 31, 0xA6EA, 1, 1, 0x0));
break;
default:
return RTK_PHYLIB_ERR_INPUT;
}
return ret;
}
int rtk_phylib_826xb_macsec_init(rtk_phydev *phydev)
{
int32 ret = 0;
RTK_PHYLIB_ERR_CHK(rtk_phylib_mmd_write(phydev, 30, 0x2e0, 1, 0, 0b11));
RTK_PHYLIB_ERR_CHK(rtk_phylib_mmd_write(phydev, 30, 0x2d8, 15, 0, 0x5313));
RTK_PHYLIB_ERR_CHK(rtk_phylib_mmd_write(phydev, 30, 0x2da, 15, 0, 0x0101));
RTK_PHYLIB_ERR_CHK(rtk_phylib_mmd_write(phydev, 30, 0x2dc, 15, 0, 0x0101));
//MACSEC_RXSYS_CFG4
RTK_PHYLIB_ERR_CHK(rtk_phylib_mmd_write(phydev, 30, 0x3c6, 7, 0, 0xa));
//MACSEC_TXLINE_CFG4
RTK_PHYLIB_ERR_CHK(rtk_phylib_mmd_write(phydev, 30, 0x37b, 7, 0, 0x6));
//loopback fifo_setting
RTK_PHYLIB_ERR_CHK(rtk_phylib_mmd_write(phydev, 30, 0x2f7, 15, 0, 0x486c));
//RA_setting
RTK_PHYLIB_ERR_CHK(rtk_phylib_mmd_write(phydev, 30, 0x3f1, 15, 0, 0x72));
RTK_PHYLIB_ERR_CHK(rtk_phylib_mmd_write(phydev, 30, 0x3f0, 15, 0, 0x0b0b));
//RA ifg
RTK_PHYLIB_ERR_CHK(rtk_phylib_mmd_write(phydev, 30, 0x3ee, 15, 13, 0x2));
return ret;
}
int rtk_phylib_826xb_macsec_bypass_set(rtk_phydev *phydev, uint32 bypass)
{
int32 ret = 0;
if (bypass != 0)
{
RTK_PHYLIB_ERR_CHK(rtk_phylib_mmd_write(phydev, 30, 0x2d8, 15, 0, 0x5313));
RTK_PHYLIB_ERR_CHK(rtk_phylib_mmd_write(phydev, 30, 0x3f1, 15, 0, 0x72));
RTK_PHYLIB_ERR_CHK(rtk_phylib_mmd_write(phydev, 30, 0x3f0, 15, 0, 0x0b0b));
RTK_PHYLIB_ERR_CHK(rtk_phylib_mmd_write(phydev, 30, 0x398, 2, 0, 0x7));
}
else
{
RTK_PHYLIB_ERR_CHK(rtk_phylib_mmd_write(phydev, 30, 0x2d8, 15, 0, 0x5111));
RTK_PHYLIB_ERR_CHK(rtk_phylib_mmd_write(phydev, 30, 0x3f1, 15, 0, 0xe871));
RTK_PHYLIB_ERR_CHK(rtk_phylib_mmd_write(phydev, 30, 0x3f0, 15, 0, 0x0c0c));
RTK_PHYLIB_ERR_CHK(rtk_phylib_mmd_write(phydev, 30, 0x398, 2, 0, 0x5));
}
return ret;
}
int rtk_phylib_826xb_macsec_bypass_get(rtk_phydev *phydev, uint32 *pBypass)
{
int32 ret = 0;
uint32 bypass_rx = 0;
uint32 bypass_tx = 0;
RTK_PHYLIB_ERR_CHK(rtk_phylib_mmd_read(phydev, 30, 0x2d8, 9, 9, &bypass_rx));
RTK_PHYLIB_ERR_CHK(rtk_phylib_mmd_read(phydev, 30, 0x2d8, 1, 1, &bypass_tx));
*pBypass = (bypass_rx == 0 && bypass_tx == 0) ? 0 : 1;
return ret;
}
/* RTCT */
int rtk_phylib_826xb_cable_test_start(rtk_phydev *phydev)
{
int32 ret = 0;
RTK_PHYLIB_ERR_CHK(rtk_phylib_mmd_write(phydev, 31, 0xa4a0, 10, 10, 1));
rtk_phylib_mdelay(1000);
RTK_PHYLIB_ERR_CHK(rtk_phylib_mmd_write(phydev, 31, 0xa422, 15, 0, 0xF2));
RTK_PHYLIB_ERR_CHK(rtk_phylib_mmd_write(phydev, 31, 0xa422, 0, 0, 1));
return 0;
}
int rtk_phylib_826xb_cable_test_finished_get(rtk_phydev *phydev, uint32 *finished)
{
int32 ret = 0;
uint32 rData = 0;
RTK_PHYLIB_ERR_CHK(rtk_phylib_mmd_read(phydev, 31, 0xA422, 15, 15, &rData));
RTK_PHYLIB_ERR_CHK(rtk_phylib_mmd_write(phydev, 31, 0xa4a0, 10, 10, 0));
*finished = rData;
return 0;
}
int rtk_phylib_826xb_cable_test_result_get(rtk_phydev *phydev, uint32 pair, rtk_rtct_channel_result_t *result)
{
int32 ret = 0;
uint32 cable_factor = 7820;
uint32 cable_offset = 585;
uint32 indr_add_ss = 0x8027 + (pair * 0x4);
uint32 indr_add_lh = 0x8028 + (pair * 0x4);
uint32 indr_add_ll = 0x8029 + (pair * 0x4);
uint32 rtct_status = 0;
uint32 rtct_len_h = 0;
uint32 rtct_len_l = 0;
int32 len_cnt = 0;
if (pair > 3)
return RTK_PHYLIB_ERR_INPUT;
rtk_phylib_memset(result, 0x0, sizeof(rtk_rtct_channel_result_t));
RTK_PHYLIB_ERR_CHK(rtk_phylib_826xb_sram_read(phydev, indr_add_ss, 15, 8, &rtct_status));
RTK_PHYLIB_ERR_CHK(rtk_phylib_826xb_sram_read(phydev, indr_add_lh, 15, 8, &rtct_len_h));
RTK_PHYLIB_ERR_CHK(rtk_phylib_826xb_sram_read(phydev, indr_add_ll, 15, 8, &rtct_len_l));
result->cable_status = RTK_PHYLIB_CABLE_STATUS_NORMAL;
switch (rtct_status)
{
case 0x60: /* normal */
result->cable_status = RTK_PHYLIB_CABLE_STATUS_NORMAL;
break;
case 0x48: /* open */
result->cable_status |= RTK_PHYLIB_CABLE_STATUS_OPEN;
break;
case 0x50: /* short */
result->cable_status |= RTK_PHYLIB_CABLE_STATUS_SHORT;
break;
case 0xC0: /* inter pair short */
result->cable_status |= RTK_PHYLIB_CABLE_STATUS_INTER_PAIR_SHORT;
break;
case 0x42: /* mismatch-open */
result->cable_status |= RTK_PHYLIB_CABLE_STATUS_MISMATCH;
result->cable_status |= RTK_PHYLIB_CABLE_STATUS_OPEN;
break;
case 0x44: /* mismatch-short */
result->cable_status |= RTK_PHYLIB_CABLE_STATUS_MISMATCH;
result->cable_status |= RTK_PHYLIB_CABLE_STATUS_SHORT;
break;
default:
result->cable_status |= RTK_PHYLIB_CABLE_STATUS_INTER_PAIR_SHORT;
break;
}
len_cnt = ((int32)rtct_len_h << 8) + (int32)rtct_len_l - cable_offset;
if (len_cnt < 0)
result->length_cm = 0;
else
result->length_cm = ((uint32)len_cnt * 10000)/cable_factor;
if ((rtct_status == 0x60) && (result->length_cm < 1500)) //status is normal && length < 15M
result->length_cm = result->length_cm/2;
return 0;
}
/* Interrupt */
int rtk_phylib_826xb_intr_enable(rtk_phydev *phydev, uint32 en)
{
int32 ret = 0;
/* enable normal interrupt IMR_INT_PHY0 */
RTK_PHYLIB_ERR_CHK(rtk_phylib_mmd_write(phydev, 30, 0xE1, 0, 0, (en == 0) ? 0x0 : 0x1));
return ret;
}
int rtk_phylib_826xb_intr_read_clear(rtk_phydev *phydev, uint32 *status)
{
int32 ret = 0;
uint32 rData = 0;
uint32 rStatus = 0;
RTK_PHYLIB_ERR_CHK(rtk_phylib_mmd_read(phydev, 31, 0xA43A, 15, 0, &rData));
if(rData & BIT_1)
rStatus |= RTK_PHY_INTR_RLFD;
if(rData & BIT_2)
rStatus |= RTK_PHY_INTR_NEXT_PAGE_RECV;
if(rData & BIT_3)
rStatus |= RTK_PHY_INTR_AN_COMPLETE;
if(rData & BIT_4)
rStatus |= RTK_PHY_INTR_LINK_CHANGE;
if(rData & BIT_9)
rStatus |= RTK_PHY_INTR_ALDPS_STATE_CHANGE;
if(rData & BIT_11)
rStatus |= RTK_PHY_INTR_FATAL_ERROR;
if(rData & BIT_7)
rStatus |= RTK_PHY_INTR_WOL;
RTK_PHYLIB_ERR_CHK(rtk_phylib_mmd_read(phydev, 30, 0xE2, 15, 0, &rData));
if(rData & BIT_3)
rStatus |= RTK_PHY_INTR_TM_LOW;
if(rData & BIT_4)
rStatus |= RTK_PHY_INTR_TM_HIGH;
if(rData & BIT_6)
rStatus |= RTK_PHY_INTR_MACSEC;
RTK_PHYLIB_ERR_CHK(rtk_phylib_mmd_write(phydev, 30, 0xE2, 15, 0, 0xFF));
RTK_PHYLIB_ERR_CHK(rtk_phylib_mmd_write(phydev, 30, 0x2DC, 15, 0, 0xFF));
*status = rStatus;
return ret;
}
int rtk_phylib_826xb_intr_init(rtk_phydev *phydev)
{
int32 ret = 0;
uint32 status = 0;
/* Disable all IMR*/
RTK_PHYLIB_ERR_CHK(rtk_phylib_mmd_write(phydev, 30, 0xE1, 15, 0, 0));
RTK_PHYLIB_ERR_CHK(rtk_phylib_mmd_write(phydev, 30, 0xE3, 15, 0, 0));
/* source */
RTK_PHYLIB_ERR_CHK(rtk_phylib_mmd_write(phydev, 30, 0xE4, 15, 0, 0x1));
RTK_PHYLIB_ERR_CHK(rtk_phylib_mmd_write(phydev, 30, 0xE0, 15, 0, 0x2F));
/* init common link change */
RTK_PHYLIB_ERR_CHK(rtk_phylib_mmd_write(phydev, 31, 0xA424, 15, 0, 0x10));
/* init rlfd */
RTK_PHYLIB_ERR_CHK(rtk_phylib_mmd_write(phydev, 31, 0xA442, 15, 15, 0x1));
RTK_PHYLIB_ERR_CHK(rtk_phylib_mmd_write(phydev, 31, 0xA448, 7, 7, 0x1));
/* init tm */
RTK_PHYLIB_ERR_CHK(rtk_phylib_mmd_write(phydev, 30, 0x1A0, 11, 11, 0x1));
RTK_PHYLIB_ERR_CHK(rtk_phylib_mmd_write(phydev, 30, 0x19D, 11, 11, 0x1));
RTK_PHYLIB_ERR_CHK(rtk_phylib_mmd_write(phydev, 30, 0x1A1, 11, 11, 0x1));
RTK_PHYLIB_ERR_CHK(rtk_phylib_mmd_write(phydev, 30, 0x19F, 11, 11, 0x1));
/* init WOL */
RTK_PHYLIB_ERR_CHK(rtk_phylib_mmd_write(phydev, 31, 0xA424, 7, 7, 0x1));
/* clear status */
RTK_PHYLIB_ERR_CHK(rtk_phylib_826xb_intr_read_clear(phydev, &status));
return ret;
}
int rtk_phylib_826xb_link_down_power_saving_set(rtk_phydev *phydev, uint32 ena)
{
int32 ret = 0;
uint32 data = (ena > 0) ? 0x1 : 0x0;
RTK_PHYLIB_ERR_CHK(rtk_phylib_mmd_write(phydev, 31, 0xA430, 2, 2, data));
return ret;
}
int rtk_phylib_826xb_link_down_power_saving_get(rtk_phydev *phydev, uint32 *pEna)
{
int32 ret = 0;
uint32 data = 0;
RTK_PHYLIB_ERR_CHK(rtk_phylib_mmd_read(phydev, 31, 0xA430, 2, 2, &data));
*pEna = data;
return ret;
}
int rtk_phylib_826xb_wol_reset(rtk_phydev *phydev)
{
int32 ret = 0;
RTK_PHYLIB_ERR_CHK(rtk_phylib_mmd_write(phydev, 31, 0xD8A2, 15, 15, 0));
RTK_PHYLIB_ERR_CHK(rtk_phylib_mmd_write(phydev, 31, 0xD8A2, 15, 15, 1));
return ret;
}
int rtk_phylib_826xb_wol_set(rtk_phydev *phydev, uint32 wol_opts)
{
int32 ret = 0;
RTK_PHYLIB_ERR_CHK(rtk_phylib_mmd_write(phydev, 31, 0xD8A0, 13, 13, (wol_opts & RTK_WOL_OPT_LINK) ? 1 : 0));
RTK_PHYLIB_ERR_CHK(rtk_phylib_mmd_write(phydev, 31, 0xD8A0, 12, 12, (wol_opts & RTK_WOL_OPT_MAGIC) ? 1 : 0));
RTK_PHYLIB_ERR_CHK(rtk_phylib_mmd_write(phydev, 31, 0xD8A0, 10, 10, (wol_opts & RTK_WOL_OPT_UCAST) ? 1 : 0));
RTK_PHYLIB_ERR_CHK(rtk_phylib_mmd_write(phydev, 31, 0xD8A0, 9, 9, (wol_opts & RTK_WOL_OPT_MCAST) ? 1 : 0));
RTK_PHYLIB_ERR_CHK(rtk_phylib_mmd_write(phydev, 31, 0xD8A0, 8, 8, (wol_opts & RTK_WOL_OPT_BCAST) ? 1 : 0));
return ret;
}
int rtk_phylib_826xb_wol_get(rtk_phydev *phydev, uint32 *pWol_opts)
{
int32 ret = 0;
uint32 data = 0;
uint32 wol_opts = 0;
RTK_PHYLIB_ERR_CHK(rtk_phylib_mmd_read(phydev, 31, 0xD8A0, 13, 13, &data));
wol_opts |= ((data) ? RTK_WOL_OPT_LINK : 0);
RTK_PHYLIB_ERR_CHK(rtk_phylib_mmd_read(phydev, 31, 0xD8A0, 12, 12, &data));
wol_opts |= ((data) ? RTK_WOL_OPT_MAGIC : 0);
RTK_PHYLIB_ERR_CHK(rtk_phylib_mmd_read(phydev, 31, 0xD8A0, 10, 10, &data));
wol_opts |= ((data) ? RTK_WOL_OPT_UCAST : 0);
RTK_PHYLIB_ERR_CHK(rtk_phylib_mmd_read(phydev, 31, 0xD8A0, 9, 9, &data));
wol_opts |= ((data) ? RTK_WOL_OPT_MCAST : 0);
RTK_PHYLIB_ERR_CHK(rtk_phylib_mmd_read(phydev, 31, 0xD8A0, 8, 8, &data));
wol_opts |= ((data) ? RTK_WOL_OPT_BCAST : 0);
*pWol_opts = wol_opts;
return ret;
}
int rtk_phylib_826xb_wol_unicast_addr_set(rtk_phydev *phydev, uint8 *mac_addr)
{
int32 ret = 0;
RTK_PHYLIB_ERR_CHK(rtk_phylib_mmd_write(phydev, 31, 0xD8C0, 15, 0, (mac_addr[1] << 8 | mac_addr[0])));
RTK_PHYLIB_ERR_CHK(rtk_phylib_mmd_write(phydev, 31, 0xD8C2, 15, 0, (mac_addr[3] << 8 | mac_addr[2])));
RTK_PHYLIB_ERR_CHK(rtk_phylib_mmd_write(phydev, 31, 0xD8C4, 15, 0, (mac_addr[5] << 8 | mac_addr[4])));
return ret;
}
uint32 rtk_phylib_826xb_wol_multicast_mac2offset(uint8 *mac_addr)
{
uint32 crc = 0xFFFFFFFF;
uint32 i = 0, j = 0;
uint32 b0 = 0, b1 = 0, b2 = 0, b3 = 0, b4 = 0, b5 = 0;
for (i = 0; i < 6; i++) {
crc ^= mac_addr[i];
for (j = 0; j < 8; j++) {
if (crc & 1) {
crc = (crc >> 1) ^ 0xEDB88320;
} else {
crc >>= 1;
}
}
}
crc = ~crc;
b5 = ((crc & 0b000001) << 5 );
b4 = ((crc & 0b000010) << 3 );
b3 = ((crc & 0b000100) << 1 );
b2 = (((crc & 0b001000) ? 0 : 1) << 2 );
b1 = (((crc & 0b010000) ? 0 : 1) << 1 );
b0 = (((crc & 0b100000) ? 0 : 1) << 0 );
return (b5 | b4 | b3 | b2 | b1 | b0);
}
int rtk_phylib_826xb_wol_multicast_mask_add(rtk_phydev *phydev, uint32 offset)
{
const uint32 cfg_reg[4] = {0xD8C6, 0xD8C8, 0xD8CA, 0xD8CC};
int32 ret = 0;
uint32 idx = offset/16;
uint32 multicast_cfg = 0;
RTK_PHYLIB_ERR_CHK(rtk_phylib_mmd_read(phydev, 31, cfg_reg[idx], 15, 0, &multicast_cfg));
multicast_cfg = (multicast_cfg | (0b1 << (offset % 16)));
RTK_PHYLIB_ERR_CHK(rtk_phylib_mmd_write(phydev, 31, cfg_reg[idx], 15, 0, multicast_cfg));
return ret;
}
int rtk_phylib_826xb_wol_multicast_mask_reset(rtk_phydev *phydev)
{
const uint32 cfg_reg[4] = {0xD8C6, 0xD8C8, 0xD8CA, 0xD8CC};
int32 ret = 0;
uint32 idx = 0;
for (idx = 0; idx < 4; idx++)
{
RTK_PHYLIB_ERR_CHK(rtk_phylib_mmd_write(phydev, 31, cfg_reg[idx], 15, 0, 0));
}
return ret;
}

View file

@ -0,0 +1,46 @@
/*
* SPDX-License-Identifier: GPL-2.0-only
*
* Copyright (c) 2023 Realtek Semiconductor Corp. All rights reserved.
*/
#ifndef __RTK_PHYLIB_RTL826XB_H
#define __RTK_PHYLIB_RTL826XB_H
#include "rtk_phylib.h"
/* Register Access*/
int rtk_phylib_826xb_sds_read(rtk_phydev *phydev, uint32 page, uint32 reg, uint8 msb, uint8 lsb, uint32 *pData);
int rtk_phylib_826xb_sds_write(rtk_phydev *phydev, uint32 page, uint32 reg, uint8 msb, uint8 lsb, uint32 data);
/* Interrupt */
int rtk_phylib_826xb_intr_enable(rtk_phydev *phydev, uint32 en);
int rtk_phylib_826xb_intr_read_clear(rtk_phydev *phydev, uint32 *status);
int rtk_phylib_826xb_intr_init(rtk_phydev *phydev);
/* Cable Test */
int rtk_phylib_826xb_cable_test_start(rtk_phydev *phydev);;
int rtk_phylib_826xb_cable_test_finished_get(rtk_phydev *phydev, uint32 *finished);
int rtk_phylib_826xb_cable_test_result_get(rtk_phydev *phydev, uint32 pair, rtk_rtct_channel_result_t *result);
/* MACsec */
int rtk_phylib_826xb_macsec_init(rtk_phydev *phydev);
int rtk_phylib_826xb_macsec_read(rtk_phydev *phydev, rtk_macsec_dir_t dir, uint32 reg, uint8 msb, uint8 lsb, uint32 *pData);
int rtk_phylib_826xb_macsec_write(rtk_phydev *phydev, rtk_macsec_dir_t dir, uint32 reg, uint8 msb, uint8 lsb, uint32 data);
int rtk_phylib_826xb_macsec_bypass_set(rtk_phydev *phydev, uint32 bypass);
int rtk_phylib_826xb_macsec_bypass_get(rtk_phydev *phydev, uint32 *pBypass);
/* Link-down-power-saving/EDPD */
int rtk_phylib_826xb_link_down_power_saving_set(rtk_phydev *phydev, uint32 ena);
int rtk_phylib_826xb_link_down_power_saving_get(rtk_phydev *phydev, uint32 *pEna);
/* Wake on Lan */
int rtk_phylib_826xb_wol_reset(rtk_phydev *phydev);
int rtk_phylib_826xb_wol_set(rtk_phydev *phydev, uint32 wol_opts);
int rtk_phylib_826xb_wol_get(rtk_phydev *phydev, uint32 *pWol_opts);
int rtk_phylib_826xb_wol_unicast_addr_set(rtk_phydev *phydev, uint8 *mac_addr);
int rtk_phylib_826xb_wol_multicast_mask_add(rtk_phydev *phydev, uint32 offset);
int rtk_phylib_826xb_wol_multicast_mask_reset(rtk_phydev *phydev);
uint32 rtk_phylib_826xb_wol_multicast_mac2offset(uint8 *mac_addr);
#endif /* __RTK_PHYLIB_RTL826XB_H */

View file

@ -0,0 +1,117 @@
/*
* SPDX-License-Identifier: GPL-2.0-only
*
* Copyright (c) 2023 Realtek Semiconductor Corp. All rights reserved.
*/
#ifndef __COMMON_TYPE_H__
#define __COMMON_TYPE_H__
/*
* Symbol Definition
*/
#define USING_RTSTK_PKT_AS_RAIL
#ifndef NULL
#define NULL 0
#endif
#ifndef TRUE
#define TRUE 1
#endif
#ifndef FALSE
#define FALSE 0
#endif
#ifndef ETHER_ADDR_LEN
#define ETHER_ADDR_LEN 6
#endif
#ifndef IP6_ADDR_LEN
#define IP6_ADDR_LEN 16
#endif
/*
* Data Type Declaration
*/
#ifndef uint64
typedef unsigned long long uint64;
#endif
#ifndef int64
typedef signed long long int64;
#endif
#ifndef uint32
typedef unsigned int uint32;
#endif
#ifndef int32
typedef signed int int32;
#endif
#ifndef uint16
typedef unsigned short uint16;
#endif
#ifndef int16
typedef signed short int16;
#endif
#ifndef uint8
typedef unsigned char uint8;
#endif
#ifndef int8
typedef signed char int8;
#endif
//#define CONFIG_SDK_WORDSIZE_64 /* not ready */
#ifdef CONFIG_SDK_WORDSIZE_64
typedef long int intptr;
typedef unsigned long int uintptr;
#else
typedef int intptr;
typedef unsigned int uintptr;
#endif
#ifndef ipaddr_t
typedef uint32 ipaddr_t; /* ipv4 address type */
#endif
/* configuration mode type */
typedef enum rtk_enable_e
{
DISABLED = 0,
ENABLED,
RTK_ENABLE_END
} rtk_enable_t;
/* initial state of module */
typedef enum init_state_e
{
INIT_NOT_COMPLETED = 0,
INIT_COMPLETED,
INIT_STATE_END
} init_state_t;
/* ethernet address type */
typedef struct rtk_mac_s
{
uint8 octet[ETHER_ADDR_LEN];
} rtk_mac_t;
typedef uint32 osal_time_t;
typedef uint32 osal_usecs_t;
/*
* Macro Definition
*/
#endif /* __COMMON_TYPE_H__ */