Compare commits

...

14 commits

Author SHA1 Message Date
plappermaul
7eaa7a866d
Update README.md 2025-10-14 08:41:49 +02:00
plappermaul
e779d22744
Update README.md 2025-10-14 08:39:15 +02:00
plappermaul
70966621fe
Update README.md 2025-10-14 08:34:02 +02:00
plappermaul
7784885531
Update README.md 2025-10-14 08:33:42 +02:00
plappermaul
a5df4c7f92
Update README.md 2025-10-14 08:30:09 +02:00
plappermaul
3206f0ea1b
Update README.md 2025-10-14 08:29:47 +02:00
Markus Stockhausen
7476476f19 move roadmap 2025-10-14 02:28:50 -04:00
plappermaul
ffc566094b
Update README.md 2025-10-14 08:28:19 +02:00
plappermaul
13158f996d
Update README.md 2025-10-14 08:27:07 +02:00
plappermaul
c01cc521d4
Update README.md 2025-10-14 08:26:00 +02:00
plappermaul
d677b9e6c9
Update README.md 2025-10-14 08:18:10 +02:00
plappermaul
dc6a53c04c
Update README.md 2025-10-14 08:15:23 +02:00
Markus Stockhausen
febe023171 nothing special 2025-10-14 02:04:20 -04:00
Markus Stockhausen
9b3bf98ee9 add BE550 sources 2025-10-14 01:59:03 -04:00
680 changed files with 487130 additions and 1 deletions

View file

@ -1,13 +1,24 @@
This repository serves as a well selected collection of Realtek switch SoC documentation and source. Focus is on the Otto platform with the RTL838x (maple), RTL839x (cypress), RTL930x (longan) and RTL931x (mango) chips.
- Roadmap https://github.com/plappermaul/realtek-doc/blob/main/Roadmap.pdf
- [Roadmap](/datasheets/Roadmap.pdf) for the Realtek switch platform
- Additional infos can be found at https://svanheule.net/switches/start
What RTK (development kit) we have
- Some fragments from the TP-Link BE550 GPL. It has several drivers for Realtek PHYs that are mainly used on non-Realtek platforms. Download from https://www.tp-link.com/en/support/download/
- The most recent SDK from 2022 is based on the D-Link DMS-1250 GPL source. It contains a lot of modern PHY code for RTL8218E, RTL8224, RTL8261 and RTL8264. Download from https://tsd.dlink.com.tw/GPL.asp
- An older SDK from 2016 is based on the Zyxel XGS1210-12 GPL source. It contains code for a totally unknown RTL8284 PHY. Download can be requested from https://www.zyxel.com/global/en/form/gpl-oss-software-notice
PHY Direct Links
PHY | BE550 | DMS-1250 | XGS-1210
--- | --- | --- | ---
RTL8226x/RTL8221B | [U-Boot](/sources/uboot-be550/drivers/net/rtl8221b) | [RTK](/sources/rtk-dms1250/src/hal/phy) [U-Boot](/sources/uboot-xgs1210/board/Realtek/switch/sdk/src/hal/phy) | [RTK](/sources/rtk-xgs1210/src/hal/phy) [U-Boot](/sources/uboot-xgs1210/board/Realtek/switch/sdk/src/hal/phy)
RTL8251B | [U-Boot](/sources/uboot-be550/drivers/net/rtl8251b) | - | -
RTL8261x/RTL8264x | [U-Boot](/sources/uboot-be550/drivers/net/ipq_common) [RTK](/sources/rtk-be550/src/hal/phy) | [RTK](/sources/rtk-dms1250/src/hal/phy) |
... | ... | ...
The PDFs we have
- GPON/EON controller RTL9601 datasheets

View file

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,25 @@
# 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_osal.o
# files from SDK
rtk-ms-phy-objs += phy_patch.o
rtk-ms-phy-objs += phy_rtl826xb_patch.o
# rtk phylib
rtk-ms-phy-objs += rtk_phylib.o
rtk-ms-phy-objs += rtk_phylib_rtl826xb.o
ifdef CONFIG_MACSEC
rtk-ms-phy-objs += rtk_macsec.o
rtk-ms-phy-objs += rtk_phylib_macsec.o
endif

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,174 @@
/*
* 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__ */

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,888 @@
/*
* SPDX-License-Identifier: GPL-2.0-only
*
* Copyright (c) 2023 Realtek Semiconductor Corp. All rights reserved.
*/
#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)
{
PR_DBG("[%s] ctx->prepare: %u\n", __FUNCTION__, ctx->prepare);
if (ctx->prepare)
return 0;
return rtk_phylib_macsec_enable_set(ctx->phydev, 1);
}
static int rtk_macsec_dev_stop(struct macsec_context *ctx)
{
PR_DBG("[%s] ctx->prepare: %u\n", __FUNCTION__, ctx->prepare);
if (ctx->prepare)
return 0;
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;
PR_DBG("[%s] ctx->prepare: %u\n", __FUNCTION__, ctx->prepare);
if (ctx->prepare)
{
return 0;
}
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;
PR_DBG("[%s] ctx->prepare: %u\n", __FUNCTION__, ctx->prepare);
if (ctx->prepare)
{
return 0;
}
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 (ctx->prepare)
{
return 0;
}
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));
PR_DBG("[%s] ctx->prepare: %u\n", __FUNCTION__, ctx->prepare);
if (ctx->prepare)
{
return 0;
}
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;
PR_DBG("[%s] ctx->prepare: %u\n", __FUNCTION__, ctx->prepare);
if (ctx->prepare)
{
return 0;
}
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 (ctx->prepare)
{
return 0;
}
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;
PR_DBG("[%s] ctx->prepare: %u\n", __FUNCTION__, ctx->prepare);
if (ctx->prepare)
{
return 0;
}
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;
PR_DBG("[%s] ctx->prepare: %u\n", __FUNCTION__, ctx->prepare);
if (ctx->prepare)
{
return 0;
}
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;
PR_DBG("[%s] ctx->prepare: %u\n", __FUNCTION__, ctx->prepare);
if (ctx->prepare)
{
return 0;
}
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;
PR_DBG("[%s] ctx->prepare: %u\n", __FUNCTION__, ctx->prepare);
if (ctx->prepare)
{
return 0;
}
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;
PR_DBG("[%s] ctx->prepare: %u\n", __FUNCTION__, ctx->prepare);
if (ctx->prepare)
{
return 0;
}
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;
PR_DBG("[%s] ctx->prepare: %u\n", __FUNCTION__, ctx->prepare);
if (ctx->prepare)
{
return 0;
}
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 (ctx->prepare)
{
return 0;
}
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 (ctx->prepare)
{
return 0;
}
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 (ctx->prepare)
{
return 0;
}
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 (ctx->prepare)
{
return 0;
}
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 (ctx->prepare)
{
return 0;
}
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,56 @@
/*
* 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>
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;
}
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,99 @@
/*
* 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) 0
#define HWP_9310_FAMILY_ID(_unit) 0
#define RTK_9300_FAMILY_ID(_unit) 0
#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
/* 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,654 @@
/*
* 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/netdevice.h>
#include <linux/mii.h>
#include <linux/debugfs.h>
#include "phy_rtl826xb_patch.h"
#include "rtk_phylib_rtl826xb.h"
#include "rtk_phylib_macsec.h"
#include "rtk_phylib.h"
#include "rtk_phy.h"
static struct dentry *phy_sm_barrier = NULL;
static struct dentry *allow_phy_up_toggle = NULL;
static unsigned int allow_phy_up = 0;
static int rtkphy_c45_suspend(struct phy_device *phydev);
#if 0
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;
}
#endif
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);
linkmode_set_bit(ETHTOOL_LINK_MODE_Pause_BIT,
phydev->supported);
linkmode_set_bit(ETHTOOL_LINK_MODE_Asym_Pause_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;
phy_sm_barrier = debugfs_create_dir("rtk_phy_sm_barrier", NULL);
if ( !phy_sm_barrier )
{
printk("cannot create rtk debugfs dir phy_sm_barrier.\n");
}
else
{
allow_phy_up_toggle = debugfs_create_u32("allow_phy_up", S_IRUGO | S_IWUSR, phy_sm_barrier, &allow_phy_up);
if ( !allow_phy_up_toggle )
{
printk("cannot create rtk debugfs allow_phy_up.\n");
debugfs_remove_recursive(phy_sm_barrier);
phy_sm_barrier = NULL;
}
else
printk("create rtk debugfs allow_phy_up.\n");
}
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;
}
rtkphy_c45_suspend(phydev);
#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)
{
if ( !likely(READ_ONCE(allow_phy_up)) )
{
return 0;
}
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;
u32 adv,status;
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);
}
linkmode_and(phydev->advertising, phydev->advertising,
phydev->supported);
status = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_ADVERTISE);
linkmode_mod_bit(ETHTOOL_LINK_MODE_Pause_BIT,
phydev->advertising, status & ADVERTISE_PAUSE_CAP);
linkmode_mod_bit(ETHTOOL_LINK_MODE_Asym_Pause_BIT,
phydev->advertising, status & ADVERTISE_PAUSE_ASYM);
adv = linkmode_adv_to_mii_adv_t(phydev->advertising);
ret = phy_modify_mmd_changed(phydev, MDIO_MMD_AN, MDIO_AN_ADVERTISE,
ADVERTISE_ALL | ADVERTISE_100BASE4,
adv);
if (ret < 0)
return ret;
if (ret > 0)
changed = true;
adv = linkmode_adv_to_mii_10gbt_adv_t(phydev->advertising);
ret = phy_modify_mmd_changed(phydev, MDIO_MMD_AN, MDIO_AN_10GBT_CTRL,
MDIO_AN_10GBT_CTRL_ADV10G |
MDIO_AN_10GBT_CTRL_ADV5G |
MDIO_AN_10GBT_CTRL_ADV2_5G, adv);
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);
}
#if 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;
}
#endif
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 int 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 0;
}
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,
#if 0
.cable_test_start = rtl826xb_cable_test_start,
.cable_test_get_status = rtl826xb_cable_test_get_status,
#endif
.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,
#if 0
.cable_test_start = rtl826xb_cable_test_start,
.cable_test_get_status = rtl826xb_cable_test_get_status,
#endif
.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,
},
};
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) },
{ },
};
MODULE_DEVICE_TABLE(mdio, rtk_phy_tbl);
MODULE_AUTHOR("Realtek");
MODULE_DESCRIPTION("Realtek PHY drivers");
MODULE_LICENSE("GPL");

View file

@ -0,0 +1,20 @@
/*
* 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
#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,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>
#include <linux/delay.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,257 @@
/*
* 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_END
} rtk_phylib_phy_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,394 @@
/*
* 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
/* 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;
#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,568 @@
/*
* 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 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 - 255;
if (len_cnt < 0)
result->length_cm = 0;
else
result->length_cm = ((uint32)len_cnt * 10000)/cable_factor;
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__ */

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,124 @@
/*
Ported to U-Boot by Christian Pellegrin <chri@ascensit.com>
Based on sources from the Linux kernel (pcnet_cs.c, 8390.h) and
eCOS(if_dp83902a.c, if_dp83902a.h). Both of these 2 wonderful world
are GPL, so this is, of course, GPL.
*/
/* Generic NS8390 register definitions. */
/* This file is part of Donald Becker's 8390 drivers, and is distributed
under the same license. Auto-loading of 8390.o only in v2.2 - Paul G.
Some of these names and comments originated from the Crynwr
packet drivers, which are distributed under the GPL. */
#ifndef _8390_h
#define _8390_h
/* Some generic ethernet register configurations. */
#define E8390_TX_IRQ_MASK 0xa /* For register EN0_ISR */
#define E8390_RX_IRQ_MASK 0x5
#define E8390_RXCONFIG 0x4 /* EN0_RXCR: broadcasts, no multicast,errors */
#define E8390_RXOFF 0x20 /* EN0_RXCR: Accept no packets */
#define E8390_TXCONFIG 0x00 /* EN0_TXCR: Normal transmit mode */
#define E8390_TXOFF 0x02 /* EN0_TXCR: Transmitter off */
/* Register accessed at EN_CMD, the 8390 base addr. */
#define E8390_STOP 0x01 /* Stop and reset the chip */
#define E8390_START 0x02 /* Start the chip, clear reset */
#define E8390_TRANS 0x04 /* Transmit a frame */
#define E8390_RREAD 0x08 /* Remote read */
#define E8390_RWRITE 0x10 /* Remote write */
#define E8390_NODMA 0x20 /* Remote DMA */
#define E8390_PAGE0 0x00 /* Select page chip registers */
#define E8390_PAGE1 0x40 /* using the two high-order bits */
#define E8390_PAGE2 0x80 /* Page 3 is invalid. */
/*
* Only generate indirect loads given a machine that needs them.
* - removed AMIGA_PCMCIA from this list, handled as ISA io now
*/
#define n2k_inb(port) (*((volatile unsigned char *)(port+CONFIG_DRIVER_NE2000_BASE)))
#define n2k_outb(val,port) (*((volatile unsigned char *)(port+CONFIG_DRIVER_NE2000_BASE)) = val)
#define EI_SHIFT(x) (x)
#define E8390_CMD EI_SHIFT(0x00) /* The command register (for all pages) */
/* Page 0 register offsets. */
#define EN0_CLDALO EI_SHIFT(0x01) /* Low byte of current local dma addr RD */
#define EN0_STARTPG EI_SHIFT(0x01) /* Starting page of ring bfr WR */
#define EN0_CLDAHI EI_SHIFT(0x02) /* High byte of current local dma addr RD */
#define EN0_STOPPG EI_SHIFT(0x02) /* Ending page +1 of ring bfr WR */
#define EN0_BOUNDARY EI_SHIFT(0x03) /* Boundary page of ring bfr RD WR */
#define EN0_TSR EI_SHIFT(0x04) /* Transmit status reg RD */
#define EN0_TPSR EI_SHIFT(0x04) /* Transmit starting page WR */
#define EN0_NCR EI_SHIFT(0x05) /* Number of collision reg RD */
#define EN0_TCNTLO EI_SHIFT(0x05) /* Low byte of tx byte count WR */
#define EN0_FIFO EI_SHIFT(0x06) /* FIFO RD */
#define EN0_TCNTHI EI_SHIFT(0x06) /* High byte of tx byte count WR */
#define EN0_ISR EI_SHIFT(0x07) /* Interrupt status reg RD WR */
#define EN0_CRDALO EI_SHIFT(0x08) /* low byte of current remote dma address RD */
#define EN0_RSARLO EI_SHIFT(0x08) /* Remote start address reg 0 */
#define EN0_CRDAHI EI_SHIFT(0x09) /* high byte, current remote dma address RD */
#define EN0_RSARHI EI_SHIFT(0x09) /* Remote start address reg 1 */
#define EN0_RCNTLO EI_SHIFT(0x0a) /* Remote byte count reg WR */
#define EN0_RCNTHI EI_SHIFT(0x0b) /* Remote byte count reg WR */
#define EN0_RSR EI_SHIFT(0x0c) /* rx status reg RD */
#define EN0_RXCR EI_SHIFT(0x0c) /* RX configuration reg WR */
#define EN0_TXCR EI_SHIFT(0x0d) /* TX configuration reg WR */
#define EN0_COUNTER0 EI_SHIFT(0x0d) /* Rcv alignment error counter RD */
#define EN0_DCFG EI_SHIFT(0x0e) /* Data configuration reg WR */
#define EN0_COUNTER1 EI_SHIFT(0x0e) /* Rcv CRC error counter RD */
#define EN0_IMR EI_SHIFT(0x0f) /* Interrupt mask reg WR */
#define EN0_COUNTER2 EI_SHIFT(0x0f) /* Rcv missed frame error counter RD */
/* Bits in EN0_ISR - Interrupt status register */
#define ENISR_RX 0x01 /* Receiver, no error */
#define ENISR_TX 0x02 /* Transmitter, no error */
#define ENISR_RX_ERR 0x04 /* Receiver, with error */
#define ENISR_TX_ERR 0x08 /* Transmitter, with error */
#define ENISR_OVER 0x10 /* Receiver overwrote the ring */
#define ENISR_COUNTERS 0x20 /* Counters need emptying */
#define ENISR_RDC 0x40 /* remote dma complete */
#define ENISR_RESET 0x80 /* Reset completed */
#define ENISR_ALL 0x3f /* Interrupts we will enable */
/* Bits in EN0_DCFG - Data config register */
#define ENDCFG_WTS 0x01 /* word transfer mode selection */
#define ENDCFG_BOS 0x02 /* byte order selection */
#define ENDCFG_AUTO_INIT 0x10 /* Auto-init to remove packets from ring */
#define ENDCFG_FIFO 0x40 /* 8 bytes */
/* Page 1 register offsets. */
#define EN1_PHYS EI_SHIFT(0x01) /* This board's physical enet addr RD WR */
#define EN1_PHYS_SHIFT(i) EI_SHIFT(i+1) /* Get and set mac address */
#define EN1_CURPAG EI_SHIFT(0x07) /* Current memory page RD WR */
#define EN1_MULT EI_SHIFT(0x08) /* Multicast filter mask array (8 bytes) RD WR */
#define EN1_MULT_SHIFT(i) EI_SHIFT(8+i) /* Get and set multicast filter */
/* Bits in received packet status byte and EN0_RSR*/
#define ENRSR_RXOK 0x01 /* Received a good packet */
#define ENRSR_CRC 0x02 /* CRC error */
#define ENRSR_FAE 0x04 /* frame alignment error */
#define ENRSR_FO 0x08 /* FIFO overrun */
#define ENRSR_MPA 0x10 /* missed pkt */
#define ENRSR_PHY 0x20 /* physical/multicast address */
#define ENRSR_DIS 0x40 /* receiver disable. set in monitor mode */
#define ENRSR_DEF 0x80 /* deferring */
/* Transmitted packet status, EN0_TSR. */
#define ENTSR_PTX 0x01 /* Packet transmitted without error */
#define ENTSR_ND 0x02 /* The transmit wasn't deferred. */
#define ENTSR_COL 0x04 /* The transmit collided at least once. */
#define ENTSR_ABT 0x08 /* The transmit collided 16 times, and was deferred. */
#define ENTSR_CRS 0x10 /* The carrier sense was lost. */
#define ENTSR_FU 0x20 /* A "FIFO underrun" occurred during transmit. */
#define ENTSR_CDH 0x40 /* The collision detect "heartbeat" signal was lost. */
#define ENTSR_OWC 0x80 /* There was an out-of-window collision. */
#define NIC_RECEIVE_MONITOR_MODE 0x20
#endif /* _8390_h */

View file

@ -0,0 +1,158 @@
config DM_ETH
bool "Enable Driver Model for Ethernet drivers"
depends on DM
help
Enable driver model for Ethernet.
The eth_*() interface will be implemented by the UC_ETH class
This is currently implemented in net/eth.c
Look in include/net.h for details.
config PHYLIB
bool "Ethernet PHY (physical media interface) support"
help
Enable Ethernet PHY (physical media interface) support.
menuconfig NETDEVICES
bool "Network device support"
depends on NET
default y if DM_ETH
help
You must select Y to enable any network device support
Generally if you have any networking support this is a given
If unsure, say Y
if NETDEVICES
config ALTERA_TSE
bool "Altera Triple-Speed Ethernet MAC support"
depends on DM_ETH
select PHYLIB
help
This driver supports the Altera Triple-Speed (TSE) Ethernet MAC.
Please find details on the "Triple-Speed Ethernet MegaCore Function
Resource Center" of Altera.
config E1000
bool "Intel PRO/1000 Gigabit Ethernet support"
help
This driver supports Intel(R) PRO/1000 gigabit ethernet family of
adapters. For more information on how to identify your adapter, go
to the Adapter & Driver ID Guide at:
<http://support.intel.com/support/network/adapter/pro100/21397.htm>
config E1000_SPI_GENERIC
bool "Allow access to the Intel 8257x SPI bus"
depends on E1000
help
Allow generic access to the SPI bus on the Intel 8257x, for
example with the "sspi" command.
config E1000_SPI
bool "Enable SPI bus utility code"
depends on E1000
help
Utility code for direct access to the SPI bus on Intel 8257x.
This does not do anything useful unless you set at least one
of CONFIG_CMD_E1000 or CONFIG_E1000_SPI_GENERIC.
config CMD_E1000
bool "Enable the e1000 command"
depends on E1000
help
This enables the 'e1000' management command for E1000 devices. When
used on devices with SPI support you can reprogram the EEPROM from
U-Boot.
config ETH_SANDBOX
depends on DM_ETH && SANDBOX
default y
bool "Sandbox: Mocked Ethernet driver"
help
This driver simply responds with fake ARP replies and ping
replies that are used to verify network stack functionality
This driver is particularly useful in the test/dm/eth.c tests
config ETH_SANDBOX_RAW
depends on DM_ETH && SANDBOX
default y
bool "Sandbox: Bridge to Linux Raw Sockets"
help
This driver is a bridge from the bottom of the network stack
in U-Boot to the RAW AF_PACKET API in Linux. This allows real
network traffic to be tested from within sandbox. See
board/sandbox/README.sandbox for more details.
config ETH_DESIGNWARE
bool "Synopsys Designware Ethernet MAC"
select PHYLIB
help
This MAC is present in SoCs from various vendors. It supports
100Mbit and 1 Gbit operation. You must enable CONFIG_PHYLIB to
provide the PHY (physical media interface).
config PCH_GBE
bool "Intel Platform Controller Hub EG20T GMAC driver"
depends on DM_ETH && DM_PCI
select PHYLIB
help
This MAC is present in Intel Platform Controller Hub EG20T. It
supports 10/100/1000 Mbps operation.
config ZYNQ_GEM
depends on DM_ETH && (ARCH_ZYNQ || ARCH_ZYNQMP)
select PHYLIB
bool "Xilinx Ethernet GEM"
help
This MAC is present in Xilinx Zynq and ZynqMP SoCs.
endif # NETDEVICES
config IPQ_QCA_AQUANTIA_PHY
bool "Aquantia PHY support"
help
Enable Aquantia PHY support.
config ATHRS17C_SWITCH
bool "QTI S17C switch support"
help
Enable QTI S17C switch support.
config QCA8084_PHY
depends on QCA8081_PHY
bool "Enable QCA8084 Ethernet Chip support"
if QCA8084_PHY
config QCA8084_PHY_MODE
bool "Enable QCA8084 PHY Mode support"
help
Enable QCA8084 PHY Mode support.
config QCA8084_SWT_MODE
bool "Enable QCA8084 Switch Mode support"
help
Enable QCA8084 Switch Mode support.
if QCA8084_SWT_MODE
config QCA8084_BYPASS_MODE
bool "Enable QCA8084 By-pass support"
help
Enable QCA808 By-pass support.
endif # QCA8084_SWT_MODE
config QCA8084_DEBUG
bool "Enable QCA8084 Debug support"
help
Enable QCA8084 Debug support.
endif # QCA8084_PHY
config IPQ_QTI_BIT_BANGMII
bool "Enable MDIO Gpio bit bang support"
depends on BITBANGMII

View file

@ -0,0 +1,236 @@
#
# (C) Copyright 2006
# Wolfgang Denk, DENX Software Engineering, wd@denx.de.
#
# SPDX-License-Identifier: GPL-2.0+
#
ccflags-y += -I$(srctree)/board/qca/arm/ipq40xx -I$(srctree)/board/qca/arm/common
ccflags-y += -I$(srctree)/drivers/net/ipq_common
ifeq ($(strip $(CONFIG_TP_EXT_PHY_RTL8251B)),y)
ccflags-y += -I$(srctree)/drivers/net/rtl8251b
endif
ifeq ($(strip $(CONFIG_TP_EXT_SWITCH_RTL8367_ALL)),y)
ccflags-y += -I$(srctree)/drivers/net/rtl8367_common_V1_4_2 -I$(srctree)/drivers/net/rtl8367_common_V1_4_2/dal/ -I$(srctree)/drivers/net/rtl8367_common_V1_4_2/dal/rtl8367d/ -I$(srctree)/drivers/net/rtl8367_common_V1_4_2/dal/rtl8367c/
EXTRA_CFLAGS += $(TP_SDK_CFLAGS) -DMDC_MDIO_OPERATION -DCONFIG_DAL_RTL8367D
endif
ifeq ($(strip $(CONFIG_TP_EXT_PHY_RTL8221B)),y)
ccflags-y += -I$(srctree)/drivers/net/rtl8221b
endif
ifeq ($(strip $(CONFIG_TP_EXT_SWITCH_RTL8372)),y)
ccflags-y += -I$(srctree)/drivers/net/rtl8372 -I$(srctree)/drivers/net/rtl8372/dal/ -I$(srctree)/drivers/net/rtl8372/dal/rtl8373/
EXTRA_CFLAGS += $(TP_SDK_CFLAGS) -DCONFIG_TP_EXT_SWITCH_RTL8372 -DCONFIG_TP_EXT_SWITCH -DMDC_MDIO_OPERATION
endif
obj-$(CONFIG_PPC4xx_EMAC) += 4xx_enet.o
obj-$(CONFIG_ALTERA_TSE) += altera_tse.o
obj-$(CONFIG_ARMADA100_FEC) += armada100_fec.o
obj-$(CONFIG_DRIVER_AT91EMAC) += at91_emac.o
obj-$(CONFIG_DRIVER_AX88180) += ax88180.o
obj-$(CONFIG_BCM_SF2_ETH) += bcm-sf2-eth.o
obj-$(CONFIG_BCM_SF2_ETH_GMAC) += bcm-sf2-eth-gmac.o
obj-$(CONFIG_BFIN_MAC) += bfin_mac.o
obj-$(CONFIG_CALXEDA_XGMAC) += calxedaxgmac.o
obj-$(CONFIG_CS8900) += cs8900.o
obj-$(CONFIG_TULIP) += dc2114x.o
obj-$(CONFIG_ETH_DESIGNWARE) += designware.o
obj-$(CONFIG_DRIVER_DM9000) += dm9000x.o
obj-$(CONFIG_DNET) += dnet.o
obj-$(CONFIG_E1000) += e1000.o
obj-$(CONFIG_E1000_SPI) += e1000_spi.o
obj-$(CONFIG_EEPRO100) += eepro100.o
obj-$(CONFIG_SUNXI_EMAC) += sunxi_emac.o
obj-$(CONFIG_ENC28J60) += enc28j60.o
obj-$(CONFIG_EP93XX) += ep93xx_eth.o
obj-$(CONFIG_ETHOC) += ethoc.o
obj-$(CONFIG_FEC_MXC) += fec_mxc.o
obj-$(CONFIG_FSLDMAFEC) += fsl_mcdmafec.o mcfmii.o
obj-$(CONFIG_FTGMAC100) += ftgmac100.o
obj-$(CONFIG_FTMAC110) += ftmac110.o
obj-$(CONFIG_FTMAC100) += ftmac100.o
obj-$(CONFIG_GRETH) += greth.o
obj-$(CONFIG_DRIVER_TI_KEYSTONE_NET) += keystone_net.o
obj-$(CONFIG_KS8851_MLL) += ks8851_mll.o
obj-$(CONFIG_LAN91C96) += lan91c96.o
obj-$(CONFIG_LPC32XX_ETH) += lpc32xx_eth.o
obj-$(CONFIG_MACB) += macb.o
obj-$(CONFIG_MCFFEC) += mcffec.o mcfmii.o
obj-$(CONFIG_MPC5xxx_FEC) += mpc5xxx_fec.o
obj-$(CONFIG_MPC512x_FEC) += mpc512x_fec.o
obj-$(CONFIG_MVGBE) += mvgbe.o
obj-$(CONFIG_MVNETA) += mvneta.o
obj-$(CONFIG_NATSEMI) += natsemi.o
obj-$(CONFIG_DRIVER_NE2000) += ne2000.o ne2000_base.o
obj-$(CONFIG_DRIVER_AX88796L) += ax88796.o ne2000_base.o
obj-$(CONFIG_NETCONSOLE) += netconsole.o
obj-$(CONFIG_NS8382X) += ns8382x.o
obj-$(CONFIG_PCH_GBE) += pch_gbe.o
obj-$(CONFIG_PCNET) += pcnet.o
obj-$(CONFIG_RTL8139) += rtl8139.o
obj-$(CONFIG_RTL8169) += rtl8169.o
obj-$(CONFIG_ETH_SANDBOX) += sandbox.o
obj-$(CONFIG_ETH_SANDBOX_RAW) += sandbox-raw.o
obj-$(CONFIG_SH_ETHER) += sh_eth.o
obj-$(CONFIG_SMC91111) += smc91111.o
obj-$(CONFIG_SMC911X) += smc911x.o
obj-$(CONFIG_DRIVER_TI_EMAC) += davinci_emac.o
obj-$(CONFIG_TSEC_ENET) += tsec.o fsl_mdio.o
obj-$(CONFIG_DRIVER_TI_CPSW) += cpsw.o
obj-$(CONFIG_FMAN_ENET) += fsl_mdio.o
obj-$(CONFIG_TSI108_ETH) += tsi108_eth.o
obj-$(CONFIG_ULI526X) += uli526x.o
obj-$(CONFIG_VSC7385_ENET) += vsc7385.o
obj-$(CONFIG_XILINX_AXIEMAC) += xilinx_axi_emac.o
obj-$(CONFIG_XILINX_EMACLITE) += xilinx_emaclite.o
obj-$(CONFIG_XILINX_LL_TEMAC) += xilinx_ll_temac.o xilinx_ll_temac_mdio.o \
xilinx_ll_temac_fifo.o xilinx_ll_temac_sdma.o
obj-$(CONFIG_ZYNQ_GEM) += zynq_gem.o
obj-$(CONFIG_FSL_MC_ENET) += fsl-mc/
obj-$(CONFIG_FSL_MC_ENET) += ldpaa_eth/
obj-$(CONFIG_FSL_MEMAC) += fm/memac_phy.o
obj-$(CONFIG_VSC9953) += vsc9953.o
obj-$(CONFIG_IPQ40XX_EDMA) += ipq40xx/ipq40xx_edma_eth.o
obj-$(CONFIG_IPQ40XX_ESS) += ipq40xx/ipq40xx_ess_sw.o
obj-$(CONFIG_IPQ_SNPS_GMAC) += ipq806x/ipq_gmac_eth.o
obj-$(CONFIG_IPQ_SWITCH_ATHRS17) += ipq806x/athrs17_phy.o
obj-$(CONFIG_IPQ_SWITCH_QCA8511) += ipq806x/qca8511.o
obj-$(CONFIG_IPQ807X_EDMA) += ipq807x/ipq807x_edma.o
obj-$(CONFIG_IPQ807X_EDMA) += ipq807x/ipq807x_ppe.o
obj-$(CONFIG_IPQ807X_EDMA) += ipq807x/ipq807x_uniphy.o
obj-$(CONFIG_IPQ6018_EDMA) += ipq6018/ipq6018_edma.o
obj-$(CONFIG_IPQ6018_EDMA) += ipq6018/ipq6018_ppe.o
obj-$(CONFIG_IPQ6018_EDMA) += ipq6018/ipq6018_uniphy.o
obj-$(CONFIG_IPQ9574_EDMA) += ipq9574/ipq9574_ppe.o
obj-$(CONFIG_IPQ9574_EDMA) += ipq9574/ipq9574_uniphy.o
obj-$(CONFIG_IPQ9574_EDMA) += ipq9574/ipq9574_edma.o
obj-$(CONFIG_IPQ5018_GMAC) += ipq5018/ipq5018_gmac.o
obj-$(CONFIG_IPQ5018_GMAC) += ipq5018/ipq5018_uniphy.o
obj-$(CONFIG_IPQ5018_MDIO) += ipq5018/ipq5018_mdio.o
obj-$(CONFIG_IPQ5018_GMAC) += ipq5018/athrs17_phy.o
obj-$(CONFIG_IPQ5332_EDMA) += ipq5332/ipq5332_edma.o
obj-$(CONFIG_IPQ5332_EDMA) += ipq5332/ipq5332_ppe.o
ifndef CONFIG_IPQ5332_RUMI
obj-$(CONFIG_IPQ5332_EDMA) += ipq5332/ipq5332_uniphy.o
endif
ifeq ($(strip $(CONFIG_TP_EXT_SWITCH_RTL8367_ALL)),y)
obj-$(CONFIG_IPQ5332_EDMA) += rtl8367_common_V1_4_2/dal/rtl8367d/rtl8367d_asicdrv.o
obj-$(CONFIG_IPQ5332_EDMA) += rtl8367_common_V1_4_2/dal/rtl8367d/rtl8367d_smi.o
obj-$(CONFIG_IPQ5332_EDMA) += rtl8367_common_V1_4_2/dal/rtl8367d/dal_rtl8367d_acl.o
obj-$(CONFIG_IPQ5332_EDMA) += rtl8367_common_V1_4_2/dal/rtl8367d/dal_rtl8367d_cpu.o
obj-$(CONFIG_IPQ5332_EDMA) += rtl8367_common_V1_4_2/dal/rtl8367d/dal_rtl8367d_dot1x.o
obj-$(CONFIG_IPQ5332_EDMA) += rtl8367_common_V1_4_2/dal/rtl8367d/dal_rtl8367d_eee.o
obj-$(CONFIG_IPQ5332_EDMA) += rtl8367_common_V1_4_2/dal/rtl8367d/dal_rtl8367d_gpio.o
obj-$(CONFIG_IPQ5332_EDMA) += rtl8367_common_V1_4_2/dal/rtl8367d/dal_rtl8367d_igmp.o
obj-$(CONFIG_IPQ5332_EDMA) += rtl8367_common_V1_4_2/dal/rtl8367d/dal_rtl8367d_interrupt.o
obj-$(CONFIG_IPQ5332_EDMA) += rtl8367_common_V1_4_2/dal/rtl8367d/dal_rtl8367d_l2.o
obj-$(CONFIG_IPQ5332_EDMA) += rtl8367_common_V1_4_2/dal/rtl8367d/dal_rtl8367d_leaky.o
obj-$(CONFIG_IPQ5332_EDMA) += rtl8367_common_V1_4_2/dal/rtl8367d/dal_rtl8367d_led.o
obj-$(CONFIG_IPQ5332_EDMA) += rtl8367_common_V1_4_2/dal/rtl8367d/dal_rtl8367d_mapper.o
obj-$(CONFIG_IPQ5332_EDMA) += rtl8367_common_V1_4_2/dal/rtl8367d/dal_rtl8367d_mirror.o
obj-$(CONFIG_IPQ5332_EDMA) += rtl8367_common_V1_4_2/dal/rtl8367d/dal_rtl8367d_port.o
obj-$(CONFIG_IPQ5332_EDMA) += rtl8367_common_V1_4_2/dal/rtl8367d/dal_rtl8367d_qos.o
obj-$(CONFIG_IPQ5332_EDMA) += rtl8367_common_V1_4_2/dal/rtl8367d/dal_rtl8367d_rate.o
obj-$(CONFIG_IPQ5332_EDMA) += rtl8367_common_V1_4_2/dal/rtl8367d/dal_rtl8367d_rldp.o
obj-$(CONFIG_IPQ5332_EDMA) += rtl8367_common_V1_4_2/dal/rtl8367d/dal_rtl8367d_stat.o
obj-$(CONFIG_IPQ5332_EDMA) += rtl8367_common_V1_4_2/dal/rtl8367d/dal_rtl8367d_storm.o
obj-$(CONFIG_IPQ5332_EDMA) += rtl8367_common_V1_4_2/dal/rtl8367d/dal_rtl8367d_svlan.o
obj-$(CONFIG_IPQ5332_EDMA) += rtl8367_common_V1_4_2/dal/rtl8367d/dal_rtl8367d_switch.o
obj-$(CONFIG_IPQ5332_EDMA) += rtl8367_common_V1_4_2/dal/rtl8367d/dal_rtl8367d_trap.o
obj-$(CONFIG_IPQ5332_EDMA) += rtl8367_common_V1_4_2/dal/rtl8367d/dal_rtl8367d_trunk.o
obj-$(CONFIG_IPQ5332_EDMA) += rtl8367_common_V1_4_2/dal/rtl8367d/dal_rtl8367d_vlan.o
obj-$(CONFIG_IPQ5332_EDMA) += rtl8367_common_V1_4_2/rtk_switch.o
obj-$(CONFIG_IPQ5332_EDMA) += rtl8367_common_V1_4_2/port.o
obj-$(CONFIG_IPQ5332_EDMA) += rtl8367_common_V1_4_2/vlan.o
obj-$(CONFIG_IPQ5332_EDMA) += rtl8367_common_V1_4_2/chip.o
obj-$(CONFIG_IPQ5332_EDMA) += rtl8367_common_V1_4_2/dal/dal_mgmt.o
endif
ifeq ($(strip $(CONFIG_TP_EXT_SWITCH_RTL8372)),y)
obj-$(CONFIG_IPQ5332_EDMA) += rtl8372/dal/rtl8373/dal_rtl8373_switch.o
obj-$(CONFIG_IPQ5332_EDMA) += rtl8372/dal/rtl8373/dal_rtl8373_port.o
obj-$(CONFIG_IPQ5332_EDMA) += rtl8372/dal/rtl8373/dal_rtl8373_acl.o
obj-$(CONFIG_IPQ5332_EDMA) += rtl8372/dal/rtl8373/dal_rtl8373_fc.o
obj-$(CONFIG_IPQ5332_EDMA) += rtl8372/dal/rtl8373/dal_rtl8373_isolation.o
obj-$(CONFIG_IPQ5332_EDMA) += rtl8372/dal/rtl8373/rtl8373_asicdrv.o
obj-$(CONFIG_IPQ5332_EDMA) += rtl8372/dal/rtl8373/dal_rtl8373_mirror.o
obj-$(CONFIG_IPQ5332_EDMA) += rtl8372/dal/rtl8373/dal_rtl8373_rate.o
obj-$(CONFIG_IPQ5332_EDMA) += rtl8372/dal/rtl8373/dal_rtl8373_svlan.o
obj-$(CONFIG_IPQ5332_EDMA) += rtl8372/dal/rtl8373/rtl8373_regField_list.o
obj-$(CONFIG_IPQ5332_EDMA) += rtl8372/dal/rtl8373/dal_rtl8373_cpuTag.o
obj-$(CONFIG_IPQ5332_EDMA) += rtl8372/dal/rtl8373/dal_rtl8373_gpio.o
obj-$(CONFIG_IPQ5332_EDMA) += rtl8372/dal/rtl8373/dal_rtl8373_led.o
obj-$(CONFIG_IPQ5332_EDMA) += rtl8372/dal/rtl8373/dal_rtl8373_nic.o
obj-$(CONFIG_IPQ5332_EDMA) += rtl8372/dal/rtl8373/dal_rtl8373_rma.o
obj-$(CONFIG_IPQ5332_EDMA) += rtl8372/dal/rtl8373/dal_rtl8373_switch.o
obj-$(CONFIG_IPQ5332_EDMA) += rtl8372/dal/rtl8373/rtl8373_reg_list.o
obj-$(CONFIG_IPQ5332_EDMA) += rtl8372/dal/rtl8373/dal_rtl8373_dos.o
obj-$(CONFIG_IPQ5332_EDMA) += rtl8372/dal/rtl8373/dal_rtl8373_hsb.o
obj-$(CONFIG_IPQ5332_EDMA) += rtl8372/dal/rtl8373/dal_rtl8373_lut.o
obj-$(CONFIG_IPQ5332_EDMA) += rtl8372/dal/rtl8373/dal_rtl8373_parser.o
obj-$(CONFIG_IPQ5332_EDMA) += rtl8372/dal/rtl8373/dal_rtl8373_rtkpp.o
obj-$(CONFIG_IPQ5332_EDMA) += rtl8372/dal/rtl8373/dal_rtl8373_trunk.o
obj-$(CONFIG_IPQ5332_EDMA) += rtl8372/dal/rtl8373/rtl8373_smi.o
obj-$(CONFIG_IPQ5332_EDMA) += rtl8372/dal/rtl8373/dal_rtl8373_dot1x.o
obj-$(CONFIG_IPQ5332_EDMA) += rtl8372/dal/rtl8373/dal_rtl8373_i2c.o
obj-$(CONFIG_IPQ5332_EDMA) += rtl8372/dal/rtl8373/dal_rtl8373_macsec.o
obj-$(CONFIG_IPQ5332_EDMA) += rtl8372/dal/rtl8373/dal_rtl8373_sharemeter.o
obj-$(CONFIG_IPQ5332_EDMA) += rtl8372/dal/rtl8373/dal_rtl8373_vlan.o
obj-$(CONFIG_IPQ5332_EDMA) += rtl8372/dal/rtl8373/rtl8373_tableField_list.o
obj-$(CONFIG_IPQ5332_EDMA) += rtl8372/dal/rtl8373/dal_rtl8373_drv.o
obj-$(CONFIG_IPQ5332_EDMA) += rtl8372/dal/rtl8373/dal_rtl8373_igmp.o
obj-$(CONFIG_IPQ5332_EDMA) += rtl8372/dal/rtl8373/dal_rtl8373_mapper.o
obj-$(CONFIG_IPQ5332_EDMA) += rtl8372/dal/rtl8373/dal_rtl8373_ptp.o
obj-$(CONFIG_IPQ5332_EDMA) += rtl8372/dal/rtl8373/dal_rtl8373_storm.o
obj-$(CONFIG_IPQ5332_EDMA) += rtl8372/dal/rtl8373/dal_rtl8373_wol.o
obj-$(CONFIG_IPQ5332_EDMA) += rtl8372/dal/rtl8373/rtl8373_table_list.o
obj-$(CONFIG_IPQ5332_EDMA) += rtl8372/dal/rtl8373/dal_rtl8373_eee.o
obj-$(CONFIG_IPQ5332_EDMA) += rtl8372/dal/rtl8373/dal_rtl8373_interrupt.o
obj-$(CONFIG_IPQ5332_EDMA) += rtl8372/dal/rtl8373/dal_rtl8373_mib.o
obj-$(CONFIG_IPQ5332_EDMA) += rtl8372/dal/rtl8373/dal_rtl8373_qos.o
obj-$(CONFIG_IPQ5332_EDMA) += rtl8372/dal/rtl8373/dal_rtl8373_stp.o
obj-$(CONFIG_IPQ5332_EDMA) += rtl8372/rtk_switch.o
obj-$(CONFIG_IPQ5332_EDMA) += rtl8372/port.o
obj-$(CONFIG_IPQ5332_EDMA) += rtl8372/vlan.o
obj-$(CONFIG_IPQ5332_EDMA) += rtl8372/chip.o
obj-$(CONFIG_IPQ5332_EDMA) += rtl8372/identify.o
obj-$(CONFIG_IPQ5332_EDMA) += rtl8372/dal/dal_mgmt.o
endif
ifeq ($(strip $(CONFIG_TP_EXT_PHY_RTL8261B)),y)
obj-$(CONFIG_TP_EXT_PHY_RTL8261B) += ipq_common/rtl8261_phy.o
obj-$(CONFIG_TP_EXT_PHY_RTL8261B) += ipq_common/rtl8261_patch.o
endif
ifeq ($(strip $(CONFIG_TP_EXT_PHY_RTL8251B)),y)
obj-$(CONFIG_TP_EXT_PHY_RTL8251B) += rtl8251b/nic_rtl8251b.o
obj-$(CONFIG_TP_EXT_PHY_RTL8251B) += rtl8251b/nic_rtl8251b_init.o
endif
ifeq ($(strip $(CONFIG_TP_EXT_PHY_RTL8221B)),y)
obj-$(CONFIG_TP_EXT_PHY_RTL8221B) += rtl8221b/nic_rtl8226b.o
obj-$(CONFIG_TP_EXT_PHY_RTL8221B) += rtl8221b/nic_rtl8226b_init.o
endif
obj-$(CONFIG_IPQ_MDIO) += ipq_common/ipq_mdio.o
obj-$(CONFIG_IPQ_QTI_BIT_BANGMII) += ipq_common/ipq_bitbangmii.o
obj-$(CONFIG_GEPHY) += ipq_common/ipq_gephy.o
obj-$(CONFIG_QCA8075_PHY) += ipq_common/ipq_qca8075.o
obj-$(CONFIG_QCA8084_PHY) += ipq_common/ipq_qca8084.o
obj-$(CONFIG_QCA8084_PHY) += ipq_common/ipq_qca8084_clk.o
obj-$(CONFIG_QCA8084_PHY) += ipq_common/ipq_qca8084_interface_ctrl.o
obj-$(CONFIG_ATHRS17C_SWITCH) += ipq_common/athrs17_phy.o
obj-$(CONFIG_IPQ9574_QCA8075_PHY) += ipq9574/ipq9574_qca8075.o
obj-$(CONFIG_QCA8033_PHY) += ipq_common/ipq_qca8033.o
obj-$(CONFIG_QCA8081_PHY) += ipq_common/ipq_qca8081.o
obj-$(CONFIG_IPQ_QCA_AQUANTIA_PHY) += ipq_common/ipq_aquantia_phy.o
obj-$(CONFIG_QCA_AQUANTIA_PHY) += ipq807x/ipq807x_aquantia_phy.o
obj-$(CONFIG_IPQ6018_QCA_AQUANTIA_PHY) += ipq6018/ipq6018_aquantia_phy.o
obj-$(CONFIG_IPQ9574_QCA_AQUANTIA_PHY) += ipq9574/ipq9574_aquantia_phy.o

View file

@ -0,0 +1,714 @@
/*
* Altera 10/100/1000 triple speed ethernet mac driver
*
* Copyright (C) 2008 Altera Corporation.
* Copyright (C) 2010 Thomas Chou <thomas@wytron.com.tw>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <common.h>
#include <dm.h>
#include <errno.h>
#include <fdt_support.h>
#include <memalign.h>
#include <miiphy.h>
#include <net.h>
#include <asm/cache.h>
#include <asm/dma-mapping.h>
#include <asm/io.h>
#include "altera_tse.h"
DECLARE_GLOBAL_DATA_PTR;
static inline void alt_sgdma_construct_descriptor(
struct alt_sgdma_descriptor *desc,
struct alt_sgdma_descriptor *next,
void *read_addr,
void *write_addr,
u16 length_or_eop,
int generate_eop,
int read_fixed,
int write_fixed_or_sop)
{
u8 val;
/*
* Mark the "next" descriptor as "not" owned by hardware. This prevents
* The SGDMA controller from continuing to process the chain.
*/
next->descriptor_control = next->descriptor_control &
~ALT_SGDMA_DESCRIPTOR_CONTROL_OWNED_BY_HW_MSK;
memset(desc, 0, sizeof(struct alt_sgdma_descriptor));
desc->source = virt_to_phys(read_addr);
desc->destination = virt_to_phys(write_addr);
desc->next = virt_to_phys(next);
desc->bytes_to_transfer = length_or_eop;
/*
* Set the descriptor control block as follows:
* - Set "owned by hardware" bit
* - Optionally set "generate EOP" bit
* - Optionally set the "read from fixed address" bit
* - Optionally set the "write to fixed address bit (which serves
* serves as a "generate SOP" control bit in memory-to-stream mode).
* - Set the 4-bit atlantic channel, if specified
*
* Note this step is performed after all other descriptor information
* has been filled out so that, if the controller already happens to be
* pointing at this descriptor, it will not run (via the "owned by
* hardware" bit) until all other descriptor has been set up.
*/
val = ALT_SGDMA_DESCRIPTOR_CONTROL_OWNED_BY_HW_MSK;
if (generate_eop)
val |= ALT_SGDMA_DESCRIPTOR_CONTROL_GENERATE_EOP_MSK;
if (read_fixed)
val |= ALT_SGDMA_DESCRIPTOR_CONTROL_READ_FIXED_ADDRESS_MSK;
if (write_fixed_or_sop)
val |= ALT_SGDMA_DESCRIPTOR_CONTROL_WRITE_FIXED_ADDRESS_MSK;
desc->descriptor_control = val;
}
static int alt_sgdma_wait_transfer(struct alt_sgdma_registers *regs)
{
int status;
ulong ctime;
/* Wait for the descriptor (chain) to complete */
ctime = get_timer(0);
while (1) {
status = readl(&regs->status);
if (!(status & ALT_SGDMA_STATUS_BUSY_MSK))
break;
if (get_timer(ctime) > ALT_TSE_SGDMA_BUSY_TIMEOUT) {
status = -ETIMEDOUT;
debug("sgdma timeout\n");
break;
}
}
/* Clear Run */
writel(0, &regs->control);
/* Clear status */
writel(0xff, &regs->status);
return status;
}
static int alt_sgdma_start_transfer(struct alt_sgdma_registers *regs,
struct alt_sgdma_descriptor *desc)
{
u32 val;
/* Point the controller at the descriptor */
writel(virt_to_phys(desc), &regs->next_descriptor_pointer);
/*
* Set up SGDMA controller to:
* - Disable interrupt generation
* - Run once a valid descriptor is written to controller
* - Stop on an error with any particular descriptor
*/
val = ALT_SGDMA_CONTROL_RUN_MSK | ALT_SGDMA_CONTROL_STOP_DMA_ER_MSK;
writel(val, &regs->control);
return 0;
}
static void tse_adjust_link(struct altera_tse_priv *priv,
struct phy_device *phydev)
{
struct alt_tse_mac *mac_dev = priv->mac_dev;
u32 refvar;
if (!phydev->link) {
debug("%s: No link.\n", phydev->dev->name);
return;
}
refvar = readl(&mac_dev->command_config);
if (phydev->duplex)
refvar |= ALTERA_TSE_CMD_HD_ENA_MSK;
else
refvar &= ~ALTERA_TSE_CMD_HD_ENA_MSK;
switch (phydev->speed) {
case 1000:
refvar |= ALTERA_TSE_CMD_ETH_SPEED_MSK;
refvar &= ~ALTERA_TSE_CMD_ENA_10_MSK;
break;
case 100:
refvar &= ~ALTERA_TSE_CMD_ETH_SPEED_MSK;
refvar &= ~ALTERA_TSE_CMD_ENA_10_MSK;
break;
case 10:
refvar &= ~ALTERA_TSE_CMD_ETH_SPEED_MSK;
refvar |= ALTERA_TSE_CMD_ENA_10_MSK;
break;
}
writel(refvar, &mac_dev->command_config);
}
static int altera_tse_send_sgdma(struct udevice *dev, void *packet, int length)
{
struct altera_tse_priv *priv = dev_get_priv(dev);
struct alt_sgdma_descriptor *tx_desc = priv->tx_desc;
alt_sgdma_construct_descriptor(
tx_desc,
tx_desc + 1,
packet, /* read addr */
NULL, /* write addr */
length, /* length or EOP ,will change for each tx */
1, /* gen eop */
0, /* read fixed */
1 /* write fixed or sop */
);
/* send the packet */
alt_sgdma_start_transfer(priv->sgdma_tx, tx_desc);
alt_sgdma_wait_transfer(priv->sgdma_tx);
debug("sent %d bytes\n", tx_desc->actual_bytes_transferred);
return tx_desc->actual_bytes_transferred;
}
static int altera_tse_recv_sgdma(struct udevice *dev, int flags,
uchar **packetp)
{
struct altera_tse_priv *priv = dev_get_priv(dev);
struct alt_sgdma_descriptor *rx_desc = priv->rx_desc;
int packet_length;
if (rx_desc->descriptor_status &
ALT_SGDMA_DESCRIPTOR_STATUS_TERMINATED_BY_EOP_MSK) {
alt_sgdma_wait_transfer(priv->sgdma_rx);
packet_length = rx_desc->actual_bytes_transferred;
debug("recv %d bytes\n", packet_length);
*packetp = priv->rx_buf;
return packet_length;
}
return -EAGAIN;
}
static int altera_tse_free_pkt_sgdma(struct udevice *dev, uchar *packet,
int length)
{
struct altera_tse_priv *priv = dev_get_priv(dev);
struct alt_sgdma_descriptor *rx_desc = priv->rx_desc;
alt_sgdma_construct_descriptor(
rx_desc,
rx_desc + 1,
NULL, /* read addr */
priv->rx_buf, /* write addr */
0, /* length or EOP */
0, /* gen eop */
0, /* read fixed */
0 /* write fixed or sop */
);
/* setup the sgdma */
alt_sgdma_start_transfer(priv->sgdma_rx, rx_desc);
debug("recv setup\n");
return 0;
}
static void altera_tse_stop_mac(struct altera_tse_priv *priv)
{
struct alt_tse_mac *mac_dev = priv->mac_dev;
u32 status;
ulong ctime;
/* reset the mac */
writel(ALTERA_TSE_CMD_SW_RESET_MSK, &mac_dev->command_config);
ctime = get_timer(0);
while (1) {
status = readl(&mac_dev->command_config);
if (!(status & ALTERA_TSE_CMD_SW_RESET_MSK))
break;
if (get_timer(ctime) > ALT_TSE_SW_RESET_TIMEOUT) {
debug("Reset mac timeout\n");
break;
}
}
}
static void altera_tse_stop_sgdma(struct udevice *dev)
{
struct altera_tse_priv *priv = dev_get_priv(dev);
struct alt_sgdma_registers *rx_sgdma = priv->sgdma_rx;
struct alt_sgdma_registers *tx_sgdma = priv->sgdma_tx;
struct alt_sgdma_descriptor *rx_desc = priv->rx_desc;
int ret;
/* clear rx desc & wait for sgdma to complete */
rx_desc->descriptor_control = 0;
writel(0, &rx_sgdma->control);
ret = alt_sgdma_wait_transfer(rx_sgdma);
if (ret == -ETIMEDOUT)
writel(ALT_SGDMA_CONTROL_SOFTWARERESET_MSK,
&rx_sgdma->control);
writel(0, &tx_sgdma->control);
ret = alt_sgdma_wait_transfer(tx_sgdma);
if (ret == -ETIMEDOUT)
writel(ALT_SGDMA_CONTROL_SOFTWARERESET_MSK,
&tx_sgdma->control);
}
static void msgdma_reset(struct msgdma_csr *csr)
{
u32 status;
ulong ctime;
/* Reset mSGDMA */
writel(MSGDMA_CSR_STAT_MASK, &csr->status);
writel(MSGDMA_CSR_CTL_RESET, &csr->control);
ctime = get_timer(0);
while (1) {
status = readl(&csr->status);
if (!(status & MSGDMA_CSR_STAT_RESETTING))
break;
if (get_timer(ctime) > ALT_TSE_SW_RESET_TIMEOUT) {
debug("Reset msgdma timeout\n");
break;
}
}
/* Clear status */
writel(MSGDMA_CSR_STAT_MASK, &csr->status);
}
static u32 msgdma_wait(struct msgdma_csr *csr)
{
u32 status;
ulong ctime;
/* Wait for the descriptor to complete */
ctime = get_timer(0);
while (1) {
status = readl(&csr->status);
if (!(status & MSGDMA_CSR_STAT_BUSY))
break;
if (get_timer(ctime) > ALT_TSE_SGDMA_BUSY_TIMEOUT) {
debug("sgdma timeout\n");
break;
}
}
/* Clear status */
writel(MSGDMA_CSR_STAT_MASK, &csr->status);
return status;
}
static int altera_tse_send_msgdma(struct udevice *dev, void *packet,
int length)
{
struct altera_tse_priv *priv = dev_get_priv(dev);
struct msgdma_extended_desc *desc = priv->tx_desc;
u32 tx_buf = virt_to_phys(packet);
u32 status;
writel(tx_buf, &desc->read_addr_lo);
writel(0, &desc->read_addr_hi);
writel(0, &desc->write_addr_lo);
writel(0, &desc->write_addr_hi);
writel(length, &desc->len);
writel(0, &desc->burst_seq_num);
writel(MSGDMA_DESC_TX_STRIDE, &desc->stride);
writel(MSGDMA_DESC_CTL_TX_SINGLE, &desc->control);
status = msgdma_wait(priv->sgdma_tx);
debug("sent %d bytes, status %08x\n", length, status);
return 0;
}
static int altera_tse_recv_msgdma(struct udevice *dev, int flags,
uchar **packetp)
{
struct altera_tse_priv *priv = dev_get_priv(dev);
struct msgdma_csr *csr = priv->sgdma_rx;
struct msgdma_response *resp = priv->rx_resp;
u32 level, length, status;
level = readl(&csr->resp_fill_level);
if (level & 0xffff) {
length = readl(&resp->bytes_transferred);
status = readl(&resp->status);
debug("recv %d bytes, status %08x\n", length, status);
*packetp = priv->rx_buf;
return length;
}
return -EAGAIN;
}
static int altera_tse_free_pkt_msgdma(struct udevice *dev, uchar *packet,
int length)
{
struct altera_tse_priv *priv = dev_get_priv(dev);
struct msgdma_extended_desc *desc = priv->rx_desc;
u32 rx_buf = virt_to_phys(priv->rx_buf);
writel(0, &desc->read_addr_lo);
writel(0, &desc->read_addr_hi);
writel(rx_buf, &desc->write_addr_lo);
writel(0, &desc->write_addr_hi);
writel(PKTSIZE_ALIGN, &desc->len);
writel(0, &desc->burst_seq_num);
writel(MSGDMA_DESC_RX_STRIDE, &desc->stride);
writel(MSGDMA_DESC_CTL_RX_SINGLE, &desc->control);
debug("recv setup\n");
return 0;
}
static void altera_tse_stop_msgdma(struct udevice *dev)
{
struct altera_tse_priv *priv = dev_get_priv(dev);
msgdma_reset(priv->sgdma_rx);
msgdma_reset(priv->sgdma_tx);
}
static int tse_mdio_read(struct mii_dev *bus, int addr, int devad, int reg)
{
struct altera_tse_priv *priv = bus->priv;
struct alt_tse_mac *mac_dev = priv->mac_dev;
u32 value;
/* set mdio address */
writel(addr, &mac_dev->mdio_phy1_addr);
/* get the data */
value = readl(&mac_dev->mdio_phy1[reg]);
return value & 0xffff;
}
static int tse_mdio_write(struct mii_dev *bus, int addr, int devad, int reg,
u16 val)
{
struct altera_tse_priv *priv = bus->priv;
struct alt_tse_mac *mac_dev = priv->mac_dev;
/* set mdio address */
writel(addr, &mac_dev->mdio_phy1_addr);
/* set the data */
writel(val, &mac_dev->mdio_phy1[reg]);
return 0;
}
static int tse_mdio_init(const char *name, struct altera_tse_priv *priv)
{
struct mii_dev *bus = mdio_alloc();
if (!bus) {
printf("Failed to allocate MDIO bus\n");
return -ENOMEM;
}
bus->read = tse_mdio_read;
bus->write = tse_mdio_write;
snprintf(bus->name, sizeof(bus->name), name);
bus->priv = (void *)priv;
return mdio_register(bus);
}
static int tse_phy_init(struct altera_tse_priv *priv, void *dev)
{
struct phy_device *phydev;
unsigned int mask = 0xffffffff;
if (priv->phyaddr)
mask = 1 << priv->phyaddr;
phydev = phy_find_by_mask(priv->bus, mask, priv->interface);
if (!phydev)
return -ENODEV;
phy_connect_dev(phydev, dev);
phydev->supported &= PHY_GBIT_FEATURES;
phydev->advertising = phydev->supported;
priv->phydev = phydev;
phy_config(phydev);
return 0;
}
static int altera_tse_write_hwaddr(struct udevice *dev)
{
struct altera_tse_priv *priv = dev_get_priv(dev);
struct alt_tse_mac *mac_dev = priv->mac_dev;
struct eth_pdata *pdata = dev_get_platdata(dev);
u8 *hwaddr = pdata->enetaddr;
u32 mac_lo, mac_hi;
mac_lo = (hwaddr[3] << 24) | (hwaddr[2] << 16) |
(hwaddr[1] << 8) | hwaddr[0];
mac_hi = (hwaddr[5] << 8) | hwaddr[4];
debug("Set MAC address to 0x%04x%08x\n", mac_hi, mac_lo);
writel(mac_lo, &mac_dev->mac_addr_0);
writel(mac_hi, &mac_dev->mac_addr_1);
writel(mac_lo, &mac_dev->supp_mac_addr_0_0);
writel(mac_hi, &mac_dev->supp_mac_addr_0_1);
writel(mac_lo, &mac_dev->supp_mac_addr_1_0);
writel(mac_hi, &mac_dev->supp_mac_addr_1_1);
writel(mac_lo, &mac_dev->supp_mac_addr_2_0);
writel(mac_hi, &mac_dev->supp_mac_addr_2_1);
writel(mac_lo, &mac_dev->supp_mac_addr_3_0);
writel(mac_hi, &mac_dev->supp_mac_addr_3_1);
return 0;
}
static int altera_tse_send(struct udevice *dev, void *packet, int length)
{
struct altera_tse_priv *priv = dev_get_priv(dev);
unsigned long tx_buf = (unsigned long)packet;
flush_dcache_range(tx_buf, tx_buf + length);
return priv->ops->send(dev, packet, length);
}
static int altera_tse_recv(struct udevice *dev, int flags, uchar **packetp)
{
struct altera_tse_priv *priv = dev_get_priv(dev);
return priv->ops->recv(dev, flags, packetp);
}
static int altera_tse_free_pkt(struct udevice *dev, uchar *packet,
int length)
{
struct altera_tse_priv *priv = dev_get_priv(dev);
unsigned long rx_buf = (unsigned long)priv->rx_buf;
invalidate_dcache_range(rx_buf, rx_buf + PKTSIZE_ALIGN);
return priv->ops->free_pkt(dev, packet, length);
}
static void altera_tse_stop(struct udevice *dev)
{
struct altera_tse_priv *priv = dev_get_priv(dev);
priv->ops->stop(dev);
altera_tse_stop_mac(priv);
}
static int altera_tse_start(struct udevice *dev)
{
struct altera_tse_priv *priv = dev_get_priv(dev);
struct alt_tse_mac *mac_dev = priv->mac_dev;
u32 val;
int ret;
/* need to create sgdma */
debug("Configuring rx desc\n");
altera_tse_free_pkt(dev, priv->rx_buf, PKTSIZE_ALIGN);
/* start TSE */
debug("Configuring TSE Mac\n");
/* Initialize MAC registers */
writel(PKTSIZE_ALIGN, &mac_dev->max_frame_length);
writel(priv->rx_fifo_depth - 16, &mac_dev->rx_sel_empty_threshold);
writel(0, &mac_dev->rx_sel_full_threshold);
writel(priv->tx_fifo_depth - 16, &mac_dev->tx_sel_empty_threshold);
writel(0, &mac_dev->tx_sel_full_threshold);
writel(8, &mac_dev->rx_almost_empty_threshold);
writel(8, &mac_dev->rx_almost_full_threshold);
writel(8, &mac_dev->tx_almost_empty_threshold);
writel(3, &mac_dev->tx_almost_full_threshold);
/* NO Shift */
writel(0, &mac_dev->rx_cmd_stat);
writel(0, &mac_dev->tx_cmd_stat);
/* enable MAC */
val = ALTERA_TSE_CMD_TX_ENA_MSK | ALTERA_TSE_CMD_RX_ENA_MSK;
writel(val, &mac_dev->command_config);
/* Start up the PHY */
ret = phy_startup(priv->phydev);
if (ret) {
debug("Could not initialize PHY %s\n",
priv->phydev->dev->name);
return ret;
}
tse_adjust_link(priv, priv->phydev);
if (!priv->phydev->link)
return -EIO;
return 0;
}
static const struct tse_ops tse_sgdma_ops = {
.send = altera_tse_send_sgdma,
.recv = altera_tse_recv_sgdma,
.free_pkt = altera_tse_free_pkt_sgdma,
.stop = altera_tse_stop_sgdma,
};
static const struct tse_ops tse_msgdma_ops = {
.send = altera_tse_send_msgdma,
.recv = altera_tse_recv_msgdma,
.free_pkt = altera_tse_free_pkt_msgdma,
.stop = altera_tse_stop_msgdma,
};
static int altera_tse_probe(struct udevice *dev)
{
struct eth_pdata *pdata = dev_get_platdata(dev);
struct altera_tse_priv *priv = dev_get_priv(dev);
void *blob = (void *)gd->fdt_blob;
int node = dev->of_offset;
const char *list, *end;
const fdt32_t *cell;
void *base, *desc_mem = NULL;
unsigned long addr, size;
int parent, addrc, sizec;
int len, idx;
int ret;
priv->dma_type = dev_get_driver_data(dev);
if (priv->dma_type == ALT_SGDMA)
priv->ops = &tse_sgdma_ops;
else
priv->ops = &tse_msgdma_ops;
/*
* decode regs. there are multiple reg tuples, and they need to
* match with reg-names.
*/
parent = fdt_parent_offset(blob, node);
of_bus_default_count_cells(blob, parent, &addrc, &sizec);
list = fdt_getprop(blob, node, "reg-names", &len);
if (!list)
return -ENOENT;
end = list + len;
cell = fdt_getprop(blob, node, "reg", &len);
if (!cell)
return -ENOENT;
idx = 0;
while (list < end) {
addr = fdt_translate_address((void *)blob,
node, cell + idx);
size = fdt_addr_to_cpu(cell[idx + addrc]);
base = map_physmem(addr, size, MAP_NOCACHE);
len = strlen(list);
if (strcmp(list, "control_port") == 0)
priv->mac_dev = base;
else if (strcmp(list, "rx_csr") == 0)
priv->sgdma_rx = base;
else if (strcmp(list, "rx_desc") == 0)
priv->rx_desc = base;
else if (strcmp(list, "rx_resp") == 0)
priv->rx_resp = base;
else if (strcmp(list, "tx_csr") == 0)
priv->sgdma_tx = base;
else if (strcmp(list, "tx_desc") == 0)
priv->tx_desc = base;
else if (strcmp(list, "s1") == 0)
desc_mem = base;
idx += addrc + sizec;
list += (len + 1);
}
/* decode fifo depth */
priv->rx_fifo_depth = fdtdec_get_int(blob, node,
"rx-fifo-depth", 0);
priv->tx_fifo_depth = fdtdec_get_int(blob, node,
"tx-fifo-depth", 0);
/* decode phy */
addr = fdtdec_get_int(blob, node,
"phy-handle", 0);
addr = fdt_node_offset_by_phandle(blob, addr);
priv->phyaddr = fdtdec_get_int(blob, addr,
"reg", 0);
/* init desc */
if (priv->dma_type == ALT_SGDMA) {
len = sizeof(struct alt_sgdma_descriptor) * 4;
if (!desc_mem) {
desc_mem = dma_alloc_coherent(len, &addr);
if (!desc_mem)
return -ENOMEM;
}
memset(desc_mem, 0, len);
priv->tx_desc = desc_mem;
priv->rx_desc = priv->tx_desc +
2 * sizeof(struct alt_sgdma_descriptor);
}
/* allocate recv packet buffer */
priv->rx_buf = malloc_cache_aligned(PKTSIZE_ALIGN);
if (!priv->rx_buf)
return -ENOMEM;
/* stop controller */
debug("Reset TSE & SGDMAs\n");
altera_tse_stop(dev);
/* start the phy */
priv->interface = pdata->phy_interface;
tse_mdio_init(dev->name, priv);
priv->bus = miiphy_get_dev_by_name(dev->name);
ret = tse_phy_init(priv, dev);
return ret;
}
static int altera_tse_ofdata_to_platdata(struct udevice *dev)
{
struct eth_pdata *pdata = dev_get_platdata(dev);
const char *phy_mode;
pdata->phy_interface = -1;
phy_mode = fdt_getprop(gd->fdt_blob, dev->of_offset, "phy-mode", NULL);
if (phy_mode)
pdata->phy_interface = phy_get_interface_by_name(phy_mode);
if (pdata->phy_interface == -1) {
debug("%s: Invalid PHY interface '%s'\n", __func__, phy_mode);
return -EINVAL;
}
return 0;
}
static const struct eth_ops altera_tse_ops = {
.start = altera_tse_start,
.send = altera_tse_send,
.recv = altera_tse_recv,
.free_pkt = altera_tse_free_pkt,
.stop = altera_tse_stop,
.write_hwaddr = altera_tse_write_hwaddr,
};
static const struct udevice_id altera_tse_ids[] = {
{ .compatible = "altr,tse-msgdma-1.0", .data = ALT_MSGDMA },
{ .compatible = "altr,tse-1.0", .data = ALT_SGDMA },
{}
};
U_BOOT_DRIVER(altera_tse) = {
.name = "altera_tse",
.id = UCLASS_ETH,
.of_match = altera_tse_ids,
.ops = &altera_tse_ops,
.ofdata_to_platdata = altera_tse_ofdata_to_platdata,
.platdata_auto_alloc_size = sizeof(struct eth_pdata),
.priv_auto_alloc_size = sizeof(struct altera_tse_priv),
.probe = altera_tse_probe,
};

View file

@ -0,0 +1,231 @@
/*
* Altera 10/100/1000 triple speed ethernet mac
*
* Copyright (C) 2008 Altera Corporation.
* Copyright (C) 2010 Thomas Chou <thomas@wytron.com.tw>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#ifndef _ALTERA_TSE_H_
#define _ALTERA_TSE_H_
#define __packed_1_ __packed __aligned(1)
/* dma type */
#define ALT_SGDMA 0
#define ALT_MSGDMA 1
/* SGDMA Stuff */
#define ALT_SGDMA_STATUS_BUSY_MSK BIT(4)
#define ALT_SGDMA_CONTROL_RUN_MSK BIT(5)
#define ALT_SGDMA_CONTROL_STOP_DMA_ER_MSK BIT(6)
#define ALT_SGDMA_CONTROL_SOFTWARERESET_MSK BIT(16)
/*
* Descriptor control bit masks & offsets
*
* Note: The control byte physically occupies bits [31:24] in memory.
* The following bit-offsets are expressed relative to the LSB of
* the control register bitfield.
*/
#define ALT_SGDMA_DESCRIPTOR_CONTROL_GENERATE_EOP_MSK BIT(0)
#define ALT_SGDMA_DESCRIPTOR_CONTROL_READ_FIXED_ADDRESS_MSK BIT(1)
#define ALT_SGDMA_DESCRIPTOR_CONTROL_WRITE_FIXED_ADDRESS_MSK BIT(2)
#define ALT_SGDMA_DESCRIPTOR_CONTROL_OWNED_BY_HW_MSK BIT(7)
/*
* Descriptor status bit masks & offsets
*
* Note: The status byte physically occupies bits [23:16] in memory.
* The following bit-offsets are expressed relative to the LSB of
* the status register bitfield.
*/
#define ALT_SGDMA_DESCRIPTOR_STATUS_TERMINATED_BY_EOP_MSK BIT(7)
/*
* The SGDMA controller buffer descriptor allocates
* 64 bits for each address. To support ANSI C, the
* struct implementing a descriptor places 32-bits
* of padding directly above each address; each pad must
* be cleared when initializing a descriptor.
*/
/*
* Buffer Descriptor data structure
*
*/
struct alt_sgdma_descriptor {
u32 source; /* the address of data to be read. */
u32 source_pad;
u32 destination; /* the address to write data */
u32 destination_pad;
u32 next; /* the next descriptor in the list. */
u32 next_pad;
u16 bytes_to_transfer; /* the number of bytes to transfer */
u8 read_burst;
u8 write_burst;
u16 actual_bytes_transferred;/* bytes transferred by DMA */
u8 descriptor_status;
u8 descriptor_control;
} __packed_1_;
/* SG-DMA Control/Status Slave registers map */
struct alt_sgdma_registers {
u32 status;
u32 status_pad[3];
u32 control;
u32 control_pad[3];
u32 next_descriptor_pointer;
u32 descriptor_pad[3];
};
/* mSGDMA Stuff */
/* mSGDMA extended descriptor format */
struct msgdma_extended_desc {
u32 read_addr_lo; /* data buffer source address low bits */
u32 write_addr_lo; /* data buffer destination address low bits */
u32 len;
u32 burst_seq_num;
u32 stride;
u32 read_addr_hi; /* data buffer source address high bits */
u32 write_addr_hi; /* data buffer destination address high bits */
u32 control; /* characteristics of the transfer */
};
/* mSGDMA descriptor control field bit definitions */
#define MSGDMA_DESC_CTL_GEN_SOP BIT(8)
#define MSGDMA_DESC_CTL_GEN_EOP BIT(9)
#define MSGDMA_DESC_CTL_END_ON_EOP BIT(12)
#define MSGDMA_DESC_CTL_END_ON_LEN BIT(13)
#define MSGDMA_DESC_CTL_GO BIT(31)
/* Tx buffer control flags */
#define MSGDMA_DESC_CTL_TX_SINGLE (MSGDMA_DESC_CTL_GEN_SOP | \
MSGDMA_DESC_CTL_GEN_EOP | \
MSGDMA_DESC_CTL_GO)
#define MSGDMA_DESC_CTL_RX_SINGLE (MSGDMA_DESC_CTL_END_ON_EOP | \
MSGDMA_DESC_CTL_END_ON_LEN | \
MSGDMA_DESC_CTL_GO)
/* mSGDMA extended descriptor stride definitions */
#define MSGDMA_DESC_TX_STRIDE 0x00010001
#define MSGDMA_DESC_RX_STRIDE 0x00010001
/* mSGDMA dispatcher control and status register map */
struct msgdma_csr {
u32 status; /* Read/Clear */
u32 control; /* Read/Write */
u32 rw_fill_level;
u32 resp_fill_level; /* bit 15:0 */
u32 rw_seq_num;
u32 pad[3]; /* reserved */
};
/* mSGDMA CSR status register bit definitions */
#define MSGDMA_CSR_STAT_BUSY BIT(0)
#define MSGDMA_CSR_STAT_RESETTING BIT(6)
#define MSGDMA_CSR_STAT_MASK 0x3FF
/* mSGDMA CSR control register bit definitions */
#define MSGDMA_CSR_CTL_RESET BIT(1)
/* mSGDMA response register map */
struct msgdma_response {
u32 bytes_transferred;
u32 status;
};
/* TSE Stuff */
#define ALTERA_TSE_CMD_TX_ENA_MSK BIT(0)
#define ALTERA_TSE_CMD_RX_ENA_MSK BIT(1)
#define ALTERA_TSE_CMD_ETH_SPEED_MSK BIT(3)
#define ALTERA_TSE_CMD_HD_ENA_MSK BIT(10)
#define ALTERA_TSE_CMD_SW_RESET_MSK BIT(13)
#define ALTERA_TSE_CMD_ENA_10_MSK BIT(25)
#define ALT_TSE_SW_RESET_TIMEOUT (3 * CONFIG_SYS_HZ)
#define ALT_TSE_SGDMA_BUSY_TIMEOUT (3 * CONFIG_SYS_HZ)
/* MAC register Space */
struct alt_tse_mac {
u32 megacore_revision;
u32 scratch_pad;
u32 command_config;
u32 mac_addr_0;
u32 mac_addr_1;
u32 max_frame_length;
u32 pause_quanta;
u32 rx_sel_empty_threshold;
u32 rx_sel_full_threshold;
u32 tx_sel_empty_threshold;
u32 tx_sel_full_threshold;
u32 rx_almost_empty_threshold;
u32 rx_almost_full_threshold;
u32 tx_almost_empty_threshold;
u32 tx_almost_full_threshold;
u32 mdio_phy0_addr;
u32 mdio_phy1_addr;
u32 reserved1[0x29];
/*FIFO control register. */
u32 tx_cmd_stat;
u32 rx_cmd_stat;
u32 reserved2[0x44];
/*Registers 0 to 31 within PHY device 0/1 */
u32 mdio_phy0[0x20];
u32 mdio_phy1[0x20];
/*4 Supplemental MAC Addresses */
u32 supp_mac_addr_0_0;
u32 supp_mac_addr_0_1;
u32 supp_mac_addr_1_0;
u32 supp_mac_addr_1_1;
u32 supp_mac_addr_2_0;
u32 supp_mac_addr_2_1;
u32 supp_mac_addr_3_0;
u32 supp_mac_addr_3_1;
u32 reserved3[0x38];
};
struct tse_ops {
int (*send)(struct udevice *dev, void *packet, int length);
int (*recv)(struct udevice *dev, int flags, uchar **packetp);
int (*free_pkt)(struct udevice *dev, uchar *packet, int length);
void (*stop)(struct udevice *dev);
};
struct altera_tse_priv {
struct alt_tse_mac *mac_dev;
void *sgdma_rx;
void *sgdma_tx;
unsigned int rx_fifo_depth;
unsigned int tx_fifo_depth;
void *rx_desc;
void *tx_desc;
void *rx_resp;
unsigned char *rx_buf;
unsigned int phyaddr;
unsigned int interface;
struct phy_device *phydev;
struct mii_dev *bus;
const struct tse_ops *ops;
int dma_type;
};
#endif /* _ALTERA_TSE_H_ */

View file

@ -0,0 +1,727 @@
/*
* (C) Copyright 2011
* eInfochips Ltd. <www.einfochips.com>
* Written-by: Ajay Bhargav <ajay.bhargav@einfochips.com>
*
* (C) Copyright 2010
* Marvell Semiconductor <www.marvell.com>
* Contributor: Mahavir Jain <mjain@marvell.com>
*
* SPDX-License-Identifier: GPL-2.0+
*/
#include <common.h>
#include <net.h>
#include <malloc.h>
#include <miiphy.h>
#include <netdev.h>
#include <asm/types.h>
#include <asm/byteorder.h>
#include <linux/err.h>
#include <linux/mii.h>
#include <asm/io.h>
#include <asm/arch/armada100.h>
#include "armada100_fec.h"
#define PHY_ADR_REQ 0xFF /* Magic number to read/write PHY address */
#ifdef DEBUG
static int eth_dump_regs(struct eth_device *dev)
{
struct armdfec_device *darmdfec = to_darmdfec(dev);
struct armdfec_reg *regs = darmdfec->regs;
unsigned int i = 0;
printf("\noffset: phy_adr, value: 0x%x\n", readl(&regs->phyadr));
printf("offset: smi, value: 0x%x\n", readl(&regs->smi));
for (i = 0x400; i <= 0x4e4; i += 4)
printf("offset: 0x%x, value: 0x%x\n",
i, readl(ARMD1_FEC_BASE + i));
return 0;
}
#endif
static int armdfec_phy_timeout(u32 *reg, u32 flag, int cond)
{
u32 timeout = PHY_WAIT_ITERATIONS;
u32 reg_val;
while (--timeout) {
reg_val = readl(reg);
if (cond && (reg_val & flag))
break;
else if (!cond && !(reg_val & flag))
break;
udelay(PHY_WAIT_MICRO_SECONDS);
}
return !timeout;
}
static int smi_reg_read(const char *devname, u8 phy_addr, u8 phy_reg,
u16 *value)
{
struct eth_device *dev = eth_get_dev_by_name(devname);
struct armdfec_device *darmdfec = to_darmdfec(dev);
struct armdfec_reg *regs = darmdfec->regs;
u32 val;
if (phy_addr == PHY_ADR_REQ && phy_reg == PHY_ADR_REQ) {
val = readl(&regs->phyadr);
*value = val & 0x1f;
return 0;
}
/* check parameters */
if (phy_addr > PHY_MASK) {
printf("ARMD100 FEC: (%s) Invalid phy address: 0x%X\n",
__func__, phy_addr);
return -EINVAL;
}
if (phy_reg > PHY_MASK) {
printf("ARMD100 FEC: (%s) Invalid register offset: 0x%X\n",
__func__, phy_reg);
return -EINVAL;
}
/* wait for the SMI register to become available */
if (armdfec_phy_timeout(&regs->smi, SMI_BUSY, false)) {
printf("ARMD100 FEC: (%s) PHY busy timeout\n", __func__);
return -1;
}
writel((phy_addr << 16) | (phy_reg << 21) | SMI_OP_R, &regs->smi);
/* now wait for the data to be valid */
if (armdfec_phy_timeout(&regs->smi, SMI_R_VALID, true)) {
val = readl(&regs->smi);
printf("ARMD100 FEC: (%s) PHY Read timeout, val=0x%x\n",
__func__, val);
return -1;
}
val = readl(&regs->smi);
*value = val & 0xffff;
return 0;
}
static int smi_reg_write(const char *devname,
u8 phy_addr, u8 phy_reg, u16 value)
{
struct eth_device *dev = eth_get_dev_by_name(devname);
struct armdfec_device *darmdfec = to_darmdfec(dev);
struct armdfec_reg *regs = darmdfec->regs;
if (phy_addr == PHY_ADR_REQ && phy_reg == PHY_ADR_REQ) {
clrsetbits_le32(&regs->phyadr, 0x1f, value & 0x1f);
return 0;
}
/* check parameters */
if (phy_addr > PHY_MASK) {
printf("ARMD100 FEC: (%s) Invalid phy address\n", __func__);
return -EINVAL;
}
if (phy_reg > PHY_MASK) {
printf("ARMD100 FEC: (%s) Invalid register offset\n", __func__);
return -EINVAL;
}
/* wait for the SMI register to become available */
if (armdfec_phy_timeout(&regs->smi, SMI_BUSY, false)) {
printf("ARMD100 FEC: (%s) PHY busy timeout\n", __func__);
return -1;
}
writel((phy_addr << 16) | (phy_reg << 21) | SMI_OP_W | (value & 0xffff),
&regs->smi);
return 0;
}
/*
* Abort any transmit and receive operations and put DMA
* in idle state. AT and AR bits are cleared upon entering
* in IDLE state. So poll those bits to verify operation.
*/
static void abortdma(struct eth_device *dev)
{
struct armdfec_device *darmdfec = to_darmdfec(dev);
struct armdfec_reg *regs = darmdfec->regs;
int delay;
int maxretries = 40;
u32 tmp;
while (--maxretries) {
writel(SDMA_CMD_AR | SDMA_CMD_AT, &regs->sdma_cmd);
udelay(100);
delay = 10;
while (--delay) {
tmp = readl(&regs->sdma_cmd);
if (!(tmp & (SDMA_CMD_AR | SDMA_CMD_AT)))
break;
udelay(10);
}
if (delay)
break;
}
if (!maxretries)
printf("ARMD100 FEC: (%s) DMA Stuck\n", __func__);
}
static inline u32 nibble_swapping_32_bit(u32 x)
{
return ((x & 0xf0f0f0f0) >> 4) | ((x & 0x0f0f0f0f) << 4);
}
static inline u32 nibble_swapping_16_bit(u32 x)
{
return ((x & 0x0000f0f0) >> 4) | ((x & 0x00000f0f) << 4);
}
static inline u32 flip_4_bits(u32 x)
{
return ((x & 0x01) << 3) | ((x & 0x002) << 1)
| ((x & 0x04) >> 1) | ((x & 0x008) >> 3);
}
/*
* This function will calculate the hash function of the address.
* depends on the hash mode and hash size.
* Inputs
* mach - the 2 most significant bytes of the MAC address.
* macl - the 4 least significant bytes of the MAC address.
* Outputs
* return the calculated entry.
*/
static u32 hash_function(u32 mach, u32 macl)
{
u32 hashresult;
u32 addrh;
u32 addrl;
u32 addr0;
u32 addr1;
u32 addr2;
u32 addr3;
u32 addrhswapped;
u32 addrlswapped;
addrh = nibble_swapping_16_bit(mach);
addrl = nibble_swapping_32_bit(macl);
addrhswapped = flip_4_bits(addrh & 0xf)
+ ((flip_4_bits((addrh >> 4) & 0xf)) << 4)
+ ((flip_4_bits((addrh >> 8) & 0xf)) << 8)
+ ((flip_4_bits((addrh >> 12) & 0xf)) << 12);
addrlswapped = flip_4_bits(addrl & 0xf)
+ ((flip_4_bits((addrl >> 4) & 0xf)) << 4)
+ ((flip_4_bits((addrl >> 8) & 0xf)) << 8)
+ ((flip_4_bits((addrl >> 12) & 0xf)) << 12)
+ ((flip_4_bits((addrl >> 16) & 0xf)) << 16)
+ ((flip_4_bits((addrl >> 20) & 0xf)) << 20)
+ ((flip_4_bits((addrl >> 24) & 0xf)) << 24)
+ ((flip_4_bits((addrl >> 28) & 0xf)) << 28);
addrh = addrhswapped;
addrl = addrlswapped;
addr0 = (addrl >> 2) & 0x03f;
addr1 = (addrl & 0x003) | (((addrl >> 8) & 0x7f) << 2);
addr2 = (addrl >> 15) & 0x1ff;
addr3 = ((addrl >> 24) & 0x0ff) | ((addrh & 1) << 8);
hashresult = (addr0 << 9) | (addr1 ^ addr2 ^ addr3);
hashresult = hashresult & 0x07ff;
return hashresult;
}
/*
* This function will add an entry to the address table.
* depends on the hash mode and hash size that was initialized.
* Inputs
* mach - the 2 most significant bytes of the MAC address.
* macl - the 4 least significant bytes of the MAC address.
* skip - if 1, skip this address.
* rd - the RD field in the address table.
* Outputs
* address table entry is added.
* 0 if success.
* -ENOSPC if table full
*/
static int add_del_hash_entry(struct armdfec_device *darmdfec, u32 mach,
u32 macl, u32 rd, u32 skip, int del)
{
struct addr_table_entry_t *entry, *start;
u32 newhi;
u32 newlo;
u32 i;
newlo = (((mach >> 4) & 0xf) << 15)
| (((mach >> 0) & 0xf) << 11)
| (((mach >> 12) & 0xf) << 7)
| (((mach >> 8) & 0xf) << 3)
| (((macl >> 20) & 0x1) << 31)
| (((macl >> 16) & 0xf) << 27)
| (((macl >> 28) & 0xf) << 23)
| (((macl >> 24) & 0xf) << 19)
| (skip << HTESKIP) | (rd << HTERDBIT)
| HTEVALID;
newhi = (((macl >> 4) & 0xf) << 15)
| (((macl >> 0) & 0xf) << 11)
| (((macl >> 12) & 0xf) << 7)
| (((macl >> 8) & 0xf) << 3)
| (((macl >> 21) & 0x7) << 0);
/*
* Pick the appropriate table, start scanning for free/reusable
* entries at the index obtained by hashing the specified MAC address
*/
start = (struct addr_table_entry_t *)(darmdfec->htpr);
entry = start + hash_function(mach, macl);
for (i = 0; i < HOP_NUMBER; i++) {
if (!(entry->lo & HTEVALID)) {
break;
} else {
/* if same address put in same position */
if (((entry->lo & 0xfffffff8) == (newlo & 0xfffffff8))
&& (entry->hi == newhi))
break;
}
if (entry == start + 0x7ff)
entry = start;
else
entry++;
}
if (((entry->lo & 0xfffffff8) != (newlo & 0xfffffff8)) &&
(entry->hi != newhi) && del)
return 0;
if (i == HOP_NUMBER) {
if (!del) {
printf("ARMD100 FEC: (%s) table section is full\n",
__func__);
return -ENOSPC;
} else {
return 0;
}
}
/*
* Update the selected entry
*/
if (del) {
entry->hi = 0;
entry->lo = 0;
} else {
entry->hi = newhi;
entry->lo = newlo;
}
return 0;
}
/*
* Create an addressTable entry from MAC address info
* found in the specifed net_device struct
*
* Input : pointer to ethernet interface network device structure
* Output : N/A
*/
static void update_hash_table_mac_address(struct armdfec_device *darmdfec,
u8 *oaddr, u8 *addr)
{
u32 mach;
u32 macl;
/* Delete old entry */
if (oaddr) {
mach = (oaddr[0] << 8) | oaddr[1];
macl = (oaddr[2] << 24) | (oaddr[3] << 16) |
(oaddr[4] << 8) | oaddr[5];
add_del_hash_entry(darmdfec, mach, macl, 1, 0, HASH_DELETE);
}
/* Add new entry */
mach = (addr[0] << 8) | addr[1];
macl = (addr[2] << 24) | (addr[3] << 16) | (addr[4] << 8) | addr[5];
add_del_hash_entry(darmdfec, mach, macl, 1, 0, HASH_ADD);
}
/* Address Table Initialization */
static void init_hashtable(struct eth_device *dev)
{
struct armdfec_device *darmdfec = to_darmdfec(dev);
struct armdfec_reg *regs = darmdfec->regs;
memset(darmdfec->htpr, 0, HASH_ADDR_TABLE_SIZE);
writel((u32)darmdfec->htpr, &regs->htpr);
}
/*
* This detects PHY chip from address 0-31 by reading PHY status
* registers. PHY chip can be connected at any of this address.
*/
static int ethernet_phy_detect(struct eth_device *dev)
{
u32 val;
u16 tmp, mii_status;
u8 addr;
for (addr = 0; addr < 32; addr++) {
if (miiphy_read(dev->name, addr, MII_BMSR, &mii_status) != 0)
/* try next phy */
continue;
/* invalid MII status. More validation required here... */
if (mii_status == 0 || mii_status == 0xffff)
/* try next phy */
continue;
if (miiphy_read(dev->name, addr, MII_PHYSID1, &tmp) != 0)
/* try next phy */
continue;
val = tmp << 16;
if (miiphy_read(dev->name, addr, MII_PHYSID2, &tmp) != 0)
/* try next phy */
continue;
val |= tmp;
if ((val & 0xfffffff0) != 0)
return addr;
}
return -1;
}
static void armdfec_init_rx_desc_ring(struct armdfec_device *darmdfec)
{
struct rx_desc *p_rx_desc;
int i;
/* initialize the Rx descriptors ring */
p_rx_desc = darmdfec->p_rxdesc;
for (i = 0; i < RINGSZ; i++) {
p_rx_desc->cmd_sts = BUF_OWNED_BY_DMA | RX_EN_INT;
p_rx_desc->buf_size = PKTSIZE_ALIGN;
p_rx_desc->byte_cnt = 0;
p_rx_desc->buf_ptr = darmdfec->p_rxbuf + i * PKTSIZE_ALIGN;
if (i == (RINGSZ - 1)) {
p_rx_desc->nxtdesc_p = darmdfec->p_rxdesc;
} else {
p_rx_desc->nxtdesc_p = (struct rx_desc *)
((u32)p_rx_desc + ARMDFEC_RXQ_DESC_ALIGNED_SIZE);
p_rx_desc = p_rx_desc->nxtdesc_p;
}
}
darmdfec->p_rxdesc_curr = darmdfec->p_rxdesc;
}
static int armdfec_init(struct eth_device *dev, bd_t *bd)
{
struct armdfec_device *darmdfec = to_darmdfec(dev);
struct armdfec_reg *regs = darmdfec->regs;
int phy_adr;
u32 temp;
armdfec_init_rx_desc_ring(darmdfec);
/* Disable interrupts */
writel(0, &regs->im);
writel(0, &regs->ic);
/* Write to ICR to clear interrupts. */
writel(0, &regs->iwc);
/*
* Abort any transmit and receive operations and put DMA
* in idle state.
*/
abortdma(dev);
/* Initialize address hash table */
init_hashtable(dev);
/* SDMA configuration */
writel(SDCR_BSZ8 | /* Burst size = 32 bytes */
SDCR_RIFB | /* Rx interrupt on frame */
SDCR_BLMT | /* Little endian transmit */
SDCR_BLMR | /* Little endian receive */
SDCR_RC_MAX_RETRANS, /* Max retransmit count */
&regs->sdma_conf);
/* Port Configuration */
writel(PCR_HS, &regs->pconf); /* Hash size is 1/2kb */
/* Set extended port configuration */
writel(PCXR_2BSM | /* Two byte suffix aligns IP hdr */
PCXR_DSCP_EN | /* Enable DSCP in IP */
PCXR_MFL_1536 | /* Set MTU = 1536 */
PCXR_FLP | /* do not force link pass */
PCXR_TX_HIGH_PRI, /* Transmit - high priority queue */
&regs->pconf_ext);
update_hash_table_mac_address(darmdfec, NULL, dev->enetaddr);
/* Update TX and RX queue descriptor register */
temp = (u32)&regs->txcdp[TXQ];
writel((u32)darmdfec->p_txdesc, temp);
temp = (u32)&regs->rxfdp[RXQ];
writel((u32)darmdfec->p_rxdesc, temp);
temp = (u32)&regs->rxcdp[RXQ];
writel((u32)darmdfec->p_rxdesc_curr, temp);
/* Enable Interrupts */
writel(ALL_INTS, &regs->im);
/* Enable Ethernet Port */
setbits_le32(&regs->pconf, PCR_EN);
/* Enable RX DMA engine */
setbits_le32(&regs->sdma_cmd, SDMA_CMD_ERD);
#ifdef DEBUG
eth_dump_regs(dev);
#endif
#if (defined(CONFIG_MII) || defined(CONFIG_CMD_MII))
#if defined(CONFIG_PHY_BASE_ADR)
miiphy_write(dev->name, PHY_ADR_REQ, PHY_ADR_REQ, CONFIG_PHY_BASE_ADR);
#else
/* Search phy address from range 0-31 */
phy_adr = ethernet_phy_detect(dev);
if (phy_adr < 0) {
printf("ARMD100 FEC: PHY not detected at address range 0-31\n");
return -1;
} else {
debug("ARMD100 FEC: PHY detected at addr %d\n", phy_adr);
miiphy_write(dev->name, PHY_ADR_REQ, PHY_ADR_REQ, phy_adr);
}
#endif
#if defined(CONFIG_SYS_FAULT_ECHO_LINK_DOWN)
/* Wait up to 5s for the link status */
for (i = 0; i < 5; i++) {
u16 phy_adr;
miiphy_read(dev->name, 0xFF, 0xFF, &phy_adr);
/* Return if we get link up */
if (miiphy_link(dev->name, phy_adr))
return 0;
udelay(1000000);
}
printf("ARMD100 FEC: No link on %s\n", dev->name);
return -1;
#endif
#endif
return 0;
}
static void armdfec_halt(struct eth_device *dev)
{
struct armdfec_device *darmdfec = to_darmdfec(dev);
struct armdfec_reg *regs = darmdfec->regs;
/* Stop RX DMA */
clrbits_le32(&regs->sdma_cmd, SDMA_CMD_ERD);
/*
* Abort any transmit and receive operations and put DMA
* in idle state.
*/
abortdma(dev);
/* Disable interrupts */
writel(0, &regs->im);
writel(0, &regs->ic);
writel(0, &regs->iwc);
/* Disable Port */
clrbits_le32(&regs->pconf, PCR_EN);
}
static int armdfec_send(struct eth_device *dev, void *dataptr, int datasize)
{
struct armdfec_device *darmdfec = to_darmdfec(dev);
struct armdfec_reg *regs = darmdfec->regs;
struct tx_desc *p_txdesc = darmdfec->p_txdesc;
void *p = (void *)dataptr;
int retry = PHY_WAIT_ITERATIONS * PHY_WAIT_MICRO_SECONDS;
u32 cmd_sts, temp;
/* Copy buffer if it's misaligned */
if ((u32)dataptr & 0x07) {
if (datasize > PKTSIZE_ALIGN) {
printf("ARMD100 FEC: Non-aligned data too large (%d)\n",
datasize);
return -1;
}
memcpy(darmdfec->p_aligned_txbuf, p, datasize);
p = darmdfec->p_aligned_txbuf;
}
p_txdesc->cmd_sts = TX_ZERO_PADDING | TX_GEN_CRC;
p_txdesc->cmd_sts |= TX_FIRST_DESC | TX_LAST_DESC;
p_txdesc->cmd_sts |= BUF_OWNED_BY_DMA;
p_txdesc->cmd_sts |= TX_EN_INT;
p_txdesc->buf_ptr = p;
p_txdesc->byte_cnt = datasize;
/* Apply send command using high priority TX queue */
temp = (u32)&regs->txcdp[TXQ];
writel((u32)p_txdesc, temp);
writel(SDMA_CMD_TXDL | SDMA_CMD_TXDH | SDMA_CMD_ERD, &regs->sdma_cmd);
/*
* wait for packet xmit completion
*/
cmd_sts = readl(&p_txdesc->cmd_sts);
while (cmd_sts & BUF_OWNED_BY_DMA) {
/* return fail if error is detected */
if ((cmd_sts & (TX_ERROR | TX_LAST_DESC)) ==
(TX_ERROR | TX_LAST_DESC)) {
printf("ARMD100 FEC: (%s) in xmit packet\n", __func__);
return -1;
}
cmd_sts = readl(&p_txdesc->cmd_sts);
if (!(retry--)) {
printf("ARMD100 FEC: (%s) xmit packet timeout!\n",
__func__);
return -1;
}
}
return 0;
}
static int armdfec_recv(struct eth_device *dev)
{
struct armdfec_device *darmdfec = to_darmdfec(dev);
struct rx_desc *p_rxdesc_curr = darmdfec->p_rxdesc_curr;
u32 cmd_sts;
u32 timeout = 0;
u32 temp;
/* wait untill rx packet available or timeout */
do {
if (timeout < PHY_WAIT_ITERATIONS * PHY_WAIT_MICRO_SECONDS) {
timeout++;
} else {
debug("ARMD100 FEC: %s time out...\n", __func__);
return -1;
}
} while (readl(&p_rxdesc_curr->cmd_sts) & BUF_OWNED_BY_DMA);
if (p_rxdesc_curr->byte_cnt != 0) {
debug("ARMD100 FEC: %s: Received %d byte Packet @ 0x%x"
"(cmd_sts= %08x)\n", __func__,
(u32)p_rxdesc_curr->byte_cnt,
(u32)p_rxdesc_curr->buf_ptr,
(u32)p_rxdesc_curr->cmd_sts);
}
/*
* In case received a packet without first/last bits on
* OR the error summary bit is on,
* the packets needs to be dropeed.
*/
cmd_sts = readl(&p_rxdesc_curr->cmd_sts);
if ((cmd_sts & (RX_FIRST_DESC | RX_LAST_DESC)) !=
(RX_FIRST_DESC | RX_LAST_DESC)) {
printf("ARMD100 FEC: (%s) Dropping packet spread on"
" multiple descriptors\n", __func__);
} else if (cmd_sts & RX_ERROR) {
printf("ARMD100 FEC: (%s) Dropping packet with errors\n",
__func__);
} else {
/* !!! call higher layer processing */
debug("ARMD100 FEC: (%s) Sending Received packet to"
" upper layer (net_process_received_packet)\n", __func__);
/*
* let the upper layer handle the packet, subtract offset
* as two dummy bytes are added in received buffer see
* PORT_CONFIG_EXT register bit TWO_Byte_Stuff_Mode bit.
*/
net_process_received_packet(
p_rxdesc_curr->buf_ptr + RX_BUF_OFFSET,
(int)(p_rxdesc_curr->byte_cnt - RX_BUF_OFFSET));
}
/*
* free these descriptors and point next in the ring
*/
p_rxdesc_curr->cmd_sts = BUF_OWNED_BY_DMA | RX_EN_INT;
p_rxdesc_curr->buf_size = PKTSIZE_ALIGN;
p_rxdesc_curr->byte_cnt = 0;
temp = (u32)&darmdfec->p_rxdesc_curr;
writel((u32)p_rxdesc_curr->nxtdesc_p, temp);
return 0;
}
int armada100_fec_register(unsigned long base_addr)
{
struct armdfec_device *darmdfec;
struct eth_device *dev;
darmdfec = malloc(sizeof(struct armdfec_device));
if (!darmdfec)
goto error;
memset(darmdfec, 0, sizeof(struct armdfec_device));
darmdfec->htpr = memalign(8, HASH_ADDR_TABLE_SIZE);
if (!darmdfec->htpr)
goto error1;
darmdfec->p_rxdesc = memalign(PKTALIGN,
ARMDFEC_RXQ_DESC_ALIGNED_SIZE * RINGSZ + 1);
if (!darmdfec->p_rxdesc)
goto error1;
darmdfec->p_rxbuf = memalign(PKTALIGN, RINGSZ * PKTSIZE_ALIGN + 1);
if (!darmdfec->p_rxbuf)
goto error1;
darmdfec->p_aligned_txbuf = memalign(8, PKTSIZE_ALIGN);
if (!darmdfec->p_aligned_txbuf)
goto error1;
darmdfec->p_txdesc = memalign(PKTALIGN, sizeof(struct tx_desc) + 1);
if (!darmdfec->p_txdesc)
goto error1;
dev = &darmdfec->dev;
/* Assign ARMADA100 Fast Ethernet Controller Base Address */
darmdfec->regs = (void *)base_addr;
/* must be less than sizeof(dev->name) */
strcpy(dev->name, "armd-fec0");
dev->init = armdfec_init;
dev->halt = armdfec_halt;
dev->send = armdfec_send;
dev->recv = armdfec_recv;
eth_register(dev);
#if defined(CONFIG_MII) || defined(CONFIG_CMD_MII)
miiphy_register(dev->name, smi_reg_read, smi_reg_write);
#endif
return 0;
error1:
free(darmdfec->p_aligned_txbuf);
free(darmdfec->p_rxbuf);
free(darmdfec->p_rxdesc);
free(darmdfec->htpr);
error:
free(darmdfec);
printf("AMD100 FEC: (%s) Failed to allocate memory\n", __func__);
return -1;
}

View file

@ -0,0 +1,209 @@
/*
* (C) Copyright 2011
* eInfochips Ltd. <www.einfochips.com>
* Written-by: Ajay Bhargav <ajay.bhargav@einfochips.com>
*
* (C) Copyright 2010
* Marvell Semiconductor <www.marvell.com>
* Contributor: Mahavir Jain <mjain@marvell.com>
*
* SPDX-License-Identifier: GPL-2.0+
*/
#ifndef __ARMADA100_FEC_H__
#define __ARMADA100_FEC_H__
#define PORT_NUM 0x0
/* RX & TX descriptor command */
#define BUF_OWNED_BY_DMA (1<<31)
/* RX descriptor status */
#define RX_EN_INT (1<<23)
#define RX_FIRST_DESC (1<<17)
#define RX_LAST_DESC (1<<16)
#define RX_ERROR (1<<15)
/* TX descriptor command */
#define TX_EN_INT (1<<23)
#define TX_GEN_CRC (1<<22)
#define TX_ZERO_PADDING (1<<18)
#define TX_FIRST_DESC (1<<17)
#define TX_LAST_DESC (1<<16)
#define TX_ERROR (1<<15)
/* smi register */
#define SMI_BUSY (1<<28) /* 0 - Write, 1 - Read */
#define SMI_R_VALID (1<<27) /* 0 - Write, 1 - Read */
#define SMI_OP_W (0<<26) /* Write operation */
#define SMI_OP_R (1<<26) /* Read operation */
#define HASH_ADD 0
#define HASH_DELETE 1
#define HASH_ADDR_TABLE_SIZE 0x4000 /* 16K (1/2K address - PCR_HS == 1) */
#define HOP_NUMBER 12
#define PHY_WAIT_ITERATIONS 1000 /* 1000 iterations * 10uS = 10mS max */
#define PHY_WAIT_MICRO_SECONDS 10
#define ETH_HW_IP_ALIGN 2 /* hw aligns IP header */
#define ETH_EXTRA_HEADER (6+6+2+4)
/* dest+src addr+protocol id+crc */
#define MAX_PKT_SIZE 1536
/* Bit definitions of the SDMA Config Reg */
#define SDCR_BSZ_OFF 12
#define SDCR_BSZ8 (3<<SDCR_BSZ_OFF)
#define SDCR_BSZ4 (2<<SDCR_BSZ_OFF)
#define SDCR_BSZ2 (1<<SDCR_BSZ_OFF)
#define SDCR_BSZ1 (0<<SDCR_BSZ_OFF)
#define SDCR_BLMR (1<<6)
#define SDCR_BLMT (1<<7)
#define SDCR_RIFB (1<<9)
#define SDCR_RC_OFF 2
#define SDCR_RC_MAX_RETRANS (0xf << SDCR_RC_OFF)
/* SDMA_CMD */
#define SDMA_CMD_AT (1<<31)
#define SDMA_CMD_TXDL (1<<24)
#define SDMA_CMD_TXDH (1<<23)
#define SDMA_CMD_AR (1<<15)
#define SDMA_CMD_ERD (1<<7)
/* Bit definitions of the Port Config Reg */
#define PCR_HS (1<<12)
#define PCR_EN (1<<7)
#define PCR_PM (1<<0)
/* Bit definitions of the Port Config Extend Reg */
#define PCXR_2BSM (1<<28)
#define PCXR_DSCP_EN (1<<21)
#define PCXR_MFL_1518 (0<<14)
#define PCXR_MFL_1536 (1<<14)
#define PCXR_MFL_2048 (2<<14)
#define PCXR_MFL_64K (3<<14)
#define PCXR_FLP (1<<11)
#define PCXR_PRIO_TX_OFF 3
#define PCXR_TX_HIGH_PRI (7<<PCXR_PRIO_TX_OFF)
/*
* * Bit definitions of the Interrupt Cause Reg
* * and Interrupt MASK Reg is the same
* */
#define ICR_RXBUF (1<<0)
#define ICR_TXBUF_H (1<<2)
#define ICR_TXBUF_L (1<<3)
#define ICR_TXEND_H (1<<6)
#define ICR_TXEND_L (1<<7)
#define ICR_RXERR (1<<8)
#define ICR_TXERR_H (1<<10)
#define ICR_TXERR_L (1<<11)
#define ICR_TX_UDR (1<<13)
#define ICR_MII_CH (1<<28)
#define ALL_INTS (ICR_TXBUF_H | ICR_TXBUF_L | ICR_TX_UDR |\
ICR_TXERR_H | ICR_TXERR_L |\
ICR_TXEND_H | ICR_TXEND_L |\
ICR_RXBUF | ICR_RXERR | ICR_MII_CH)
#define PHY_MASK 0x0000001f
#define to_darmdfec(_kd) container_of(_kd, struct armdfec_device, dev)
/* Size of a Tx/Rx descriptor used in chain list data structure */
#define ARMDFEC_RXQ_DESC_ALIGNED_SIZE \
(((sizeof(struct rx_desc) / PKTALIGN) + 1) * PKTALIGN)
#define RX_BUF_OFFSET 0x2
#define RXQ 0x0 /* RX Queue 0 */
#define TXQ 0x1 /* TX Queue 1 */
struct addr_table_entry_t {
u32 lo;
u32 hi;
};
/* Bit fields of a Hash Table Entry */
enum hash_table_entry {
HTEVALID = 1,
HTESKIP = 2,
HTERD = 4,
HTERDBIT = 2
};
struct tx_desc {
u32 cmd_sts; /* Command/status field */
u16 reserved;
u16 byte_cnt; /* buffer byte count */
u8 *buf_ptr; /* pointer to buffer for this descriptor */
struct tx_desc *nextdesc_p; /* Pointer to next descriptor */
};
struct rx_desc {
u32 cmd_sts; /* Descriptor command status */
u16 byte_cnt; /* Descriptor buffer byte count */
u16 buf_size; /* Buffer size */
u8 *buf_ptr; /* Descriptor buffer pointer */
struct rx_desc *nxtdesc_p; /* Next descriptor pointer */
};
/*
* Armada100 Fast Ethernet controller Registers
* Refer Datasheet Appendix A.22
*/
struct armdfec_reg {
u32 phyadr; /* PHY Address */
u32 pad1[3];
u32 smi; /* SMI */
u32 pad2[0xFB];
u32 pconf; /* Port configuration */
u32 pad3;
u32 pconf_ext; /* Port configuration extend */
u32 pad4;
u32 pcmd; /* Port Command */
u32 pad5;
u32 pstatus; /* Port Status */
u32 pad6;
u32 spar; /* Serial Parameters */
u32 pad7;
u32 htpr; /* Hash table pointer */
u32 pad8;
u32 fcsal; /* Flow control source address low */
u32 pad9;
u32 fcsah; /* Flow control source address high */
u32 pad10;
u32 sdma_conf; /* SDMA configuration */
u32 pad11;
u32 sdma_cmd; /* SDMA command */
u32 pad12;
u32 ic; /* Interrupt cause */
u32 iwc; /* Interrupt write to clear */
u32 im; /* Interrupt mask */
u32 pad13;
u32 *eth_idscpp[4]; /* Eth0 IP Differentiated Services Code
Point to Priority 0 Low */
u32 eth_vlan_p; /* Eth0 VLAN Priority Tag to Priority */
u32 pad14[3];
struct rx_desc *rxfdp[4]; /* Ethernet First Rx Descriptor
Pointer */
u32 pad15[4];
struct rx_desc *rxcdp[4]; /* Ethernet Current Rx Descriptor
Pointer */
u32 pad16[0x0C];
struct tx_desc *txcdp[2]; /* Ethernet Current Tx Descriptor
Pointer */
};
struct armdfec_device {
struct eth_device dev;
struct armdfec_reg *regs;
struct tx_desc *p_txdesc;
struct rx_desc *p_rxdesc;
struct rx_desc *p_rxdesc_curr;
u8 *p_rxbuf;
u8 *p_aligned_txbuf;
u8 *htpr; /* hash pointer */
};
#endif /* __ARMADA100_FEC_H__ */

View file

@ -0,0 +1,508 @@
/*
* Copyright (C) 2009 BuS Elektronik GmbH & Co. KG
* Jens Scharsig (esw@bus-elektronik.de)
*
* (C) Copyright 2003
* Author : Hamid Ikdoumi (Atmel)
* SPDX-License-Identifier: GPL-2.0+
*/
#include <common.h>
#include <asm/io.h>
#include <asm/arch/hardware.h>
#include <asm/arch/at91_emac.h>
#include <asm/arch/at91_pmc.h>
#include <asm/arch/at91_pio.h>
#include <net.h>
#include <netdev.h>
#include <malloc.h>
#include <miiphy.h>
#include <linux/mii.h>
#undef MII_DEBUG
#undef ET_DEBUG
#if (CONFIG_SYS_RX_ETH_BUFFER > 1024)
#error AT91 EMAC supports max 1024 RX buffers. \
Please decrease the CONFIG_SYS_RX_ETH_BUFFER value
#endif
#ifndef CONFIG_DRIVER_AT91EMAC_PHYADDR
#define CONFIG_DRIVER_AT91EMAC_PHYADDR 0
#endif
/* MDIO clock must not exceed 2.5 MHz, so enable MCK divider */
#if (AT91C_MASTER_CLOCK > 80000000)
#define HCLK_DIV AT91_EMAC_CFG_MCLK_64
#elif (AT91C_MASTER_CLOCK > 40000000)
#define HCLK_DIV AT91_EMAC_CFG_MCLK_32
#elif (AT91C_MASTER_CLOCK > 20000000)
#define HCLK_DIV AT91_EMAC_CFG_MCLK_16
#else
#define HCLK_DIV AT91_EMAC_CFG_MCLK_8
#endif
#ifdef ET_DEBUG
#define DEBUG_AT91EMAC 1
#else
#define DEBUG_AT91EMAC 0
#endif
#ifdef MII_DEBUG
#define DEBUG_AT91PHY 1
#else
#define DEBUG_AT91PHY 0
#endif
#ifndef CONFIG_DRIVER_AT91EMAC_QUIET
#define VERBOSEP 1
#else
#define VERBOSEP 0
#endif
#define RBF_ADDR 0xfffffffc
#define RBF_OWNER (1<<0)
#define RBF_WRAP (1<<1)
#define RBF_BROADCAST (1<<31)
#define RBF_MULTICAST (1<<30)
#define RBF_UNICAST (1<<29)
#define RBF_EXTERNAL (1<<28)
#define RBF_UNKNOWN (1<<27)
#define RBF_SIZE 0x07ff
#define RBF_LOCAL4 (1<<26)
#define RBF_LOCAL3 (1<<25)
#define RBF_LOCAL2 (1<<24)
#define RBF_LOCAL1 (1<<23)
#define RBF_FRAMEMAX CONFIG_SYS_RX_ETH_BUFFER
#define RBF_FRAMELEN 0x600
typedef struct {
unsigned long addr, size;
} rbf_t;
typedef struct {
rbf_t rbfdt[RBF_FRAMEMAX];
unsigned long rbindex;
} emac_device;
void at91emac_EnableMDIO(at91_emac_t *at91mac)
{
/* Mac CTRL reg set for MDIO enable */
writel(readl(&at91mac->ctl) | AT91_EMAC_CTL_MPE, &at91mac->ctl);
}
void at91emac_DisableMDIO(at91_emac_t *at91mac)
{
/* Mac CTRL reg set for MDIO disable */
writel(readl(&at91mac->ctl) & ~AT91_EMAC_CTL_MPE, &at91mac->ctl);
}
int at91emac_read(at91_emac_t *at91mac, unsigned char addr,
unsigned char reg, unsigned short *value)
{
unsigned long netstat;
at91emac_EnableMDIO(at91mac);
writel(AT91_EMAC_MAN_HIGH | AT91_EMAC_MAN_RW_R |
AT91_EMAC_MAN_REGA(reg) | AT91_EMAC_MAN_CODE_802_3 |
AT91_EMAC_MAN_PHYA(addr),
&at91mac->man);
do {
netstat = readl(&at91mac->sr);
debug_cond(DEBUG_AT91PHY, "poll SR %08lx\n", netstat);
} while (!(netstat & AT91_EMAC_SR_IDLE));
*value = readl(&at91mac->man) & AT91_EMAC_MAN_DATA_MASK;
at91emac_DisableMDIO(at91mac);
debug_cond(DEBUG_AT91PHY,
"AT91PHY read %p REG(%d)=%x\n", at91mac, reg, *value);
return 0;
}
int at91emac_write(at91_emac_t *at91mac, unsigned char addr,
unsigned char reg, unsigned short value)
{
unsigned long netstat;
debug_cond(DEBUG_AT91PHY,
"AT91PHY write %p REG(%d)=%p\n", at91mac, reg, &value);
at91emac_EnableMDIO(at91mac);
writel(AT91_EMAC_MAN_HIGH | AT91_EMAC_MAN_RW_W |
AT91_EMAC_MAN_REGA(reg) | AT91_EMAC_MAN_CODE_802_3 |
AT91_EMAC_MAN_PHYA(addr) | (value & AT91_EMAC_MAN_DATA_MASK),
&at91mac->man);
do {
netstat = readl(&at91mac->sr);
debug_cond(DEBUG_AT91PHY, "poll SR %08lx\n", netstat);
} while (!(netstat & AT91_EMAC_SR_IDLE));
at91emac_DisableMDIO(at91mac);
return 0;
}
#if defined(CONFIG_MII) || defined(CONFIG_CMD_MII)
at91_emac_t *get_emacbase_by_name(const char *devname)
{
struct eth_device *netdev;
netdev = eth_get_dev_by_name(devname);
return (at91_emac_t *) netdev->iobase;
}
int at91emac_mii_read(const char *devname, unsigned char addr,
unsigned char reg, unsigned short *value)
{
at91_emac_t *emac;
emac = get_emacbase_by_name(devname);
at91emac_read(emac , addr, reg, value);
return 0;
}
int at91emac_mii_write(const char *devname, unsigned char addr,
unsigned char reg, unsigned short value)
{
at91_emac_t *emac;
emac = get_emacbase_by_name(devname);
at91emac_write(emac, addr, reg, value);
return 0;
}
#endif
static int at91emac_phy_reset(struct eth_device *netdev)
{
int i;
u16 status, adv;
at91_emac_t *emac;
emac = (at91_emac_t *) netdev->iobase;
adv = ADVERTISE_CSMA | ADVERTISE_ALL;
at91emac_write(emac, CONFIG_DRIVER_AT91EMAC_PHYADDR,
MII_ADVERTISE, adv);
debug_cond(VERBOSEP, "%s: Starting autonegotiation...\n", netdev->name);
at91emac_write(emac, CONFIG_DRIVER_AT91EMAC_PHYADDR, MII_BMCR,
(BMCR_ANENABLE | BMCR_ANRESTART));
for (i = 0; i < 30000; i++) {
at91emac_read(emac, CONFIG_DRIVER_AT91EMAC_PHYADDR,
MII_BMSR, &status);
if (status & BMSR_ANEGCOMPLETE)
break;
udelay(100);
}
if (status & BMSR_ANEGCOMPLETE) {
debug_cond(VERBOSEP,
"%s: Autonegotiation complete\n", netdev->name);
} else {
printf("%s: Autonegotiation timed out (status=0x%04x)\n",
netdev->name, status);
return -1;
}
return 0;
}
static int at91emac_phy_init(struct eth_device *netdev)
{
u16 phy_id, status, adv, lpa;
int media, speed, duplex;
int i;
at91_emac_t *emac;
emac = (at91_emac_t *) netdev->iobase;
/* Check if the PHY is up to snuff... */
at91emac_read(emac, CONFIG_DRIVER_AT91EMAC_PHYADDR,
MII_PHYSID1, &phy_id);
if (phy_id == 0xffff) {
printf("%s: No PHY present\n", netdev->name);
return -1;
}
at91emac_read(emac, CONFIG_DRIVER_AT91EMAC_PHYADDR,
MII_BMSR, &status);
if (!(status & BMSR_LSTATUS)) {
/* Try to re-negotiate if we don't have link already. */
if (at91emac_phy_reset(netdev))
return -2;
for (i = 0; i < 100000 / 100; i++) {
at91emac_read(emac, CONFIG_DRIVER_AT91EMAC_PHYADDR,
MII_BMSR, &status);
if (status & BMSR_LSTATUS)
break;
udelay(100);
}
}
if (!(status & BMSR_LSTATUS)) {
debug_cond(VERBOSEP, "%s: link down\n", netdev->name);
return -3;
} else {
at91emac_read(emac, CONFIG_DRIVER_AT91EMAC_PHYADDR,
MII_ADVERTISE, &adv);
at91emac_read(emac, CONFIG_DRIVER_AT91EMAC_PHYADDR,
MII_LPA, &lpa);
media = mii_nway_result(lpa & adv);
speed = (media & (ADVERTISE_100FULL | ADVERTISE_100HALF)
? 1 : 0);
duplex = (media & ADVERTISE_FULL) ? 1 : 0;
debug_cond(VERBOSEP, "%s: link up, %sMbps %s-duplex\n",
netdev->name,
speed ? "100" : "10",
duplex ? "full" : "half");
}
return 0;
}
int at91emac_UpdateLinkSpeed(at91_emac_t *emac)
{
unsigned short stat1;
at91emac_read(emac, CONFIG_DRIVER_AT91EMAC_PHYADDR, MII_BMSR, &stat1);
if (!(stat1 & BMSR_LSTATUS)) /* link status up? */
return -1;
if (stat1 & BMSR_100FULL) {
/*set Emac for 100BaseTX and Full Duplex */
writel(readl(&emac->cfg) |
AT91_EMAC_CFG_SPD | AT91_EMAC_CFG_FD,
&emac->cfg);
return 0;
}
if (stat1 & BMSR_10FULL) {
/*set MII for 10BaseT and Full Duplex */
writel((readl(&emac->cfg) &
~(AT91_EMAC_CFG_SPD | AT91_EMAC_CFG_FD)
) | AT91_EMAC_CFG_FD,
&emac->cfg);
return 0;
}
if (stat1 & BMSR_100HALF) {
/*set MII for 100BaseTX and Half Duplex */
writel((readl(&emac->cfg) &
~(AT91_EMAC_CFG_SPD | AT91_EMAC_CFG_FD)
) | AT91_EMAC_CFG_SPD,
&emac->cfg);
return 0;
}
if (stat1 & BMSR_10HALF) {
/*set MII for 10BaseT and Half Duplex */
writel((readl(&emac->cfg) &
~(AT91_EMAC_CFG_SPD | AT91_EMAC_CFG_FD)),
&emac->cfg);
return 0;
}
return 0;
}
static int at91emac_init(struct eth_device *netdev, bd_t *bd)
{
int i;
u32 value;
emac_device *dev;
at91_emac_t *emac;
at91_pio_t *pio = (at91_pio_t *) ATMEL_BASE_PIO;
at91_pmc_t *pmc = (at91_pmc_t *) ATMEL_BASE_PMC;
emac = (at91_emac_t *) netdev->iobase;
dev = (emac_device *) netdev->priv;
/* PIO Disable Register */
value = ATMEL_PMX_AA_EMDIO | ATMEL_PMX_AA_EMDC |
ATMEL_PMX_AA_ERXER | ATMEL_PMX_AA_ERX1 |
ATMEL_PMX_AA_ERX0 | ATMEL_PMX_AA_ECRS |
ATMEL_PMX_AA_ETX1 | ATMEL_PMX_AA_ETX0 |
ATMEL_PMX_AA_ETXEN | ATMEL_PMX_AA_EREFCK;
writel(value, &pio->pioa.pdr);
writel(value, &pio->pioa.asr);
#ifdef CONFIG_RMII
value = ATMEL_PMX_BA_ERXCK;
#else
value = ATMEL_PMX_BA_ERXCK | ATMEL_PMX_BA_ECOL |
ATMEL_PMX_BA_ERXDV | ATMEL_PMX_BA_ERX3 |
ATMEL_PMX_BA_ERX2 | ATMEL_PMX_BA_ETXER |
ATMEL_PMX_BA_ETX3 | ATMEL_PMX_BA_ETX2;
#endif
writel(value, &pio->piob.pdr);
writel(value, &pio->piob.bsr);
writel(1 << ATMEL_ID_EMAC, &pmc->pcer);
writel(readl(&emac->ctl) | AT91_EMAC_CTL_CSR, &emac->ctl);
/* Init Ethernet buffers */
for (i = 0; i < RBF_FRAMEMAX; i++) {
dev->rbfdt[i].addr = (unsigned long) net_rx_packets[i];
dev->rbfdt[i].size = 0;
}
dev->rbfdt[RBF_FRAMEMAX - 1].addr |= RBF_WRAP;
dev->rbindex = 0;
writel((u32) &(dev->rbfdt[0]), &emac->rbqp);
writel(readl(&emac->rsr) &
~(AT91_EMAC_RSR_OVR | AT91_EMAC_RSR_REC | AT91_EMAC_RSR_BNA),
&emac->rsr);
value = AT91_EMAC_CFG_CAF | AT91_EMAC_CFG_NBC |
HCLK_DIV;
#ifdef CONFIG_RMII
value |= AT91_EMAC_CFG_RMII;
#endif
writel(value, &emac->cfg);
writel(readl(&emac->ctl) | AT91_EMAC_CTL_TE | AT91_EMAC_CTL_RE,
&emac->ctl);
if (!at91emac_phy_init(netdev)) {
at91emac_UpdateLinkSpeed(emac);
return 0;
}
return -1;
}
static void at91emac_halt(struct eth_device *netdev)
{
at91_emac_t *emac;
emac = (at91_emac_t *) netdev->iobase;
writel(readl(&emac->ctl) & ~(AT91_EMAC_CTL_TE | AT91_EMAC_CTL_RE),
&emac->ctl);
debug_cond(DEBUG_AT91EMAC, "halt MAC\n");
}
static int at91emac_send(struct eth_device *netdev, void *packet, int length)
{
at91_emac_t *emac;
emac = (at91_emac_t *) netdev->iobase;
while (!(readl(&emac->tsr) & AT91_EMAC_TSR_BNQ))
;
writel((u32) packet, &emac->tar);
writel(AT91_EMAC_TCR_LEN(length), &emac->tcr);
while (AT91_EMAC_TCR_LEN(readl(&emac->tcr)))
;
debug_cond(DEBUG_AT91EMAC, "Send %d\n", length);
writel(readl(&emac->tsr) | AT91_EMAC_TSR_COMP, &emac->tsr);
return 0;
}
static int at91emac_recv(struct eth_device *netdev)
{
emac_device *dev;
at91_emac_t *emac;
rbf_t *rbfp;
int size;
emac = (at91_emac_t *) netdev->iobase;
dev = (emac_device *) netdev->priv;
rbfp = &dev->rbfdt[dev->rbindex];
while (rbfp->addr & RBF_OWNER) {
size = rbfp->size & RBF_SIZE;
net_process_received_packet(net_rx_packets[dev->rbindex], size);
debug_cond(DEBUG_AT91EMAC, "Recv[%ld]: %d bytes @ %lx\n",
dev->rbindex, size, rbfp->addr);
rbfp->addr &= ~RBF_OWNER;
rbfp->size = 0;
if (dev->rbindex < (RBF_FRAMEMAX-1))
dev->rbindex++;
else
dev->rbindex = 0;
rbfp = &(dev->rbfdt[dev->rbindex]);
if (!(rbfp->addr & RBF_OWNER))
writel(readl(&emac->rsr) | AT91_EMAC_RSR_REC,
&emac->rsr);
}
if (readl(&emac->isr) & AT91_EMAC_IxR_RBNA) {
/* EMAC silicon bug 41.3.1 workaround 1 */
writel(readl(&emac->ctl) & ~AT91_EMAC_CTL_RE, &emac->ctl);
writel(readl(&emac->ctl) | AT91_EMAC_CTL_RE, &emac->ctl);
dev->rbindex = 0;
printf("%s: reset receiver (EMAC dead lock bug)\n",
netdev->name);
}
return 0;
}
static int at91emac_write_hwaddr(struct eth_device *netdev)
{
at91_emac_t *emac;
at91_pmc_t *pmc = (at91_pmc_t *) ATMEL_BASE_PMC;
emac = (at91_emac_t *) netdev->iobase;
writel(1 << ATMEL_ID_EMAC, &pmc->pcer);
debug_cond(DEBUG_AT91EMAC,
"init MAC-ADDR %02x:%02x:%02x:%02x:%02x:%02x\n",
netdev->enetaddr[5], netdev->enetaddr[4], netdev->enetaddr[3],
netdev->enetaddr[2], netdev->enetaddr[1], netdev->enetaddr[0]);
writel( (netdev->enetaddr[0] | netdev->enetaddr[1] << 8 |
netdev->enetaddr[2] << 16 | netdev->enetaddr[3] << 24),
&emac->sa2l);
writel((netdev->enetaddr[4] | netdev->enetaddr[5] << 8), &emac->sa2h);
debug_cond(DEBUG_AT91EMAC, "init MAC-ADDR %x%x\n",
readl(&emac->sa2h), readl(&emac->sa2l));
return 0;
}
int at91emac_register(bd_t *bis, unsigned long iobase)
{
emac_device *emac;
emac_device *emacfix;
struct eth_device *dev;
if (iobase == 0)
iobase = ATMEL_BASE_EMAC;
emac = malloc(sizeof(*emac)+512);
if (emac == NULL)
return -1;
dev = malloc(sizeof(*dev));
if (dev == NULL) {
free(emac);
return -1;
}
/* alignment as per Errata (64 bytes) is insufficient! */
emacfix = (emac_device *) (((unsigned long) emac + 0x1ff) & 0xFFFFFE00);
memset(emacfix, 0, sizeof(emac_device));
memset(dev, 0, sizeof(*dev));
sprintf(dev->name, "emac");
dev->iobase = iobase;
dev->priv = emacfix;
dev->init = at91emac_init;
dev->halt = at91emac_halt;
dev->send = at91emac_send;
dev->recv = at91emac_recv;
dev->write_hwaddr = at91emac_write_hwaddr;
eth_register(dev);
#if defined(CONFIG_MII) || defined(CONFIG_CMD_MII)
miiphy_register(dev->name, at91emac_mii_read, at91emac_mii_write);
#endif
return 1;
}

View file

@ -0,0 +1,757 @@
/*
* ax88180: ASIX AX88180 Non-PCI Gigabit Ethernet u-boot driver
*
* This program is free software; you can distribute it and/or modify
* it under the terms of the GNU General Public License (Version 2) as
* published by the Free Software Foundation.
* This program is distributed in the hope it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston MA 02111-1307,
* USA.
*/
/*
* ========================================================================
* ASIX AX88180 Non-PCI 16/32-bit Gigabit Ethernet Linux Driver
*
* The AX88180 Ethernet controller is a high performance and highly
* integrated local CPU bus Ethernet controller with embedded 40K bytes
* SRAM and supports both 16-bit and 32-bit SRAM-Like interfaces for any
* embedded systems.
* The AX88180 is a single chip 10/100/1000Mbps Gigabit Ethernet
* controller that supports both MII and RGMII interfaces and is
* compliant to IEEE 802.3, IEEE 802.3u and IEEE 802.3z standards.
*
* Please visit ASIX's web site (http://www.asix.com.tw) for more
* details.
*
* Module Name : ax88180.c
* Date : 2008-07-07
* History
* 09/06/2006 : New release for AX88180 US2 chip.
* 07/07/2008 : Fix up the coding style and using inline functions
* instead of macros
* ========================================================================
*/
#include <common.h>
#include <command.h>
#include <net.h>
#include <malloc.h>
#include <linux/mii.h>
#include "ax88180.h"
/*
* ===========================================================================
* Local SubProgram Declaration
* ===========================================================================
*/
static void ax88180_rx_handler (struct eth_device *dev);
static int ax88180_phy_initial (struct eth_device *dev);
static void ax88180_media_config (struct eth_device *dev);
static unsigned long get_CicadaPHY_media_mode (struct eth_device *dev);
static unsigned long get_MarvellPHY_media_mode (struct eth_device *dev);
static unsigned short ax88180_mdio_read (struct eth_device *dev,
unsigned long regaddr);
static void ax88180_mdio_write (struct eth_device *dev,
unsigned long regaddr, unsigned short regdata);
/*
* ===========================================================================
* Local SubProgram Bodies
* ===========================================================================
*/
static int ax88180_mdio_check_complete (struct eth_device *dev)
{
int us_cnt = 10000;
unsigned short tmpval;
/* MDIO read/write should not take more than 10 ms */
while (--us_cnt) {
tmpval = INW (dev, MDIOCTRL);
if (((tmpval & READ_PHY) == 0) && ((tmpval & WRITE_PHY) == 0))
break;
}
return us_cnt;
}
static unsigned short
ax88180_mdio_read (struct eth_device *dev, unsigned long regaddr)
{
struct ax88180_private *priv = (struct ax88180_private *)dev->priv;
unsigned long tmpval = 0;
OUTW (dev, (READ_PHY | (regaddr << 8) | priv->PhyAddr), MDIOCTRL);
if (ax88180_mdio_check_complete (dev))
tmpval = INW (dev, MDIODP);
else
printf ("Failed to read PHY register!\n");
return (unsigned short)(tmpval & 0xFFFF);
}
static void
ax88180_mdio_write (struct eth_device *dev, unsigned long regaddr,
unsigned short regdata)
{
struct ax88180_private *priv = (struct ax88180_private *)dev->priv;
OUTW (dev, regdata, MDIODP);
OUTW (dev, (WRITE_PHY | (regaddr << 8) | priv->PhyAddr), MDIOCTRL);
if (!ax88180_mdio_check_complete (dev))
printf ("Failed to write PHY register!\n");
}
static int ax88180_phy_reset (struct eth_device *dev)
{
unsigned short delay_cnt = 500;
ax88180_mdio_write (dev, MII_BMCR, (BMCR_RESET | BMCR_ANENABLE));
/* Wait for the reset to complete, or time out (500 ms) */
while (ax88180_mdio_read (dev, MII_BMCR) & BMCR_RESET) {
udelay (1000);
if (--delay_cnt == 0) {
printf ("Failed to reset PHY!\n");
return -1;
}
}
return 0;
}
static void ax88180_mac_reset (struct eth_device *dev)
{
unsigned long tmpval;
unsigned char i;
struct {
unsigned short offset, value;
} program_seq[] = {
{
MISC, MISC_NORMAL}, {
RXINDICATOR, DEFAULT_RXINDICATOR}, {
TXCMD, DEFAULT_TXCMD}, {
TXBS, DEFAULT_TXBS}, {
TXDES0, DEFAULT_TXDES0}, {
TXDES1, DEFAULT_TXDES1}, {
TXDES2, DEFAULT_TXDES2}, {
TXDES3, DEFAULT_TXDES3}, {
TXCFG, DEFAULT_TXCFG}, {
MACCFG2, DEFAULT_MACCFG2}, {
MACCFG3, DEFAULT_MACCFG3}, {
TXLEN, DEFAULT_TXLEN}, {
RXBTHD0, DEFAULT_RXBTHD0}, {
RXBTHD1, DEFAULT_RXBTHD1}, {
RXFULTHD, DEFAULT_RXFULTHD}, {
DOGTHD0, DEFAULT_DOGTHD0}, {
DOGTHD1, DEFAULT_DOGTHD1},};
OUTW (dev, MISC_RESET_MAC, MISC);
tmpval = INW (dev, MISC);
for (i = 0; i < ARRAY_SIZE(program_seq); i++)
OUTW (dev, program_seq[i].value, program_seq[i].offset);
}
static int ax88180_poll_tx_complete (struct eth_device *dev)
{
struct ax88180_private *priv = (struct ax88180_private *)dev->priv;
unsigned long tmpval, txbs_txdp;
int TimeOutCnt = 10000;
txbs_txdp = 1 << priv->NextTxDesc;
while (TimeOutCnt--) {
tmpval = INW (dev, TXBS);
if ((tmpval & txbs_txdp) == 0)
break;
udelay (100);
}
if (TimeOutCnt)
return 0;
else
return -TimeOutCnt;
}
static void ax88180_rx_handler (struct eth_device *dev)
{
struct ax88180_private *priv = (struct ax88180_private *)dev->priv;
unsigned long data_size;
unsigned short rxcurt_ptr, rxbound_ptr, next_ptr;
int i;
#if defined (CONFIG_DRIVER_AX88180_16BIT)
unsigned short *rxdata = (unsigned short *)net_rx_packets[0];
#else
unsigned long *rxdata = (unsigned long *)net_rx_packets[0];
#endif
unsigned short count;
rxcurt_ptr = INW (dev, RXCURT);
rxbound_ptr = INW (dev, RXBOUND);
next_ptr = (rxbound_ptr + 1) & RX_PAGE_NUM_MASK;
debug ("ax88180: RX original RXBOUND=0x%04x,"
" RXCURT=0x%04x\n", rxbound_ptr, rxcurt_ptr);
while (next_ptr != rxcurt_ptr) {
OUTW (dev, RX_START_READ, RXINDICATOR);
data_size = READ_RXBUF (dev) & 0xFFFF;
if ((data_size == 0) || (data_size > MAX_RX_SIZE)) {
OUTW (dev, RX_STOP_READ, RXINDICATOR);
ax88180_mac_reset (dev);
printf ("ax88180: Invalid Rx packet length!"
" (len=0x%04lx)\n", data_size);
debug ("ax88180: RX RXBOUND=0x%04x,"
"RXCURT=0x%04x\n", rxbound_ptr, rxcurt_ptr);
return;
}
rxbound_ptr += (((data_size + 0xF) & 0xFFF0) >> 4) + 1;
rxbound_ptr &= RX_PAGE_NUM_MASK;
/* Comput access times */
count = (data_size + priv->PadSize) >> priv->BusWidth;
for (i = 0; i < count; i++) {
*(rxdata + i) = READ_RXBUF (dev);
}
OUTW (dev, RX_STOP_READ, RXINDICATOR);
/* Pass the packet up to the protocol layers. */
net_process_received_packet(net_rx_packets[0], data_size);
OUTW (dev, rxbound_ptr, RXBOUND);
rxcurt_ptr = INW (dev, RXCURT);
rxbound_ptr = INW (dev, RXBOUND);
next_ptr = (rxbound_ptr + 1) & RX_PAGE_NUM_MASK;
debug ("ax88180: RX updated RXBOUND=0x%04x,"
"RXCURT=0x%04x\n", rxbound_ptr, rxcurt_ptr);
}
return;
}
static int ax88180_phy_initial (struct eth_device *dev)
{
struct ax88180_private *priv = (struct ax88180_private *)dev->priv;
unsigned long tmp_regval;
unsigned short phyaddr;
/* Search for first avaliable PHY chipset */
#ifdef CONFIG_PHY_ADDR
phyaddr = CONFIG_PHY_ADDR;
#else
for (phyaddr = 0; phyaddr < 32; ++phyaddr)
#endif
{
priv->PhyAddr = phyaddr;
priv->PhyID0 = ax88180_mdio_read(dev, MII_PHYSID1);
priv->PhyID1 = ax88180_mdio_read(dev, MII_PHYSID2);
switch (priv->PhyID0) {
case MARVELL_ALASKA_PHYSID0:
debug("ax88180: Found Marvell Alaska PHY family."
" (PHY Addr=0x%x)\n", priv->PhyAddr);
switch (priv->PhyID1) {
case MARVELL_88E1118_PHYSID1:
ax88180_mdio_write(dev, M88E1118_PAGE_SEL, 2);
ax88180_mdio_write(dev, M88E1118_CR,
M88E1118_CR_DEFAULT);
ax88180_mdio_write(dev, M88E1118_PAGE_SEL, 3);
ax88180_mdio_write(dev, M88E1118_LEDCTL,
M88E1118_LEDCTL_DEFAULT);
ax88180_mdio_write(dev, M88E1118_LEDMIX,
M88E1118_LEDMIX_LED050 | M88E1118_LEDMIX_LED150 | 0x15);
ax88180_mdio_write(dev, M88E1118_PAGE_SEL, 0);
default: /* Default to 88E1111 Phy */
tmp_regval = ax88180_mdio_read(dev, M88E1111_EXT_SSR);
if ((tmp_regval & HWCFG_MODE_MASK) != RGMII_COPPER_MODE)
ax88180_mdio_write(dev, M88E1111_EXT_SCR,
DEFAULT_EXT_SCR);
}
if (ax88180_phy_reset(dev) < 0)
return 0;
ax88180_mdio_write(dev, M88_IER, LINK_CHANGE_INT);
return 1;
case CICADA_CIS8201_PHYSID0:
debug("ax88180: Found CICADA CIS8201 PHY"
" chipset. (PHY Addr=0x%x)\n", priv->PhyAddr);
ax88180_mdio_write(dev, CIS_IMR,
(CIS_INT_ENABLE | LINK_CHANGE_INT));
/* Set CIS_SMI_PRIORITY bit before force the media mode */
tmp_regval = ax88180_mdio_read(dev, CIS_AUX_CTRL_STATUS);
tmp_regval &= ~CIS_SMI_PRIORITY;
ax88180_mdio_write(dev, CIS_AUX_CTRL_STATUS, tmp_regval);
return 1;
case 0xffff:
/* No PHY at this addr */
break;
default:
printf("ax88180: Unknown PHY chipset %#x at addr %#x\n",
priv->PhyID0, priv->PhyAddr);
break;
}
}
printf("ax88180: Unknown PHY chipset!!\n");
return 0;
}
static void ax88180_media_config (struct eth_device *dev)
{
struct ax88180_private *priv = (struct ax88180_private *)dev->priv;
unsigned long bmcr_val, bmsr_val;
unsigned long rxcfg_val, maccfg0_val, maccfg1_val;
unsigned long RealMediaMode;
int i;
/* Waiting 2 seconds for PHY link stable */
for (i = 0; i < 20000; i++) {
bmsr_val = ax88180_mdio_read (dev, MII_BMSR);
if (bmsr_val & BMSR_LSTATUS) {
break;
}
udelay (100);
}
bmsr_val = ax88180_mdio_read (dev, MII_BMSR);
debug ("ax88180: BMSR=0x%04x\n", (unsigned int)bmsr_val);
if (bmsr_val & BMSR_LSTATUS) {
bmcr_val = ax88180_mdio_read (dev, MII_BMCR);
if (bmcr_val & BMCR_ANENABLE) {
/*
* Waiting for Auto-negotiation completion, this may
* take up to 5 seconds.
*/
debug ("ax88180: Auto-negotiation is "
"enabled. Waiting for NWay completion..\n");
for (i = 0; i < 50000; i++) {
bmsr_val = ax88180_mdio_read (dev, MII_BMSR);
if (bmsr_val & BMSR_ANEGCOMPLETE) {
break;
}
udelay (100);
}
} else
debug ("ax88180: Auto-negotiation is disabled.\n");
debug ("ax88180: BMCR=0x%04x, BMSR=0x%04x\n",
(unsigned int)bmcr_val, (unsigned int)bmsr_val);
/* Get real media mode here */
switch (priv->PhyID0) {
case MARVELL_ALASKA_PHYSID0:
RealMediaMode = get_MarvellPHY_media_mode(dev);
break;
case CICADA_CIS8201_PHYSID0:
RealMediaMode = get_CicadaPHY_media_mode(dev);
break;
default:
RealMediaMode = MEDIA_1000FULL;
break;
}
priv->LinkState = INS_LINK_UP;
switch (RealMediaMode) {
case MEDIA_1000FULL:
debug ("ax88180: 1000Mbps Full-duplex mode.\n");
rxcfg_val = RXFLOW_ENABLE | DEFAULT_RXCFG;
maccfg0_val = TXFLOW_ENABLE | DEFAULT_MACCFG0;
maccfg1_val = GIGA_MODE_EN | RXFLOW_EN |
FULLDUPLEX | DEFAULT_MACCFG1;
break;
case MEDIA_1000HALF:
debug ("ax88180: 1000Mbps Half-duplex mode.\n");
rxcfg_val = DEFAULT_RXCFG;
maccfg0_val = DEFAULT_MACCFG0;
maccfg1_val = GIGA_MODE_EN | DEFAULT_MACCFG1;
break;
case MEDIA_100FULL:
debug ("ax88180: 100Mbps Full-duplex mode.\n");
rxcfg_val = RXFLOW_ENABLE | DEFAULT_RXCFG;
maccfg0_val = SPEED100 | TXFLOW_ENABLE
| DEFAULT_MACCFG0;
maccfg1_val = RXFLOW_EN | FULLDUPLEX | DEFAULT_MACCFG1;
break;
case MEDIA_100HALF:
debug ("ax88180: 100Mbps Half-duplex mode.\n");
rxcfg_val = DEFAULT_RXCFG;
maccfg0_val = SPEED100 | DEFAULT_MACCFG0;
maccfg1_val = DEFAULT_MACCFG1;
break;
case MEDIA_10FULL:
debug ("ax88180: 10Mbps Full-duplex mode.\n");
rxcfg_val = RXFLOW_ENABLE | DEFAULT_RXCFG;
maccfg0_val = TXFLOW_ENABLE | DEFAULT_MACCFG0;
maccfg1_val = RXFLOW_EN | FULLDUPLEX | DEFAULT_MACCFG1;
break;
case MEDIA_10HALF:
debug ("ax88180: 10Mbps Half-duplex mode.\n");
rxcfg_val = DEFAULT_RXCFG;
maccfg0_val = DEFAULT_MACCFG0;
maccfg1_val = DEFAULT_MACCFG1;
break;
default:
debug ("ax88180: Unknow media mode.\n");
rxcfg_val = DEFAULT_RXCFG;
maccfg0_val = DEFAULT_MACCFG0;
maccfg1_val = DEFAULT_MACCFG1;
priv->LinkState = INS_LINK_DOWN;
break;
}
} else {
rxcfg_val = DEFAULT_RXCFG;
maccfg0_val = DEFAULT_MACCFG0;
maccfg1_val = DEFAULT_MACCFG1;
priv->LinkState = INS_LINK_DOWN;
}
OUTW (dev, rxcfg_val, RXCFG);
OUTW (dev, maccfg0_val, MACCFG0);
OUTW (dev, maccfg1_val, MACCFG1);
return;
}
static unsigned long get_MarvellPHY_media_mode (struct eth_device *dev)
{
unsigned long m88_ssr;
unsigned long MediaMode;
m88_ssr = ax88180_mdio_read (dev, M88_SSR);
switch (m88_ssr & SSR_MEDIA_MASK) {
case SSR_1000FULL:
MediaMode = MEDIA_1000FULL;
break;
case SSR_1000HALF:
MediaMode = MEDIA_1000HALF;
break;
case SSR_100FULL:
MediaMode = MEDIA_100FULL;
break;
case SSR_100HALF:
MediaMode = MEDIA_100HALF;
break;
case SSR_10FULL:
MediaMode = MEDIA_10FULL;
break;
case SSR_10HALF:
MediaMode = MEDIA_10HALF;
break;
default:
MediaMode = MEDIA_UNKNOWN;
break;
}
return MediaMode;
}
static unsigned long get_CicadaPHY_media_mode (struct eth_device *dev)
{
unsigned long tmp_regval;
unsigned long MediaMode;
tmp_regval = ax88180_mdio_read (dev, CIS_AUX_CTRL_STATUS);
switch (tmp_regval & CIS_MEDIA_MASK) {
case CIS_1000FULL:
MediaMode = MEDIA_1000FULL;
break;
case CIS_1000HALF:
MediaMode = MEDIA_1000HALF;
break;
case CIS_100FULL:
MediaMode = MEDIA_100FULL;
break;
case CIS_100HALF:
MediaMode = MEDIA_100HALF;
break;
case CIS_10FULL:
MediaMode = MEDIA_10FULL;
break;
case CIS_10HALF:
MediaMode = MEDIA_10HALF;
break;
default:
MediaMode = MEDIA_UNKNOWN;
break;
}
return MediaMode;
}
static void ax88180_halt (struct eth_device *dev)
{
/* Disable AX88180 TX/RX functions */
OUTW (dev, WAKEMOD, CMD);
}
static int ax88180_init (struct eth_device *dev, bd_t * bd)
{
struct ax88180_private *priv = (struct ax88180_private *)dev->priv;
unsigned short tmp_regval;
ax88180_mac_reset (dev);
/* Disable interrupt */
OUTW (dev, CLEAR_IMR, IMR);
/* Disable AX88180 TX/RX functions */
OUTW (dev, WAKEMOD, CMD);
/* Fill the MAC address */
tmp_regval =
dev->enetaddr[0] | (((unsigned short)dev->enetaddr[1]) << 8);
OUTW (dev, tmp_regval, MACID0);
tmp_regval =
dev->enetaddr[2] | (((unsigned short)dev->enetaddr[3]) << 8);
OUTW (dev, tmp_regval, MACID1);
tmp_regval =
dev->enetaddr[4] | (((unsigned short)dev->enetaddr[5]) << 8);
OUTW (dev, tmp_regval, MACID2);
ax88180_media_config (dev);
OUTW (dev, DEFAULT_RXFILTER, RXFILTER);
/* Initial variables here */
priv->FirstTxDesc = TXDP0;
priv->NextTxDesc = TXDP0;
/* Check if there is any invalid interrupt status and clear it. */
OUTW (dev, INW (dev, ISR), ISR);
/* Start AX88180 TX/RX functions */
OUTW (dev, (RXEN | TXEN | WAKEMOD), CMD);
return 0;
}
/* Get a data block via Ethernet */
static int ax88180_recv (struct eth_device *dev)
{
unsigned short ISR_Status;
unsigned short tmp_regval;
/* Read and check interrupt status here. */
ISR_Status = INW (dev, ISR);
while (ISR_Status) {
/* Clear the interrupt status */
OUTW (dev, ISR_Status, ISR);
debug ("\nax88180: The interrupt status = 0x%04x\n",
ISR_Status);
if (ISR_Status & ISR_PHY) {
/* Read ISR register once to clear PHY interrupt bit */
tmp_regval = ax88180_mdio_read (dev, M88_ISR);
ax88180_media_config (dev);
}
if ((ISR_Status & ISR_RX) || (ISR_Status & ISR_RXBUFFOVR)) {
ax88180_rx_handler (dev);
}
/* Read and check interrupt status again */
ISR_Status = INW (dev, ISR);
}
return 0;
}
/* Send a data block via Ethernet. */
static int ax88180_send(struct eth_device *dev, void *packet, int length)
{
struct ax88180_private *priv = (struct ax88180_private *)dev->priv;
unsigned short TXDES_addr;
unsigned short txcmd_txdp, txbs_txdp;
unsigned short tmp_data;
int i;
#if defined (CONFIG_DRIVER_AX88180_16BIT)
volatile unsigned short *txdata = (volatile unsigned short *)packet;
#else
volatile unsigned long *txdata = (volatile unsigned long *)packet;
#endif
unsigned short count;
if (priv->LinkState != INS_LINK_UP) {
return 0;
}
priv->FirstTxDesc = priv->NextTxDesc;
txbs_txdp = 1 << priv->FirstTxDesc;
debug ("ax88180: TXDP%d is available\n", priv->FirstTxDesc);
txcmd_txdp = priv->FirstTxDesc << 13;
TXDES_addr = TXDES0 + (priv->FirstTxDesc << 2);
OUTW (dev, (txcmd_txdp | length | TX_START_WRITE), TXCMD);
/* Comput access times */
count = (length + priv->PadSize) >> priv->BusWidth;
for (i = 0; i < count; i++) {
WRITE_TXBUF (dev, *(txdata + i));
}
OUTW (dev, txcmd_txdp | length, TXCMD);
OUTW (dev, txbs_txdp, TXBS);
OUTW (dev, (TXDPx_ENABLE | length), TXDES_addr);
priv->NextTxDesc = (priv->NextTxDesc + 1) & TXDP_MASK;
/*
* Check the available transmit descriptor, if we had exhausted all
* transmit descriptor ,then we have to wait for at least one free
* descriptor
*/
txbs_txdp = 1 << priv->NextTxDesc;
tmp_data = INW (dev, TXBS);
if (tmp_data & txbs_txdp) {
if (ax88180_poll_tx_complete (dev) < 0) {
ax88180_mac_reset (dev);
priv->FirstTxDesc = TXDP0;
priv->NextTxDesc = TXDP0;
printf ("ax88180: Transmit time out occurred!\n");
}
}
return 0;
}
static void ax88180_read_mac_addr (struct eth_device *dev)
{
unsigned short macid0_val, macid1_val, macid2_val;
unsigned short tmp_regval;
unsigned short i;
/* Reload MAC address from EEPROM */
OUTW (dev, RELOAD_EEPROM, PROMCTRL);
/* Waiting for reload eeprom completion */
for (i = 0; i < 500; i++) {
tmp_regval = INW (dev, PROMCTRL);
if ((tmp_regval & RELOAD_EEPROM) == 0)
break;
udelay (1000);
}
/* Get MAC addresses */
macid0_val = INW (dev, MACID0);
macid1_val = INW (dev, MACID1);
macid2_val = INW (dev, MACID2);
if (((macid0_val | macid1_val | macid2_val) != 0) &&
((macid0_val & 0x01) == 0)) {
dev->enetaddr[0] = (unsigned char)macid0_val;
dev->enetaddr[1] = (unsigned char)(macid0_val >> 8);
dev->enetaddr[2] = (unsigned char)macid1_val;
dev->enetaddr[3] = (unsigned char)(macid1_val >> 8);
dev->enetaddr[4] = (unsigned char)macid2_val;
dev->enetaddr[5] = (unsigned char)(macid2_val >> 8);
}
}
/*
===========================================================================
<<<<<< Exported SubProgram Bodies >>>>>>
===========================================================================
*/
int ax88180_initialize (bd_t * bis)
{
struct eth_device *dev;
struct ax88180_private *priv;
dev = (struct eth_device *)malloc (sizeof *dev);
if (NULL == dev)
return 0;
memset (dev, 0, sizeof *dev);
priv = (struct ax88180_private *)malloc (sizeof (*priv));
if (NULL == priv)
return 0;
memset (priv, 0, sizeof *priv);
sprintf (dev->name, "ax88180");
dev->iobase = AX88180_BASE;
dev->priv = priv;
dev->init = ax88180_init;
dev->halt = ax88180_halt;
dev->send = ax88180_send;
dev->recv = ax88180_recv;
priv->BusWidth = BUS_WIDTH_32;
priv->PadSize = 3;
#if defined (CONFIG_DRIVER_AX88180_16BIT)
OUTW (dev, (START_BASE >> 8), BASE);
OUTW (dev, DECODE_EN, DECODE);
priv->BusWidth = BUS_WIDTH_16;
priv->PadSize = 1;
#endif
ax88180_mac_reset (dev);
/* Disable interrupt */
OUTW (dev, CLEAR_IMR, IMR);
/* Disable AX88180 TX/RX functions */
OUTW (dev, WAKEMOD, CMD);
ax88180_read_mac_addr (dev);
eth_register (dev);
return ax88180_phy_initial (dev);
}

View file

@ -0,0 +1,396 @@
/* ax88180.h: ASIX AX88180 Non-PCI Gigabit Ethernet u-boot driver */
/*
*
* This program is free software; you can distribute it and/or modify it
* under the terms of the GNU General Public License (Version 2) as
* published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place - Suite 330, Boston MA 02111-1307, USA.
*
*/
#ifndef _AX88180_H_
#define _AX88180_H_
#include <asm/io.h>
#include <asm/types.h>
#include <config.h>
typedef enum _ax88180_link_state {
INS_LINK_DOWN,
INS_LINK_UP,
INS_LINK_UNKNOWN
} ax88180_link_state;
struct ax88180_private {
unsigned char BusWidth;
unsigned char PadSize;
unsigned short PhyAddr;
unsigned short PhyID0;
unsigned short PhyID1;
unsigned short FirstTxDesc;
unsigned short NextTxDesc;
ax88180_link_state LinkState;
};
#define BUS_WIDTH_16 1
#define BUS_WIDTH_32 2
#define ENABLE_JUMBO 1
#define DISABLE_JUMBO 0
#define ENABLE_BURST 1
#define DISABLE_BURST 0
#define NORMAL_RX_MODE 0
#define RX_LOOPBACK_MODE 1
#define RX_INIFINIT_LOOP_MODE 2
#define TX_INIFINIT_LOOP_MODE 3
#define DEFAULT_ETH_MTU 1500
/* Jumbo packet size 4086 bytes included 4 bytes CRC*/
#define MAX_JUMBO_MTU 4072
/* Max Tx Jumbo size 4086 bytes included 4 bytes CRC */
#define MAX_TX_JUMBO_SIZE 4086
/* Max Rx Jumbo size is 15K Bytes */
#define MAX_RX_SIZE 0x3C00
#define MARVELL_ALASKA_PHYSID0 0x141
#define MARVELL_88E1118_PHYSID1 0xE40
#define CICADA_CIS8201_PHYSID0 0x000F
#define MEDIA_AUTO 0
#define MEDIA_1000FULL 1
#define MEDIA_1000HALF 2
#define MEDIA_100FULL 3
#define MEDIA_100HALF 4
#define MEDIA_10FULL 5
#define MEDIA_10HALF 6
#define MEDIA_UNKNOWN 7
#define AUTO_MEDIA 0
#define FORCE_MEDIA 1
#define TXDP_MASK 3
#define TXDP0 0
#define TXDP1 1
#define TXDP2 2
#define TXDP3 3
#define CMD_MAP_SIZE 0x100
#if defined (CONFIG_DRIVER_AX88180_16BIT)
#define AX88180_MEMORY_SIZE 0x00004000
#define START_BASE 0x1000
#define RX_BUF_SIZE 0x1000
#define TX_BUF_SIZE 0x0F00
#define TX_BASE START_BASE
#define CMD_BASE (TX_BASE + TX_BUF_SIZE)
#define RX_BASE (CMD_BASE + CMD_MAP_SIZE)
#else
#define AX88180_MEMORY_SIZE 0x00010000
#define RX_BUF_SIZE 0x8000
#define TX_BUF_SIZE 0x7C00
#define RX_BASE 0x0000
#define TX_BASE (RX_BASE + RX_BUF_SIZE)
#define CMD_BASE (TX_BASE + TX_BUF_SIZE)
#endif
/* AX88180 Memory Mapping Definition */
#define RXBUFFER_START RX_BASE
#define RX_PACKET_LEN_OFFSET 0
#define RX_PAGE_NUM_MASK 0x7FF /* RX pages 0~7FFh */
#define TXBUFFER_START TX_BASE
/* AX88180 MAC Register Definition */
#define DECODE (0)
#define DECODE_EN 0x00000001
#define BASE (6)
#define CMD (CMD_BASE + 0x0000)
#define WAKEMOD 0x00000001
#define TXEN 0x00000100
#define RXEN 0x00000200
#define DEFAULT_CMD WAKEMOD
#define IMR (CMD_BASE + 0x0004)
#define IMR_RXBUFFOVR 0x00000001
#define IMR_WATCHDOG 0x00000002
#define IMR_TX 0x00000008
#define IMR_RX 0x00000010
#define IMR_PHY 0x00000020
#define CLEAR_IMR 0x00000000
#define DEFAULT_IMR (IMR_PHY | IMR_RX | IMR_TX |\
IMR_RXBUFFOVR | IMR_WATCHDOG)
#define ISR (CMD_BASE + 0x0008)
#define ISR_RXBUFFOVR 0x00000001
#define ISR_WATCHDOG 0x00000002
#define ISR_TX 0x00000008
#define ISR_RX 0x00000010
#define ISR_PHY 0x00000020
#define TXCFG (CMD_BASE + 0x0010)
#define AUTOPAD_CRC 0x00000050
#define DEFAULT_TXCFG AUTOPAD_CRC
#define TXCMD (CMD_BASE + 0x0014)
#define TXCMD_TXDP_MASK 0x00006000
#define TXCMD_TXDP0 0x00000000
#define TXCMD_TXDP1 0x00002000
#define TXCMD_TXDP2 0x00004000
#define TXCMD_TXDP3 0x00006000
#define TX_START_WRITE 0x00008000
#define TX_STOP_WRITE 0x00000000
#define DEFAULT_TXCMD 0x00000000
#define TXBS (CMD_BASE + 0x0018)
#define TXDP0_USED 0x00000001
#define TXDP1_USED 0x00000002
#define TXDP2_USED 0x00000004
#define TXDP3_USED 0x00000008
#define DEFAULT_TXBS 0x00000000
#define TXDES0 (CMD_BASE + 0x0020)
#define TXDPx_ENABLE 0x00008000
#define TXDPx_LEN_MASK 0x00001FFF
#define DEFAULT_TXDES0 0x00000000
#define TXDES1 (CMD_BASE + 0x0024)
#define TXDPx_ENABLE 0x00008000
#define TXDPx_LEN_MASK 0x00001FFF
#define DEFAULT_TXDES1 0x00000000
#define TXDES2 (CMD_BASE + 0x0028)
#define TXDPx_ENABLE 0x00008000
#define TXDPx_LEN_MASK 0x00001FFF
#define DEFAULT_TXDES2 0x00000000
#define TXDES3 (CMD_BASE + 0x002C)
#define TXDPx_ENABLE 0x00008000
#define TXDPx_LEN_MASK 0x00001FFF
#define DEFAULT_TXDES3 0x00000000
#define RXCFG (CMD_BASE + 0x0030)
#define RXBUFF_PROTECT 0x00000001
#define RXTCPCRC_CHECK 0x00000010
#define RXFLOW_ENABLE 0x00000100
#define DEFAULT_RXCFG RXBUFF_PROTECT
#define RXCURT (CMD_BASE + 0x0034)
#define DEFAULT_RXCURT 0x00000000
#define RXBOUND (CMD_BASE + 0x0038)
#define DEFAULT_RXBOUND 0x7FF /* RX pages 0~7FFh */
#define MACCFG0 (CMD_BASE + 0x0040)
#define MACCFG0_BIT3_0 0x00000007
#define IPGT_VAL 0x00000150
#define TXFLOW_ENABLE 0x00001000
#define SPEED100 0x00008000
#define DEFAULT_MACCFG0 (IPGT_VAL | MACCFG0_BIT3_0)
#define MACCFG1 (CMD_BASE + 0x0044)
#define RGMII_EN 0x00000002
#define RXFLOW_EN 0x00000020
#define FULLDUPLEX 0x00000040
#define MAX_JUMBO_LEN 0x00000780
#define RXJUMBO_EN 0x00000800
#define GIGA_MODE_EN 0x00001000
#define RXCRC_CHECK 0x00002000
#define RXPAUSE_DA_CHECK 0x00004000
#define JUMBO_LEN_4K 0x00000200
#define JUMBO_LEN_15K 0x00000780
#define DEFAULT_MACCFG1 (RXCRC_CHECK | RXPAUSE_DA_CHECK | \
RGMII_EN)
#define CICADA_DEFAULT_MACCFG1 (RXCRC_CHECK | RXPAUSE_DA_CHECK)
#define MACCFG2 (CMD_BASE + 0x0048)
#define MACCFG2_BIT15_8 0x00000100
#define JAM_LIMIT_MASK 0x000000FC
#define DEFAULT_JAM_LIMIT 0x00000064
#define DEFAULT_MACCFG2 MACCFG2_BIT15_8
#define MACCFG3 (CMD_BASE + 0x004C)
#define IPGR2_VAL 0x0000000E
#define IPGR1_VAL 0x00000600
#define NOABORT 0x00008000
#define DEFAULT_MACCFG3 (IPGR1_VAL | IPGR2_VAL)
#define TXPAUT (CMD_BASE + 0x0054)
#define DEFAULT_TXPAUT 0x001FE000
#define RXBTHD0 (CMD_BASE + 0x0058)
#define DEFAULT_RXBTHD0 0x00000300
#define RXBTHD1 (CMD_BASE + 0x005C)
#define DEFAULT_RXBTHD1 0x00000600
#define RXFULTHD (CMD_BASE + 0x0060)
#define DEFAULT_RXFULTHD 0x00000100
#define MISC (CMD_BASE + 0x0068)
/* Normal operation mode */
#define MISC_NORMAL 0x00000003
/* Clear bit 0 to reset MAC */
#define MISC_RESET_MAC 0x00000002
/* Clear bit 1 to reset PHY */
#define MISC_RESET_PHY 0x00000001
/* Clear bit 0 and 1 to reset MAC and PHY */
#define MISC_RESET_MAC_PHY 0x00000000
#define DEFAULT_MISC MISC_NORMAL
#define MACID0 (CMD_BASE + 0x0070)
#define MACID1 (CMD_BASE + 0x0074)
#define MACID2 (CMD_BASE + 0x0078)
#define TXLEN (CMD_BASE + 0x007C)
#define DEFAULT_TXLEN 0x000005FC
#define RXFILTER (CMD_BASE + 0x0080)
#define RX_RXANY 0x00000001
#define RX_MULTICAST 0x00000002
#define RX_UNICAST 0x00000004
#define RX_BROADCAST 0x00000008
#define RX_MULTI_HASH 0x00000010
#define DISABLE_RXFILTER 0x00000000
#define DEFAULT_RXFILTER (RX_BROADCAST + RX_UNICAST)
#define MDIOCTRL (CMD_BASE + 0x0084)
#define PHY_ADDR_MASK 0x0000001F
#define REG_ADDR_MASK 0x00001F00
#define READ_PHY 0x00004000
#define WRITE_PHY 0x00008000
#define MDIODP (CMD_BASE + 0x0088)
#define GPIOCTRL (CMD_BASE + 0x008C)
#define RXINDICATOR (CMD_BASE + 0x0090)
#define RX_START_READ 0x00000001
#define RX_STOP_READ 0x00000000
#define DEFAULT_RXINDICATOR RX_STOP_READ
#define TXST (CMD_BASE + 0x0094)
#define MDCCLKPAT (CMD_BASE + 0x00A0)
#define RXIPCRCCNT (CMD_BASE + 0x00A4)
#define RXCRCCNT (CMD_BASE + 0x00A8)
#define TXFAILCNT (CMD_BASE + 0x00AC)
#define PROMDP (CMD_BASE + 0x00B0)
#define PROMCTRL (CMD_BASE + 0x00B4)
#define RELOAD_EEPROM 0x00000200
#define MAXRXLEN (CMD_BASE + 0x00B8)
#define HASHTAB0 (CMD_BASE + 0x00C0)
#define HASHTAB1 (CMD_BASE + 0x00C4)
#define HASHTAB2 (CMD_BASE + 0x00C8)
#define HASHTAB3 (CMD_BASE + 0x00CC)
#define DOGTHD0 (CMD_BASE + 0x00E0)
#define DEFAULT_DOGTHD0 0x0000FFFF
#define DOGTHD1 (CMD_BASE + 0x00E4)
#define START_WATCHDOG_TIMER 0x00008000
#define DEFAULT_DOGTHD1 0x00000FFF
#define SOFTRST (CMD_BASE + 0x00EC)
#define SOFTRST_NORMAL 0x00000003
#define SOFTRST_RESET_MAC 0x00000002
/* Marvell 88E1111 Gigabit PHY Register Definition */
#define M88_SSR 0x0011
#define SSR_SPEED_MASK 0xC000
#define SSR_SPEED_1000 0x8000
#define SSR_SPEED_100 0x4000
#define SSR_SPEED_10 0x0000
#define SSR_DUPLEX 0x2000
#define SSR_MEDIA_RESOLVED_OK 0x0800
#define SSR_MEDIA_MASK (SSR_SPEED_MASK | SSR_DUPLEX)
#define SSR_1000FULL (SSR_SPEED_1000 | SSR_DUPLEX)
#define SSR_1000HALF SSR_SPEED_1000
#define SSR_100FULL (SSR_SPEED_100 | SSR_DUPLEX)
#define SSR_100HALF SSR_SPEED_100
#define SSR_10FULL (SSR_SPEED_10 | SSR_DUPLEX)
#define SSR_10HALF SSR_SPEED_10
#define M88_IER 0x0012
#define LINK_CHANGE_INT 0x0400
#define M88_ISR 0x0013
#define LINK_CHANGE_STATUS 0x0400
#define M88E1111_EXT_SCR 0x0014
#define RGMII_RXCLK_DELAY 0x0080
#define RGMII_TXCLK_DELAY 0x0002
#define DEFAULT_EXT_SCR (RGMII_TXCLK_DELAY | RGMII_RXCLK_DELAY)
#define M88E1111_EXT_SSR 0x001B
#define HWCFG_MODE_MASK 0x000F
#define RGMII_COPPER_MODE 0x000B
/* Marvell 88E1118 Gigabit PHY Register Definition */
#define M88E1118_CR 0x14
#define M88E1118_CR_RGMII_RXCLK_DELAY 0x0020
#define M88E1118_CR_RGMII_TXCLK_DELAY 0x0010
#define M88E1118_CR_DEFAULT (M88E1118_CR_RGMII_TXCLK_DELAY | \
M88E1118_CR_RGMII_RXCLK_DELAY)
#define M88E1118_LEDCTL 0x10 /* Reg 16 on page 3 */
#define M88E1118_LEDCTL_LED2INT 0x200
#define M88E1118_LEDCTL_LED2BLNK 0x400
#define M88E1118_LEDCTL_LED0DUALMODE1 0xc
#define M88E1118_LEDCTL_LED0DUALMODE2 0xd
#define M88E1118_LEDCTL_LED0DUALMODE3 0xe
#define M88E1118_LEDCTL_LED0DUALMODE4 0xf
#define M88E1118_LEDCTL_DEFAULT (M88E1118_LEDCTL_LED2BLNK | \
M88E1118_LEDCTL_LED0DUALMODE4)
#define M88E1118_LEDMIX 0x11 /* Reg 17 on page 3 */
#define M88E1118_LEDMIX_LED050 0x4
#define M88E1118_LEDMIX_LED150 0x8
#define M88E1118_PAGE_SEL 0x16 /* Reg page select */
/* CICADA CIS8201 Gigabit PHY Register Definition */
#define CIS_IMR 0x0019
#define CIS_INT_ENABLE 0x8000
#define CIS_LINK_CHANGE_INT 0x2000
#define CIS_ISR 0x001A
#define CIS_INT_PENDING 0x8000
#define CIS_LINK_CHANGE_STATUS 0x2000
#define CIS_AUX_CTRL_STATUS 0x001C
#define CIS_AUTONEG_COMPLETE 0x8000
#define CIS_SPEED_MASK 0x0018
#define CIS_SPEED_1000 0x0010
#define CIS_SPEED_100 0x0008
#define CIS_SPEED_10 0x0000
#define CIS_DUPLEX 0x0020
#define CIS_MEDIA_MASK (CIS_SPEED_MASK | CIS_DUPLEX)
#define CIS_1000FULL (CIS_SPEED_1000 | CIS_DUPLEX)
#define CIS_1000HALF CIS_SPEED_1000
#define CIS_100FULL (CIS_SPEED_100 | CIS_DUPLEX)
#define CIS_100HALF CIS_SPEED_100
#define CIS_10FULL (CIS_SPEED_10 | CIS_DUPLEX)
#define CIS_10HALF CIS_SPEED_10
#define CIS_SMI_PRIORITY 0x0004
static inline unsigned short INW (struct eth_device *dev, unsigned long addr)
{
return le16_to_cpu(readw(addr + (void *)dev->iobase));
}
/*
Access RXBUFFER_START/TXBUFFER_START to read RX buffer/write TX buffer
*/
#if defined (CONFIG_DRIVER_AX88180_16BIT)
static inline void OUTW (struct eth_device *dev, unsigned short command, unsigned long addr)
{
writew(cpu_to_le16(command), addr + (void *)dev->iobase);
}
static inline unsigned short READ_RXBUF (struct eth_device *dev)
{
return le16_to_cpu(readw(RXBUFFER_START + (void *)dev->iobase));
}
static inline void WRITE_TXBUF (struct eth_device *dev, unsigned short data)
{
writew(cpu_to_le16(data), TXBUFFER_START + (void *)dev->iobase);
}
#else
static inline void OUTW (struct eth_device *dev, unsigned short command, unsigned long addr)
{
writel(cpu_to_le32(command), addr + (void *)dev->iobase);
}
static inline unsigned long READ_RXBUF (struct eth_device *dev)
{
return le32_to_cpu(readl(RXBUFFER_START + (void *)dev->iobase));
}
static inline void WRITE_TXBUF (struct eth_device *dev, unsigned long data)
{
writel(cpu_to_le32(data), TXBUFFER_START + (void *)dev->iobase);
}
#endif
#endif /* _AX88180_H_ */

View file

@ -0,0 +1,144 @@
/*
* (c) 2007 Nobuhiro Iwamatsu <iwamatsu@nigauri.org>
*
* SPDX-License-Identifier: GPL-2.0+
*/
#include <common.h>
#include "ax88796.h"
/*
* Set 1 bit data
*/
static void ax88796_bitset(u32 bit)
{
/* DATA1 */
if( bit )
EEDI_HIGH;
else
EEDI_LOW;
EECLK_LOW;
udelay(1000);
EECLK_HIGH;
udelay(1000);
EEDI_LOW;
}
/*
* Get 1 bit data
*/
static u8 ax88796_bitget(void)
{
u8 bit;
EECLK_LOW;
udelay(1000);
/* DATA */
bit = EEDO;
EECLK_HIGH;
udelay(1000);
return bit;
}
/*
* Send COMMAND to EEPROM
*/
static void ax88796_eep_cmd(u8 cmd)
{
ax88796_bitset(BIT_DUMMY);
switch(cmd){
case MAC_EEP_READ:
ax88796_bitset(1);
ax88796_bitset(1);
ax88796_bitset(0);
break;
case MAC_EEP_WRITE:
ax88796_bitset(1);
ax88796_bitset(0);
ax88796_bitset(1);
break;
case MAC_EEP_ERACE:
ax88796_bitset(1);
ax88796_bitset(1);
ax88796_bitset(1);
break;
case MAC_EEP_EWEN:
ax88796_bitset(1);
ax88796_bitset(0);
ax88796_bitset(0);
break;
case MAC_EEP_EWDS:
ax88796_bitset(1);
ax88796_bitset(0);
ax88796_bitset(0);
break;
default:
break;
}
}
static void ax88796_eep_setaddr(u16 addr)
{
int i ;
for( i = 7 ; i >= 0 ; i-- )
ax88796_bitset(addr & (1 << i));
}
/*
* Get data from EEPROM
*/
static u16 ax88796_eep_getdata(void)
{
ushort data = 0;
int i;
ax88796_bitget(); /* DUMMY */
for( i = 0 ; i < 16 ; i++ ){
data <<= 1;
data |= ax88796_bitget();
}
return data;
}
static void ax88796_mac_read(u8 *buff)
{
int i ;
u16 data;
u16 addr = 0;
for( i = 0 ; i < 3; i++ )
{
EECS_HIGH;
EEDI_LOW;
udelay(1000);
/* READ COMMAND */
ax88796_eep_cmd(MAC_EEP_READ);
/* ADDRESS */
ax88796_eep_setaddr(addr++);
/* GET DATA */
data = ax88796_eep_getdata();
*buff++ = (uchar)(data & 0xff);
*buff++ = (uchar)((data >> 8) & 0xff);
EECLK_LOW;
EEDI_LOW;
EECS_LOW;
}
}
int get_prom(u8* mac_addr, u8* base_addr)
{
u8 prom[32];
int i;
ax88796_mac_read(prom);
for (i = 0; i < 6; i++){
mac_addr[i] = prom[i];
}
return 1;
}

View file

@ -0,0 +1,67 @@
/*
* AX88796L(NE2000) support
*
* (c) 2007 Nobuhiro Iwamatsu <iwamatsu@nigauri.org>
*
* SPDX-License-Identifier: GPL-2.0+
*/
#ifndef __DRIVERS_AX88796L_H__
#define __DRIVERS_AX88796L_H__
#define DP_DATA (0x10 << 1)
#define START_PG 0x40 /* First page of TX buffer */
#define START_PG2 0x48
#define STOP_PG 0x80 /* Last page +1 of RX ring */
#define TX_PAGES 12
#define RX_START (START_PG+TX_PAGES)
#define RX_END STOP_PG
#define AX88796L_BASE_ADDRESS CONFIG_DRIVER_NE2000_BASE
#define AX88796L_BYTE_ACCESS 0x00001000
#define AX88796L_OFFSET 0x00000400
#define AX88796L_ADDRESS_BYTE AX88796L_BASE_ADDRESS + \
AX88796L_BYTE_ACCESS + AX88796L_OFFSET
#define AX88796L_REG_MEMR AX88796L_ADDRESS_BYTE + (0x14<<1)
#define AX88796L_REG_CR AX88796L_ADDRESS_BYTE + (0x00<<1)
#define AX88796L_CR (*(vu_short *)(AX88796L_REG_CR))
#define AX88796L_MEMR (*(vu_short *)(AX88796L_REG_MEMR))
#define EECS_HIGH (AX88796L_MEMR |= 0x10)
#define EECS_LOW (AX88796L_MEMR &= 0xef)
#define EECLK_HIGH (AX88796L_MEMR |= 0x80)
#define EECLK_LOW (AX88796L_MEMR &= 0x7f)
#define EEDI_HIGH (AX88796L_MEMR |= 0x20)
#define EEDI_LOW (AX88796L_MEMR &= 0xdf)
#define EEDO ((AX88796L_MEMR & 0x40)>>6)
#define PAGE0_SET (AX88796L_CR &= 0x3f)
#define PAGE1_SET (AX88796L_CR = (AX88796L_CR & 0x3f) | 0x40)
#define BIT_DUMMY 0
#define MAC_EEP_READ 1
#define MAC_EEP_WRITE 2
#define MAC_EEP_ERACE 3
#define MAC_EEP_EWEN 4
#define MAC_EEP_EWDS 5
/* R7780MP Specific code */
#if defined(CONFIG_R7780MP)
#define ISA_OFFSET 0x1400
#define DP_IN(_b_, _o_, _d_) (_d_) = \
*( (vu_short *) ((_b_) + ((_o_) * 2) + ISA_OFFSET))
#define DP_OUT(_b_, _o_, _d_) \
*((vu_short *)((_b_) + ((_o_) * 2) + ISA_OFFSET)) = (_d_)
#define DP_IN_DATA(_b_, _d_) (_d_) = *( (vu_short *) ((_b_) + ISA_OFFSET))
#define DP_OUT_DATA(_b_, _d_) *( (vu_short *) ((_b_)+ISA_OFFSET)) = (_d_)
#else
/* Please change for your target boards */
#define ISA_OFFSET 0x0000
#define DP_IN(_b_, _o_, _d_) (_d_) = *( (vu_short *)((_b_)+(_o_ )+ISA_OFFSET))
#define DP_OUT(_b_, _o_, _d_) *((vu_short *)((_b_)+(_o_)+ISA_OFFSET)) = (_d_)
#define DP_IN_DATA(_b_, _d_) (_d_) = *( (vu_short *) ((_b_)+ISA_OFFSET))
#define DP_OUT_DATA(_b_, _d_) *( (vu_short *) ((_b_)+ISA_OFFSET)) = (_d_)
#endif
#endif /* __DRIVERS_AX88796L_H__ */

View file

@ -0,0 +1,971 @@
/*
* Copyright 2014 Broadcom Corporation.
*
* SPDX-License-Identifier: GPL-2.0+
*/
#ifdef BCM_GMAC_DEBUG
#ifndef DEBUG
#define DEBUG
#endif
#endif
#include <config.h>
#include <common.h>
#include <malloc.h>
#include <net.h>
#include <asm/io.h>
#include <phy.h>
#include "bcm-sf2-eth.h"
#include "bcm-sf2-eth-gmac.h"
#define SPINWAIT(exp, us) { \
uint countdown = (us) + 9; \
while ((exp) && (countdown >= 10)) {\
udelay(10); \
countdown -= 10; \
} \
}
static int gmac_disable_dma(struct eth_dma *dma, int dir);
static int gmac_enable_dma(struct eth_dma *dma, int dir);
/* DMA Descriptor */
typedef struct {
/* misc control bits */
uint32_t ctrl1;
/* buffer count and address extension */
uint32_t ctrl2;
/* memory address of the date buffer, bits 31:0 */
uint32_t addrlow;
/* memory address of the date buffer, bits 63:32 */
uint32_t addrhigh;
} dma64dd_t;
uint32_t g_dmactrlflags;
static uint32_t dma_ctrlflags(uint32_t mask, uint32_t flags)
{
debug("%s enter\n", __func__);
g_dmactrlflags &= ~mask;
g_dmactrlflags |= flags;
/* If trying to enable parity, check if parity is actually supported */
if (g_dmactrlflags & DMA_CTRL_PEN) {
uint32_t control;
control = readl(GMAC0_DMA_TX_CTRL_ADDR);
writel(control | D64_XC_PD, GMAC0_DMA_TX_CTRL_ADDR);
if (readl(GMAC0_DMA_TX_CTRL_ADDR) & D64_XC_PD) {
/*
* We *can* disable it, therefore it is supported;
* restore control register
*/
writel(control, GMAC0_DMA_TX_CTRL_ADDR);
} else {
/* Not supported, don't allow it to be enabled */
g_dmactrlflags &= ~DMA_CTRL_PEN;
}
}
return g_dmactrlflags;
}
static inline void reg32_clear_bits(uint32_t reg, uint32_t value)
{
uint32_t v = readl(reg);
v &= ~(value);
writel(v, reg);
}
static inline void reg32_set_bits(uint32_t reg, uint32_t value)
{
uint32_t v = readl(reg);
v |= value;
writel(v, reg);
}
#ifdef BCM_GMAC_DEBUG
static void dma_tx_dump(struct eth_dma *dma)
{
dma64dd_t *descp = NULL;
uint8_t *bufp;
int i;
printf("TX DMA Register:\n");
printf("control:0x%x; ptr:0x%x; addrl:0x%x; addrh:0x%x; stat0:0x%x, stat1:0x%x\n",
readl(GMAC0_DMA_TX_CTRL_ADDR),
readl(GMAC0_DMA_TX_PTR_ADDR),
readl(GMAC0_DMA_TX_ADDR_LOW_ADDR),
readl(GMAC0_DMA_TX_ADDR_HIGH_ADDR),
readl(GMAC0_DMA_TX_STATUS0_ADDR),
readl(GMAC0_DMA_TX_STATUS1_ADDR));
printf("TX Descriptors:\n");
for (i = 0; i < TX_BUF_NUM; i++) {
descp = (dma64dd_t *)(dma->tx_desc_aligned) + i;
printf("ctrl1:0x%08x; ctrl2:0x%08x; addr:0x%x 0x%08x\n",
descp->ctrl1, descp->ctrl2,
descp->addrhigh, descp->addrlow);
}
printf("TX Buffers:\n");
/* Initialize TX DMA descriptor table */
for (i = 0; i < TX_BUF_NUM; i++) {
bufp = (uint8_t *)(dma->tx_buf + i * TX_BUF_SIZE);
printf("buf%d:0x%x; ", i, (uint32_t)bufp);
}
printf("\n");
}
static void dma_rx_dump(struct eth_dma *dma)
{
dma64dd_t *descp = NULL;
uint8_t *bufp;
int i;
printf("RX DMA Register:\n");
printf("control:0x%x; ptr:0x%x; addrl:0x%x; addrh:0x%x; stat0:0x%x, stat1:0x%x\n",
readl(GMAC0_DMA_RX_CTRL_ADDR),
readl(GMAC0_DMA_RX_PTR_ADDR),
readl(GMAC0_DMA_RX_ADDR_LOW_ADDR),
readl(GMAC0_DMA_RX_ADDR_HIGH_ADDR),
readl(GMAC0_DMA_RX_STATUS0_ADDR),
readl(GMAC0_DMA_RX_STATUS1_ADDR));
printf("RX Descriptors:\n");
for (i = 0; i < RX_BUF_NUM; i++) {
descp = (dma64dd_t *)(dma->rx_desc_aligned) + i;
printf("ctrl1:0x%08x; ctrl2:0x%08x; addr:0x%x 0x%08x\n",
descp->ctrl1, descp->ctrl2,
descp->addrhigh, descp->addrlow);
}
printf("RX Buffers:\n");
for (i = 0; i < RX_BUF_NUM; i++) {
bufp = dma->rx_buf + i * RX_BUF_SIZE;
printf("buf%d:0x%x; ", i, (uint32_t)bufp);
}
printf("\n");
}
#endif
static int dma_tx_init(struct eth_dma *dma)
{
dma64dd_t *descp = NULL;
uint8_t *bufp;
int i;
uint32_t ctrl;
debug("%s enter\n", __func__);
/* clear descriptor memory */
memset((void *)(dma->tx_desc_aligned), 0,
TX_BUF_NUM * sizeof(dma64dd_t));
memset(dma->tx_buf, 0, TX_BUF_NUM * TX_BUF_SIZE);
/* Initialize TX DMA descriptor table */
for (i = 0; i < TX_BUF_NUM; i++) {
descp = (dma64dd_t *)(dma->tx_desc_aligned) + i;
bufp = dma->tx_buf + i * TX_BUF_SIZE;
/* clear buffer memory */
memset((void *)bufp, 0, TX_BUF_SIZE);
ctrl = 0;
/* if last descr set endOfTable */
if (i == (TX_BUF_NUM-1))
ctrl = D64_CTRL1_EOT;
descp->ctrl1 = ctrl;
descp->ctrl2 = 0;
descp->addrlow = (uint32_t)bufp;
descp->addrhigh = 0;
}
/* flush descriptor and buffer */
descp = dma->tx_desc_aligned;
bufp = dma->tx_buf;
flush_dcache_range((unsigned long)descp,
(unsigned long)(descp +
sizeof(dma64dd_t) * TX_BUF_NUM));
flush_dcache_range((unsigned long)(bufp),
(unsigned long)(bufp + TX_BUF_SIZE * TX_BUF_NUM));
/* initialize the DMA channel */
writel((uint32_t)(dma->tx_desc_aligned), GMAC0_DMA_TX_ADDR_LOW_ADDR);
writel(0, GMAC0_DMA_TX_ADDR_HIGH_ADDR);
/* now update the dma last descriptor */
writel(((uint32_t)(dma->tx_desc_aligned)) & D64_XP_LD_MASK,
GMAC0_DMA_TX_PTR_ADDR);
return 0;
}
static int dma_rx_init(struct eth_dma *dma)
{
uint32_t last_desc;
dma64dd_t *descp = NULL;
uint8_t *bufp;
uint32_t ctrl;
int i;
debug("%s enter\n", __func__);
/* clear descriptor memory */
memset((void *)(dma->rx_desc_aligned), 0,
RX_BUF_NUM * sizeof(dma64dd_t));
/* clear buffer memory */
memset(dma->rx_buf, 0, RX_BUF_NUM * RX_BUF_SIZE);
/* Initialize RX DMA descriptor table */
for (i = 0; i < RX_BUF_NUM; i++) {
descp = (dma64dd_t *)(dma->rx_desc_aligned) + i;
bufp = dma->rx_buf + i * RX_BUF_SIZE;
ctrl = 0;
/* if last descr set endOfTable */
if (i == (RX_BUF_NUM - 1))
ctrl = D64_CTRL1_EOT;
descp->ctrl1 = ctrl;
descp->ctrl2 = RX_BUF_SIZE;
descp->addrlow = (uint32_t)bufp;
descp->addrhigh = 0;
last_desc = ((uint32_t)(descp) & D64_XP_LD_MASK)
+ sizeof(dma64dd_t);
}
descp = dma->rx_desc_aligned;
bufp = dma->rx_buf;
/* flush descriptor and buffer */
flush_dcache_range((unsigned long)descp,
(unsigned long)(descp +
sizeof(dma64dd_t) * RX_BUF_NUM));
flush_dcache_range((unsigned long)(bufp),
(unsigned long)(bufp + RX_BUF_SIZE * RX_BUF_NUM));
/* initailize the DMA channel */
writel((uint32_t)descp, GMAC0_DMA_RX_ADDR_LOW_ADDR);
writel(0, GMAC0_DMA_RX_ADDR_HIGH_ADDR);
/* now update the dma last descriptor */
writel(last_desc, GMAC0_DMA_RX_PTR_ADDR);
return 0;
}
static int dma_init(struct eth_dma *dma)
{
debug(" %s enter\n", __func__);
/*
* Default flags: For backwards compatibility both
* Rx Overflow Continue and Parity are DISABLED.
*/
dma_ctrlflags(DMA_CTRL_ROC | DMA_CTRL_PEN, 0);
debug("rx burst len 0x%x\n",
(readl(GMAC0_DMA_RX_CTRL_ADDR) & D64_RC_BL_MASK)
>> D64_RC_BL_SHIFT);
debug("tx burst len 0x%x\n",
(readl(GMAC0_DMA_TX_CTRL_ADDR) & D64_XC_BL_MASK)
>> D64_XC_BL_SHIFT);
dma_tx_init(dma);
dma_rx_init(dma);
/* From end of chip_init() */
/* enable the overflow continue feature and disable parity */
dma_ctrlflags(DMA_CTRL_ROC | DMA_CTRL_PEN /* mask */,
DMA_CTRL_ROC /* value */);
return 0;
}
static int dma_deinit(struct eth_dma *dma)
{
debug(" %s enter\n", __func__);
gmac_disable_dma(dma, MAC_DMA_RX);
gmac_disable_dma(dma, MAC_DMA_TX);
free(dma->tx_buf);
dma->tx_buf = NULL;
free(dma->tx_desc);
dma->tx_desc = NULL;
dma->tx_desc_aligned = NULL;
free(dma->rx_buf);
dma->rx_buf = NULL;
free(dma->rx_desc);
dma->rx_desc = NULL;
dma->rx_desc_aligned = NULL;
return 0;
}
int gmac_tx_packet(struct eth_dma *dma, void *packet, int length)
{
uint8_t *bufp = dma->tx_buf + dma->cur_tx_index * TX_BUF_SIZE;
/* kick off the dma */
size_t len = length;
int txout = dma->cur_tx_index;
uint32_t flags;
dma64dd_t *descp = NULL;
uint32_t ctrl;
uint32_t last_desc = (((uint32_t)dma->tx_desc_aligned) +
sizeof(dma64dd_t)) & D64_XP_LD_MASK;
size_t buflen;
debug("%s enter\n", __func__);
/* load the buffer */
memcpy(bufp, packet, len);
/* Add 4 bytes for Ethernet FCS/CRC */
buflen = len + 4;
ctrl = (buflen & D64_CTRL2_BC_MASK);
/* the transmit will only be one frame or set SOF, EOF */
/* also set int on completion */
flags = D64_CTRL1_SOF | D64_CTRL1_IOC | D64_CTRL1_EOF;
/* txout points to the descriptor to uset */
/* if last descriptor then set EOT */
if (txout == (TX_BUF_NUM - 1)) {
flags |= D64_CTRL1_EOT;
last_desc = ((uint32_t)(dma->tx_desc_aligned)) & D64_XP_LD_MASK;
}
/* write the descriptor */
descp = ((dma64dd_t *)(dma->tx_desc_aligned)) + txout;
descp->addrlow = (uint32_t)bufp;
descp->addrhigh = 0;
descp->ctrl1 = flags;
descp->ctrl2 = ctrl;
/* flush descriptor and buffer */
flush_dcache_range((unsigned long)descp,
(unsigned long)(descp + sizeof(dma64dd_t)));
flush_dcache_range((unsigned long)bufp,
(unsigned long)(bufp + TX_BUF_SIZE));
/* now update the dma last descriptor */
writel(last_desc, GMAC0_DMA_TX_PTR_ADDR);
/* tx dma should be enabled so packet should go out */
/* update txout */
dma->cur_tx_index = (txout + 1) & (TX_BUF_NUM - 1);
return 0;
}
bool gmac_check_tx_done(struct eth_dma *dma)
{
/* wait for tx to complete */
uint32_t intstatus;
bool xfrdone = false;
debug("%s enter\n", __func__);
intstatus = readl(GMAC0_INT_STATUS_ADDR);
debug("int(0x%x)\n", intstatus);
if (intstatus & (I_XI0 | I_XI1 | I_XI2 | I_XI3)) {
xfrdone = true;
/* clear the int bits */
intstatus &= ~(I_XI0 | I_XI1 | I_XI2 | I_XI3);
writel(intstatus, GMAC0_INT_STATUS_ADDR);
} else {
debug("Tx int(0x%x)\n", intstatus);
}
return xfrdone;
}
int gmac_check_rx_done(struct eth_dma *dma, uint8_t *buf)
{
void *bufp, *datap;
size_t rcvlen = 0, buflen = 0;
uint32_t stat0 = 0, stat1 = 0;
uint32_t control, offset;
uint8_t statbuf[HWRXOFF*2];
int index, curr, active;
dma64dd_t *descp = NULL;
/* udelay(50); */
/*
* this api will check if a packet has been received.
* If so it will return the address of the buffer and current
* descriptor index will be incremented to the
* next descriptor. Once done with the frame the buffer should be
* added back onto the descriptor and the lastdscr should be updated
* to this descriptor.
*/
index = dma->cur_rx_index;
offset = (uint32_t)(dma->rx_desc_aligned);
stat0 = readl(GMAC0_DMA_RX_STATUS0_ADDR) & D64_RS0_CD_MASK;
stat1 = readl(GMAC0_DMA_RX_STATUS1_ADDR) & D64_RS0_CD_MASK;
curr = ((stat0 - offset) & D64_RS0_CD_MASK) / sizeof(dma64dd_t);
active = ((stat1 - offset) & D64_RS0_CD_MASK) / sizeof(dma64dd_t);
/* check if any frame */
if (index == curr)
return -1;
debug("received packet\n");
debug("expect(0x%x) curr(0x%x) active(0x%x)\n", index, curr, active);
/* remove warning */
if (index == active)
;
/* get the packet pointer that corresponds to the rx descriptor */
bufp = dma->rx_buf + index * RX_BUF_SIZE;
descp = (dma64dd_t *)(dma->rx_desc_aligned) + index;
/* flush descriptor and buffer */
flush_dcache_range((unsigned long)descp,
(unsigned long)(descp + sizeof(dma64dd_t)));
flush_dcache_range((unsigned long)bufp,
(unsigned long)(bufp + RX_BUF_SIZE));
buflen = (descp->ctrl2 & D64_CTRL2_BC_MASK);
stat0 = readl(GMAC0_DMA_RX_STATUS0_ADDR);
stat1 = readl(GMAC0_DMA_RX_STATUS1_ADDR);
debug("bufp(0x%x) index(0x%x) buflen(0x%x) stat0(0x%x) stat1(0x%x)\n",
(uint32_t)bufp, index, buflen, stat0, stat1);
dma->cur_rx_index = (index + 1) & (RX_BUF_NUM - 1);
/* get buffer offset */
control = readl(GMAC0_DMA_RX_CTRL_ADDR);
offset = (control & D64_RC_RO_MASK) >> D64_RC_RO_SHIFT;
rcvlen = *(uint16_t *)bufp;
debug("Received %d bytes\n", rcvlen);
/* copy status into temp buf then copy data from rx buffer */
memcpy(statbuf, bufp, offset);
datap = (void *)((uint32_t)bufp + offset);
memcpy(buf, datap, rcvlen);
/* update descriptor that is being added back on ring */
descp->ctrl2 = RX_BUF_SIZE;
descp->addrlow = (uint32_t)bufp;
descp->addrhigh = 0;
/* flush descriptor */
flush_dcache_range((unsigned long)descp,
(unsigned long)(descp + sizeof(dma64dd_t)));
/* set the lastdscr for the rx ring */
writel(((uint32_t)descp) & D64_XP_LD_MASK, GMAC0_DMA_RX_PTR_ADDR);
return (int)rcvlen;
}
static int gmac_disable_dma(struct eth_dma *dma, int dir)
{
int status;
debug("%s enter\n", __func__);
if (dir == MAC_DMA_TX) {
/* address PR8249/PR7577 issue */
/* suspend tx DMA first */
writel(D64_XC_SE, GMAC0_DMA_TX_CTRL_ADDR);
SPINWAIT(((status = (readl(GMAC0_DMA_TX_STATUS0_ADDR) &
D64_XS0_XS_MASK)) !=
D64_XS0_XS_DISABLED) &&
(status != D64_XS0_XS_IDLE) &&
(status != D64_XS0_XS_STOPPED), 10000);
/*
* PR2414 WAR: DMA engines are not disabled until
* transfer finishes
*/
writel(0, GMAC0_DMA_TX_CTRL_ADDR);
SPINWAIT(((status = (readl(GMAC0_DMA_TX_STATUS0_ADDR) &
D64_XS0_XS_MASK)) !=
D64_XS0_XS_DISABLED), 10000);
/* wait for the last transaction to complete */
udelay(2);
status = (status == D64_XS0_XS_DISABLED);
} else {
/*
* PR2414 WAR: DMA engines are not disabled until
* transfer finishes
*/
writel(0, GMAC0_DMA_RX_CTRL_ADDR);
SPINWAIT(((status = (readl(GMAC0_DMA_RX_STATUS0_ADDR) &
D64_RS0_RS_MASK)) !=
D64_RS0_RS_DISABLED), 10000);
status = (status == D64_RS0_RS_DISABLED);
}
return status;
}
static int gmac_enable_dma(struct eth_dma *dma, int dir)
{
uint32_t control;
debug("%s enter\n", __func__);
if (dir == MAC_DMA_TX) {
dma->cur_tx_index = 0;
/*
* These bits 20:18 (burstLen) of control register can be
* written but will take effect only if these bits are
* valid. So this will not affect previous versions
* of the DMA. They will continue to have those bits set to 0.
*/
control = readl(GMAC0_DMA_TX_CTRL_ADDR);
control |= D64_XC_XE;
if ((g_dmactrlflags & DMA_CTRL_PEN) == 0)
control |= D64_XC_PD;
writel(control, GMAC0_DMA_TX_CTRL_ADDR);
/* initailize the DMA channel */
writel((uint32_t)(dma->tx_desc_aligned),
GMAC0_DMA_TX_ADDR_LOW_ADDR);
writel(0, GMAC0_DMA_TX_ADDR_HIGH_ADDR);
} else {
dma->cur_rx_index = 0;
control = (readl(GMAC0_DMA_RX_CTRL_ADDR) &
D64_RC_AE) | D64_RC_RE;
if ((g_dmactrlflags & DMA_CTRL_PEN) == 0)
control |= D64_RC_PD;
if (g_dmactrlflags & DMA_CTRL_ROC)
control |= D64_RC_OC;
/*
* These bits 20:18 (burstLen) of control register can be
* written but will take effect only if these bits are
* valid. So this will not affect previous versions
* of the DMA. They will continue to have those bits set to 0.
*/
control &= ~D64_RC_BL_MASK;
/* Keep default Rx burstlen */
control |= readl(GMAC0_DMA_RX_CTRL_ADDR) & D64_RC_BL_MASK;
control |= HWRXOFF << D64_RC_RO_SHIFT;
writel(control, GMAC0_DMA_RX_CTRL_ADDR);
/*
* the rx descriptor ring should have
* the addresses set properly;
* set the lastdscr for the rx ring
*/
writel(((uint32_t)(dma->rx_desc_aligned) +
(RX_BUF_NUM - 1) * RX_BUF_SIZE) &
D64_XP_LD_MASK, GMAC0_DMA_RX_PTR_ADDR);
}
return 0;
}
bool gmac_mii_busywait(unsigned int timeout)
{
uint32_t tmp = 0;
while (timeout > 10) {
tmp = readl(GMAC_MII_CTRL_ADDR);
if (tmp & (1 << GMAC_MII_BUSY_SHIFT)) {
udelay(10);
timeout -= 10;
} else {
break;
}
}
return tmp & (1 << GMAC_MII_BUSY_SHIFT);
}
int gmac_miiphy_read(const char *devname, unsigned char phyaddr,
unsigned char reg, unsigned short *value)
{
uint32_t tmp = 0;
(void)devname;
/* Busy wait timeout is 1ms */
if (gmac_mii_busywait(1000)) {
error("%s: Prepare MII read: MII/MDIO busy\n", __func__);
return -1;
}
/* Read operation */
tmp = GMAC_MII_DATA_READ_CMD;
tmp |= (phyaddr << GMAC_MII_PHY_ADDR_SHIFT) |
(reg << GMAC_MII_PHY_REG_SHIFT);
debug("MII read cmd 0x%x, phy 0x%x, reg 0x%x\n", tmp, phyaddr, reg);
writel(tmp, GMAC_MII_DATA_ADDR);
if (gmac_mii_busywait(1000)) {
error("%s: MII read failure: MII/MDIO busy\n", __func__);
return -1;
}
*value = readl(GMAC_MII_DATA_ADDR) & 0xffff;
debug("MII read data 0x%x\n", *value);
return 0;
}
int gmac_miiphy_write(const char *devname, unsigned char phyaddr,
unsigned char reg, unsigned short value)
{
uint32_t tmp = 0;
(void)devname;
/* Busy wait timeout is 1ms */
if (gmac_mii_busywait(1000)) {
error("%s: Prepare MII write: MII/MDIO busy\n", __func__);
return -1;
}
/* Write operation */
tmp = GMAC_MII_DATA_WRITE_CMD | (value & 0xffff);
tmp |= ((phyaddr << GMAC_MII_PHY_ADDR_SHIFT) |
(reg << GMAC_MII_PHY_REG_SHIFT));
debug("MII write cmd 0x%x, phy 0x%x, reg 0x%x, data 0x%x\n",
tmp, phyaddr, reg, value);
writel(tmp, GMAC_MII_DATA_ADDR);
if (gmac_mii_busywait(1000)) {
error("%s: MII write failure: MII/MDIO busy\n", __func__);
return -1;
}
return 0;
}
void gmac_init_reset(void)
{
debug("%s enter\n", __func__);
/* set command config reg CC_SR */
reg32_set_bits(UNIMAC0_CMD_CFG_ADDR, CC_SR);
udelay(GMAC_RESET_DELAY);
}
void gmac_clear_reset(void)
{
debug("%s enter\n", __func__);
/* clear command config reg CC_SR */
reg32_clear_bits(UNIMAC0_CMD_CFG_ADDR, CC_SR);
udelay(GMAC_RESET_DELAY);
}
static void gmac_enable_local(bool en)
{
uint32_t cmdcfg;
debug("%s enter\n", __func__);
/* read command config reg */
cmdcfg = readl(UNIMAC0_CMD_CFG_ADDR);
/* put mac in reset */
gmac_init_reset();
cmdcfg |= CC_SR;
/* first deassert rx_ena and tx_ena while in reset */
cmdcfg &= ~(CC_RE | CC_TE);
/* write command config reg */
writel(cmdcfg, UNIMAC0_CMD_CFG_ADDR);
/* bring mac out of reset */
gmac_clear_reset();
/* if not enable exit now */
if (!en)
return;
/* enable the mac transmit and receive paths now */
udelay(2);
cmdcfg &= ~CC_SR;
cmdcfg |= (CC_RE | CC_TE);
/* assert rx_ena and tx_ena when out of reset to enable the mac */
writel(cmdcfg, UNIMAC0_CMD_CFG_ADDR);
return;
}
int gmac_enable(void)
{
gmac_enable_local(1);
/* clear interrupts */
writel(I_INTMASK, GMAC0_INT_STATUS_ADDR);
return 0;
}
int gmac_disable(void)
{
gmac_enable_local(0);
return 0;
}
int gmac_set_speed(int speed, int duplex)
{
uint32_t cmdcfg;
uint32_t hd_ena;
uint32_t speed_cfg;
hd_ena = duplex ? 0 : CC_HD;
if (speed == 1000) {
speed_cfg = 2;
} else if (speed == 100) {
speed_cfg = 1;
} else if (speed == 10) {
speed_cfg = 0;
} else {
error("%s: Invalid GMAC speed(%d)!\n", __func__, speed);
return -1;
}
cmdcfg = readl(UNIMAC0_CMD_CFG_ADDR);
cmdcfg &= ~(CC_ES_MASK | CC_HD);
cmdcfg |= ((speed_cfg << CC_ES_SHIFT) | hd_ena);
printf("Change GMAC speed to %dMB\n", speed);
debug("GMAC speed cfg 0x%x\n", cmdcfg);
writel(cmdcfg, UNIMAC0_CMD_CFG_ADDR);
return 0;
}
int gmac_set_mac_addr(unsigned char *mac)
{
/* set our local address */
debug("GMAC: %02x:%02x:%02x:%02x:%02x:%02x\n",
mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
writel(htonl(*(uint32_t *)mac), UNIMAC0_MAC_MSB_ADDR);
writew(htons(*(uint32_t *)&mac[4]), UNIMAC0_MAC_LSB_ADDR);
return 0;
}
int gmac_mac_init(struct eth_device *dev)
{
struct eth_info *eth = (struct eth_info *)(dev->priv);
struct eth_dma *dma = &(eth->dma);
uint32_t tmp;
uint32_t cmdcfg;
int chipid;
debug("%s enter\n", __func__);
/* Always use GMAC0 */
printf("Using GMAC%d\n", 0);
/* Reset AMAC0 core */
writel(0, AMAC0_IDM_RESET_ADDR);
tmp = readl(AMAC0_IO_CTRL_DIRECT_ADDR);
/* Set clock */
tmp &= ~(1 << AMAC0_IO_CTRL_CLK_250_SEL_SHIFT);
tmp |= (1 << AMAC0_IO_CTRL_GMII_MODE_SHIFT);
/* Set Tx clock */
tmp &= ~(1 << AMAC0_IO_CTRL_DEST_SYNC_MODE_EN_SHIFT);
writel(tmp, AMAC0_IO_CTRL_DIRECT_ADDR);
/* reset gmac */
/*
* As AMAC is just reset, NO need?
* set eth_data into loopback mode to ensure no rx traffic
* gmac_loopback(eth_data, TRUE);
* ET_TRACE(("%s gmac loopback\n", __func__));
* udelay(1);
*/
cmdcfg = readl(UNIMAC0_CMD_CFG_ADDR);
cmdcfg &= ~(CC_TE | CC_RE | CC_RPI | CC_TAI | CC_HD | CC_ML |
CC_CFE | CC_RL | CC_RED | CC_PE | CC_TPI |
CC_PAD_EN | CC_PF);
cmdcfg |= (CC_PROM | CC_NLC | CC_CFE);
/* put mac in reset */
gmac_init_reset();
writel(cmdcfg, UNIMAC0_CMD_CFG_ADDR);
gmac_clear_reset();
/* enable clear MIB on read */
reg32_set_bits(GMAC0_DEV_CTRL_ADDR, DC_MROR);
/* PHY: set smi_master to drive mdc_clk */
reg32_set_bits(GMAC0_PHY_CTRL_ADDR, PC_MTE);
/* clear persistent sw intstatus */
writel(0, GMAC0_INT_STATUS_ADDR);
if (dma_init(dma) < 0) {
error("%s: GMAC dma_init failed\n", __func__);
goto err_exit;
}
chipid = CHIPID;
printf("%s: Chip ID: 0x%x\n", __func__, chipid);
/* set switch bypass mode */
tmp = readl(SWITCH_GLOBAL_CONFIG_ADDR);
tmp |= (1 << CDRU_SWITCH_BYPASS_SWITCH_SHIFT);
/* Switch mode */
/* tmp &= ~(1 << CDRU_SWITCH_BYPASS_SWITCH_SHIFT); */
writel(tmp, SWITCH_GLOBAL_CONFIG_ADDR);
tmp = readl(CRMU_CHIP_IO_PAD_CONTROL_ADDR);
tmp &= ~(1 << CDRU_IOMUX_FORCE_PAD_IN_SHIFT);
writel(tmp, CRMU_CHIP_IO_PAD_CONTROL_ADDR);
/* Set MDIO to internal GPHY */
tmp = readl(GMAC_MII_CTRL_ADDR);
/* Select internal MDC/MDIO bus*/
tmp &= ~(1 << GMAC_MII_CTRL_BYP_SHIFT);
/* select MDC/MDIO connecting to on-chip internal PHYs */
tmp &= ~(1 << GMAC_MII_CTRL_EXT_SHIFT);
/*
* give bit[6:0](MDCDIV) with required divisor to set
* the MDC clock frequency, 66MHZ/0x1A=2.5MHZ
*/
tmp |= 0x1A;
writel(tmp, GMAC_MII_CTRL_ADDR);
if (gmac_mii_busywait(1000)) {
error("%s: Configure MDIO: MII/MDIO busy\n", __func__);
goto err_exit;
}
/* Configure GMAC0 */
/* enable one rx interrupt per received frame */
writel(1 << GMAC0_IRL_FRAMECOUNT_SHIFT, GMAC0_INTR_RECV_LAZY_ADDR);
/* read command config reg */
cmdcfg = readl(UNIMAC0_CMD_CFG_ADDR);
/* enable 802.3x tx flow control (honor received PAUSE frames) */
cmdcfg &= ~CC_RPI;
/* enable promiscuous mode */
cmdcfg |= CC_PROM;
/* Disable loopback mode */
cmdcfg &= ~CC_ML;
/* set the speed */
cmdcfg &= ~(CC_ES_MASK | CC_HD);
/* Set to 1Gbps and full duplex by default */
cmdcfg |= (2 << CC_ES_SHIFT);
/* put mac in reset */
gmac_init_reset();
/* write register */
writel(cmdcfg, UNIMAC0_CMD_CFG_ADDR);
/* bring mac out of reset */
gmac_clear_reset();
/* set max frame lengths; account for possible vlan tag */
writel(PKTSIZE + 32, UNIMAC0_FRM_LENGTH_ADDR);
return 0;
err_exit:
dma_deinit(dma);
return -1;
}
int gmac_add(struct eth_device *dev)
{
struct eth_info *eth = (struct eth_info *)(dev->priv);
struct eth_dma *dma = &(eth->dma);
void *tmp;
/*
* Desc has to be 16-byte aligned ?
* If it is 8-byte aligned by malloc, fail Tx
*/
tmp = malloc(sizeof(dma64dd_t) * TX_BUF_NUM + 8);
if (tmp == NULL) {
printf("%s: Failed to allocate TX desc Buffer\n", __func__);
return -1;
}
dma->tx_desc = (void *)tmp;
dma->tx_desc_aligned = (void *)(((uint32_t)tmp) & (~0xf));
debug("TX Descriptor Buffer: %p; length: 0x%x\n",
dma->tx_desc_aligned, sizeof(dma64dd_t) * TX_BUF_NUM);
tmp = malloc(TX_BUF_SIZE * TX_BUF_NUM);
if (tmp == NULL) {
printf("%s: Failed to allocate TX Data Buffer\n", __func__);
free(dma->tx_desc);
return -1;
}
dma->tx_buf = (uint8_t *)tmp;
debug("TX Data Buffer: %p; length: 0x%x\n",
dma->tx_buf, TX_BUF_SIZE * TX_BUF_NUM);
/* Desc has to be 16-byte aligned ? */
tmp = malloc(sizeof(dma64dd_t) * RX_BUF_NUM + 8);
if (tmp == NULL) {
printf("%s: Failed to allocate RX Descriptor\n", __func__);
free(dma->tx_desc);
free(dma->tx_buf);
return -1;
}
dma->rx_desc = tmp;
dma->rx_desc_aligned = (void *)(((uint32_t)tmp) & (~0xf));
debug("RX Descriptor Buffer: %p, length: 0x%x\n",
dma->rx_desc_aligned, sizeof(dma64dd_t) * RX_BUF_NUM);
tmp = malloc(RX_BUF_SIZE * RX_BUF_NUM);
if (tmp == NULL) {
printf("%s: Failed to allocate RX Data Buffer\n", __func__);
free(dma->tx_desc);
free(dma->tx_buf);
free(dma->rx_desc);
return -1;
}
dma->rx_buf = tmp;
debug("RX Data Buffer: %p; length: 0x%x\n",
dma->rx_buf, RX_BUF_SIZE * RX_BUF_NUM);
g_dmactrlflags = 0;
eth->phy_interface = PHY_INTERFACE_MODE_GMII;
dma->tx_packet = gmac_tx_packet;
dma->check_tx_done = gmac_check_tx_done;
dma->check_rx_done = gmac_check_rx_done;
dma->enable_dma = gmac_enable_dma;
dma->disable_dma = gmac_disable_dma;
eth->miiphy_read = gmac_miiphy_read;
eth->miiphy_write = gmac_miiphy_write;
eth->mac_init = gmac_mac_init;
eth->disable_mac = gmac_disable;
eth->enable_mac = gmac_enable;
eth->set_mac_addr = gmac_set_mac_addr;
eth->set_mac_speed = gmac_set_speed;
return 0;
}

View file

@ -0,0 +1,224 @@
/*
* Copyright 2014 Broadcom Corporation.
*
* SPDX-License-Identifier: GPL-2.0+
*/
#ifndef _BCM_SF2_ETH_GMAC_H_
#define _BCM_SF2_ETH_GMAC_H_
#define BCM_SF2_ETH_MAC_NAME "gmac"
#ifndef ETHHW_PORT_INT
#define ETHHW_PORT_INT 8
#endif
#define GMAC0_REG_BASE 0x18042000
#define GMAC0_DEV_CTRL_ADDR GMAC0_REG_BASE
#define GMAC0_INT_STATUS_ADDR (GMAC0_REG_BASE + 0x020)
#define GMAC0_INTR_RECV_LAZY_ADDR (GMAC0_REG_BASE + 0x100)
#define GMAC0_PHY_CTRL_ADDR (GMAC0_REG_BASE + 0x188)
#define GMAC_DMA_PTR_OFFSET 0x04
#define GMAC_DMA_ADDR_LOW_OFFSET 0x08
#define GMAC_DMA_ADDR_HIGH_OFFSET 0x0c
#define GMAC_DMA_STATUS0_OFFSET 0x10
#define GMAC_DMA_STATUS1_OFFSET 0x14
#define GMAC0_DMA_TX_CTRL_ADDR (GMAC0_REG_BASE + 0x200)
#define GMAC0_DMA_TX_PTR_ADDR \
(GMAC0_DMA_TX_CTRL_ADDR + GMAC_DMA_PTR_OFFSET)
#define GMAC0_DMA_TX_ADDR_LOW_ADDR \
(GMAC0_DMA_TX_CTRL_ADDR + GMAC_DMA_ADDR_LOW_OFFSET)
#define GMAC0_DMA_TX_ADDR_HIGH_ADDR \
(GMAC0_DMA_TX_CTRL_ADDR + GMAC_DMA_ADDR_HIGH_OFFSET)
#define GMAC0_DMA_TX_STATUS0_ADDR \
(GMAC0_DMA_TX_CTRL_ADDR + GMAC_DMA_STATUS0_OFFSET)
#define GMAC0_DMA_TX_STATUS1_ADDR \
(GMAC0_DMA_TX_CTRL_ADDR + GMAC_DMA_STATUS1_OFFSET)
#define GMAC0_DMA_RX_CTRL_ADDR (GMAC0_REG_BASE + 0x220)
#define GMAC0_DMA_RX_PTR_ADDR \
(GMAC0_DMA_RX_CTRL_ADDR + GMAC_DMA_PTR_OFFSET)
#define GMAC0_DMA_RX_ADDR_LOW_ADDR \
(GMAC0_DMA_RX_CTRL_ADDR + GMAC_DMA_ADDR_LOW_OFFSET)
#define GMAC0_DMA_RX_ADDR_HIGH_ADDR \
(GMAC0_DMA_RX_CTRL_ADDR + GMAC_DMA_ADDR_HIGH_OFFSET)
#define GMAC0_DMA_RX_STATUS0_ADDR \
(GMAC0_DMA_RX_CTRL_ADDR + GMAC_DMA_STATUS0_OFFSET)
#define GMAC0_DMA_RX_STATUS1_ADDR \
(GMAC0_DMA_RX_CTRL_ADDR + GMAC_DMA_STATUS1_OFFSET)
#define UNIMAC0_CMD_CFG_ADDR (GMAC0_REG_BASE + 0x808)
#define UNIMAC0_MAC_MSB_ADDR (GMAC0_REG_BASE + 0x80c)
#define UNIMAC0_MAC_LSB_ADDR (GMAC0_REG_BASE + 0x810)
#define UNIMAC0_FRM_LENGTH_ADDR (GMAC0_REG_BASE + 0x814)
#define GMAC0_IRL_FRAMECOUNT_SHIFT 24
/* transmit channel control */
/* transmit enable */
#define D64_XC_XE 0x00000001
/* transmit suspend request */
#define D64_XC_SE 0x00000002
/* parity check disable */
#define D64_XC_PD 0x00000800
/* BurstLen bits */
#define D64_XC_BL_MASK 0x001C0000
#define D64_XC_BL_SHIFT 18
/* transmit descriptor table pointer */
/* last valid descriptor */
#define D64_XP_LD_MASK 0x00001fff
/* transmit channel status */
/* transmit state */
#define D64_XS0_XS_MASK 0xf0000000
#define D64_XS0_XS_SHIFT 28
#define D64_XS0_XS_DISABLED 0x00000000
#define D64_XS0_XS_ACTIVE 0x10000000
#define D64_XS0_XS_IDLE 0x20000000
#define D64_XS0_XS_STOPPED 0x30000000
#define D64_XS0_XS_SUSP 0x40000000
/* receive channel control */
/* receive enable */
#define D64_RC_RE 0x00000001
/* address extension bits */
#define D64_RC_AE 0x00030000
/* overflow continue */
#define D64_RC_OC 0x00000400
/* parity check disable */
#define D64_RC_PD 0x00000800
/* receive frame offset */
#define D64_RC_RO_MASK 0x000000fe
#define D64_RC_RO_SHIFT 1
/* BurstLen bits */
#define D64_RC_BL_MASK 0x001C0000
#define D64_RC_BL_SHIFT 18
/* flags for dma controller */
/* partity enable */
#define DMA_CTRL_PEN (1 << 0)
/* rx overflow continue */
#define DMA_CTRL_ROC (1 << 1)
/* receive descriptor table pointer */
/* last valid descriptor */
#define D64_RP_LD_MASK 0x00001fff
/* receive channel status */
/* current descriptor pointer */
#define D64_RS0_CD_MASK 0x00001fff
/* receive state */
#define D64_RS0_RS_MASK 0xf0000000
#define D64_RS0_RS_SHIFT 28
#define D64_RS0_RS_DISABLED 0x00000000
#define D64_RS0_RS_ACTIVE 0x10000000
#define D64_RS0_RS_IDLE 0x20000000
#define D64_RS0_RS_STOPPED 0x30000000
#define D64_RS0_RS_SUSP 0x40000000
/* descriptor control flags 1 */
/* core specific flags */
#define D64_CTRL_COREFLAGS 0x0ff00000
/* end of descriptor table */
#define D64_CTRL1_EOT ((uint32_t)1 << 28)
/* interrupt on completion */
#define D64_CTRL1_IOC ((uint32_t)1 << 29)
/* end of frame */
#define D64_CTRL1_EOF ((uint32_t)1 << 30)
/* start of frame */
#define D64_CTRL1_SOF ((uint32_t)1 << 31)
/* descriptor control flags 2 */
/* buffer byte count. real data len must <= 16KB */
#define D64_CTRL2_BC_MASK 0x00007fff
/* address extension bits */
#define D64_CTRL2_AE 0x00030000
#define D64_CTRL2_AE_SHIFT 16
/* parity bit */
#define D64_CTRL2_PARITY 0x00040000
/* control flags in the range [27:20] are core-specific and not defined here */
#define D64_CTRL_CORE_MASK 0x0ff00000
#define DC_MROR 0x00000010
#define PC_MTE 0x00800000
/* command config */
#define CC_TE 0x00000001
#define CC_RE 0x00000002
#define CC_ES_MASK 0x0000000c
#define CC_ES_SHIFT 2
#define CC_PROM 0x00000010
#define CC_PAD_EN 0x00000020
#define CC_CF 0x00000040
#define CC_PF 0x00000080
#define CC_RPI 0x00000100
#define CC_TAI 0x00000200
#define CC_HD 0x00000400
#define CC_HD_SHIFT 10
#define CC_SR 0x00002000
#define CC_ML 0x00008000
#define CC_AE 0x00400000
#define CC_CFE 0x00800000
#define CC_NLC 0x01000000
#define CC_RL 0x02000000
#define CC_RED 0x04000000
#define CC_PE 0x08000000
#define CC_TPI 0x10000000
#define CC_AT 0x20000000
#define I_PDEE 0x00000400
#define I_PDE 0x00000800
#define I_DE 0x00001000
#define I_RDU 0x00002000
#define I_RFO 0x00004000
#define I_XFU 0x00008000
#define I_RI 0x00010000
#define I_XI0 0x01000000
#define I_XI1 0x02000000
#define I_XI2 0x04000000
#define I_XI3 0x08000000
#define I_ERRORS (I_PDEE | I_PDE | I_DE | I_RDU | I_RFO | I_XFU)
#define DEF_INTMASK (I_XI0 | I_XI1 | I_XI2 | I_XI3 | I_RI | I_ERRORS)
#define I_INTMASK 0x0f01fcff
#define CHIP_DRU_BASE 0x0301d000
#define CRMU_CHIP_IO_PAD_CONTROL_ADDR (CHIP_DRU_BASE + 0x0bc)
#define SWITCH_GLOBAL_CONFIG_ADDR (CHIP_DRU_BASE + 0x194)
#define CDRU_IOMUX_FORCE_PAD_IN_SHIFT 0
#define CDRU_SWITCH_BYPASS_SWITCH_SHIFT 13
#define AMAC0_IDM_RESET_ADDR 0x18110800
#define AMAC0_IO_CTRL_DIRECT_ADDR 0x18110408
#define AMAC0_IO_CTRL_CLK_250_SEL_SHIFT 6
#define AMAC0_IO_CTRL_GMII_MODE_SHIFT 5
#define AMAC0_IO_CTRL_DEST_SYNC_MODE_EN_SHIFT 3
#define CHIPA_CHIP_ID_ADDR 0x18000000
#define CHIPID (readl(CHIPA_CHIP_ID_ADDR) & 0xFFFF)
#define CHIPREV (((readl(CHIPA_CHIP_ID_ADDR) >> 16) & 0xF)
#define CHIPSKU (((readl(CHIPA_CHIP_ID_ADDR) >> 20) & 0xF)
#define GMAC_MII_CTRL_ADDR 0x18002000
#define GMAC_MII_CTRL_BYP_SHIFT 10
#define GMAC_MII_CTRL_EXT_SHIFT 9
#define GMAC_MII_DATA_ADDR 0x18002004
#define GMAC_MII_DATA_READ_CMD 0x60020000
#define GMAC_MII_DATA_WRITE_CMD 0x50020000
#define GMAC_MII_BUSY_SHIFT 8
#define GMAC_MII_PHY_ADDR_SHIFT 23
#define GMAC_MII_PHY_REG_SHIFT 18
#define GMAC_RESET_DELAY 2
#define HWRXOFF 30
#define MAXNAMEL 8
#define NUMTXQ 4
int gmac_add(struct eth_device *dev);
#endif /* _BCM_SF2_ETH_GMAC_H_ */

View file

@ -0,0 +1,262 @@
/*
* Copyright 2014 Broadcom Corporation.
*
* SPDX-License-Identifier: GPL-2.0+
*/
#include <common.h>
#include <malloc.h>
#include <net.h>
#include <config.h>
#include <phy.h>
#include <miiphy.h>
#include <asm/io.h>
#include <netdev.h>
#include "bcm-sf2-eth.h"
#if defined(CONFIG_BCM_SF2_ETH_GMAC)
#include "bcm-sf2-eth-gmac.h"
#else
#error "bcm_sf2_eth: NEED to define a MAC!"
#endif
#define BCM_NET_MODULE_DESCRIPTION "Broadcom Starfighter2 Ethernet driver"
#define BCM_NET_MODULE_VERSION "0.1"
#define BCM_SF2_ETH_DEV_NAME "bcm_sf2"
static const char banner[] =
BCM_NET_MODULE_DESCRIPTION " " BCM_NET_MODULE_VERSION "\n";
static int bcm_sf2_eth_init(struct eth_device *dev)
{
struct eth_info *eth = (struct eth_info *)(dev->priv);
struct eth_dma *dma = &(eth->dma);
struct phy_device *phydev;
int rc = 0;
int i;
rc = eth->mac_init(dev);
if (rc) {
error("%s: Couldn't cofigure MAC!\n", __func__);
return rc;
}
/* disable DMA */
dma->disable_dma(dma, MAC_DMA_RX);
dma->disable_dma(dma, MAC_DMA_TX);
eth->port_num = 0;
debug("Connecting PHY 0...\n");
phydev = phy_connect(miiphy_get_dev_by_name(dev->name),
0, dev, eth->phy_interface);
if (phydev != NULL) {
eth->port[0] = phydev;
eth->port_num += 1;
} else {
debug("No PHY found for port 0\n");
}
for (i = 0; i < eth->port_num; i++)
phy_config(eth->port[i]);
return rc;
}
/*
* u-boot net functions
*/
static int bcm_sf2_eth_send(struct eth_device *dev, void *packet, int length)
{
struct eth_dma *dma = &(((struct eth_info *)(dev->priv))->dma);
uint8_t *buf = (uint8_t *)packet;
int rc = 0;
int i = 0;
debug("%s enter\n", __func__);
/* load buf and start transmit */
rc = dma->tx_packet(dma, buf, length);
if (rc) {
debug("ERROR - Tx failed\n");
return rc;
}
while (!(dma->check_tx_done(dma))) {
udelay(100);
debug(".");
i++;
if (i > 20) {
error("%s: Tx timeout: retried 20 times\n", __func__);
rc = -1;
break;
}
}
debug("%s exit rc(0x%x)\n", __func__, rc);
return rc;
}
static int bcm_sf2_eth_receive(struct eth_device *dev)
{
struct eth_dma *dma = &(((struct eth_info *)(dev->priv))->dma);
uint8_t *buf = (uint8_t *)net_rx_packets[0];
int rcvlen;
int rc = 0;
int i = 0;
while (1) {
/* Poll Rx queue to get a packet */
rcvlen = dma->check_rx_done(dma, buf);
if (rcvlen < 0) {
/* No packet received */
rc = -1;
debug("\nNO More Rx\n");
break;
} else if ((rcvlen == 0) || (rcvlen > RX_BUF_SIZE)) {
error("%s: Wrong Ethernet packet size (%d B), skip!\n",
__func__, rcvlen);
break;
} else {
debug("recieved\n");
/* Forward received packet to uboot network handler */
net_process_received_packet(buf, rcvlen);
if (++i >= PKTBUFSRX)
i = 0;
buf = net_rx_packets[i];
}
}
return rc;
}
static int bcm_sf2_eth_write_hwaddr(struct eth_device *dev)
{
struct eth_info *eth = (struct eth_info *)(dev->priv);
printf(" ETH MAC: %02x:%02x:%02x:%02x:%02x:%02x\n",
dev->enetaddr[0], dev->enetaddr[1], dev->enetaddr[2],
dev->enetaddr[3], dev->enetaddr[4], dev->enetaddr[5]);
return eth->set_mac_addr(dev->enetaddr);
}
static int bcm_sf2_eth_open(struct eth_device *dev, bd_t *bt)
{
struct eth_info *eth = (struct eth_info *)(dev->priv);
struct eth_dma *dma = &(eth->dma);
int i;
debug("Enabling BCM SF2 Ethernet.\n");
eth->enable_mac();
/* enable tx and rx DMA */
dma->enable_dma(dma, MAC_DMA_RX);
dma->enable_dma(dma, MAC_DMA_TX);
/*
* Need to start PHY here because link speed can change
* before each ethernet operation
*/
for (i = 0; i < eth->port_num; i++) {
if (phy_startup(eth->port[i])) {
error("%s: PHY %d startup failed!\n", __func__, i);
if (i == CONFIG_BCM_SF2_ETH_DEFAULT_PORT) {
error("%s: No default port %d!\n", __func__, i);
return -1;
}
}
}
/* Set MAC speed using default port */
i = CONFIG_BCM_SF2_ETH_DEFAULT_PORT;
debug("PHY %d: speed:%d, duplex:%d, link:%d\n", i,
eth->port[i]->speed, eth->port[i]->duplex, eth->port[i]->link);
eth->set_mac_speed(eth->port[i]->speed, eth->port[i]->duplex);
debug("Enable Ethernet Done.\n");
return 0;
}
static void bcm_sf2_eth_close(struct eth_device *dev)
{
struct eth_info *eth = (struct eth_info *)(dev->priv);
struct eth_dma *dma = &(eth->dma);
/* disable DMA */
dma->disable_dma(dma, MAC_DMA_RX);
dma->disable_dma(dma, MAC_DMA_TX);
eth->disable_mac();
}
int bcm_sf2_eth_register(bd_t *bis, u8 dev_num)
{
struct eth_device *dev;
struct eth_info *eth;
int rc;
dev = (struct eth_device *)malloc(sizeof(struct eth_device));
if (dev == NULL) {
error("%s: Not enough memory!\n", __func__);
return -1;
}
eth = (struct eth_info *)malloc(sizeof(struct eth_info));
if (eth == NULL) {
error("%s: Not enough memory!\n", __func__);
return -1;
}
printf(banner);
memset(dev, 0, sizeof(*dev));
sprintf(dev->name, "%s_%s-%hu", BCM_SF2_ETH_DEV_NAME,
BCM_SF2_ETH_MAC_NAME, dev_num);
dev->priv = (void *)eth;
dev->iobase = 0;
dev->init = bcm_sf2_eth_open;
dev->halt = bcm_sf2_eth_close;
dev->send = bcm_sf2_eth_send;
dev->recv = bcm_sf2_eth_receive;
dev->write_hwaddr = bcm_sf2_eth_write_hwaddr;
#ifdef CONFIG_BCM_SF2_ETH_GMAC
if (gmac_add(dev)) {
free(eth);
free(dev);
error("%s: Adding GMAC failed!\n", __func__);
return -1;
}
#else
#error "bcm_sf2_eth: NEED to register a MAC!"
#endif
eth_register(dev);
#ifdef CONFIG_CMD_MII
miiphy_register(dev->name, eth->miiphy_read, eth->miiphy_write);
#endif
/* Initialization */
debug("Ethernet initialization ...");
rc = bcm_sf2_eth_init(dev);
if (rc != 0) {
error("%s: configuration failed!\n", __func__);
return -1;
}
printf("Basic ethernet functionality initialized\n");
return 0;
}

View file

@ -0,0 +1,70 @@
/*
* Copyright 2014 Broadcom Corporation.
*
* SPDX-License-Identifier: GPL-2.0+
*/
#ifndef _BCM_SF2_ETH_H_
#define _BCM_SF2_ETH_H_
#include <phy.h>
#define RX_BUF_SIZE 2048
/* RX_BUF_NUM must be power of 2 */
#define RX_BUF_NUM 32
#define TX_BUF_SIZE 2048
/* TX_BUF_NUM must be power of 2 */
#define TX_BUF_NUM 2
/* Support 2 Ethernet ports now */
#define BCM_ETH_MAX_PORT_NUM 2
#define CONFIG_BCM_SF2_ETH_DEFAULT_PORT 0
enum {
MAC_DMA_TX = 1,
MAC_DMA_RX = 2
};
struct eth_dma {
void *tx_desc_aligned;
void *rx_desc_aligned;
void *tx_desc;
void *rx_desc;
uint8_t *tx_buf;
uint8_t *rx_buf;
int cur_tx_index;
int cur_rx_index;
int (*tx_packet)(struct eth_dma *dma, void *packet, int length);
bool (*check_tx_done)(struct eth_dma *dma);
int (*check_rx_done)(struct eth_dma *dma, uint8_t *buf);
int (*enable_dma)(struct eth_dma *dma, int dir);
int (*disable_dma)(struct eth_dma *dma, int dir);
};
struct eth_info {
struct eth_dma dma;
phy_interface_t phy_interface;
struct phy_device *port[BCM_ETH_MAX_PORT_NUM];
int port_num;
int (*miiphy_read)(const char *devname, unsigned char phyaddr,
unsigned char reg, unsigned short *value);
int (*miiphy_write)(const char *devname, unsigned char phyaddr,
unsigned char reg, unsigned short value);
int (*mac_init)(struct eth_device *dev);
int (*enable_mac)(void);
int (*disable_mac)(void);
int (*set_mac_addr)(unsigned char *mac);
int (*set_mac_speed)(int speed, int duplex);
};
#endif /* _BCM_SF2_ETH_H_ */

View file

@ -0,0 +1,498 @@
/*
* Driver for Blackfin On-Chip MAC device
*
* Copyright (c) 2005-2008 Analog Device, Inc.
*
* Licensed under the GPL-2 or later.
*/
#include <common.h>
#include <config.h>
#include <net.h>
#include <netdev.h>
#include <command.h>
#include <malloc.h>
#include <miiphy.h>
#include <linux/mii.h>
#include <asm/blackfin.h>
#include <asm/clock.h>
#include <asm/portmux.h>
#include <asm/mach-common/bits/dma.h>
#include <asm/mach-common/bits/emac.h>
#include <asm/mach-common/bits/pll.h>
#include "bfin_mac.h"
#ifndef CONFIG_PHY_ADDR
# define CONFIG_PHY_ADDR 1
#endif
#ifndef CONFIG_PHY_CLOCK_FREQ
# define CONFIG_PHY_CLOCK_FREQ 2500000
#endif
#ifdef CONFIG_POST
#include <post.h>
#endif
#define RXBUF_BASE_ADDR 0xFF900000
#define TXBUF_BASE_ADDR 0xFF800000
#define TX_BUF_CNT 1
#define TOUT_LOOP 1000000
static ADI_ETHER_BUFFER *txbuf[TX_BUF_CNT];
static ADI_ETHER_BUFFER *rxbuf[PKTBUFSRX];
static u16 txIdx; /* index of the current RX buffer */
static u16 rxIdx; /* index of the current TX buffer */
/* DMAx_CONFIG values at DMA Restart */
static const union {
u16 data;
ADI_DMA_CONFIG_REG reg;
} txdmacfg = {
.reg = {
.b_DMA_EN = 1, /* enabled */
.b_WNR = 0, /* read from memory */
.b_WDSIZE = 2, /* wordsize is 32 bits */
.b_DMA2D = 0,
.b_RESTART = 0,
.b_DI_SEL = 0,
.b_DI_EN = 0, /* no interrupt */
.b_NDSIZE = 5, /* 5 half words is desc size */
.b_FLOW = 7 /* large desc flow */
},
};
static int bfin_miiphy_wait(void)
{
/* poll the STABUSY bit */
while (bfin_read_EMAC_STAADD() & STABUSY)
continue;
return 0;
}
static int bfin_miiphy_read(const char *devname, uchar addr, uchar reg, ushort *val)
{
if (bfin_miiphy_wait())
return 1;
bfin_write_EMAC_STAADD(SET_PHYAD(addr) | SET_REGAD(reg) | STABUSY);
if (bfin_miiphy_wait())
return 1;
*val = bfin_read_EMAC_STADAT();
return 0;
}
static int bfin_miiphy_write(const char *devname, uchar addr, uchar reg, ushort val)
{
if (bfin_miiphy_wait())
return 1;
bfin_write_EMAC_STADAT(val);
bfin_write_EMAC_STAADD(SET_PHYAD(addr) | SET_REGAD(reg) | STAOP | STABUSY);
return 0;
}
int bfin_EMAC_initialize(bd_t *bis)
{
struct eth_device *dev;
dev = malloc(sizeof(*dev));
if (dev == NULL)
hang();
memset(dev, 0, sizeof(*dev));
strcpy(dev->name, "bfin_mac");
dev->iobase = 0;
dev->priv = 0;
dev->init = bfin_EMAC_init;
dev->halt = bfin_EMAC_halt;
dev->send = bfin_EMAC_send;
dev->recv = bfin_EMAC_recv;
dev->write_hwaddr = bfin_EMAC_setup_addr;
eth_register(dev);
#if defined(CONFIG_MII) || defined(CONFIG_CMD_MII)
miiphy_register(dev->name, bfin_miiphy_read, bfin_miiphy_write);
#endif
return 0;
}
static int bfin_EMAC_send(struct eth_device *dev, void *packet, int length)
{
int i;
int result = 0;
if (length <= 0) {
printf("Ethernet: bad packet size: %d\n", length);
goto out;
}
if (bfin_read_DMA2_IRQ_STATUS() & DMA_ERR) {
printf("Ethernet: tx DMA error\n");
goto out;
}
for (i = 0; (bfin_read_DMA2_IRQ_STATUS() & DMA_RUN); ++i) {
if (i > TOUT_LOOP) {
puts("Ethernet: tx time out\n");
goto out;
}
}
txbuf[txIdx]->FrmData->NoBytes = length;
memcpy(txbuf[txIdx]->FrmData->Dest, (void *)packet, length);
txbuf[txIdx]->Dma[0].START_ADDR = (u32) txbuf[txIdx]->FrmData;
bfin_write_DMA2_NEXT_DESC_PTR(txbuf[txIdx]->Dma);
bfin_write_DMA2_CONFIG(txdmacfg.data);
bfin_write_EMAC_OPMODE(bfin_read_EMAC_OPMODE() | TE);
for (i = 0; (txbuf[txIdx]->StatusWord & TX_COMP) == 0; i++) {
if (i > TOUT_LOOP) {
puts("Ethernet: tx error\n");
goto out;
}
}
result = txbuf[txIdx]->StatusWord;
txbuf[txIdx]->StatusWord = 0;
if ((txIdx + 1) >= TX_BUF_CNT)
txIdx = 0;
else
txIdx++;
out:
debug("BFIN EMAC send: length = %d\n", length);
return result;
}
static int bfin_EMAC_recv(struct eth_device *dev)
{
int length = 0;
for (;;) {
if ((rxbuf[rxIdx]->StatusWord & RX_COMP) == 0) {
length = -1;
break;
}
if ((rxbuf[rxIdx]->StatusWord & RX_DMAO) != 0) {
printf("Ethernet: rx dma overrun\n");
break;
}
if ((rxbuf[rxIdx]->StatusWord & RX_OK) == 0) {
printf("Ethernet: rx error\n");
break;
}
length = rxbuf[rxIdx]->StatusWord & 0x000007FF;
if (length <= 4) {
printf("Ethernet: bad frame\n");
break;
}
debug("%s: len = %d\n", __func__, length - 4);
net_rx_packets[rxIdx] = rxbuf[rxIdx]->FrmData->Dest;
net_process_received_packet(net_rx_packets[rxIdx], length - 4);
bfin_write_DMA1_IRQ_STATUS(DMA_DONE | DMA_ERR);
rxbuf[rxIdx]->StatusWord = 0x00000000;
if ((rxIdx + 1) >= PKTBUFSRX)
rxIdx = 0;
else
rxIdx++;
}
return length;
}
/**************************************************************
*
* Ethernet Initialization Routine
*
*************************************************************/
/* MDC = SCLK / MDC_freq / 2 - 1 */
#define MDC_FREQ_TO_DIV(mdc_freq) (get_sclk() / (mdc_freq) / 2 - 1)
#ifndef CONFIG_BFIN_MAC_PINS
# ifdef CONFIG_RMII
# define CONFIG_BFIN_MAC_PINS P_RMII0
# else
# define CONFIG_BFIN_MAC_PINS P_MII0
# endif
#endif
static int bfin_miiphy_init(struct eth_device *dev, int *opmode)
{
const unsigned short pins[] = CONFIG_BFIN_MAC_PINS;
u16 phydat;
size_t count;
/* Enable PHY output */
bfin_write_VR_CTL(bfin_read_VR_CTL() | CLKBUFOE);
/* Set all the pins to peripheral mode */
peripheral_request_list(pins, "bfin_mac");
/* Odd word alignment for Receive Frame DMA word */
/* Configure checksum support and rcve frame word alignment */
bfin_write_EMAC_SYSCTL(RXDWA | RXCKS | SET_MDCDIV(MDC_FREQ_TO_DIV(CONFIG_PHY_CLOCK_FREQ)));
/* turn on auto-negotiation and wait for link to come up */
bfin_miiphy_write(dev->name, CONFIG_PHY_ADDR, MII_BMCR, BMCR_ANENABLE);
count = 0;
while (1) {
++count;
if (bfin_miiphy_read(dev->name, CONFIG_PHY_ADDR, MII_BMSR, &phydat))
return -1;
if (phydat & BMSR_LSTATUS)
break;
if (count > 30000) {
printf("%s: link down, check cable\n", dev->name);
return -1;
}
udelay(100);
}
/* see what kind of link we have */
if (bfin_miiphy_read(dev->name, CONFIG_PHY_ADDR, MII_LPA, &phydat))
return -1;
if (phydat & LPA_DUPLEX)
*opmode = FDMODE;
else
*opmode = 0;
bfin_write_EMAC_MMC_CTL(RSTC | CROLL);
bfin_write_EMAC_VLAN1(EMAC_VLANX_DEF_VAL);
bfin_write_EMAC_VLAN2(EMAC_VLANX_DEF_VAL);
/* Initialize the TX DMA channel registers */
bfin_write_DMA2_X_COUNT(0);
bfin_write_DMA2_X_MODIFY(4);
bfin_write_DMA2_Y_COUNT(0);
bfin_write_DMA2_Y_MODIFY(0);
/* Initialize the RX DMA channel registers */
bfin_write_DMA1_X_COUNT(0);
bfin_write_DMA1_X_MODIFY(4);
bfin_write_DMA1_Y_COUNT(0);
bfin_write_DMA1_Y_MODIFY(0);
return 0;
}
static int bfin_EMAC_setup_addr(struct eth_device *dev)
{
bfin_write_EMAC_ADDRLO(
dev->enetaddr[0] |
dev->enetaddr[1] << 8 |
dev->enetaddr[2] << 16 |
dev->enetaddr[3] << 24
);
bfin_write_EMAC_ADDRHI(
dev->enetaddr[4] |
dev->enetaddr[5] << 8
);
return 0;
}
static int bfin_EMAC_init(struct eth_device *dev, bd_t *bd)
{
u32 opmode;
int dat;
int i;
debug("Eth_init: ......\n");
txIdx = 0;
rxIdx = 0;
/* Initialize System Register */
if (bfin_miiphy_init(dev, &dat) < 0)
return -1;
/* Initialize EMAC address */
bfin_EMAC_setup_addr(dev);
/* Initialize TX and RX buffer */
for (i = 0; i < PKTBUFSRX; i++) {
rxbuf[i] = SetupRxBuffer(i);
if (i > 0) {
rxbuf[i - 1]->Dma[1].NEXT_DESC_PTR = rxbuf[i]->Dma;
if (i == (PKTBUFSRX - 1))
rxbuf[i]->Dma[1].NEXT_DESC_PTR = rxbuf[0]->Dma;
}
}
for (i = 0; i < TX_BUF_CNT; i++) {
txbuf[i] = SetupTxBuffer(i);
if (i > 0) {
txbuf[i - 1]->Dma[1].NEXT_DESC_PTR = txbuf[i]->Dma;
if (i == (TX_BUF_CNT - 1))
txbuf[i]->Dma[1].NEXT_DESC_PTR = txbuf[0]->Dma;
}
}
/* Set RX DMA */
bfin_write_DMA1_NEXT_DESC_PTR(rxbuf[0]->Dma);
bfin_write_DMA1_CONFIG(rxbuf[0]->Dma[0].CONFIG_DATA);
/* Wait MII done */
bfin_miiphy_wait();
/* We enable only RX here */
/* ASTP : Enable Automatic Pad Stripping
PR : Promiscuous Mode for test
PSF : Receive frames with total length less than 64 bytes.
FDMODE : Full Duplex Mode
LB : Internal Loopback for test
RE : Receiver Enable */
if (dat == FDMODE)
opmode = ASTP | FDMODE | PSF;
else
opmode = ASTP | PSF;
opmode |= RE;
#ifdef CONFIG_RMII
opmode |= TE | RMII;
#endif
/* Turn on the EMAC */
bfin_write_EMAC_OPMODE(opmode);
return 0;
}
static void bfin_EMAC_halt(struct eth_device *dev)
{
debug("Eth_halt: ......\n");
/* Turn off the EMAC */
bfin_write_EMAC_OPMODE(0);
/* Turn off the EMAC RX DMA */
bfin_write_DMA1_CONFIG(0);
bfin_write_DMA2_CONFIG(0);
}
ADI_ETHER_BUFFER *SetupRxBuffer(int no)
{
ADI_ETHER_FRAME_BUFFER *frmbuf;
ADI_ETHER_BUFFER *buf;
int nobytes_buffer = sizeof(ADI_ETHER_BUFFER[2]) / 2; /* ensure a multi. of 4 */
int total_size = nobytes_buffer + RECV_BUFSIZE;
buf = (void *) (RXBUF_BASE_ADDR + no * total_size);
frmbuf = (void *) (RXBUF_BASE_ADDR + no * total_size + nobytes_buffer);
memset(buf, 0x00, nobytes_buffer);
buf->FrmData = frmbuf;
memset(frmbuf, 0xfe, RECV_BUFSIZE);
/* set up first desc to point to receive frame buffer */
buf->Dma[0].NEXT_DESC_PTR = &(buf->Dma[1]);
buf->Dma[0].START_ADDR = (u32) buf->FrmData;
buf->Dma[0].CONFIG.b_DMA_EN = 1; /* enabled */
buf->Dma[0].CONFIG.b_WNR = 1; /* Write to memory */
buf->Dma[0].CONFIG.b_WDSIZE = 2; /* wordsize is 32 bits */
buf->Dma[0].CONFIG.b_NDSIZE = 5; /* 5 half words is desc size. */
buf->Dma[0].CONFIG.b_FLOW = 7; /* large desc flow */
/* set up second desc to point to status word */
buf->Dma[1].NEXT_DESC_PTR = buf->Dma;
buf->Dma[1].START_ADDR = (u32) & buf->IPHdrChksum;
buf->Dma[1].CONFIG.b_DMA_EN = 1; /* enabled */
buf->Dma[1].CONFIG.b_WNR = 1; /* Write to memory */
buf->Dma[1].CONFIG.b_WDSIZE = 2; /* wordsize is 32 bits */
buf->Dma[1].CONFIG.b_DI_EN = 1; /* enable interrupt */
buf->Dma[1].CONFIG.b_NDSIZE = 5; /* must be 0 when FLOW is 0 */
buf->Dma[1].CONFIG.b_FLOW = 7; /* stop */
return buf;
}
ADI_ETHER_BUFFER *SetupTxBuffer(int no)
{
ADI_ETHER_FRAME_BUFFER *frmbuf;
ADI_ETHER_BUFFER *buf;
int nobytes_buffer = sizeof(ADI_ETHER_BUFFER[2]) / 2; /* ensure a multi. of 4 */
int total_size = nobytes_buffer + RECV_BUFSIZE;
buf = (void *) (TXBUF_BASE_ADDR + no * total_size);
frmbuf = (void *) (TXBUF_BASE_ADDR + no * total_size + nobytes_buffer);
memset(buf, 0x00, nobytes_buffer);
buf->FrmData = frmbuf;
memset(frmbuf, 0x00, RECV_BUFSIZE);
/* set up first desc to point to receive frame buffer */
buf->Dma[0].NEXT_DESC_PTR = &(buf->Dma[1]);
buf->Dma[0].START_ADDR = (u32) buf->FrmData;
buf->Dma[0].CONFIG.b_DMA_EN = 1; /* enabled */
buf->Dma[0].CONFIG.b_WNR = 0; /* Read to memory */
buf->Dma[0].CONFIG.b_WDSIZE = 2; /* wordsize is 32 bits */
buf->Dma[0].CONFIG.b_NDSIZE = 5; /* 5 half words is desc size. */
buf->Dma[0].CONFIG.b_FLOW = 7; /* large desc flow */
/* set up second desc to point to status word */
buf->Dma[1].NEXT_DESC_PTR = &(buf->Dma[0]);
buf->Dma[1].START_ADDR = (u32) & buf->StatusWord;
buf->Dma[1].CONFIG.b_DMA_EN = 1; /* enabled */
buf->Dma[1].CONFIG.b_WNR = 1; /* Write to memory */
buf->Dma[1].CONFIG.b_WDSIZE = 2; /* wordsize is 32 bits */
buf->Dma[1].CONFIG.b_DI_EN = 1; /* enable interrupt */
buf->Dma[1].CONFIG.b_NDSIZE = 0; /* must be 0 when FLOW is 0 */
buf->Dma[1].CONFIG.b_FLOW = 0; /* stop */
return buf;
}
#if defined(CONFIG_POST) && defined(CONFIG_SYS_POST_ETHER)
int ether_post_test(int flags)
{
uchar buf[64];
int i, value = 0;
int length;
uint addr;
printf("\n--------");
bfin_EMAC_init(NULL, NULL);
/* construct the package */
addr = bfin_read_EMAC_ADDRLO();
buf[0] = buf[6] = addr;
buf[1] = buf[7] = addr >> 8;
buf[2] = buf[8] = addr >> 16;
buf[3] = buf[9] = addr >> 24;
addr = bfin_read_EMAC_ADDRHI();
buf[4] = buf[10] = addr;
buf[5] = buf[11] = addr >> 8;
buf[12] = 0x08; /* Type: ARP */
buf[13] = 0x06;
buf[14] = 0x00; /* Hardware type: Ethernet */
buf[15] = 0x01;
buf[16] = 0x08; /* Protocal type: IP */
buf[17] = 0x00;
buf[18] = 0x06; /* Hardware size */
buf[19] = 0x04; /* Protocol size */
buf[20] = 0x00; /* Opcode: request */
buf[21] = 0x01;
for (i = 0; i < 42; i++)
buf[i + 22] = i;
printf("--------Send 64 bytes......\n");
bfin_EMAC_send(NULL, buf, 64);
for (i = 0; i < 100; i++) {
udelay(10000);
if ((rxbuf[rxIdx]->StatusWord & RX_COMP) != 0) {
value = 1;
break;
}
}
if (value == 0) {
printf("--------EMAC can't receive any data\n");
eth_halt();
return -1;
}
length = rxbuf[rxIdx]->StatusWord & 0x000007FF - 4;
for (i = 0; i < length; i++) {
if (rxbuf[rxIdx]->FrmData->Dest[i] != buf[i]) {
printf("--------EMAC receive error data!\n");
eth_halt();
return -1;
}
}
printf("--------receive %d bytes, matched\n", length);
bfin_EMAC_halt(NULL);
return 0;
}
#endif

View file

@ -0,0 +1,65 @@
/*
* bfin_mac.h - some defines/structures for the Blackfin on-chip MAC.
*
* Copyright (c) 2005-2008 Analog Device, Inc.
*
* Licensed under the GPL-2 or later.
*/
#ifndef __BFIN_MAC_H__
#define __BFIN_MAC_H__
#define RECV_BUFSIZE (0x614)
typedef struct ADI_DMA_CONFIG_REG {
u16 b_DMA_EN:1; /* 0 Enabled */
u16 b_WNR:1; /* 1 Direction */
u16 b_WDSIZE:2; /* 2:3 Transfer word size */
u16 b_DMA2D:1; /* 4 DMA mode */
u16 b_RESTART:1; /* 5 Retain FIFO */
u16 b_DI_SEL:1; /* 6 Data interrupt timing select */
u16 b_DI_EN:1; /* 7 Data interrupt enabled */
u16 b_NDSIZE:4; /* 8:11 Flex descriptor size */
u16 b_FLOW:3; /* 12:14Flow */
} ADI_DMA_CONFIG_REG;
typedef struct adi_ether_frame_buffer {
u16 NoBytes; /* the no. of following bytes */
u8 Dest[6]; /* destination MAC address */
u8 Srce[6]; /* source MAC address */
u16 LTfield; /* length/type field */
u8 Data[0]; /* payload bytes */
} ADI_ETHER_FRAME_BUFFER;
/* 16 bytes/struct */
typedef struct dma_descriptor {
struct dma_descriptor *NEXT_DESC_PTR;
u32 START_ADDR;
union {
u16 CONFIG_DATA;
ADI_DMA_CONFIG_REG CONFIG;
};
} DMA_DESCRIPTOR;
/* 10 bytes/struct in 12 bytes */
typedef struct adi_ether_buffer {
DMA_DESCRIPTOR Dma[2]; /* first for the frame, second for the status */
ADI_ETHER_FRAME_BUFFER *FrmData;/* pointer to data */
struct adi_ether_buffer *pNext; /* next buffer */
struct adi_ether_buffer *pPrev; /* prev buffer */
u16 IPHdrChksum; /* the IP header checksum */
u16 IPPayloadChksum; /* the IP header and payload checksum */
volatile u32 StatusWord; /* the frame status word */
} ADI_ETHER_BUFFER;
/* 40 bytes/struct in 44 bytes */
static ADI_ETHER_BUFFER *SetupRxBuffer(int no);
static ADI_ETHER_BUFFER *SetupTxBuffer(int no);
static int bfin_EMAC_init(struct eth_device *dev, bd_t *bd);
static void bfin_EMAC_halt(struct eth_device *dev);
static int bfin_EMAC_send(struct eth_device *dev, void *packet, int length);
static int bfin_EMAC_recv(struct eth_device *dev);
static int bfin_EMAC_setup_addr(struct eth_device *dev);
#endif

View file

@ -0,0 +1,544 @@
/*
* Copyright 2010-2011 Calxeda, Inc.
*
* SPDX-License-Identifier: GPL-2.0+
*/
#include <common.h>
#include <malloc.h>
#include <linux/compiler.h>
#include <linux/err.h>
#include <asm/io.h>
#define TX_NUM_DESC 1
#define RX_NUM_DESC 32
#define MAC_TIMEOUT (5*CONFIG_SYS_HZ)
#define ETH_BUF_SZ 2048
#define TX_BUF_SZ (ETH_BUF_SZ * TX_NUM_DESC)
#define RX_BUF_SZ (ETH_BUF_SZ * RX_NUM_DESC)
#define RXSTART 0x00000002
#define TXSTART 0x00002000
#define RXENABLE 0x00000004
#define TXENABLE 0x00000008
#define XGMAC_CONTROL_SPD 0x40000000
#define XGMAC_CONTROL_SPD_MASK 0x60000000
#define XGMAC_CONTROL_SARC 0x10000000
#define XGMAC_CONTROL_SARK_MASK 0x18000000
#define XGMAC_CONTROL_CAR 0x04000000
#define XGMAC_CONTROL_CAR_MASK 0x06000000
#define XGMAC_CONTROL_CAR_SHIFT 25
#define XGMAC_CONTROL_DP 0x01000000
#define XGMAC_CONTROL_WD 0x00800000
#define XGMAC_CONTROL_JD 0x00400000
#define XGMAC_CONTROL_JE 0x00100000
#define XGMAC_CONTROL_LM 0x00001000
#define XGMAC_CONTROL_IPC 0x00000400
#define XGMAC_CONTROL_ACS 0x00000080
#define XGMAC_CONTROL_DDIC 0x00000010
#define XGMAC_CONTROL_TE 0x00000008
#define XGMAC_CONTROL_RE 0x00000004
#define XGMAC_DMA_BUSMODE_RESET 0x00000001
#define XGMAC_DMA_BUSMODE_DSL 0x00000004
#define XGMAC_DMA_BUSMODE_DSL_MASK 0x0000007c
#define XGMAC_DMA_BUSMODE_DSL_SHIFT 2
#define XGMAC_DMA_BUSMODE_ATDS 0x00000080
#define XGMAC_DMA_BUSMODE_PBL_MASK 0x00003f00
#define XGMAC_DMA_BUSMODE_PBL_SHIFT 8
#define XGMAC_DMA_BUSMODE_FB 0x00010000
#define XGMAC_DMA_BUSMODE_USP 0x00800000
#define XGMAC_DMA_BUSMODE_8PBL 0x01000000
#define XGMAC_DMA_BUSMODE_AAL 0x02000000
#define XGMAC_DMA_AXIMODE_ENLPI 0x80000000
#define XGMAC_DMA_AXIMODE_MGK 0x40000000
#define XGMAC_DMA_AXIMODE_WROSR 0x00100000
#define XGMAC_DMA_AXIMODE_WROSR_MASK 0x00F00000
#define XGMAC_DMA_AXIMODE_WROSR_SHIFT 20
#define XGMAC_DMA_AXIMODE_RDOSR 0x00010000
#define XGMAC_DMA_AXIMODE_RDOSR_MASK 0x000F0000
#define XGMAC_DMA_AXIMODE_RDOSR_SHIFT 16
#define XGMAC_DMA_AXIMODE_AAL 0x00001000
#define XGMAC_DMA_AXIMODE_BLEN256 0x00000080
#define XGMAC_DMA_AXIMODE_BLEN128 0x00000040
#define XGMAC_DMA_AXIMODE_BLEN64 0x00000020
#define XGMAC_DMA_AXIMODE_BLEN32 0x00000010
#define XGMAC_DMA_AXIMODE_BLEN16 0x00000008
#define XGMAC_DMA_AXIMODE_BLEN8 0x00000004
#define XGMAC_DMA_AXIMODE_BLEN4 0x00000002
#define XGMAC_DMA_AXIMODE_UNDEF 0x00000001
#define XGMAC_CORE_OMR_RTC_SHIFT 3
#define XGMAC_CORE_OMR_RTC_MASK 0x00000018
#define XGMAC_CORE_OMR_RTC 0x00000010
#define XGMAC_CORE_OMR_RSF 0x00000020
#define XGMAC_CORE_OMR_DT 0x00000040
#define XGMAC_CORE_OMR_FEF 0x00000080
#define XGMAC_CORE_OMR_EFC 0x00000100
#define XGMAC_CORE_OMR_RFA_SHIFT 9
#define XGMAC_CORE_OMR_RFA_MASK 0x00000E00
#define XGMAC_CORE_OMR_RFD_SHIFT 12
#define XGMAC_CORE_OMR_RFD_MASK 0x00007000
#define XGMAC_CORE_OMR_TTC_SHIFT 16
#define XGMAC_CORE_OMR_TTC_MASK 0x00030000
#define XGMAC_CORE_OMR_TTC 0x00020000
#define XGMAC_CORE_OMR_FTF 0x00100000
#define XGMAC_CORE_OMR_TSF 0x00200000
#define FIFO_MINUS_1K 0x0
#define FIFO_MINUS_2K 0x1
#define FIFO_MINUS_3K 0x2
#define FIFO_MINUS_4K 0x3
#define FIFO_MINUS_6K 0x4
#define FIFO_MINUS_8K 0x5
#define FIFO_MINUS_12K 0x6
#define FIFO_MINUS_16K 0x7
#define XGMAC_CORE_FLOW_PT_SHIFT 16
#define XGMAC_CORE_FLOW_PT_MASK 0xFFFF0000
#define XGMAC_CORE_FLOW_PT 0x00010000
#define XGMAC_CORE_FLOW_DZQP 0x00000080
#define XGMAC_CORE_FLOW_PLT_SHIFT 4
#define XGMAC_CORE_FLOW_PLT_MASK 0x00000030
#define XGMAC_CORE_FLOW_PLT 0x00000010
#define XGMAC_CORE_FLOW_UP 0x00000008
#define XGMAC_CORE_FLOW_RFE 0x00000004
#define XGMAC_CORE_FLOW_TFE 0x00000002
#define XGMAC_CORE_FLOW_FCB 0x00000001
/* XGMAC Descriptor Defines */
#define MAX_DESC_BUF_SZ (0x2000 - 8)
#define RXDESC_EXT_STATUS 0x00000001
#define RXDESC_CRC_ERR 0x00000002
#define RXDESC_RX_ERR 0x00000008
#define RXDESC_RX_WDOG 0x00000010
#define RXDESC_FRAME_TYPE 0x00000020
#define RXDESC_GIANT_FRAME 0x00000080
#define RXDESC_LAST_SEG 0x00000100
#define RXDESC_FIRST_SEG 0x00000200
#define RXDESC_VLAN_FRAME 0x00000400
#define RXDESC_OVERFLOW_ERR 0x00000800
#define RXDESC_LENGTH_ERR 0x00001000
#define RXDESC_SA_FILTER_FAIL 0x00002000
#define RXDESC_DESCRIPTOR_ERR 0x00004000
#define RXDESC_ERROR_SUMMARY 0x00008000
#define RXDESC_FRAME_LEN_OFFSET 16
#define RXDESC_FRAME_LEN_MASK 0x3fff0000
#define RXDESC_DA_FILTER_FAIL 0x40000000
#define RXDESC1_END_RING 0x00008000
#define RXDESC_IP_PAYLOAD_MASK 0x00000003
#define RXDESC_IP_PAYLOAD_UDP 0x00000001
#define RXDESC_IP_PAYLOAD_TCP 0x00000002
#define RXDESC_IP_PAYLOAD_ICMP 0x00000003
#define RXDESC_IP_HEADER_ERR 0x00000008
#define RXDESC_IP_PAYLOAD_ERR 0x00000010
#define RXDESC_IPV4_PACKET 0x00000040
#define RXDESC_IPV6_PACKET 0x00000080
#define TXDESC_UNDERFLOW_ERR 0x00000001
#define TXDESC_JABBER_TIMEOUT 0x00000002
#define TXDESC_LOCAL_FAULT 0x00000004
#define TXDESC_REMOTE_FAULT 0x00000008
#define TXDESC_VLAN_FRAME 0x00000010
#define TXDESC_FRAME_FLUSHED 0x00000020
#define TXDESC_IP_HEADER_ERR 0x00000040
#define TXDESC_PAYLOAD_CSUM_ERR 0x00000080
#define TXDESC_ERROR_SUMMARY 0x00008000
#define TXDESC_SA_CTRL_INSERT 0x00040000
#define TXDESC_SA_CTRL_REPLACE 0x00080000
#define TXDESC_2ND_ADDR_CHAINED 0x00100000
#define TXDESC_END_RING 0x00200000
#define TXDESC_CSUM_IP 0x00400000
#define TXDESC_CSUM_IP_PAYLD 0x00800000
#define TXDESC_CSUM_ALL 0x00C00000
#define TXDESC_CRC_EN_REPLACE 0x01000000
#define TXDESC_CRC_EN_APPEND 0x02000000
#define TXDESC_DISABLE_PAD 0x04000000
#define TXDESC_FIRST_SEG 0x10000000
#define TXDESC_LAST_SEG 0x20000000
#define TXDESC_INTERRUPT 0x40000000
#define DESC_OWN 0x80000000
#define DESC_BUFFER1_SZ_MASK 0x00001fff
#define DESC_BUFFER2_SZ_MASK 0x1fff0000
#define DESC_BUFFER2_SZ_OFFSET 16
struct xgmac_regs {
u32 config;
u32 framefilter;
u32 resv_1[4];
u32 flow_control;
u32 vlantag;
u32 version;
u32 vlaninclude;
u32 resv_2[2];
u32 pacestretch;
u32 vlanhash;
u32 resv_3;
u32 intreg;
struct {
u32 hi; /* 0x40 */
u32 lo; /* 0x44 */
} macaddr[16];
u32 resv_4[0xd0];
u32 core_opmode; /* 0x400 */
u32 resv_5[0x2bf];
u32 busmode; /* 0xf00 */
u32 txpoll;
u32 rxpoll;
u32 rxdesclist;
u32 txdesclist;
u32 dma_status;
u32 dma_opmode;
u32 intenable;
u32 resv_6[2];
u32 axi_mode; /* 0xf28 */
};
struct xgmac_dma_desc {
__le32 flags;
__le32 buf_size;
__le32 buf1_addr; /* Buffer 1 Address Pointer */
__le32 buf2_addr; /* Buffer 2 Address Pointer */
__le32 ext_status;
__le32 res[3];
};
/* XGMAC Descriptor Access Helpers */
static inline void desc_set_buf_len(struct xgmac_dma_desc *p, u32 buf_sz)
{
if (buf_sz > MAX_DESC_BUF_SZ)
p->buf_size = cpu_to_le32(MAX_DESC_BUF_SZ |
(buf_sz - MAX_DESC_BUF_SZ) << DESC_BUFFER2_SZ_OFFSET);
else
p->buf_size = cpu_to_le32(buf_sz);
}
static inline int desc_get_buf_len(struct xgmac_dma_desc *p)
{
u32 len = le32_to_cpu(p->buf_size);
return (len & DESC_BUFFER1_SZ_MASK) +
((len & DESC_BUFFER2_SZ_MASK) >> DESC_BUFFER2_SZ_OFFSET);
}
static inline void desc_init_rx_desc(struct xgmac_dma_desc *p, int ring_size,
int buf_sz)
{
struct xgmac_dma_desc *end = p + ring_size - 1;
memset(p, 0, sizeof(*p) * ring_size);
for (; p <= end; p++)
desc_set_buf_len(p, buf_sz);
end->buf_size |= cpu_to_le32(RXDESC1_END_RING);
}
static inline void desc_init_tx_desc(struct xgmac_dma_desc *p, u32 ring_size)
{
memset(p, 0, sizeof(*p) * ring_size);
p[ring_size - 1].flags = cpu_to_le32(TXDESC_END_RING);
}
static inline int desc_get_owner(struct xgmac_dma_desc *p)
{
return le32_to_cpu(p->flags) & DESC_OWN;
}
static inline void desc_set_rx_owner(struct xgmac_dma_desc *p)
{
/* Clear all fields and set the owner */
p->flags = cpu_to_le32(DESC_OWN);
}
static inline void desc_set_tx_owner(struct xgmac_dma_desc *p, u32 flags)
{
u32 tmpflags = le32_to_cpu(p->flags);
tmpflags &= TXDESC_END_RING;
tmpflags |= flags | DESC_OWN;
p->flags = cpu_to_le32(tmpflags);
}
static inline void *desc_get_buf_addr(struct xgmac_dma_desc *p)
{
return (void *)le32_to_cpu(p->buf1_addr);
}
static inline void desc_set_buf_addr(struct xgmac_dma_desc *p,
void *paddr, int len)
{
p->buf1_addr = cpu_to_le32(paddr);
if (len > MAX_DESC_BUF_SZ)
p->buf2_addr = cpu_to_le32(paddr + MAX_DESC_BUF_SZ);
}
static inline void desc_set_buf_addr_and_size(struct xgmac_dma_desc *p,
void *paddr, int len)
{
desc_set_buf_len(p, len);
desc_set_buf_addr(p, paddr, len);
}
static inline int desc_get_rx_frame_len(struct xgmac_dma_desc *p)
{
u32 data = le32_to_cpu(p->flags);
u32 len = (data & RXDESC_FRAME_LEN_MASK) >> RXDESC_FRAME_LEN_OFFSET;
if (data & RXDESC_FRAME_TYPE)
len -= 4;
return len;
}
struct calxeda_eth_dev {
struct xgmac_dma_desc rx_chain[RX_NUM_DESC];
struct xgmac_dma_desc tx_chain[TX_NUM_DESC];
char rxbuffer[RX_BUF_SZ];
u32 tx_currdesc;
u32 rx_currdesc;
struct eth_device *dev;
} __aligned(32);
/*
* Initialize a descriptor ring. Calxeda XGMAC is configured to use
* advanced descriptors.
*/
static void init_rx_desc(struct calxeda_eth_dev *priv)
{
struct xgmac_dma_desc *rxdesc = priv->rx_chain;
struct xgmac_regs *regs = (struct xgmac_regs *)priv->dev->iobase;
void *rxbuffer = priv->rxbuffer;
int i;
desc_init_rx_desc(rxdesc, RX_NUM_DESC, ETH_BUF_SZ);
writel((ulong)rxdesc, &regs->rxdesclist);
for (i = 0; i < RX_NUM_DESC; i++) {
desc_set_buf_addr(rxdesc + i, rxbuffer + (i * ETH_BUF_SZ),
ETH_BUF_SZ);
desc_set_rx_owner(rxdesc + i);
}
}
static void init_tx_desc(struct calxeda_eth_dev *priv)
{
struct xgmac_regs *regs = (struct xgmac_regs *)priv->dev->iobase;
desc_init_tx_desc(priv->tx_chain, TX_NUM_DESC);
writel((ulong)priv->tx_chain, &regs->txdesclist);
}
static int xgmac_reset(struct eth_device *dev)
{
struct xgmac_regs *regs = (struct xgmac_regs *)dev->iobase;
int timeout = MAC_TIMEOUT;
u32 value;
value = readl(&regs->config) & XGMAC_CONTROL_SPD_MASK;
writel(XGMAC_DMA_BUSMODE_RESET, &regs->busmode);
while ((timeout-- >= 0) &&
(readl(&regs->busmode) & XGMAC_DMA_BUSMODE_RESET))
udelay(1);
writel(value, &regs->config);
return timeout;
}
static void xgmac_hwmacaddr(struct eth_device *dev)
{
struct xgmac_regs *regs = (struct xgmac_regs *)dev->iobase;
u32 macaddr[2];
memcpy(macaddr, dev->enetaddr, 6);
writel(macaddr[1], &regs->macaddr[0].hi);
writel(macaddr[0], &regs->macaddr[0].lo);
}
static int xgmac_init(struct eth_device *dev, bd_t * bis)
{
struct xgmac_regs *regs = (struct xgmac_regs *)dev->iobase;
struct calxeda_eth_dev *priv = dev->priv;
int value;
if (xgmac_reset(dev) < 0)
return -1;
/* set the hardware MAC address */
xgmac_hwmacaddr(dev);
/* set the AXI bus modes */
value = XGMAC_DMA_BUSMODE_ATDS |
(16 << XGMAC_DMA_BUSMODE_PBL_SHIFT) |
XGMAC_DMA_BUSMODE_FB | XGMAC_DMA_BUSMODE_AAL;
writel(value, &regs->busmode);
value = XGMAC_DMA_AXIMODE_AAL | XGMAC_DMA_AXIMODE_BLEN16 |
XGMAC_DMA_AXIMODE_BLEN8 | XGMAC_DMA_AXIMODE_BLEN4;
writel(value, &regs->axi_mode);
/* set flow control parameters and store and forward mode */
value = (FIFO_MINUS_12K << XGMAC_CORE_OMR_RFD_SHIFT) |
(FIFO_MINUS_4K << XGMAC_CORE_OMR_RFA_SHIFT) |
XGMAC_CORE_OMR_EFC | XGMAC_CORE_OMR_TSF;
writel(value, &regs->core_opmode);
/* enable pause frames */
value = (1024 << XGMAC_CORE_FLOW_PT_SHIFT) |
(1 << XGMAC_CORE_FLOW_PLT_SHIFT) |
XGMAC_CORE_FLOW_UP | XGMAC_CORE_FLOW_RFE | XGMAC_CORE_FLOW_TFE;
writel(value, &regs->flow_control);
/* Initialize the descriptor chains */
init_rx_desc(priv);
init_tx_desc(priv);
/* must set to 0, or when started up will cause issues */
priv->tx_currdesc = 0;
priv->rx_currdesc = 0;
/* set default core values */
value = readl(&regs->config);
value &= XGMAC_CONTROL_SPD_MASK;
value |= XGMAC_CONTROL_DDIC | XGMAC_CONTROL_ACS |
XGMAC_CONTROL_IPC | XGMAC_CONTROL_CAR;
/* Everything is ready enable both mac and DMA */
value |= RXENABLE | TXENABLE;
writel(value, &regs->config);
value = readl(&regs->dma_opmode);
value |= RXSTART | TXSTART;
writel(value, &regs->dma_opmode);
return 0;
}
static int xgmac_tx(struct eth_device *dev, void *packet, int length)
{
struct xgmac_regs *regs = (struct xgmac_regs *)dev->iobase;
struct calxeda_eth_dev *priv = dev->priv;
u32 currdesc = priv->tx_currdesc;
struct xgmac_dma_desc *txdesc = &priv->tx_chain[currdesc];
int timeout;
desc_set_buf_addr_and_size(txdesc, packet, length);
desc_set_tx_owner(txdesc, TXDESC_FIRST_SEG |
TXDESC_LAST_SEG | TXDESC_CRC_EN_APPEND);
/* write poll demand */
writel(1, &regs->txpoll);
timeout = 1000000;
while (desc_get_owner(txdesc)) {
if (timeout-- < 0) {
printf("xgmac: TX timeout\n");
return -ETIMEDOUT;
}
udelay(1);
}
priv->tx_currdesc = (currdesc + 1) & (TX_NUM_DESC - 1);
return 0;
}
static int xgmac_rx(struct eth_device *dev)
{
struct xgmac_regs *regs = (struct xgmac_regs *)dev->iobase;
struct calxeda_eth_dev *priv = dev->priv;
u32 currdesc = priv->rx_currdesc;
struct xgmac_dma_desc *rxdesc = &priv->rx_chain[currdesc];
int length = 0;
/* check if the host has the desc */
if (desc_get_owner(rxdesc))
return -1; /* something bad happened */
length = desc_get_rx_frame_len(rxdesc);
net_process_received_packet(desc_get_buf_addr(rxdesc), length);
/* set descriptor back to owned by XGMAC */
desc_set_rx_owner(rxdesc);
writel(1, &regs->rxpoll);
priv->rx_currdesc = (currdesc + 1) & (RX_NUM_DESC - 1);
return length;
}
static void xgmac_halt(struct eth_device *dev)
{
struct xgmac_regs *regs = (struct xgmac_regs *)dev->iobase;
struct calxeda_eth_dev *priv = dev->priv;
int value;
/* Disable TX/RX */
value = readl(&regs->config);
value &= ~(RXENABLE | TXENABLE);
writel(value, &regs->config);
/* Disable DMA */
value = readl(&regs->dma_opmode);
value &= ~(RXSTART | TXSTART);
writel(value, &regs->dma_opmode);
/* must set to 0, or when started up will cause issues */
priv->tx_currdesc = 0;
priv->rx_currdesc = 0;
}
int calxedaxgmac_initialize(u32 id, ulong base_addr)
{
struct eth_device *dev;
struct calxeda_eth_dev *priv;
struct xgmac_regs *regs;
u32 macaddr[2];
regs = (struct xgmac_regs *)base_addr;
/* check hardware version */
if (readl(&regs->version) != 0x1012)
return -1;
dev = malloc(sizeof(*dev));
if (!dev)
return 0;
memset(dev, 0, sizeof(*dev));
/* Structure must be aligned, because it contains the descriptors */
priv = memalign(32, sizeof(*priv));
if (!priv) {
free(dev);
return 0;
}
dev->iobase = (int)base_addr;
dev->priv = priv;
priv->dev = dev;
sprintf(dev->name, "xgmac%d", id);
/* The MAC address is already configured, so read it from registers. */
macaddr[1] = readl(&regs->macaddr[0].hi);
macaddr[0] = readl(&regs->macaddr[0].lo);
memcpy(dev->enetaddr, macaddr, 6);
dev->init = xgmac_init;
dev->send = xgmac_tx;
dev->recv = xgmac_rx;
dev->halt = xgmac_halt;
eth_register(dev);
return 1;
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,319 @@
/*
* Cirrus Logic CS8900A Ethernet
*
* (C) 2009 Ben Warren , biggerbadderben@gmail.com
* Converted to use CONFIG_NET_MULTI API
*
* (C) 2003 Wolfgang Denk, wd@denx.de
* Extension to synchronize ethaddr environment variable
* against value in EEPROM
*
* (C) Copyright 2002
* Sysgo Real-Time Solutions, GmbH <www.elinos.com>
* Marius Groeger <mgroeger@sysgo.de>
*
* Copyright (C) 1999 Ben Williamson <benw@pobox.com>
*
* This program is loaded into SRAM in bootstrap mode, where it waits
* for commands on UART1 to read and write memory, jump to code etc.
* A design goal for this program is to be entirely independent of the
* target board. Anything with a CL-PS7111 or EP7211 should be able to run
* this code in bootstrap mode. All the board specifics can be handled on
* the host.
*
* SPDX-License-Identifier: GPL-2.0+
*/
#include <common.h>
#include <command.h>
#include <asm/io.h>
#include <net.h>
#include <malloc.h>
#include "cs8900.h"
#undef DEBUG
/* packet page register access functions */
#ifdef CONFIG_CS8900_BUS32
#define REG_WRITE(v, a) writel((v),(a))
#define REG_READ(a) readl((a))
/* we don't need 16 bit initialisation on 32 bit bus */
#define get_reg_init_bus(r,d) get_reg((r),(d))
#else
#define REG_WRITE(v, a) writew((v),(a))
#define REG_READ(a) readw((a))
static u16 get_reg_init_bus(struct eth_device *dev, int regno)
{
/* force 16 bit busmode */
struct cs8900_priv *priv = (struct cs8900_priv *)(dev->priv);
uint8_t volatile * const iob = (uint8_t volatile * const)dev->iobase;
readb(iob);
readb(iob + 1);
readb(iob);
readb(iob + 1);
readb(iob);
REG_WRITE(regno, &priv->regs->pptr);
return REG_READ(&priv->regs->pdata);
}
#endif
static u16 get_reg(struct eth_device *dev, int regno)
{
struct cs8900_priv *priv = (struct cs8900_priv *)(dev->priv);
REG_WRITE(regno, &priv->regs->pptr);
return REG_READ(&priv->regs->pdata);
}
static void put_reg(struct eth_device *dev, int regno, u16 val)
{
struct cs8900_priv *priv = (struct cs8900_priv *)(dev->priv);
REG_WRITE(regno, &priv->regs->pptr);
REG_WRITE(val, &priv->regs->pdata);
}
static void cs8900_reset(struct eth_device *dev)
{
int tmo;
u16 us;
/* reset NIC */
put_reg(dev, PP_SelfCTL, get_reg(dev, PP_SelfCTL) | PP_SelfCTL_Reset);
/* wait for 200ms */
udelay(200000);
/* Wait until the chip is reset */
tmo = get_timer(0) + 1 * CONFIG_SYS_HZ;
while ((((us = get_reg_init_bus(dev, PP_SelfSTAT)) &
PP_SelfSTAT_InitD) == 0) && tmo < get_timer(0))
/*NOP*/;
}
static void cs8900_reginit(struct eth_device *dev)
{
/* receive only error free packets addressed to this card */
put_reg(dev, PP_RxCTL,
PP_RxCTL_IA | PP_RxCTL_Broadcast | PP_RxCTL_RxOK);
/* do not generate any interrupts on receive operations */
put_reg(dev, PP_RxCFG, 0);
/* do not generate any interrupts on transmit operations */
put_reg(dev, PP_TxCFG, 0);
/* do not generate any interrupts on buffer operations */
put_reg(dev, PP_BufCFG, 0);
/* enable transmitter/receiver mode */
put_reg(dev, PP_LineCTL, PP_LineCTL_Rx | PP_LineCTL_Tx);
}
void cs8900_get_enetaddr(struct eth_device *dev)
{
int i;
/* verify chip id */
if (get_reg_init_bus(dev, PP_ChipID) != 0x630e)
return;
cs8900_reset(dev);
if ((get_reg(dev, PP_SelfSTAT) &
(PP_SelfSTAT_EEPROM | PP_SelfSTAT_EEPROM_OK)) ==
(PP_SelfSTAT_EEPROM | PP_SelfSTAT_EEPROM_OK)) {
/* Load the MAC from EEPROM */
for (i = 0; i < 3; i++) {
u32 Addr;
Addr = get_reg(dev, PP_IA + i * 2);
dev->enetaddr[i * 2] = Addr & 0xFF;
dev->enetaddr[i * 2 + 1] = Addr >> 8;
}
}
}
void cs8900_halt(struct eth_device *dev)
{
/* disable transmitter/receiver mode */
put_reg(dev, PP_LineCTL, 0);
/* "shutdown" to show ChipID or kernel wouldn't find he cs8900 ... */
get_reg_init_bus(dev, PP_ChipID);
}
static int cs8900_init(struct eth_device *dev, bd_t * bd)
{
uchar *enetaddr = dev->enetaddr;
u16 id;
/* verify chip id */
id = get_reg_init_bus(dev, PP_ChipID);
if (id != 0x630e) {
printf ("CS8900 Ethernet chip not found: "
"ID=0x%04x instead 0x%04x\n", id, 0x630e);
return 1;
}
cs8900_reset (dev);
/* set the ethernet address */
put_reg(dev, PP_IA + 0, enetaddr[0] | (enetaddr[1] << 8));
put_reg(dev, PP_IA + 2, enetaddr[2] | (enetaddr[3] << 8));
put_reg(dev, PP_IA + 4, enetaddr[4] | (enetaddr[5] << 8));
cs8900_reginit(dev);
return 0;
}
/* Get a data block via Ethernet */
static int cs8900_recv(struct eth_device *dev)
{
int i;
u16 rxlen;
u16 *addr;
u16 status;
struct cs8900_priv *priv = (struct cs8900_priv *)(dev->priv);
status = get_reg(dev, PP_RER);
if ((status & PP_RER_RxOK) == 0)
return 0;
status = REG_READ(&priv->regs->rtdata);
rxlen = REG_READ(&priv->regs->rtdata);
if (rxlen > PKTSIZE_ALIGN + PKTALIGN)
debug("packet too big!\n");
for (addr = (u16 *)net_rx_packets[0], i = rxlen >> 1; i > 0; i--)
*addr++ = REG_READ(&priv->regs->rtdata);
if (rxlen & 1)
*addr++ = REG_READ(&priv->regs->rtdata);
/* Pass the packet up to the protocol layers. */
net_process_received_packet(net_rx_packets[0], rxlen);
return rxlen;
}
/* Send a data block via Ethernet. */
static int cs8900_send(struct eth_device *dev, void *packet, int length)
{
volatile u16 *addr;
int tmo;
u16 s;
struct cs8900_priv *priv = (struct cs8900_priv *)(dev->priv);
retry:
/* initiate a transmit sequence */
REG_WRITE(PP_TxCmd_TxStart_Full, &priv->regs->txcmd);
REG_WRITE(length, &priv->regs->txlen);
/* Test to see if the chip has allocated memory for the packet */
if ((get_reg(dev, PP_BusSTAT) & PP_BusSTAT_TxRDY) == 0) {
/* Oops... this should not happen! */
debug("cs: unable to send packet; retrying...\n");
for (tmo = get_timer(0) + 5 * CONFIG_SYS_HZ;
get_timer(0) < tmo;)
/*NOP*/;
cs8900_reset(dev);
cs8900_reginit(dev);
goto retry;
}
/* Write the contents of the packet */
/* assume even number of bytes */
for (addr = packet; length > 0; length -= 2)
REG_WRITE(*addr++, &priv->regs->rtdata);
/* wait for transfer to succeed */
tmo = get_timer(0) + 5 * CONFIG_SYS_HZ;
while ((s = get_reg(dev, PP_TER) & ~0x1F) == 0) {
if (get_timer(0) >= tmo)
break;
}
/* nothing */ ;
if((s & (PP_TER_CRS | PP_TER_TxOK)) != PP_TER_TxOK) {
debug("\ntransmission error %#x\n", s);
}
return 0;
}
static void cs8900_e2prom_ready(struct eth_device *dev)
{
while (get_reg(dev, PP_SelfSTAT) & SI_BUSY)
;
}
/***********************************************************/
/* read a 16-bit word out of the EEPROM */
/***********************************************************/
int cs8900_e2prom_read(struct eth_device *dev,
u8 addr, u16 *value)
{
cs8900_e2prom_ready(dev);
put_reg(dev, PP_EECMD, EEPROM_READ_CMD | addr);
cs8900_e2prom_ready(dev);
*value = get_reg(dev, PP_EEData);
return 0;
}
/***********************************************************/
/* write a 16-bit word into the EEPROM */
/***********************************************************/
int cs8900_e2prom_write(struct eth_device *dev, u8 addr, u16 value)
{
cs8900_e2prom_ready(dev);
put_reg(dev, PP_EECMD, EEPROM_WRITE_EN);
cs8900_e2prom_ready(dev);
put_reg(dev, PP_EEData, value);
put_reg(dev, PP_EECMD, EEPROM_WRITE_CMD | addr);
cs8900_e2prom_ready(dev);
put_reg(dev, PP_EECMD, EEPROM_WRITE_DIS);
cs8900_e2prom_ready(dev);
return 0;
}
int cs8900_initialize(u8 dev_num, int base_addr)
{
struct eth_device *dev;
struct cs8900_priv *priv;
dev = malloc(sizeof(*dev));
if (!dev) {
return 0;
}
memset(dev, 0, sizeof(*dev));
priv = malloc(sizeof(*priv));
if (!priv) {
free(dev);
return 0;
}
memset(priv, 0, sizeof(*priv));
priv->regs = (struct cs8900_regs *)base_addr;
dev->iobase = base_addr;
dev->priv = priv;
dev->init = cs8900_init;
dev->halt = cs8900_halt;
dev->send = cs8900_send;
dev->recv = cs8900_recv;
/* Load MAC address from EEPROM */
cs8900_get_enetaddr(dev);
sprintf(dev->name, "%s-%hu", CS8900_DRIVERNAME, dev_num);
eth_register(dev);
return 0;
}

View file

@ -0,0 +1,249 @@
#ifndef CS8900_H
#define CS8900_H
/*
* Cirrus Logic CS8900A Ethernet
*
* (C) 2009 Ben Warren , biggerbadderben@gmail.com
* Converted to use CONFIG_NET_MULTI API
*
* (C) Copyright 2002
* Sysgo Real-Time Solutions, GmbH <www.elinos.com>
* Marius Groeger <mgroeger@sysgo.de>
*
* Copyright (C) 1999 Ben Williamson <benw@pobox.com>
*
* This program is loaded into SRAM in bootstrap mode, where it waits
* for commands on UART1 to read and write memory, jump to code etc.
* A design goal for this program is to be entirely independent of the
* target board. Anything with a CL-PS7111 or EP7211 should be able to run
* this code in bootstrap mode. All the board specifics can be handled on
* the host.
*
* SPDX-License-Identifier: GPL-2.0+
*/
#include <asm/types.h>
#include <config.h>
#define CS8900_DRIVERNAME "CS8900"
/* although the registers are 16 bit, they are 32-bit aligned on the
EDB7111. so we have to read them as 32-bit registers and ignore the
upper 16-bits. i'm not sure if this holds for the EDB7211. */
#ifdef CONFIG_CS8900_BUS16
/* 16 bit aligned registers, 16 bit wide */
#define CS8900_REG u16
#elif defined(CONFIG_CS8900_BUS32)
/* 32 bit aligned registers, 16 bit wide (we ignore upper 16 bits) */
#define CS8900_REG u32
#else
#error unknown bussize ...
#endif
struct cs8900_regs {
CS8900_REG rtdata;
CS8900_REG pad0;
CS8900_REG txcmd;
CS8900_REG txlen;
CS8900_REG isq;
CS8900_REG pptr;
CS8900_REG pdata;
};
struct cs8900_priv {
struct cs8900_regs *regs;
};
#define ISQ_RxEvent 0x04
#define ISQ_TxEvent 0x08
#define ISQ_BufEvent 0x0C
#define ISQ_RxMissEvent 0x10
#define ISQ_TxColEvent 0x12
#define ISQ_EventMask 0x3F
/* packet page register offsets */
/* bus interface registers */
#define PP_ChipID 0x0000 /* Chip identifier - must be 0x630E */
#define PP_ChipRev 0x0002 /* Chip revision, model codes */
#define PP_IntReg 0x0022 /* Interrupt configuration */
#define PP_IntReg_IRQ0 0x0000 /* Use INTR0 pin */
#define PP_IntReg_IRQ1 0x0001 /* Use INTR1 pin */
#define PP_IntReg_IRQ2 0x0002 /* Use INTR2 pin */
#define PP_IntReg_IRQ3 0x0003 /* Use INTR3 pin */
/* status and control registers */
#define PP_RxCFG 0x0102 /* Receiver configuration */
#define PP_RxCFG_Skip1 0x0040 /* Skip (i.e. discard) current frame */
#define PP_RxCFG_Stream 0x0080 /* Enable streaming mode */
#define PP_RxCFG_RxOK 0x0100 /* RxOK interrupt enable */
#define PP_RxCFG_RxDMAonly 0x0200 /* Use RxDMA for all frames */
#define PP_RxCFG_AutoRxDMA 0x0400 /* Select RxDMA automatically */
#define PP_RxCFG_BufferCRC 0x0800 /* Include CRC characters in frame */
#define PP_RxCFG_CRC 0x1000 /* Enable interrupt on CRC error */
#define PP_RxCFG_RUNT 0x2000 /* Enable interrupt on RUNT frames */
#define PP_RxCFG_EXTRA 0x4000 /* Enable interrupt on frames with extra data */
#define PP_RxCTL 0x0104 /* Receiver control */
#define PP_RxCTL_IAHash 0x0040 /* Accept frames that match hash */
#define PP_RxCTL_Promiscuous 0x0080 /* Accept any frame */
#define PP_RxCTL_RxOK 0x0100 /* Accept well formed frames */
#define PP_RxCTL_Multicast 0x0200 /* Accept multicast frames */
#define PP_RxCTL_IA 0x0400 /* Accept frame that matches IA */
#define PP_RxCTL_Broadcast 0x0800 /* Accept broadcast frames */
#define PP_RxCTL_CRC 0x1000 /* Accept frames with bad CRC */
#define PP_RxCTL_RUNT 0x2000 /* Accept runt frames */
#define PP_RxCTL_EXTRA 0x4000 /* Accept frames that are too long */
#define PP_TxCFG 0x0106 /* Transmit configuration */
#define PP_TxCFG_CRS 0x0040 /* Enable interrupt on loss of carrier */
#define PP_TxCFG_SQE 0x0080 /* Enable interrupt on Signal Quality Error */
#define PP_TxCFG_TxOK 0x0100 /* Enable interrupt on successful xmits */
#define PP_TxCFG_Late 0x0200 /* Enable interrupt on "out of window" */
#define PP_TxCFG_Jabber 0x0400 /* Enable interrupt on jabber detect */
#define PP_TxCFG_Collision 0x0800 /* Enable interrupt if collision */
#define PP_TxCFG_16Collisions 0x8000 /* Enable interrupt if > 16 collisions */
#define PP_TxCmd 0x0108 /* Transmit command status */
#define PP_TxCmd_TxStart_5 0x0000 /* Start after 5 bytes in buffer */
#define PP_TxCmd_TxStart_381 0x0040 /* Start after 381 bytes in buffer */
#define PP_TxCmd_TxStart_1021 0x0080 /* Start after 1021 bytes in buffer */
#define PP_TxCmd_TxStart_Full 0x00C0 /* Start after all bytes loaded */
#define PP_TxCmd_Force 0x0100 /* Discard any pending packets */
#define PP_TxCmd_OneCollision 0x0200 /* Abort after a single collision */
#define PP_TxCmd_NoCRC 0x1000 /* Do not add CRC */
#define PP_TxCmd_NoPad 0x2000 /* Do not pad short packets */
#define PP_BufCFG 0x010A /* Buffer configuration */
#define PP_BufCFG_SWI 0x0040 /* Force interrupt via software */
#define PP_BufCFG_RxDMA 0x0080 /* Enable interrupt on Rx DMA */
#define PP_BufCFG_TxRDY 0x0100 /* Enable interrupt when ready for Tx */
#define PP_BufCFG_TxUE 0x0200 /* Enable interrupt in Tx underrun */
#define PP_BufCFG_RxMiss 0x0400 /* Enable interrupt on missed Rx packets */
#define PP_BufCFG_Rx128 0x0800 /* Enable Rx interrupt after 128 bytes */
#define PP_BufCFG_TxCol 0x1000 /* Enable int on Tx collision ctr overflow */
#define PP_BufCFG_Miss 0x2000 /* Enable int on Rx miss ctr overflow */
#define PP_BufCFG_RxDest 0x8000 /* Enable int on Rx dest addr match */
#define PP_LineCTL 0x0112 /* Line control */
#define PP_LineCTL_Rx 0x0040 /* Enable receiver */
#define PP_LineCTL_Tx 0x0080 /* Enable transmitter */
#define PP_LineCTL_AUIonly 0x0100 /* AUI interface only */
#define PP_LineCTL_AutoAUI10BT 0x0200 /* Autodetect AUI or 10BaseT interface */
#define PP_LineCTL_ModBackoffE 0x0800 /* Enable modified backoff algorithm */
#define PP_LineCTL_PolarityDis 0x1000 /* Disable Rx polarity autodetect */
#define PP_LineCTL_2partDefDis 0x2000 /* Disable two-part defferal */
#define PP_LineCTL_LoRxSquelch 0x4000 /* Reduce receiver squelch threshold */
#define PP_SelfCTL 0x0114 /* Chip self control */
#define PP_SelfCTL_Reset 0x0040 /* Self-clearing reset */
#define PP_SelfCTL_SWSuspend 0x0100 /* Initiate suspend mode */
#define PP_SelfCTL_HWSleepE 0x0200 /* Enable SLEEP input */
#define PP_SelfCTL_HWStandbyE 0x0400 /* Enable standby mode */
#define PP_SelfCTL_HC0E 0x1000 /* use HCB0 for LINK LED */
#define PP_SelfCTL_HC1E 0x2000 /* use HCB1 for BSTATUS LED */
#define PP_SelfCTL_HCB0 0x4000 /* control LINK LED if HC0E set */
#define PP_SelfCTL_HCB1 0x8000 /* control BSTATUS LED if HC1E set */
#define PP_BusCTL 0x0116 /* Bus control */
#define PP_BusCTL_ResetRxDMA 0x0040 /* Reset RxDMA pointer */
#define PP_BusCTL_DMAextend 0x0100 /* Extend DMA cycle */
#define PP_BusCTL_UseSA 0x0200 /* Assert MEMCS16 on address decode */
#define PP_BusCTL_MemoryE 0x0400 /* Enable memory mode */
#define PP_BusCTL_DMAburst 0x0800 /* Limit DMA access burst */
#define PP_BusCTL_IOCHRDYE 0x1000 /* Set IOCHRDY high impedence */
#define PP_BusCTL_RxDMAsize 0x2000 /* Set DMA buffer size 64KB */
#define PP_BusCTL_EnableIRQ 0x8000 /* Generate interrupt on interrupt event */
#define PP_TestCTL 0x0118 /* Test control */
#define PP_TestCTL_DisableLT 0x0080 /* Disable link status */
#define PP_TestCTL_ENDECloop 0x0200 /* Internal loopback */
#define PP_TestCTL_AUIloop 0x0400 /* AUI loopback */
#define PP_TestCTL_DisBackoff 0x0800 /* Disable backoff algorithm */
#define PP_TestCTL_FDX 0x4000 /* Enable full duplex mode */
#define PP_ISQ 0x0120 /* Interrupt Status Queue */
#define PP_RER 0x0124 /* Receive event */
#define PP_RER_IAHash 0x0040 /* Frame hash match */
#define PP_RER_Dribble 0x0080 /* Frame had 1-7 extra bits after last byte */
#define PP_RER_RxOK 0x0100 /* Frame received with no errors */
#define PP_RER_Hashed 0x0200 /* Frame address hashed OK */
#define PP_RER_IA 0x0400 /* Frame address matched IA */
#define PP_RER_Broadcast 0x0800 /* Broadcast frame */
#define PP_RER_CRC 0x1000 /* Frame had CRC error */
#define PP_RER_RUNT 0x2000 /* Runt frame */
#define PP_RER_EXTRA 0x4000 /* Frame was too long */
#define PP_TER 0x0128 /* Transmit event */
#define PP_TER_CRS 0x0040 /* Carrier lost */
#define PP_TER_SQE 0x0080 /* Signal Quality Error */
#define PP_TER_TxOK 0x0100 /* Packet sent without error */
#define PP_TER_Late 0x0200 /* Out of window */
#define PP_TER_Jabber 0x0400 /* Stuck transmit? */
#define PP_TER_NumCollisions 0x7800 /* Number of collisions */
#define PP_TER_16Collisions 0x8000 /* > 16 collisions */
#define PP_BER 0x012C /* Buffer event */
#define PP_BER_SWint 0x0040 /* Software interrupt */
#define PP_BER_RxDMAFrame 0x0080 /* Received framed DMAed */
#define PP_BER_Rdy4Tx 0x0100 /* Ready for transmission */
#define PP_BER_TxUnderrun 0x0200 /* Transmit underrun */
#define PP_BER_RxMiss 0x0400 /* Received frame missed */
#define PP_BER_Rx128 0x0800 /* 128 bytes received */
#define PP_BER_RxDest 0x8000 /* Received framed passed address filter */
#define PP_RxMiss 0x0130 /* Receiver miss counter */
#define PP_TxCol 0x0132 /* Transmit collision counter */
#define PP_LineSTAT 0x0134 /* Line status */
#define PP_LineSTAT_LinkOK 0x0080 /* Line is connected and working */
#define PP_LineSTAT_AUI 0x0100 /* Connected via AUI */
#define PP_LineSTAT_10BT 0x0200 /* Connected via twisted pair */
#define PP_LineSTAT_Polarity 0x1000 /* Line polarity OK (10BT only) */
#define PP_LineSTAT_CRS 0x4000 /* Frame being received */
#define PP_SelfSTAT 0x0136 /* Chip self status */
#define PP_SelfSTAT_33VActive 0x0040 /* supply voltage is 3.3V */
#define PP_SelfSTAT_InitD 0x0080 /* Chip initialization complete */
#define PP_SelfSTAT_SIBSY 0x0100 /* EEPROM is busy */
#define PP_SelfSTAT_EEPROM 0x0200 /* EEPROM present */
#define PP_SelfSTAT_EEPROM_OK 0x0400 /* EEPROM checks out */
#define PP_SelfSTAT_ELPresent 0x0800 /* External address latch logic available */
#define PP_SelfSTAT_EEsize 0x1000 /* Size of EEPROM */
#define PP_BusSTAT 0x0138 /* Bus status */
#define PP_BusSTAT_TxBid 0x0080 /* Tx error */
#define PP_BusSTAT_TxRDY 0x0100 /* Ready for Tx data */
#define PP_TDR 0x013C /* AUI Time Domain Reflectometer */
/* initiate transmit registers */
#define PP_TxCommand 0x0144 /* Tx Command */
#define PP_TxLength 0x0146 /* Tx Length */
/* address filter registers */
#define PP_LAF 0x0150 /* Logical address filter (6 bytes) */
#define PP_IA 0x0158 /* Individual address (MAC) */
/* EEPROM Kram */
#define SI_BUSY 0x0100
#define PP_EECMD 0x0040 /* NVR Interface Command register */
#define PP_EEData 0x0042 /* NVR Interface Data Register */
#define EEPROM_WRITE_EN 0x00F0
#define EEPROM_WRITE_DIS 0x0000
#define EEPROM_WRITE_CMD 0x0100
#define EEPROM_READ_CMD 0x0200
#define EEPROM_ERASE_CMD 0x0300
/* Exported functions */
int cs8900_e2prom_read(struct eth_device *dev, uchar, ushort *);
int cs8900_e2prom_write(struct eth_device *dev, uchar, ushort);
#endif /* CS8900_H */

View file

@ -0,0 +1,897 @@
/*
* Ethernet driver for TI TMS320DM644x (DaVinci) chips.
*
* Copyright (C) 2007 Sergey Kubushyn <ksi@koi8.net>
*
* Parts shamelessly stolen from TI's dm644x_emac.c. Original copyright
* follows:
*
* ----------------------------------------------------------------------------
*
* dm644x_emac.c
*
* TI DaVinci (DM644X) EMAC peripheral driver source for DV-EVM
*
* Copyright (C) 2005 Texas Instruments.
*
* ----------------------------------------------------------------------------
*
* SPDX-License-Identifier: GPL-2.0+
*
* Modifications:
* ver. 1.0: Sep 2005, Anant Gole - Created EMAC version for uBoot.
* ver 1.1: Nov 2005, Anant Gole - Extended the RX logic for multiple descriptors
*/
#include <common.h>
#include <command.h>
#include <net.h>
#include <miiphy.h>
#include <malloc.h>
#include <netdev.h>
#include <linux/compiler.h>
#include <asm/arch/emac_defs.h>
#include <asm/io.h>
#include "davinci_emac.h"
unsigned int emac_dbg = 0;
#define debug_emac(fmt,args...) if (emac_dbg) printf(fmt,##args)
#ifdef EMAC_HW_RAM_ADDR
static inline unsigned long BD_TO_HW(unsigned long x)
{
if (x == 0)
return 0;
return x - EMAC_WRAPPER_RAM_ADDR + EMAC_HW_RAM_ADDR;
}
static inline unsigned long HW_TO_BD(unsigned long x)
{
if (x == 0)
return 0;
return x - EMAC_HW_RAM_ADDR + EMAC_WRAPPER_RAM_ADDR;
}
#else
#define BD_TO_HW(x) (x)
#define HW_TO_BD(x) (x)
#endif
#ifdef DAVINCI_EMAC_GIG_ENABLE
#define emac_gigabit_enable(phy_addr) davinci_eth_gigabit_enable(phy_addr)
#else
#define emac_gigabit_enable(phy_addr) /* no gigabit to enable */
#endif
#if !defined(CONFIG_SYS_EMAC_TI_CLKDIV)
#define CONFIG_SYS_EMAC_TI_CLKDIV ((EMAC_MDIO_BUS_FREQ / \
EMAC_MDIO_CLOCK_FREQ) - 1)
#endif
static void davinci_eth_mdio_enable(void);
static int gen_init_phy(int phy_addr);
static int gen_is_phy_connected(int phy_addr);
static int gen_get_link_speed(int phy_addr);
static int gen_auto_negotiate(int phy_addr);
void eth_mdio_enable(void)
{
davinci_eth_mdio_enable();
}
/* EMAC Addresses */
static volatile emac_regs *adap_emac = (emac_regs *)EMAC_BASE_ADDR;
static volatile ewrap_regs *adap_ewrap = (ewrap_regs *)EMAC_WRAPPER_BASE_ADDR;
static volatile mdio_regs *adap_mdio = (mdio_regs *)EMAC_MDIO_BASE_ADDR;
/* EMAC descriptors */
static volatile emac_desc *emac_rx_desc = (emac_desc *)(EMAC_WRAPPER_RAM_ADDR + EMAC_RX_DESC_BASE);
static volatile emac_desc *emac_tx_desc = (emac_desc *)(EMAC_WRAPPER_RAM_ADDR + EMAC_TX_DESC_BASE);
static volatile emac_desc *emac_rx_active_head = 0;
static volatile emac_desc *emac_rx_active_tail = 0;
static int emac_rx_queue_active = 0;
/* Receive packet buffers */
static unsigned char emac_rx_buffers[EMAC_MAX_RX_BUFFERS * EMAC_RXBUF_SIZE]
__aligned(ARCH_DMA_MINALIGN);
#ifndef CONFIG_SYS_DAVINCI_EMAC_PHY_COUNT
#define CONFIG_SYS_DAVINCI_EMAC_PHY_COUNT 3
#endif
/* PHY address for a discovered PHY (0xff - not found) */
static u_int8_t active_phy_addr[CONFIG_SYS_DAVINCI_EMAC_PHY_COUNT];
/* number of PHY found active */
static u_int8_t num_phy;
phy_t phy[CONFIG_SYS_DAVINCI_EMAC_PHY_COUNT];
static inline void davinci_flush_rx_descs(void)
{
/* flush the whole RX descs area */
flush_dcache_range(EMAC_WRAPPER_RAM_ADDR + EMAC_RX_DESC_BASE,
EMAC_WRAPPER_RAM_ADDR + EMAC_TX_DESC_BASE);
}
static inline void davinci_invalidate_rx_descs(void)
{
/* invalidate the whole RX descs area */
invalidate_dcache_range(EMAC_WRAPPER_RAM_ADDR + EMAC_RX_DESC_BASE,
EMAC_WRAPPER_RAM_ADDR + EMAC_TX_DESC_BASE);
}
static inline void davinci_flush_desc(emac_desc *desc)
{
flush_dcache_range((unsigned long)desc,
(unsigned long)desc + sizeof(*desc));
}
static int davinci_eth_set_mac_addr(struct eth_device *dev)
{
unsigned long mac_hi;
unsigned long mac_lo;
/*
* Set MAC Addresses & Init multicast Hash to 0 (disable any multicast
* receive)
* Using channel 0 only - other channels are disabled
* */
writel(0, &adap_emac->MACINDEX);
mac_hi = (dev->enetaddr[3] << 24) |
(dev->enetaddr[2] << 16) |
(dev->enetaddr[1] << 8) |
(dev->enetaddr[0]);
mac_lo = (dev->enetaddr[5] << 8) |
(dev->enetaddr[4]);
writel(mac_hi, &adap_emac->MACADDRHI);
#if defined(DAVINCI_EMAC_VERSION2)
writel(mac_lo | EMAC_MAC_ADDR_IS_VALID | EMAC_MAC_ADDR_MATCH,
&adap_emac->MACADDRLO);
#else
writel(mac_lo, &adap_emac->MACADDRLO);
#endif
writel(0, &adap_emac->MACHASH1);
writel(0, &adap_emac->MACHASH2);
/* Set source MAC address - REQUIRED */
writel(mac_hi, &adap_emac->MACSRCADDRHI);
writel(mac_lo, &adap_emac->MACSRCADDRLO);
return 0;
}
static void davinci_eth_mdio_enable(void)
{
u_int32_t clkdiv;
clkdiv = CONFIG_SYS_EMAC_TI_CLKDIV;
writel((clkdiv & 0xff) |
MDIO_CONTROL_ENABLE |
MDIO_CONTROL_FAULT |
MDIO_CONTROL_FAULT_ENABLE,
&adap_mdio->CONTROL);
while (readl(&adap_mdio->CONTROL) & MDIO_CONTROL_IDLE)
;
}
/*
* Tries to find an active connected PHY. Returns 1 if address if found.
* If no active PHY (or more than one PHY) found returns 0.
* Sets active_phy_addr variable.
*/
static int davinci_eth_phy_detect(void)
{
u_int32_t phy_act_state;
int i;
int j;
unsigned int count = 0;
for (i = 0; i < CONFIG_SYS_DAVINCI_EMAC_PHY_COUNT; i++)
active_phy_addr[i] = 0xff;
udelay(1000);
phy_act_state = readl(&adap_mdio->ALIVE);
if (phy_act_state == 0)
return 0; /* No active PHYs */
debug_emac("davinci_eth_phy_detect(), ALIVE = 0x%08x\n", phy_act_state);
for (i = 0, j = 0; i < 32; i++)
if (phy_act_state & (1 << i)) {
count++;
if (count <= CONFIG_SYS_DAVINCI_EMAC_PHY_COUNT) {
active_phy_addr[j++] = i;
} else {
printf("%s: to many PHYs detected.\n",
__func__);
count = 0;
break;
}
}
num_phy = count;
return count;
}
/* Read a PHY register via MDIO inteface. Returns 1 on success, 0 otherwise */
int davinci_eth_phy_read(u_int8_t phy_addr, u_int8_t reg_num, u_int16_t *data)
{
int tmp;
while (readl(&adap_mdio->USERACCESS0) & MDIO_USERACCESS0_GO)
;
writel(MDIO_USERACCESS0_GO |
MDIO_USERACCESS0_WRITE_READ |
((reg_num & 0x1f) << 21) |
((phy_addr & 0x1f) << 16),
&adap_mdio->USERACCESS0);
/* Wait for command to complete */
while ((tmp = readl(&adap_mdio->USERACCESS0)) & MDIO_USERACCESS0_GO)
;
if (tmp & MDIO_USERACCESS0_ACK) {
*data = tmp & 0xffff;
return(1);
}
*data = -1;
return(0);
}
/* Write to a PHY register via MDIO inteface. Blocks until operation is complete. */
int davinci_eth_phy_write(u_int8_t phy_addr, u_int8_t reg_num, u_int16_t data)
{
while (readl(&adap_mdio->USERACCESS0) & MDIO_USERACCESS0_GO)
;
writel(MDIO_USERACCESS0_GO |
MDIO_USERACCESS0_WRITE_WRITE |
((reg_num & 0x1f) << 21) |
((phy_addr & 0x1f) << 16) |
(data & 0xffff),
&adap_mdio->USERACCESS0);
/* Wait for command to complete */
while (readl(&adap_mdio->USERACCESS0) & MDIO_USERACCESS0_GO)
;
return(1);
}
/* PHY functions for a generic PHY */
static int gen_init_phy(int phy_addr)
{
int ret = 1;
if (gen_get_link_speed(phy_addr)) {
/* Try another time */
ret = gen_get_link_speed(phy_addr);
}
return(ret);
}
static int gen_is_phy_connected(int phy_addr)
{
u_int16_t dummy;
return davinci_eth_phy_read(phy_addr, MII_PHYSID1, &dummy);
}
static int get_active_phy(void)
{
int i;
for (i = 0; i < num_phy; i++)
if (phy[i].get_link_speed(active_phy_addr[i]))
return i;
return -1; /* Return error if no link */
}
static int gen_get_link_speed(int phy_addr)
{
u_int16_t tmp;
if (davinci_eth_phy_read(phy_addr, MII_STATUS_REG, &tmp) &&
(tmp & 0x04)) {
#if defined(CONFIG_DRIVER_TI_EMAC_USE_RMII) && \
defined(CONFIG_MACH_DAVINCI_DA850_EVM)
davinci_eth_phy_read(phy_addr, MII_LPA, &tmp);
/* Speed doesn't matter, there is no setting for it in EMAC. */
if (tmp & (LPA_100FULL | LPA_10FULL)) {
/* set EMAC for Full Duplex */
writel(EMAC_MACCONTROL_MIIEN_ENABLE |
EMAC_MACCONTROL_FULLDUPLEX_ENABLE,
&adap_emac->MACCONTROL);
} else {
/*set EMAC for Half Duplex */
writel(EMAC_MACCONTROL_MIIEN_ENABLE,
&adap_emac->MACCONTROL);
}
if (tmp & (LPA_100FULL | LPA_100HALF))
writel(readl(&adap_emac->MACCONTROL) |
EMAC_MACCONTROL_RMIISPEED_100,
&adap_emac->MACCONTROL);
else
writel(readl(&adap_emac->MACCONTROL) &
~EMAC_MACCONTROL_RMIISPEED_100,
&adap_emac->MACCONTROL);
#endif
return(1);
}
return(0);
}
static int gen_auto_negotiate(int phy_addr)
{
u_int16_t tmp;
u_int16_t val;
unsigned long cntr = 0;
if (!davinci_eth_phy_read(phy_addr, MII_BMCR, &tmp))
return 0;
val = tmp | BMCR_FULLDPLX | BMCR_ANENABLE |
BMCR_SPEED100;
davinci_eth_phy_write(phy_addr, MII_BMCR, val);
if (!davinci_eth_phy_read(phy_addr, MII_ADVERTISE, &val))
return 0;
val |= (ADVERTISE_100FULL | ADVERTISE_100HALF | ADVERTISE_10FULL |
ADVERTISE_10HALF);
davinci_eth_phy_write(phy_addr, MII_ADVERTISE, val);
if (!davinci_eth_phy_read(phy_addr, MII_BMCR, &tmp))
return(0);
/* Restart Auto_negotiation */
tmp |= BMCR_ANRESTART;
davinci_eth_phy_write(phy_addr, MII_BMCR, tmp);
/*check AutoNegotiate complete */
do {
udelay(40000);
if (!davinci_eth_phy_read(phy_addr, MII_BMSR, &tmp))
return 0;
if (tmp & BMSR_ANEGCOMPLETE)
break;
cntr++;
} while (cntr < 200);
if (!davinci_eth_phy_read(phy_addr, MII_BMSR, &tmp))
return(0);
if (!(tmp & BMSR_ANEGCOMPLETE))
return(0);
return(gen_get_link_speed(phy_addr));
}
/* End of generic PHY functions */
#if defined(CONFIG_MII) || defined(CONFIG_CMD_MII)
static int davinci_mii_phy_read(const char *devname, unsigned char addr, unsigned char reg, unsigned short *value)
{
return(davinci_eth_phy_read(addr, reg, value) ? 0 : 1);
}
static int davinci_mii_phy_write(const char *devname, unsigned char addr, unsigned char reg, unsigned short value)
{
return(davinci_eth_phy_write(addr, reg, value) ? 0 : 1);
}
#endif
static void __attribute__((unused)) davinci_eth_gigabit_enable(int phy_addr)
{
u_int16_t data;
if (davinci_eth_phy_read(phy_addr, 0, &data)) {
if (data & (1 << 6)) { /* speed selection MSB */
/*
* Check if link detected is giga-bit
* If Gigabit mode detected, enable gigbit in MAC
*/
writel(readl(&adap_emac->MACCONTROL) |
EMAC_MACCONTROL_GIGFORCE |
EMAC_MACCONTROL_GIGABIT_ENABLE,
&adap_emac->MACCONTROL);
}
}
}
/* Eth device open */
static int davinci_eth_open(struct eth_device *dev, bd_t *bis)
{
dv_reg_p addr;
u_int32_t clkdiv, cnt;
volatile emac_desc *rx_desc;
int index;
debug_emac("+ emac_open\n");
/* Reset EMAC module and disable interrupts in wrapper */
writel(1, &adap_emac->SOFTRESET);
while (readl(&adap_emac->SOFTRESET) != 0)
;
#if defined(DAVINCI_EMAC_VERSION2)
writel(1, &adap_ewrap->softrst);
while (readl(&adap_ewrap->softrst) != 0)
;
#else
writel(0, &adap_ewrap->EWCTL);
for (cnt = 0; cnt < 5; cnt++) {
clkdiv = readl(&adap_ewrap->EWCTL);
}
#endif
#if defined(CONFIG_DRIVER_TI_EMAC_USE_RMII) && \
defined(CONFIG_MACH_DAVINCI_DA850_EVM)
adap_ewrap->c0rxen = adap_ewrap->c1rxen = adap_ewrap->c2rxen = 0;
adap_ewrap->c0txen = adap_ewrap->c1txen = adap_ewrap->c2txen = 0;
adap_ewrap->c0miscen = adap_ewrap->c1miscen = adap_ewrap->c2miscen = 0;
#endif
rx_desc = emac_rx_desc;
writel(1, &adap_emac->TXCONTROL);
writel(1, &adap_emac->RXCONTROL);
davinci_eth_set_mac_addr(dev);
/* Set DMA 8 TX / 8 RX Head pointers to 0 */
addr = &adap_emac->TX0HDP;
for(cnt = 0; cnt < 16; cnt++)
writel(0, addr++);
addr = &adap_emac->RX0HDP;
for(cnt = 0; cnt < 16; cnt++)
writel(0, addr++);
/* Clear Statistics (do this before setting MacControl register) */
addr = &adap_emac->RXGOODFRAMES;
for(cnt = 0; cnt < EMAC_NUM_STATS; cnt++)
writel(0, addr++);
/* No multicast addressing */
writel(0, &adap_emac->MACHASH1);
writel(0, &adap_emac->MACHASH2);
/* Create RX queue and set receive process in place */
emac_rx_active_head = emac_rx_desc;
for (cnt = 0; cnt < EMAC_MAX_RX_BUFFERS; cnt++) {
rx_desc->next = BD_TO_HW((u_int32_t)(rx_desc + 1));
rx_desc->buffer = &emac_rx_buffers[cnt * EMAC_RXBUF_SIZE];
rx_desc->buff_off_len = EMAC_MAX_ETHERNET_PKT_SIZE;
rx_desc->pkt_flag_len = EMAC_CPPI_OWNERSHIP_BIT;
rx_desc++;
}
/* Finalize the rx desc list */
rx_desc--;
rx_desc->next = 0;
emac_rx_active_tail = rx_desc;
emac_rx_queue_active = 1;
davinci_flush_rx_descs();
/* Enable TX/RX */
writel(EMAC_MAX_ETHERNET_PKT_SIZE, &adap_emac->RXMAXLEN);
writel(0, &adap_emac->RXBUFFEROFFSET);
/*
* No fancy configs - Use this for promiscous debug
* - EMAC_RXMBPENABLE_RXCAFEN_ENABLE
*/
writel(EMAC_RXMBPENABLE_RXBROADEN, &adap_emac->RXMBPENABLE);
/* Enable ch 0 only */
writel(1, &adap_emac->RXUNICASTSET);
/* Enable MII interface and Full duplex mode */
#if defined(CONFIG_SOC_DA8XX) || \
(defined(CONFIG_OMAP34XX) && defined(CONFIG_DRIVER_TI_EMAC_USE_RMII))
writel((EMAC_MACCONTROL_MIIEN_ENABLE |
EMAC_MACCONTROL_FULLDUPLEX_ENABLE |
EMAC_MACCONTROL_RMIISPEED_100),
&adap_emac->MACCONTROL);
#else
writel((EMAC_MACCONTROL_MIIEN_ENABLE |
EMAC_MACCONTROL_FULLDUPLEX_ENABLE),
&adap_emac->MACCONTROL);
#endif
/* Init MDIO & get link state */
clkdiv = CONFIG_SYS_EMAC_TI_CLKDIV;
writel((clkdiv & 0xff) | MDIO_CONTROL_ENABLE | MDIO_CONTROL_FAULT,
&adap_mdio->CONTROL);
/* We need to wait for MDIO to start */
udelay(1000);
index = get_active_phy();
if (index == -1)
return(0);
emac_gigabit_enable(active_phy_addr[index]);
/* Start receive process */
writel(BD_TO_HW((u_int32_t)emac_rx_desc), &adap_emac->RX0HDP);
debug_emac("- emac_open\n");
return(1);
}
/* EMAC Channel Teardown */
static void davinci_eth_ch_teardown(int ch)
{
dv_reg dly = 0xff;
dv_reg cnt;
debug_emac("+ emac_ch_teardown\n");
if (ch == EMAC_CH_TX) {
/* Init TX channel teardown */
writel(0, &adap_emac->TXTEARDOWN);
do {
/*
* Wait here for Tx teardown completion interrupt to
* occur. Note: A task delay can be called here to pend
* rather than occupying CPU cycles - anyway it has
* been found that teardown takes very few cpu cycles
* and does not affect functionality
*/
dly--;
udelay(1);
if (dly == 0)
break;
cnt = readl(&adap_emac->TX0CP);
} while (cnt != 0xfffffffc);
writel(cnt, &adap_emac->TX0CP);
writel(0, &adap_emac->TX0HDP);
} else {
/* Init RX channel teardown */
writel(0, &adap_emac->RXTEARDOWN);
do {
/*
* Wait here for Rx teardown completion interrupt to
* occur. Note: A task delay can be called here to pend
* rather than occupying CPU cycles - anyway it has
* been found that teardown takes very few cpu cycles
* and does not affect functionality
*/
dly--;
udelay(1);
if (dly == 0)
break;
cnt = readl(&adap_emac->RX0CP);
} while (cnt != 0xfffffffc);
writel(cnt, &adap_emac->RX0CP);
writel(0, &adap_emac->RX0HDP);
}
debug_emac("- emac_ch_teardown\n");
}
/* Eth device close */
static void davinci_eth_close(struct eth_device *dev)
{
debug_emac("+ emac_close\n");
davinci_eth_ch_teardown(EMAC_CH_TX); /* TX Channel teardown */
if (readl(&adap_emac->RXCONTROL) & 1)
davinci_eth_ch_teardown(EMAC_CH_RX); /* RX Channel teardown */
/* Reset EMAC module and disable interrupts in wrapper */
writel(1, &adap_emac->SOFTRESET);
#if defined(DAVINCI_EMAC_VERSION2)
writel(1, &adap_ewrap->softrst);
#else
writel(0, &adap_ewrap->EWCTL);
#endif
#if defined(CONFIG_DRIVER_TI_EMAC_USE_RMII) && \
defined(CONFIG_MACH_DAVINCI_DA850_EVM)
adap_ewrap->c0rxen = adap_ewrap->c1rxen = adap_ewrap->c2rxen = 0;
adap_ewrap->c0txen = adap_ewrap->c1txen = adap_ewrap->c2txen = 0;
adap_ewrap->c0miscen = adap_ewrap->c1miscen = adap_ewrap->c2miscen = 0;
#endif
debug_emac("- emac_close\n");
}
static int tx_send_loop = 0;
/*
* This function sends a single packet on the network and returns
* positive number (number of bytes transmitted) or negative for error
*/
static int davinci_eth_send_packet (struct eth_device *dev,
void *packet, int length)
{
int ret_status = -1;
int index;
tx_send_loop = 0;
index = get_active_phy();
if (index == -1) {
printf(" WARN: emac_send_packet: No link\n");
return (ret_status);
}
emac_gigabit_enable(active_phy_addr[index]);
/* Check packet size and if < EMAC_MIN_ETHERNET_PKT_SIZE, pad it up */
if (length < EMAC_MIN_ETHERNET_PKT_SIZE) {
length = EMAC_MIN_ETHERNET_PKT_SIZE;
}
/* Populate the TX descriptor */
emac_tx_desc->next = 0;
emac_tx_desc->buffer = (u_int8_t *) packet;
emac_tx_desc->buff_off_len = (length & 0xffff);
emac_tx_desc->pkt_flag_len = ((length & 0xffff) |
EMAC_CPPI_SOP_BIT |
EMAC_CPPI_OWNERSHIP_BIT |
EMAC_CPPI_EOP_BIT);
flush_dcache_range((unsigned long)packet,
(unsigned long)packet + length);
davinci_flush_desc(emac_tx_desc);
/* Send the packet */
writel(BD_TO_HW((unsigned long)emac_tx_desc), &adap_emac->TX0HDP);
/* Wait for packet to complete or link down */
while (1) {
if (!phy[index].get_link_speed(active_phy_addr[index])) {
davinci_eth_ch_teardown (EMAC_CH_TX);
return (ret_status);
}
emac_gigabit_enable(active_phy_addr[index]);
if (readl(&adap_emac->TXINTSTATRAW) & 0x01) {
ret_status = length;
break;
}
tx_send_loop++;
}
return (ret_status);
}
/*
* This function handles receipt of a packet from the network
*/
static int davinci_eth_rcv_packet (struct eth_device *dev)
{
volatile emac_desc *rx_curr_desc;
volatile emac_desc *curr_desc;
volatile emac_desc *tail_desc;
int status, ret = -1;
davinci_invalidate_rx_descs();
rx_curr_desc = emac_rx_active_head;
status = rx_curr_desc->pkt_flag_len;
if ((rx_curr_desc) && ((status & EMAC_CPPI_OWNERSHIP_BIT) == 0)) {
if (status & EMAC_CPPI_RX_ERROR_FRAME) {
/* Error in packet - discard it and requeue desc */
printf ("WARN: emac_rcv_pkt: Error in packet\n");
} else {
unsigned long tmp = (unsigned long)rx_curr_desc->buffer;
invalidate_dcache_range(tmp, tmp + EMAC_RXBUF_SIZE);
net_process_received_packet(
rx_curr_desc->buffer,
rx_curr_desc->buff_off_len & 0xffff);
ret = rx_curr_desc->buff_off_len & 0xffff;
}
/* Ack received packet descriptor */
writel(BD_TO_HW((ulong)rx_curr_desc), &adap_emac->RX0CP);
curr_desc = rx_curr_desc;
emac_rx_active_head =
(volatile emac_desc *) (HW_TO_BD(rx_curr_desc->next));
if (status & EMAC_CPPI_EOQ_BIT) {
if (emac_rx_active_head) {
writel(BD_TO_HW((ulong)emac_rx_active_head),
&adap_emac->RX0HDP);
} else {
emac_rx_queue_active = 0;
printf ("INFO:emac_rcv_packet: RX Queue not active\n");
}
}
/* Recycle RX descriptor */
rx_curr_desc->buff_off_len = EMAC_MAX_ETHERNET_PKT_SIZE;
rx_curr_desc->pkt_flag_len = EMAC_CPPI_OWNERSHIP_BIT;
rx_curr_desc->next = 0;
davinci_flush_desc(rx_curr_desc);
if (emac_rx_active_head == 0) {
printf ("INFO: emac_rcv_pkt: active queue head = 0\n");
emac_rx_active_head = curr_desc;
emac_rx_active_tail = curr_desc;
if (emac_rx_queue_active != 0) {
writel(BD_TO_HW((ulong)emac_rx_active_head),
&adap_emac->RX0HDP);
printf ("INFO: emac_rcv_pkt: active queue head = 0, HDP fired\n");
emac_rx_queue_active = 1;
}
} else {
tail_desc = emac_rx_active_tail;
emac_rx_active_tail = curr_desc;
tail_desc->next = BD_TO_HW((ulong) curr_desc);
status = tail_desc->pkt_flag_len;
if (status & EMAC_CPPI_EOQ_BIT) {
davinci_flush_desc(tail_desc);
writel(BD_TO_HW((ulong)curr_desc),
&adap_emac->RX0HDP);
status &= ~EMAC_CPPI_EOQ_BIT;
tail_desc->pkt_flag_len = status;
}
davinci_flush_desc(tail_desc);
}
return (ret);
}
return (0);
}
/*
* This function initializes the emac hardware. It does NOT initialize
* EMAC modules power or pin multiplexors, that is done by board_init()
* much earlier in bootup process. Returns 1 on success, 0 otherwise.
*/
int davinci_emac_initialize(void)
{
u_int32_t phy_id;
u_int16_t tmp;
int i;
int ret;
struct eth_device *dev;
dev = malloc(sizeof *dev);
if (dev == NULL)
return -1;
memset(dev, 0, sizeof *dev);
sprintf(dev->name, "DaVinci-EMAC");
dev->iobase = 0;
dev->init = davinci_eth_open;
dev->halt = davinci_eth_close;
dev->send = davinci_eth_send_packet;
dev->recv = davinci_eth_rcv_packet;
dev->write_hwaddr = davinci_eth_set_mac_addr;
eth_register(dev);
davinci_eth_mdio_enable();
/* let the EMAC detect the PHYs */
udelay(5000);
for (i = 0; i < 256; i++) {
if (readl(&adap_mdio->ALIVE))
break;
udelay(1000);
}
if (i >= 256) {
printf("No ETH PHY detected!!!\n");
return(0);
}
/* Find if PHY(s) is/are connected */
ret = davinci_eth_phy_detect();
if (!ret)
return(0);
else
debug_emac(" %d ETH PHY detected\n", ret);
/* Get PHY ID and initialize phy_ops for a detected PHY */
for (i = 0; i < num_phy; i++) {
if (!davinci_eth_phy_read(active_phy_addr[i], MII_PHYSID1,
&tmp)) {
active_phy_addr[i] = 0xff;
continue;
}
phy_id = (tmp << 16) & 0xffff0000;
if (!davinci_eth_phy_read(active_phy_addr[i], MII_PHYSID2,
&tmp)) {
active_phy_addr[i] = 0xff;
continue;
}
phy_id |= tmp & 0x0000ffff;
switch (phy_id) {
#ifdef PHY_KSZ8873
case PHY_KSZ8873:
sprintf(phy[i].name, "KSZ8873 @ 0x%02x",
active_phy_addr[i]);
phy[i].init = ksz8873_init_phy;
phy[i].is_phy_connected = ksz8873_is_phy_connected;
phy[i].get_link_speed = ksz8873_get_link_speed;
phy[i].auto_negotiate = ksz8873_auto_negotiate;
break;
#endif
#ifdef PHY_LXT972
case PHY_LXT972:
sprintf(phy[i].name, "LXT972 @ 0x%02x",
active_phy_addr[i]);
phy[i].init = lxt972_init_phy;
phy[i].is_phy_connected = lxt972_is_phy_connected;
phy[i].get_link_speed = lxt972_get_link_speed;
phy[i].auto_negotiate = lxt972_auto_negotiate;
break;
#endif
#ifdef PHY_DP83848
case PHY_DP83848:
sprintf(phy[i].name, "DP83848 @ 0x%02x",
active_phy_addr[i]);
phy[i].init = dp83848_init_phy;
phy[i].is_phy_connected = dp83848_is_phy_connected;
phy[i].get_link_speed = dp83848_get_link_speed;
phy[i].auto_negotiate = dp83848_auto_negotiate;
break;
#endif
#ifdef PHY_ET1011C
case PHY_ET1011C:
sprintf(phy[i].name, "ET1011C @ 0x%02x",
active_phy_addr[i]);
phy[i].init = gen_init_phy;
phy[i].is_phy_connected = gen_is_phy_connected;
phy[i].get_link_speed = et1011c_get_link_speed;
phy[i].auto_negotiate = gen_auto_negotiate;
break;
#endif
default:
sprintf(phy[i].name, "GENERIC @ 0x%02x",
active_phy_addr[i]);
phy[i].init = gen_init_phy;
phy[i].is_phy_connected = gen_is_phy_connected;
phy[i].get_link_speed = gen_get_link_speed;
phy[i].auto_negotiate = gen_auto_negotiate;
}
debug("Ethernet PHY: %s\n", phy[i].name);
miiphy_register(phy[i].name, davinci_mii_phy_read,
davinci_mii_phy_write);
}
#if defined(CONFIG_DRIVER_TI_EMAC_USE_RMII) && \
defined(CONFIG_MACH_DAVINCI_DA850_EVM) && \
!defined(CONFIG_DRIVER_TI_EMAC_RMII_NO_NEGOTIATE)
for (i = 0; i < num_phy; i++) {
if (phy[i].is_phy_connected(i))
phy[i].auto_negotiate(i);
}
#endif
return(1);
}

View file

@ -0,0 +1,303 @@
/*
* Copyright (C) 2011 Ilya Yanok, Emcraft Systems
*
* Based on: mach-davinci/emac_defs.h
* Copyright (C) 2007 Sergey Kubushyn <ksi@koi8.net>
*
* SPDX-License-Identifier: GPL-2.0+
*/
#ifndef _DAVINCI_EMAC_H_
#define _DAVINCI_EMAC_H_
/* Ethernet Min/Max packet size */
#define EMAC_MIN_ETHERNET_PKT_SIZE 60
#define EMAC_MAX_ETHERNET_PKT_SIZE 1518
/* Buffer size (should be aligned on 32 byte and cache line) */
#define EMAC_RXBUF_SIZE ALIGN(ALIGN(EMAC_MAX_ETHERNET_PKT_SIZE, 32),\
ARCH_DMA_MINALIGN)
/* Number of RX packet buffers
* NOTE: Only 1 buffer supported as of now
*/
#define EMAC_MAX_RX_BUFFERS 10
/***********************************************
******** Internally used macros ***************
***********************************************/
#define EMAC_CH_TX 1
#define EMAC_CH_RX 0
/* Each descriptor occupies 4 words, lets start RX desc's at 0 and
* reserve space for 64 descriptors max
*/
#define EMAC_RX_DESC_BASE 0x0
#define EMAC_TX_DESC_BASE 0x1000
/* EMAC Teardown value */
#define EMAC_TEARDOWN_VALUE 0xfffffffc
/* MII Status Register */
#define MII_STATUS_REG 1
/* Number of statistics registers */
#define EMAC_NUM_STATS 36
/* EMAC Descriptor */
typedef volatile struct _emac_desc
{
u_int32_t next; /* Pointer to next descriptor
in chain */
u_int8_t *buffer; /* Pointer to data buffer */
u_int32_t buff_off_len; /* Buffer Offset(MSW) and Length(LSW) */
u_int32_t pkt_flag_len; /* Packet Flags(MSW) and Length(LSW) */
} emac_desc;
/* CPPI bit positions */
#define EMAC_CPPI_SOP_BIT (0x80000000)
#define EMAC_CPPI_EOP_BIT (0x40000000)
#define EMAC_CPPI_OWNERSHIP_BIT (0x20000000)
#define EMAC_CPPI_EOQ_BIT (0x10000000)
#define EMAC_CPPI_TEARDOWN_COMPLETE_BIT (0x08000000)
#define EMAC_CPPI_PASS_CRC_BIT (0x04000000)
#define EMAC_CPPI_RX_ERROR_FRAME (0x03fc0000)
#define EMAC_MACCONTROL_MIIEN_ENABLE (0x20)
#define EMAC_MACCONTROL_FULLDUPLEX_ENABLE (0x1)
#define EMAC_MACCONTROL_GIGABIT_ENABLE (1 << 7)
#define EMAC_MACCONTROL_GIGFORCE (1 << 17)
#define EMAC_MACCONTROL_RMIISPEED_100 (1 << 15)
#define EMAC_MAC_ADDR_MATCH (1 << 19)
#define EMAC_MAC_ADDR_IS_VALID (1 << 20)
#define EMAC_RXMBPENABLE_RXCAFEN_ENABLE (0x200000)
#define EMAC_RXMBPENABLE_RXBROADEN (0x2000)
#define MDIO_CONTROL_IDLE (0x80000000)
#define MDIO_CONTROL_ENABLE (0x40000000)
#define MDIO_CONTROL_FAULT_ENABLE (0x40000)
#define MDIO_CONTROL_FAULT (0x80000)
#define MDIO_USERACCESS0_GO (0x80000000)
#define MDIO_USERACCESS0_WRITE_READ (0x0)
#define MDIO_USERACCESS0_WRITE_WRITE (0x40000000)
#define MDIO_USERACCESS0_ACK (0x20000000)
/* Ethernet MAC Registers Structure */
typedef struct {
dv_reg TXIDVER;
dv_reg TXCONTROL;
dv_reg TXTEARDOWN;
u_int8_t RSVD0[4];
dv_reg RXIDVER;
dv_reg RXCONTROL;
dv_reg RXTEARDOWN;
u_int8_t RSVD1[100];
dv_reg TXINTSTATRAW;
dv_reg TXINTSTATMASKED;
dv_reg TXINTMASKSET;
dv_reg TXINTMASKCLEAR;
dv_reg MACINVECTOR;
u_int8_t RSVD2[12];
dv_reg RXINTSTATRAW;
dv_reg RXINTSTATMASKED;
dv_reg RXINTMASKSET;
dv_reg RXINTMASKCLEAR;
dv_reg MACINTSTATRAW;
dv_reg MACINTSTATMASKED;
dv_reg MACINTMASKSET;
dv_reg MACINTMASKCLEAR;
u_int8_t RSVD3[64];
dv_reg RXMBPENABLE;
dv_reg RXUNICASTSET;
dv_reg RXUNICASTCLEAR;
dv_reg RXMAXLEN;
dv_reg RXBUFFEROFFSET;
dv_reg RXFILTERLOWTHRESH;
u_int8_t RSVD4[8];
dv_reg RX0FLOWTHRESH;
dv_reg RX1FLOWTHRESH;
dv_reg RX2FLOWTHRESH;
dv_reg RX3FLOWTHRESH;
dv_reg RX4FLOWTHRESH;
dv_reg RX5FLOWTHRESH;
dv_reg RX6FLOWTHRESH;
dv_reg RX7FLOWTHRESH;
dv_reg RX0FREEBUFFER;
dv_reg RX1FREEBUFFER;
dv_reg RX2FREEBUFFER;
dv_reg RX3FREEBUFFER;
dv_reg RX4FREEBUFFER;
dv_reg RX5FREEBUFFER;
dv_reg RX6FREEBUFFER;
dv_reg RX7FREEBUFFER;
dv_reg MACCONTROL;
dv_reg MACSTATUS;
dv_reg EMCONTROL;
dv_reg FIFOCONTROL;
dv_reg MACCONFIG;
dv_reg SOFTRESET;
u_int8_t RSVD5[88];
dv_reg MACSRCADDRLO;
dv_reg MACSRCADDRHI;
dv_reg MACHASH1;
dv_reg MACHASH2;
dv_reg BOFFTEST;
dv_reg TPACETEST;
dv_reg RXPAUSE;
dv_reg TXPAUSE;
u_int8_t RSVD6[16];
dv_reg RXGOODFRAMES;
dv_reg RXBCASTFRAMES;
dv_reg RXMCASTFRAMES;
dv_reg RXPAUSEFRAMES;
dv_reg RXCRCERRORS;
dv_reg RXALIGNCODEERRORS;
dv_reg RXOVERSIZED;
dv_reg RXJABBER;
dv_reg RXUNDERSIZED;
dv_reg RXFRAGMENTS;
dv_reg RXFILTERED;
dv_reg RXQOSFILTERED;
dv_reg RXOCTETS;
dv_reg TXGOODFRAMES;
dv_reg TXBCASTFRAMES;
dv_reg TXMCASTFRAMES;
dv_reg TXPAUSEFRAMES;
dv_reg TXDEFERRED;
dv_reg TXCOLLISION;
dv_reg TXSINGLECOLL;
dv_reg TXMULTICOLL;
dv_reg TXEXCESSIVECOLL;
dv_reg TXLATECOLL;
dv_reg TXUNDERRUN;
dv_reg TXCARRIERSENSE;
dv_reg TXOCTETS;
dv_reg FRAME64;
dv_reg FRAME65T127;
dv_reg FRAME128T255;
dv_reg FRAME256T511;
dv_reg FRAME512T1023;
dv_reg FRAME1024TUP;
dv_reg NETOCTETS;
dv_reg RXSOFOVERRUNS;
dv_reg RXMOFOVERRUNS;
dv_reg RXDMAOVERRUNS;
u_int8_t RSVD7[624];
dv_reg MACADDRLO;
dv_reg MACADDRHI;
dv_reg MACINDEX;
u_int8_t RSVD8[244];
dv_reg TX0HDP;
dv_reg TX1HDP;
dv_reg TX2HDP;
dv_reg TX3HDP;
dv_reg TX4HDP;
dv_reg TX5HDP;
dv_reg TX6HDP;
dv_reg TX7HDP;
dv_reg RX0HDP;
dv_reg RX1HDP;
dv_reg RX2HDP;
dv_reg RX3HDP;
dv_reg RX4HDP;
dv_reg RX5HDP;
dv_reg RX6HDP;
dv_reg RX7HDP;
dv_reg TX0CP;
dv_reg TX1CP;
dv_reg TX2CP;
dv_reg TX3CP;
dv_reg TX4CP;
dv_reg TX5CP;
dv_reg TX6CP;
dv_reg TX7CP;
dv_reg RX0CP;
dv_reg RX1CP;
dv_reg RX2CP;
dv_reg RX3CP;
dv_reg RX4CP;
dv_reg RX5CP;
dv_reg RX6CP;
dv_reg RX7CP;
} emac_regs;
/* EMAC Wrapper Registers Structure */
typedef struct {
#ifdef DAVINCI_EMAC_VERSION2
dv_reg idver;
dv_reg softrst;
dv_reg emctrl;
dv_reg c0rxthreshen;
dv_reg c0rxen;
dv_reg c0txen;
dv_reg c0miscen;
dv_reg c1rxthreshen;
dv_reg c1rxen;
dv_reg c1txen;
dv_reg c1miscen;
dv_reg c2rxthreshen;
dv_reg c2rxen;
dv_reg c2txen;
dv_reg c2miscen;
dv_reg c0rxthreshstat;
dv_reg c0rxstat;
dv_reg c0txstat;
dv_reg c0miscstat;
dv_reg c1rxthreshstat;
dv_reg c1rxstat;
dv_reg c1txstat;
dv_reg c1miscstat;
dv_reg c2rxthreshstat;
dv_reg c2rxstat;
dv_reg c2txstat;
dv_reg c2miscstat;
dv_reg c0rximax;
dv_reg c0tximax;
dv_reg c1rximax;
dv_reg c1tximax;
dv_reg c2rximax;
dv_reg c2tximax;
#else
u_int8_t RSVD0[4100];
dv_reg EWCTL;
dv_reg EWINTTCNT;
#endif
} ewrap_regs;
/* EMAC MDIO Registers Structure */
typedef struct {
dv_reg VERSION;
dv_reg CONTROL;
dv_reg ALIVE;
dv_reg LINK;
dv_reg LINKINTRAW;
dv_reg LINKINTMASKED;
u_int8_t RSVD0[8];
dv_reg USERINTRAW;
dv_reg USERINTMASKED;
dv_reg USERINTMASKSET;
dv_reg USERINTMASKCLEAR;
u_int8_t RSVD1[80];
dv_reg USERACCESS0;
dv_reg USERPHYSEL0;
dv_reg USERACCESS1;
dv_reg USERPHYSEL1;
} mdio_regs;
int davinci_eth_phy_read(u_int8_t phy_addr, u_int8_t reg_num, u_int16_t *data);
int davinci_eth_phy_write(u_int8_t phy_addr, u_int8_t reg_num, u_int16_t data);
typedef struct {
char name[64];
int (*init)(int phy_addr);
int (*is_phy_connected)(int phy_addr);
int (*get_link_speed)(int phy_addr);
int (*auto_negotiate)(int phy_addr);
} phy_t;
#endif /* _DAVINCI_EMAC_H_ */

View file

@ -0,0 +1,763 @@
/*
* SPDX-License-Identifier: GPL-2.0+
*/
#include <common.h>
#include <malloc.h>
#include <net.h>
#include <netdev.h>
#include <pci.h>
#undef DEBUG_SROM
#undef DEBUG_SROM2
#undef UPDATE_SROM
/* PCI Registers.
*/
#define PCI_CFDA_PSM 0x43
#define CFRV_RN 0x000000f0 /* Revision Number */
#define WAKEUP 0x00 /* Power Saving Wakeup */
#define SLEEP 0x80 /* Power Saving Sleep Mode */
#define DC2114x_BRK 0x0020 /* CFRV break between DC21142 & DC21143 */
/* Ethernet chip registers.
*/
#define DE4X5_BMR 0x000 /* Bus Mode Register */
#define DE4X5_TPD 0x008 /* Transmit Poll Demand Reg */
#define DE4X5_RRBA 0x018 /* RX Ring Base Address Reg */
#define DE4X5_TRBA 0x020 /* TX Ring Base Address Reg */
#define DE4X5_STS 0x028 /* Status Register */
#define DE4X5_OMR 0x030 /* Operation Mode Register */
#define DE4X5_SICR 0x068 /* SIA Connectivity Register */
#define DE4X5_APROM 0x048 /* Ethernet Address PROM */
/* Register bits.
*/
#define BMR_SWR 0x00000001 /* Software Reset */
#define STS_TS 0x00700000 /* Transmit Process State */
#define STS_RS 0x000e0000 /* Receive Process State */
#define OMR_ST 0x00002000 /* Start/Stop Transmission Command */
#define OMR_SR 0x00000002 /* Start/Stop Receive */
#define OMR_PS 0x00040000 /* Port Select */
#define OMR_SDP 0x02000000 /* SD Polarity - MUST BE ASSERTED */
#define OMR_PM 0x00000080 /* Pass All Multicast */
/* Descriptor bits.
*/
#define R_OWN 0x80000000 /* Own Bit */
#define RD_RER 0x02000000 /* Receive End Of Ring */
#define RD_LS 0x00000100 /* Last Descriptor */
#define RD_ES 0x00008000 /* Error Summary */
#define TD_TER 0x02000000 /* Transmit End Of Ring */
#define T_OWN 0x80000000 /* Own Bit */
#define TD_LS 0x40000000 /* Last Segment */
#define TD_FS 0x20000000 /* First Segment */
#define TD_ES 0x00008000 /* Error Summary */
#define TD_SET 0x08000000 /* Setup Packet */
/* The EEPROM commands include the alway-set leading bit. */
#define SROM_WRITE_CMD 5
#define SROM_READ_CMD 6
#define SROM_ERASE_CMD 7
#define SROM_HWADD 0x0014 /* Hardware Address offset in SROM */
#define SROM_RD 0x00004000 /* Read from Boot ROM */
#define EE_DATA_WRITE 0x04 /* EEPROM chip data in. */
#define EE_WRITE_0 0x4801
#define EE_WRITE_1 0x4805
#define EE_DATA_READ 0x08 /* EEPROM chip data out. */
#define SROM_SR 0x00000800 /* Select Serial ROM when set */
#define DT_IN 0x00000004 /* Serial Data In */
#define DT_CLK 0x00000002 /* Serial ROM Clock */
#define DT_CS 0x00000001 /* Serial ROM Chip Select */
#define POLL_DEMAND 1
#ifdef CONFIG_TULIP_FIX_DAVICOM
#define RESET_DM9102(dev) {\
unsigned long i;\
i=INL(dev, 0x0);\
udelay(1000);\
OUTL(dev, i | BMR_SWR, DE4X5_BMR);\
udelay(1000);\
}
#else
#define RESET_DE4X5(dev) {\
int i;\
i=INL(dev, DE4X5_BMR);\
udelay(1000);\
OUTL(dev, i | BMR_SWR, DE4X5_BMR);\
udelay(1000);\
OUTL(dev, i, DE4X5_BMR);\
udelay(1000);\
for (i=0;i<5;i++) {INL(dev, DE4X5_BMR); udelay(10000);}\
udelay(1000);\
}
#endif
#define START_DE4X5(dev) {\
s32 omr; \
omr = INL(dev, DE4X5_OMR);\
omr |= OMR_ST | OMR_SR;\
OUTL(dev, omr, DE4X5_OMR); /* Enable the TX and/or RX */\
}
#define STOP_DE4X5(dev) {\
s32 omr; \
omr = INL(dev, DE4X5_OMR);\
omr &= ~(OMR_ST|OMR_SR);\
OUTL(dev, omr, DE4X5_OMR); /* Disable the TX and/or RX */ \
}
#define NUM_RX_DESC PKTBUFSRX
#ifndef CONFIG_TULIP_FIX_DAVICOM
#define NUM_TX_DESC 1 /* Number of TX descriptors */
#else
#define NUM_TX_DESC 4
#endif
#define RX_BUFF_SZ PKTSIZE_ALIGN
#define TOUT_LOOP 1000000
#define SETUP_FRAME_LEN 192
#define ETH_ALEN 6
struct de4x5_desc {
volatile s32 status;
u32 des1;
u32 buf;
u32 next;
};
static struct de4x5_desc rx_ring[NUM_RX_DESC] __attribute__ ((aligned(32))); /* RX descriptor ring */
static struct de4x5_desc tx_ring[NUM_TX_DESC] __attribute__ ((aligned(32))); /* TX descriptor ring */
static int rx_new; /* RX descriptor ring pointer */
static int tx_new; /* TX descriptor ring pointer */
static char rxRingSize;
static char txRingSize;
#if defined(UPDATE_SROM) || !defined(CONFIG_TULIP_FIX_DAVICOM)
static void sendto_srom(struct eth_device* dev, u_int command, u_long addr);
static int getfrom_srom(struct eth_device* dev, u_long addr);
static int do_eeprom_cmd(struct eth_device *dev, u_long ioaddr,int cmd,int cmd_len);
static int do_read_eeprom(struct eth_device *dev,u_long ioaddr,int location,int addr_len);
#endif /* UPDATE_SROM || !CONFIG_TULIP_FIX_DAVICOM */
#ifdef UPDATE_SROM
static int write_srom(struct eth_device *dev, u_long ioaddr, int index, int new_value);
static void update_srom(struct eth_device *dev, bd_t *bis);
#endif
#ifndef CONFIG_TULIP_FIX_DAVICOM
static int read_srom(struct eth_device *dev, u_long ioaddr, int index);
static void read_hw_addr(struct eth_device* dev, bd_t * bis);
#endif /* CONFIG_TULIP_FIX_DAVICOM */
static void send_setup_frame(struct eth_device* dev, bd_t * bis);
static int dc21x4x_init(struct eth_device* dev, bd_t* bis);
static int dc21x4x_send(struct eth_device *dev, void *packet, int length);
static int dc21x4x_recv(struct eth_device* dev);
static void dc21x4x_halt(struct eth_device* dev);
#ifdef CONFIG_TULIP_SELECT_MEDIA
extern void dc21x4x_select_media(struct eth_device* dev);
#endif
#if defined(CONFIG_E500)
#define phys_to_bus(a) (a)
#else
#define phys_to_bus(a) pci_phys_to_mem((pci_dev_t)dev->priv, a)
#endif
static int INL(struct eth_device* dev, u_long addr)
{
return le32_to_cpu(*(volatile u_long *)(addr + dev->iobase));
}
static void OUTL(struct eth_device* dev, int command, u_long addr)
{
*(volatile u_long *)(addr + dev->iobase) = cpu_to_le32(command);
}
static struct pci_device_id supported[] = {
{ PCI_VENDOR_ID_DEC, PCI_DEVICE_ID_DEC_TULIP_FAST },
{ PCI_VENDOR_ID_DEC, PCI_DEVICE_ID_DEC_21142 },
#ifdef CONFIG_TULIP_FIX_DAVICOM
{ PCI_VENDOR_ID_DAVICOM, PCI_DEVICE_ID_DAVICOM_DM9102A },
#endif
{ }
};
int dc21x4x_initialize(bd_t *bis)
{
int idx=0;
int card_number = 0;
unsigned int cfrv;
unsigned char timer;
pci_dev_t devbusfn;
unsigned int iobase;
unsigned short status;
struct eth_device* dev;
while(1) {
devbusfn = pci_find_devices(supported, idx++);
if (devbusfn == -1) {
break;
}
/* Get the chip configuration revision register. */
pci_read_config_dword(devbusfn, PCI_REVISION_ID, &cfrv);
#ifndef CONFIG_TULIP_FIX_DAVICOM
if ((cfrv & CFRV_RN) < DC2114x_BRK ) {
printf("Error: The chip is not DC21143.\n");
continue;
}
#endif
pci_read_config_word(devbusfn, PCI_COMMAND, &status);
status |=
#ifdef CONFIG_TULIP_USE_IO
PCI_COMMAND_IO |
#else
PCI_COMMAND_MEMORY |
#endif
PCI_COMMAND_MASTER;
pci_write_config_word(devbusfn, PCI_COMMAND, status);
pci_read_config_word(devbusfn, PCI_COMMAND, &status);
#ifdef CONFIG_TULIP_USE_IO
if (!(status & PCI_COMMAND_IO)) {
printf("Error: Can not enable I/O access.\n");
continue;
}
#else
if (!(status & PCI_COMMAND_MEMORY)) {
printf("Error: Can not enable MEMORY access.\n");
continue;
}
#endif
if (!(status & PCI_COMMAND_MASTER)) {
printf("Error: Can not enable Bus Mastering.\n");
continue;
}
/* Check the latency timer for values >= 0x60. */
pci_read_config_byte(devbusfn, PCI_LATENCY_TIMER, &timer);
if (timer < 0x60) {
pci_write_config_byte(devbusfn, PCI_LATENCY_TIMER, 0x60);
}
#ifdef CONFIG_TULIP_USE_IO
/* read BAR for memory space access */
pci_read_config_dword(devbusfn, PCI_BASE_ADDRESS_0, &iobase);
iobase &= PCI_BASE_ADDRESS_IO_MASK;
#else
/* read BAR for memory space access */
pci_read_config_dword(devbusfn, PCI_BASE_ADDRESS_1, &iobase);
iobase &= PCI_BASE_ADDRESS_MEM_MASK;
#endif
debug ("dc21x4x: DEC 21142 PCI Device @0x%x\n", iobase);
dev = (struct eth_device*) malloc(sizeof *dev);
if (!dev) {
printf("Can not allocalte memory of dc21x4x\n");
break;
}
memset(dev, 0, sizeof(*dev));
#ifdef CONFIG_TULIP_FIX_DAVICOM
sprintf(dev->name, "Davicom#%d", card_number);
#else
sprintf(dev->name, "dc21x4x#%d", card_number);
#endif
#ifdef CONFIG_TULIP_USE_IO
dev->iobase = pci_io_to_phys(devbusfn, iobase);
#else
dev->iobase = pci_mem_to_phys(devbusfn, iobase);
#endif
dev->priv = (void*) devbusfn;
dev->init = dc21x4x_init;
dev->halt = dc21x4x_halt;
dev->send = dc21x4x_send;
dev->recv = dc21x4x_recv;
/* Ensure we're not sleeping. */
pci_write_config_byte(devbusfn, PCI_CFDA_PSM, WAKEUP);
udelay(10 * 1000);
#ifndef CONFIG_TULIP_FIX_DAVICOM
read_hw_addr(dev, bis);
#endif
eth_register(dev);
card_number++;
}
return card_number;
}
static int dc21x4x_init(struct eth_device* dev, bd_t* bis)
{
int i;
int devbusfn = (int) dev->priv;
/* Ensure we're not sleeping. */
pci_write_config_byte(devbusfn, PCI_CFDA_PSM, WAKEUP);
#ifdef CONFIG_TULIP_FIX_DAVICOM
RESET_DM9102(dev);
#else
RESET_DE4X5(dev);
#endif
if ((INL(dev, DE4X5_STS) & (STS_TS | STS_RS)) != 0) {
printf("Error: Cannot reset ethernet controller.\n");
return -1;
}
#ifdef CONFIG_TULIP_SELECT_MEDIA
dc21x4x_select_media(dev);
#else
OUTL(dev, OMR_SDP | OMR_PS | OMR_PM, DE4X5_OMR);
#endif
for (i = 0; i < NUM_RX_DESC; i++) {
rx_ring[i].status = cpu_to_le32(R_OWN);
rx_ring[i].des1 = cpu_to_le32(RX_BUFF_SZ);
rx_ring[i].buf = cpu_to_le32(
phys_to_bus((u32)net_rx_packets[i]));
#ifdef CONFIG_TULIP_FIX_DAVICOM
rx_ring[i].next = cpu_to_le32(
phys_to_bus((u32)&rx_ring[(i + 1) % NUM_RX_DESC]));
#else
rx_ring[i].next = 0;
#endif
}
for (i=0; i < NUM_TX_DESC; i++) {
tx_ring[i].status = 0;
tx_ring[i].des1 = 0;
tx_ring[i].buf = 0;
#ifdef CONFIG_TULIP_FIX_DAVICOM
tx_ring[i].next = cpu_to_le32(phys_to_bus((u32) &tx_ring[(i+1) % NUM_TX_DESC]));
#else
tx_ring[i].next = 0;
#endif
}
rxRingSize = NUM_RX_DESC;
txRingSize = NUM_TX_DESC;
/* Write the end of list marker to the descriptor lists. */
rx_ring[rxRingSize - 1].des1 |= cpu_to_le32(RD_RER);
tx_ring[txRingSize - 1].des1 |= cpu_to_le32(TD_TER);
/* Tell the adapter where the TX/RX rings are located. */
OUTL(dev, phys_to_bus((u32) &rx_ring), DE4X5_RRBA);
OUTL(dev, phys_to_bus((u32) &tx_ring), DE4X5_TRBA);
START_DE4X5(dev);
tx_new = 0;
rx_new = 0;
send_setup_frame(dev, bis);
return 0;
}
static int dc21x4x_send(struct eth_device *dev, void *packet, int length)
{
int status = -1;
int i;
if (length <= 0) {
printf("%s: bad packet size: %d\n", dev->name, length);
goto Done;
}
for(i = 0; tx_ring[tx_new].status & cpu_to_le32(T_OWN); i++) {
if (i >= TOUT_LOOP) {
printf("%s: tx error buffer not ready\n", dev->name);
goto Done;
}
}
tx_ring[tx_new].buf = cpu_to_le32(phys_to_bus((u32) packet));
tx_ring[tx_new].des1 = cpu_to_le32(TD_TER | TD_LS | TD_FS | length);
tx_ring[tx_new].status = cpu_to_le32(T_OWN);
OUTL(dev, POLL_DEMAND, DE4X5_TPD);
for(i = 0; tx_ring[tx_new].status & cpu_to_le32(T_OWN); i++) {
if (i >= TOUT_LOOP) {
printf(".%s: tx buffer not ready\n", dev->name);
goto Done;
}
}
if (le32_to_cpu(tx_ring[tx_new].status) & TD_ES) {
#if 0 /* test-only */
printf("TX error status = 0x%08X\n",
le32_to_cpu(tx_ring[tx_new].status));
#endif
tx_ring[tx_new].status = 0x0;
goto Done;
}
status = length;
Done:
tx_new = (tx_new+1) % NUM_TX_DESC;
return status;
}
static int dc21x4x_recv(struct eth_device* dev)
{
s32 status;
int length = 0;
for ( ; ; ) {
status = (s32)le32_to_cpu(rx_ring[rx_new].status);
if (status & R_OWN) {
break;
}
if (status & RD_LS) {
/* Valid frame status.
*/
if (status & RD_ES) {
/* There was an error.
*/
printf("RX error status = 0x%08X\n", status);
} else {
/* A valid frame received.
*/
length = (le32_to_cpu(rx_ring[rx_new].status) >> 16);
/* Pass the packet up to the protocol
* layers.
*/
net_process_received_packet(
net_rx_packets[rx_new], length - 4);
}
/* Change buffer ownership for this frame, back
* to the adapter.
*/
rx_ring[rx_new].status = cpu_to_le32(R_OWN);
}
/* Update entry information.
*/
rx_new = (rx_new + 1) % rxRingSize;
}
return length;
}
static void dc21x4x_halt(struct eth_device* dev)
{
int devbusfn = (int) dev->priv;
STOP_DE4X5(dev);
OUTL(dev, 0, DE4X5_SICR);
pci_write_config_byte(devbusfn, PCI_CFDA_PSM, SLEEP);
}
static void send_setup_frame(struct eth_device* dev, bd_t *bis)
{
int i;
char setup_frame[SETUP_FRAME_LEN];
char *pa = &setup_frame[0];
memset(pa, 0xff, SETUP_FRAME_LEN);
for (i = 0; i < ETH_ALEN; i++) {
*(pa + (i & 1)) = dev->enetaddr[i];
if (i & 0x01) {
pa += 4;
}
}
for(i = 0; tx_ring[tx_new].status & cpu_to_le32(T_OWN); i++) {
if (i >= TOUT_LOOP) {
printf("%s: tx error buffer not ready\n", dev->name);
goto Done;
}
}
tx_ring[tx_new].buf = cpu_to_le32(phys_to_bus((u32) &setup_frame[0]));
tx_ring[tx_new].des1 = cpu_to_le32(TD_TER | TD_SET| SETUP_FRAME_LEN);
tx_ring[tx_new].status = cpu_to_le32(T_OWN);
OUTL(dev, POLL_DEMAND, DE4X5_TPD);
for(i = 0; tx_ring[tx_new].status & cpu_to_le32(T_OWN); i++) {
if (i >= TOUT_LOOP) {
printf("%s: tx buffer not ready\n", dev->name);
goto Done;
}
}
if (le32_to_cpu(tx_ring[tx_new].status) != 0x7FFFFFFF) {
printf("TX error status2 = 0x%08X\n", le32_to_cpu(tx_ring[tx_new].status));
}
tx_new = (tx_new+1) % NUM_TX_DESC;
Done:
return;
}
#if defined(UPDATE_SROM) || !defined(CONFIG_TULIP_FIX_DAVICOM)
/* SROM Read and write routines.
*/
static void
sendto_srom(struct eth_device* dev, u_int command, u_long addr)
{
OUTL(dev, command, addr);
udelay(1);
}
static int
getfrom_srom(struct eth_device* dev, u_long addr)
{
s32 tmp;
tmp = INL(dev, addr);
udelay(1);
return tmp;
}
/* Note: this routine returns extra data bits for size detection. */
static int do_read_eeprom(struct eth_device *dev, u_long ioaddr, int location, int addr_len)
{
int i;
unsigned retval = 0;
int read_cmd = location | (SROM_READ_CMD << addr_len);
sendto_srom(dev, SROM_RD | SROM_SR, ioaddr);
sendto_srom(dev, SROM_RD | SROM_SR | DT_CS, ioaddr);
#ifdef DEBUG_SROM
printf(" EEPROM read at %d ", location);
#endif
/* Shift the read command bits out. */
for (i = 4 + addr_len; i >= 0; i--) {
short dataval = (read_cmd & (1 << i)) ? EE_DATA_WRITE : 0;
sendto_srom(dev, SROM_RD | SROM_SR | DT_CS | dataval, ioaddr);
udelay(10);
sendto_srom(dev, SROM_RD | SROM_SR | DT_CS | dataval | DT_CLK, ioaddr);
udelay(10);
#ifdef DEBUG_SROM2
printf("%X", getfrom_srom(dev, ioaddr) & 15);
#endif
retval = (retval << 1) | ((getfrom_srom(dev, ioaddr) & EE_DATA_READ) ? 1 : 0);
}
sendto_srom(dev, SROM_RD | SROM_SR | DT_CS, ioaddr);
#ifdef DEBUG_SROM2
printf(" :%X:", getfrom_srom(dev, ioaddr) & 15);
#endif
for (i = 16; i > 0; i--) {
sendto_srom(dev, SROM_RD | SROM_SR | DT_CS | DT_CLK, ioaddr);
udelay(10);
#ifdef DEBUG_SROM2
printf("%X", getfrom_srom(dev, ioaddr) & 15);
#endif
retval = (retval << 1) | ((getfrom_srom(dev, ioaddr) & EE_DATA_READ) ? 1 : 0);
sendto_srom(dev, SROM_RD | SROM_SR | DT_CS, ioaddr);
udelay(10);
}
/* Terminate the EEPROM access. */
sendto_srom(dev, SROM_RD | SROM_SR, ioaddr);
#ifdef DEBUG_SROM2
printf(" EEPROM value at %d is %5.5x.\n", location, retval);
#endif
return retval;
}
#endif /* UPDATE_SROM || !CONFIG_TULIP_FIX_DAVICOM */
/* This executes a generic EEPROM command, typically a write or write
* enable. It returns the data output from the EEPROM, and thus may
* also be used for reads.
*/
#if defined(UPDATE_SROM) || !defined(CONFIG_TULIP_FIX_DAVICOM)
static int do_eeprom_cmd(struct eth_device *dev, u_long ioaddr, int cmd, int cmd_len)
{
unsigned retval = 0;
#ifdef DEBUG_SROM
printf(" EEPROM op 0x%x: ", cmd);
#endif
sendto_srom(dev,SROM_RD | SROM_SR | DT_CS | DT_CLK, ioaddr);
/* Shift the command bits out. */
do {
short dataval = (cmd & (1 << cmd_len)) ? EE_WRITE_1 : EE_WRITE_0;
sendto_srom(dev,dataval, ioaddr);
udelay(10);
#ifdef DEBUG_SROM2
printf("%X", getfrom_srom(dev,ioaddr) & 15);
#endif
sendto_srom(dev,dataval | DT_CLK, ioaddr);
udelay(10);
retval = (retval << 1) | ((getfrom_srom(dev,ioaddr) & EE_DATA_READ) ? 1 : 0);
} while (--cmd_len >= 0);
sendto_srom(dev,SROM_RD | SROM_SR | DT_CS, ioaddr);
/* Terminate the EEPROM access. */
sendto_srom(dev,SROM_RD | SROM_SR, ioaddr);
#ifdef DEBUG_SROM
printf(" EEPROM result is 0x%5.5x.\n", retval);
#endif
return retval;
}
#endif /* UPDATE_SROM || !CONFIG_TULIP_FIX_DAVICOM */
#ifndef CONFIG_TULIP_FIX_DAVICOM
static int read_srom(struct eth_device *dev, u_long ioaddr, int index)
{
int ee_addr_size = do_read_eeprom(dev, ioaddr, 0xff, 8) & 0x40000 ? 8 : 6;
return do_eeprom_cmd(dev, ioaddr,
(((SROM_READ_CMD << ee_addr_size) | index) << 16)
| 0xffff, 3 + ee_addr_size + 16);
}
#endif /* CONFIG_TULIP_FIX_DAVICOM */
#ifdef UPDATE_SROM
static int write_srom(struct eth_device *dev, u_long ioaddr, int index, int new_value)
{
int ee_addr_size = do_read_eeprom(dev, ioaddr, 0xff, 8) & 0x40000 ? 8 : 6;
int i;
unsigned short newval;
udelay(10*1000); /* test-only */
#ifdef DEBUG_SROM
printf("ee_addr_size=%d.\n", ee_addr_size);
printf("Writing new entry 0x%4.4x to offset %d.\n", new_value, index);
#endif
/* Enable programming modes. */
do_eeprom_cmd(dev, ioaddr, (0x4f << (ee_addr_size-4)), 3+ee_addr_size);
/* Do the actual write. */
do_eeprom_cmd(dev, ioaddr,
(((SROM_WRITE_CMD<<ee_addr_size)|index) << 16) | new_value,
3 + ee_addr_size + 16);
/* Poll for write finished. */
sendto_srom(dev, SROM_RD | SROM_SR | DT_CS, ioaddr);
for (i = 0; i < 10000; i++) /* Typical 2000 ticks */
if (getfrom_srom(dev, ioaddr) & EE_DATA_READ)
break;
#ifdef DEBUG_SROM
printf(" Write finished after %d ticks.\n", i);
#endif
/* Disable programming. */
do_eeprom_cmd(dev, ioaddr, (0x40 << (ee_addr_size-4)), 3 + ee_addr_size);
/* And read the result. */
newval = do_eeprom_cmd(dev, ioaddr,
(((SROM_READ_CMD<<ee_addr_size)|index) << 16)
| 0xffff, 3 + ee_addr_size + 16);
#ifdef DEBUG_SROM
printf(" New value at offset %d is %4.4x.\n", index, newval);
#endif
return 1;
}
#endif
#ifndef CONFIG_TULIP_FIX_DAVICOM
static void read_hw_addr(struct eth_device *dev, bd_t *bis)
{
u_short tmp, *p = (u_short *)(&dev->enetaddr[0]);
int i, j = 0;
for (i = 0; i < (ETH_ALEN >> 1); i++) {
tmp = read_srom(dev, DE4X5_APROM, ((SROM_HWADD >> 1) + i));
*p = le16_to_cpu(tmp);
j += *p++;
}
if ((j == 0) || (j == 0x2fffd)) {
memset (dev->enetaddr, 0, ETH_ALEN);
debug ("Warning: can't read HW address from SROM.\n");
goto Done;
}
return;
Done:
#ifdef UPDATE_SROM
update_srom(dev, bis);
#endif
return;
}
#endif /* CONFIG_TULIP_FIX_DAVICOM */
#ifdef UPDATE_SROM
static void update_srom(struct eth_device *dev, bd_t *bis)
{
int i;
static unsigned short eeprom[0x40] = {
0x140b, 0x6610, 0x0000, 0x0000, /* 00 */
0x0000, 0x0000, 0x0000, 0x0000, /* 04 */
0x00a3, 0x0103, 0x0000, 0x0000, /* 08 */
0x0000, 0x1f00, 0x0000, 0x0000, /* 0c */
0x0108, 0x038d, 0x0000, 0x0000, /* 10 */
0xe078, 0x0001, 0x0040, 0x0018, /* 14 */
0x0000, 0x0000, 0x0000, 0x0000, /* 18 */
0x0000, 0x0000, 0x0000, 0x0000, /* 1c */
0x0000, 0x0000, 0x0000, 0x0000, /* 20 */
0x0000, 0x0000, 0x0000, 0x0000, /* 24 */
0x0000, 0x0000, 0x0000, 0x0000, /* 28 */
0x0000, 0x0000, 0x0000, 0x0000, /* 2c */
0x0000, 0x0000, 0x0000, 0x0000, /* 30 */
0x0000, 0x0000, 0x0000, 0x0000, /* 34 */
0x0000, 0x0000, 0x0000, 0x0000, /* 38 */
0x0000, 0x0000, 0x0000, 0x4e07, /* 3c */
};
uchar enetaddr[6];
/* Ethernet Addr... */
if (!eth_getenv_enetaddr("ethaddr", enetaddr))
return;
eeprom[0x0a] = (enetaddr[1] << 8) | enetaddr[0];
eeprom[0x0b] = (enetaddr[3] << 8) | enetaddr[2];
eeprom[0x0c] = (enetaddr[5] << 8) | enetaddr[4];
for (i=0; i<0x40; i++) {
write_srom(dev, DE4X5_APROM, i, eeprom[i]);
}
}
#endif /* UPDATE_SROM */

View file

@ -0,0 +1,676 @@
/*
* (C) Copyright 2010
* Vipin Kumar, ST Micoelectronics, vipin.kumar@st.com.
*
* SPDX-License-Identifier: GPL-2.0+
*/
/*
* Designware ethernet IP driver for U-Boot
*/
#include <common.h>
#include <dm.h>
#include <errno.h>
#include <miiphy.h>
#include <malloc.h>
#include <pci.h>
#include <linux/compiler.h>
#include <linux/err.h>
#include <asm/io.h>
#include "designware.h"
DECLARE_GLOBAL_DATA_PTR;
static int dw_mdio_read(struct mii_dev *bus, int addr, int devad, int reg)
{
struct eth_mac_regs *mac_p = bus->priv;
ulong start;
u16 miiaddr;
int timeout = CONFIG_MDIO_TIMEOUT;
miiaddr = ((addr << MIIADDRSHIFT) & MII_ADDRMSK) |
((reg << MIIREGSHIFT) & MII_REGMSK);
writel(miiaddr | MII_CLKRANGE_150_250M | MII_BUSY, &mac_p->miiaddr);
start = get_timer(0);
while (get_timer(start) < timeout) {
if (!(readl(&mac_p->miiaddr) & MII_BUSY))
return readl(&mac_p->miidata);
udelay(10);
};
return -ETIMEDOUT;
}
static int dw_mdio_write(struct mii_dev *bus, int addr, int devad, int reg,
u16 val)
{
struct eth_mac_regs *mac_p = bus->priv;
ulong start;
u16 miiaddr;
int ret = -ETIMEDOUT, timeout = CONFIG_MDIO_TIMEOUT;
writel(val, &mac_p->miidata);
miiaddr = ((addr << MIIADDRSHIFT) & MII_ADDRMSK) |
((reg << MIIREGSHIFT) & MII_REGMSK) | MII_WRITE;
writel(miiaddr | MII_CLKRANGE_150_250M | MII_BUSY, &mac_p->miiaddr);
start = get_timer(0);
while (get_timer(start) < timeout) {
if (!(readl(&mac_p->miiaddr) & MII_BUSY)) {
ret = 0;
break;
}
udelay(10);
};
return ret;
}
static int dw_mdio_init(const char *name, struct eth_mac_regs *mac_regs_p)
{
struct mii_dev *bus = mdio_alloc();
if (!bus) {
printf("Failed to allocate MDIO bus\n");
return -ENOMEM;
}
bus->read = dw_mdio_read;
bus->write = dw_mdio_write;
snprintf(bus->name, sizeof(bus->name), name);
bus->priv = (void *)mac_regs_p;
return mdio_register(bus);
}
static void tx_descs_init(struct dw_eth_dev *priv)
{
struct eth_dma_regs *dma_p = priv->dma_regs_p;
struct dmamacdescr *desc_table_p = &priv->tx_mac_descrtable[0];
char *txbuffs = &priv->txbuffs[0];
struct dmamacdescr *desc_p;
u32 idx;
for (idx = 0; idx < CONFIG_TX_DESCR_NUM; idx++) {
desc_p = &desc_table_p[idx];
desc_p->dmamac_addr = &txbuffs[idx * CONFIG_ETH_BUFSIZE];
desc_p->dmamac_next = &desc_table_p[idx + 1];
#if defined(CONFIG_DW_ALTDESCRIPTOR)
desc_p->txrx_status &= ~(DESC_TXSTS_TXINT | DESC_TXSTS_TXLAST |
DESC_TXSTS_TXFIRST | DESC_TXSTS_TXCRCDIS |
DESC_TXSTS_TXCHECKINSCTRL |
DESC_TXSTS_TXRINGEND | DESC_TXSTS_TXPADDIS);
desc_p->txrx_status |= DESC_TXSTS_TXCHAIN;
desc_p->dmamac_cntl = 0;
desc_p->txrx_status &= ~(DESC_TXSTS_MSK | DESC_TXSTS_OWNBYDMA);
#else
desc_p->dmamac_cntl = DESC_TXCTRL_TXCHAIN;
desc_p->txrx_status = 0;
#endif
}
/* Correcting the last pointer of the chain */
desc_p->dmamac_next = &desc_table_p[0];
/* Flush all Tx buffer descriptors at once */
flush_dcache_range((unsigned int)priv->tx_mac_descrtable,
(unsigned int)priv->tx_mac_descrtable +
sizeof(priv->tx_mac_descrtable));
writel((ulong)&desc_table_p[0], &dma_p->txdesclistaddr);
priv->tx_currdescnum = 0;
}
static void rx_descs_init(struct dw_eth_dev *priv)
{
struct eth_dma_regs *dma_p = priv->dma_regs_p;
struct dmamacdescr *desc_table_p = &priv->rx_mac_descrtable[0];
char *rxbuffs = &priv->rxbuffs[0];
struct dmamacdescr *desc_p;
u32 idx;
/* Before passing buffers to GMAC we need to make sure zeros
* written there right after "priv" structure allocation were
* flushed into RAM.
* Otherwise there's a chance to get some of them flushed in RAM when
* GMAC is already pushing data to RAM via DMA. This way incoming from
* GMAC data will be corrupted. */
flush_dcache_range((unsigned int)rxbuffs, (unsigned int)rxbuffs +
RX_TOTAL_BUFSIZE);
for (idx = 0; idx < CONFIG_RX_DESCR_NUM; idx++) {
desc_p = &desc_table_p[idx];
desc_p->dmamac_addr = &rxbuffs[idx * CONFIG_ETH_BUFSIZE];
desc_p->dmamac_next = &desc_table_p[idx + 1];
desc_p->dmamac_cntl =
(MAC_MAX_FRAME_SZ & DESC_RXCTRL_SIZE1MASK) |
DESC_RXCTRL_RXCHAIN;
desc_p->txrx_status = DESC_RXSTS_OWNBYDMA;
}
/* Correcting the last pointer of the chain */
desc_p->dmamac_next = &desc_table_p[0];
/* Flush all Rx buffer descriptors at once */
flush_dcache_range((unsigned int)priv->rx_mac_descrtable,
(unsigned int)priv->rx_mac_descrtable +
sizeof(priv->rx_mac_descrtable));
writel((ulong)&desc_table_p[0], &dma_p->rxdesclistaddr);
priv->rx_currdescnum = 0;
}
static int _dw_write_hwaddr(struct dw_eth_dev *priv, u8 *mac_id)
{
struct eth_mac_regs *mac_p = priv->mac_regs_p;
u32 macid_lo, macid_hi;
macid_lo = mac_id[0] + (mac_id[1] << 8) + (mac_id[2] << 16) +
(mac_id[3] << 24);
macid_hi = mac_id[4] + (mac_id[5] << 8);
writel(macid_hi, &mac_p->macaddr0hi);
writel(macid_lo, &mac_p->macaddr0lo);
return 0;
}
static void dw_adjust_link(struct eth_mac_regs *mac_p,
struct phy_device *phydev)
{
u32 conf = readl(&mac_p->conf) | FRAMEBURSTENABLE | DISABLERXOWN;
if (!phydev->link) {
printf("%s: No link.\n", phydev->dev->name);
return;
}
if (phydev->speed != 1000)
conf |= MII_PORTSELECT;
if (phydev->speed == 100)
conf |= FES_100;
if (phydev->duplex)
conf |= FULLDPLXMODE;
writel(conf, &mac_p->conf);
printf("Speed: %d, %s duplex%s\n", phydev->speed,
(phydev->duplex) ? "full" : "half",
(phydev->port == PORT_FIBRE) ? ", fiber mode" : "");
}
static void _dw_eth_halt(struct dw_eth_dev *priv)
{
struct eth_mac_regs *mac_p = priv->mac_regs_p;
struct eth_dma_regs *dma_p = priv->dma_regs_p;
writel(readl(&mac_p->conf) & ~(RXENABLE | TXENABLE), &mac_p->conf);
writel(readl(&dma_p->opmode) & ~(RXSTART | TXSTART), &dma_p->opmode);
phy_shutdown(priv->phydev);
}
static int _dw_eth_init(struct dw_eth_dev *priv, u8 *enetaddr)
{
struct eth_mac_regs *mac_p = priv->mac_regs_p;
struct eth_dma_regs *dma_p = priv->dma_regs_p;
unsigned int start;
int ret;
writel(readl(&dma_p->busmode) | DMAMAC_SRST, &dma_p->busmode);
start = get_timer(0);
while (readl(&dma_p->busmode) & DMAMAC_SRST) {
if (get_timer(start) >= CONFIG_MACRESET_TIMEOUT) {
printf("DMA reset timeout\n");
return -ETIMEDOUT;
}
mdelay(100);
};
/*
* Soft reset above clears HW address registers.
* So we have to set it here once again.
*/
_dw_write_hwaddr(priv, enetaddr);
rx_descs_init(priv);
tx_descs_init(priv);
writel(FIXEDBURST | PRIORXTX_41 | DMA_PBL, &dma_p->busmode);
#ifndef CONFIG_DW_MAC_FORCE_THRESHOLD_MODE
writel(readl(&dma_p->opmode) | FLUSHTXFIFO | STOREFORWARD,
&dma_p->opmode);
#else
writel(readl(&dma_p->opmode) | FLUSHTXFIFO,
&dma_p->opmode);
#endif
writel(readl(&dma_p->opmode) | RXSTART | TXSTART, &dma_p->opmode);
#ifdef CONFIG_DW_AXI_BURST_LEN
writel((CONFIG_DW_AXI_BURST_LEN & 0x1FF >> 1), &dma_p->axibus);
#endif
/* Start up the PHY */
ret = phy_startup(priv->phydev);
if (ret) {
printf("Could not initialize PHY %s\n",
priv->phydev->dev->name);
return ret;
}
dw_adjust_link(mac_p, priv->phydev);
if (!priv->phydev->link)
return -EIO;
writel(readl(&mac_p->conf) | RXENABLE | TXENABLE, &mac_p->conf);
return 0;
}
static int _dw_eth_send(struct dw_eth_dev *priv, void *packet, int length)
{
struct eth_dma_regs *dma_p = priv->dma_regs_p;
u32 desc_num = priv->tx_currdescnum;
struct dmamacdescr *desc_p = &priv->tx_mac_descrtable[desc_num];
uint32_t desc_start = (uint32_t)desc_p;
uint32_t desc_end = desc_start +
roundup(sizeof(*desc_p), ARCH_DMA_MINALIGN);
uint32_t data_start = (uint32_t)desc_p->dmamac_addr;
uint32_t data_end = data_start +
roundup(length, ARCH_DMA_MINALIGN);
/*
* Strictly we only need to invalidate the "txrx_status" field
* for the following check, but on some platforms we cannot
* invalidate only 4 bytes, so we flush the entire descriptor,
* which is 16 bytes in total. This is safe because the
* individual descriptors in the array are each aligned to
* ARCH_DMA_MINALIGN and padded appropriately.
*/
invalidate_dcache_range(desc_start, desc_end);
/* Check if the descriptor is owned by CPU */
if (desc_p->txrx_status & DESC_TXSTS_OWNBYDMA) {
printf("CPU not owner of tx frame\n");
return -EPERM;
}
memcpy(desc_p->dmamac_addr, packet, length);
/* Flush data to be sent */
flush_dcache_range(data_start, data_end);
#if defined(CONFIG_DW_ALTDESCRIPTOR)
desc_p->txrx_status |= DESC_TXSTS_TXFIRST | DESC_TXSTS_TXLAST;
desc_p->dmamac_cntl |= (length << DESC_TXCTRL_SIZE1SHFT) &
DESC_TXCTRL_SIZE1MASK;
desc_p->txrx_status &= ~(DESC_TXSTS_MSK);
desc_p->txrx_status |= DESC_TXSTS_OWNBYDMA;
#else
desc_p->dmamac_cntl |= ((length << DESC_TXCTRL_SIZE1SHFT) &
DESC_TXCTRL_SIZE1MASK) | DESC_TXCTRL_TXLAST |
DESC_TXCTRL_TXFIRST;
desc_p->txrx_status = DESC_TXSTS_OWNBYDMA;
#endif
/* Flush modified buffer descriptor */
flush_dcache_range(desc_start, desc_end);
/* Test the wrap-around condition. */
if (++desc_num >= CONFIG_TX_DESCR_NUM)
desc_num = 0;
priv->tx_currdescnum = desc_num;
/* Start the transmission */
writel(POLL_DATA, &dma_p->txpolldemand);
return 0;
}
static int _dw_eth_recv(struct dw_eth_dev *priv, uchar **packetp)
{
u32 status, desc_num = priv->rx_currdescnum;
struct dmamacdescr *desc_p = &priv->rx_mac_descrtable[desc_num];
int length = -EAGAIN;
uint32_t desc_start = (uint32_t)desc_p;
uint32_t desc_end = desc_start +
roundup(sizeof(*desc_p), ARCH_DMA_MINALIGN);
uint32_t data_start = (uint32_t)desc_p->dmamac_addr;
uint32_t data_end;
/* Invalidate entire buffer descriptor */
invalidate_dcache_range(desc_start, desc_end);
status = desc_p->txrx_status;
/* Check if the owner is the CPU */
if (!(status & DESC_RXSTS_OWNBYDMA)) {
length = (status & DESC_RXSTS_FRMLENMSK) >>
DESC_RXSTS_FRMLENSHFT;
/* Invalidate received data */
data_end = data_start + roundup(length, ARCH_DMA_MINALIGN);
invalidate_dcache_range(data_start, data_end);
*packetp = desc_p->dmamac_addr;
}
return length;
}
static int _dw_free_pkt(struct dw_eth_dev *priv)
{
u32 desc_num = priv->rx_currdescnum;
struct dmamacdescr *desc_p = &priv->rx_mac_descrtable[desc_num];
uint32_t desc_start = (uint32_t)desc_p;
uint32_t desc_end = desc_start +
roundup(sizeof(*desc_p), ARCH_DMA_MINALIGN);
/*
* Make the current descriptor valid again and go to
* the next one
*/
desc_p->txrx_status |= DESC_RXSTS_OWNBYDMA;
/* Flush only status field - others weren't changed */
flush_dcache_range(desc_start, desc_end);
/* Test the wrap-around condition. */
if (++desc_num >= CONFIG_RX_DESCR_NUM)
desc_num = 0;
priv->rx_currdescnum = desc_num;
return 0;
}
static int dw_phy_init(struct dw_eth_dev *priv, void *dev)
{
struct phy_device *phydev;
int mask = 0xffffffff;
#ifdef CONFIG_PHY_ADDR
mask = 1 << CONFIG_PHY_ADDR;
#endif
phydev = phy_find_by_mask(priv->bus, mask, priv->interface);
if (!phydev)
return -ENODEV;
phy_connect_dev(phydev, dev);
phydev->supported &= PHY_GBIT_FEATURES;
phydev->advertising = phydev->supported;
priv->phydev = phydev;
phy_config(phydev);
return 0;
}
#ifndef CONFIG_DM_ETH
static int dw_eth_init(struct eth_device *dev, bd_t *bis)
{
return _dw_eth_init(dev->priv, dev->enetaddr);
}
static int dw_eth_send(struct eth_device *dev, void *packet, int length)
{
return _dw_eth_send(dev->priv, packet, length);
}
static int dw_eth_recv(struct eth_device *dev)
{
uchar *packet;
int length;
length = _dw_eth_recv(dev->priv, &packet);
if (length == -EAGAIN)
return 0;
net_process_received_packet(packet, length);
_dw_free_pkt(dev->priv);
return 0;
}
static void dw_eth_halt(struct eth_device *dev)
{
return _dw_eth_halt(dev->priv);
}
static int dw_write_hwaddr(struct eth_device *dev)
{
return _dw_write_hwaddr(dev->priv, dev->enetaddr);
}
int designware_initialize(ulong base_addr, u32 interface)
{
struct eth_device *dev;
struct dw_eth_dev *priv;
dev = (struct eth_device *) malloc(sizeof(struct eth_device));
if (!dev)
return -ENOMEM;
/*
* Since the priv structure contains the descriptors which need a strict
* buswidth alignment, memalign is used to allocate memory
*/
priv = (struct dw_eth_dev *) memalign(ARCH_DMA_MINALIGN,
sizeof(struct dw_eth_dev));
if (!priv) {
free(dev);
return -ENOMEM;
}
memset(dev, 0, sizeof(struct eth_device));
memset(priv, 0, sizeof(struct dw_eth_dev));
sprintf(dev->name, "dwmac.%lx", base_addr);
dev->iobase = (int)base_addr;
dev->priv = priv;
priv->dev = dev;
priv->mac_regs_p = (struct eth_mac_regs *)base_addr;
priv->dma_regs_p = (struct eth_dma_regs *)(base_addr +
DW_DMA_BASE_OFFSET);
dev->init = dw_eth_init;
dev->send = dw_eth_send;
dev->recv = dw_eth_recv;
dev->halt = dw_eth_halt;
dev->write_hwaddr = dw_write_hwaddr;
eth_register(dev);
priv->interface = interface;
dw_mdio_init(dev->name, priv->mac_regs_p);
priv->bus = miiphy_get_dev_by_name(dev->name);
return dw_phy_init(priv, dev);
}
#endif
#ifdef CONFIG_DM_ETH
static int designware_eth_start(struct udevice *dev)
{
struct eth_pdata *pdata = dev_get_platdata(dev);
return _dw_eth_init(dev->priv, pdata->enetaddr);
}
static int designware_eth_send(struct udevice *dev, void *packet, int length)
{
struct dw_eth_dev *priv = dev_get_priv(dev);
return _dw_eth_send(priv, packet, length);
}
static int designware_eth_recv(struct udevice *dev, int flags, uchar **packetp)
{
struct dw_eth_dev *priv = dev_get_priv(dev);
return _dw_eth_recv(priv, packetp);
}
static int designware_eth_free_pkt(struct udevice *dev, uchar *packet,
int length)
{
struct dw_eth_dev *priv = dev_get_priv(dev);
return _dw_free_pkt(priv);
}
static void designware_eth_stop(struct udevice *dev)
{
struct dw_eth_dev *priv = dev_get_priv(dev);
return _dw_eth_halt(priv);
}
static int designware_eth_write_hwaddr(struct udevice *dev)
{
struct eth_pdata *pdata = dev_get_platdata(dev);
struct dw_eth_dev *priv = dev_get_priv(dev);
return _dw_write_hwaddr(priv, pdata->enetaddr);
}
static int designware_eth_bind(struct udevice *dev)
{
#ifdef CONFIG_DM_PCI
static int num_cards;
char name[20];
/* Create a unique device name for PCI type devices */
if (device_is_on_pci_bus(dev)) {
sprintf(name, "eth_designware#%u", num_cards++);
device_set_name(dev, name);
}
#endif
return 0;
}
static int designware_eth_probe(struct udevice *dev)
{
struct eth_pdata *pdata = dev_get_platdata(dev);
struct dw_eth_dev *priv = dev_get_priv(dev);
u32 iobase = pdata->iobase;
int ret;
#ifdef CONFIG_DM_PCI
/*
* If we are on PCI bus, either directly attached to a PCI root port,
* or via a PCI bridge, fill in platdata before we probe the hardware.
*/
if (device_is_on_pci_bus(dev)) {
pci_dev_t bdf = pci_get_bdf(dev);
dm_pci_read_config32(dev, PCI_BASE_ADDRESS_0, &iobase);
iobase &= PCI_BASE_ADDRESS_MEM_MASK;
iobase = pci_mem_to_phys(bdf, iobase);
pdata->iobase = iobase;
pdata->phy_interface = PHY_INTERFACE_MODE_RMII;
}
#endif
debug("%s, iobase=%x, priv=%p\n", __func__, iobase, priv);
priv->mac_regs_p = (struct eth_mac_regs *)iobase;
priv->dma_regs_p = (struct eth_dma_regs *)(iobase + DW_DMA_BASE_OFFSET);
priv->interface = pdata->phy_interface;
dw_mdio_init(dev->name, priv->mac_regs_p);
priv->bus = miiphy_get_dev_by_name(dev->name);
ret = dw_phy_init(priv, dev);
debug("%s, ret=%d\n", __func__, ret);
return ret;
}
static int designware_eth_remove(struct udevice *dev)
{
struct dw_eth_dev *priv = dev_get_priv(dev);
free(priv->phydev);
mdio_unregister(priv->bus);
mdio_free(priv->bus);
return 0;
}
static const struct eth_ops designware_eth_ops = {
.start = designware_eth_start,
.send = designware_eth_send,
.recv = designware_eth_recv,
.free_pkt = designware_eth_free_pkt,
.stop = designware_eth_stop,
.write_hwaddr = designware_eth_write_hwaddr,
};
static int designware_eth_ofdata_to_platdata(struct udevice *dev)
{
struct eth_pdata *pdata = dev_get_platdata(dev);
const char *phy_mode;
pdata->iobase = dev_get_addr(dev);
pdata->phy_interface = -1;
phy_mode = fdt_getprop(gd->fdt_blob, dev->of_offset, "phy-mode", NULL);
if (phy_mode)
pdata->phy_interface = phy_get_interface_by_name(phy_mode);
if (pdata->phy_interface == -1) {
debug("%s: Invalid PHY interface '%s'\n", __func__, phy_mode);
return -EINVAL;
}
return 0;
}
static const struct udevice_id designware_eth_ids[] = {
{ .compatible = "allwinner,sun7i-a20-gmac" },
{ .compatible = "altr,socfpga-stmmac" },
{ }
};
U_BOOT_DRIVER(eth_designware) = {
.name = "eth_designware",
.id = UCLASS_ETH,
.of_match = designware_eth_ids,
.ofdata_to_platdata = designware_eth_ofdata_to_platdata,
.bind = designware_eth_bind,
.probe = designware_eth_probe,
.remove = designware_eth_remove,
.ops = &designware_eth_ops,
.priv_auto_alloc_size = sizeof(struct dw_eth_dev),
.platdata_auto_alloc_size = sizeof(struct eth_pdata),
.flags = DM_FLAG_ALLOC_PRIV_DMA,
};
static struct pci_device_id supported[] = {
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_QRK_EMAC) },
{ }
};
U_BOOT_PCI_DEVICE(eth_designware, supported);
#endif

View file

@ -0,0 +1,238 @@
/*
* (C) Copyright 2010
* Vipin Kumar, ST Micoelectronics, vipin.kumar@st.com.
*
* SPDX-License-Identifier: GPL-2.0+
*/
#ifndef _DW_ETH_H
#define _DW_ETH_H
#define CONFIG_TX_DESCR_NUM 16
#define CONFIG_RX_DESCR_NUM 16
#define CONFIG_ETH_BUFSIZE 2048
#define TX_TOTAL_BUFSIZE (CONFIG_ETH_BUFSIZE * CONFIG_TX_DESCR_NUM)
#define RX_TOTAL_BUFSIZE (CONFIG_ETH_BUFSIZE * CONFIG_RX_DESCR_NUM)
#define CONFIG_MACRESET_TIMEOUT (3 * CONFIG_SYS_HZ)
#define CONFIG_MDIO_TIMEOUT (3 * CONFIG_SYS_HZ)
struct eth_mac_regs {
u32 conf; /* 0x00 */
u32 framefilt; /* 0x04 */
u32 hashtablehigh; /* 0x08 */
u32 hashtablelow; /* 0x0c */
u32 miiaddr; /* 0x10 */
u32 miidata; /* 0x14 */
u32 flowcontrol; /* 0x18 */
u32 vlantag; /* 0x1c */
u32 version; /* 0x20 */
u8 reserved_1[20];
u32 intreg; /* 0x38 */
u32 intmask; /* 0x3c */
u32 macaddr0hi; /* 0x40 */
u32 macaddr0lo; /* 0x44 */
};
/* MAC configuration register definitions */
#define FRAMEBURSTENABLE (1 << 21)
#define MII_PORTSELECT (1 << 15)
#define FES_100 (1 << 14)
#define DISABLERXOWN (1 << 13)
#define FULLDPLXMODE (1 << 11)
#define RXENABLE (1 << 2)
#define TXENABLE (1 << 3)
/* MII address register definitions */
#define MII_BUSY (1 << 0)
#define MII_WRITE (1 << 1)
#define MII_CLKRANGE_60_100M (0)
#define MII_CLKRANGE_100_150M (0x4)
#define MII_CLKRANGE_20_35M (0x8)
#define MII_CLKRANGE_35_60M (0xC)
#define MII_CLKRANGE_150_250M (0x10)
#define MII_CLKRANGE_250_300M (0x14)
#define MIIADDRSHIFT (11)
#define MIIREGSHIFT (6)
#define MII_REGMSK (0x1F << 6)
#define MII_ADDRMSK (0x1F << 11)
struct eth_dma_regs {
u32 busmode; /* 0x00 */
u32 txpolldemand; /* 0x04 */
u32 rxpolldemand; /* 0x08 */
u32 rxdesclistaddr; /* 0x0c */
u32 txdesclistaddr; /* 0x10 */
u32 status; /* 0x14 */
u32 opmode; /* 0x18 */
u32 intenable; /* 0x1c */
u32 reserved1[2];
u32 axibus; /* 0x28 */
u32 reserved2[7];
u32 currhosttxdesc; /* 0x48 */
u32 currhostrxdesc; /* 0x4c */
u32 currhosttxbuffaddr; /* 0x50 */
u32 currhostrxbuffaddr; /* 0x54 */
};
#define DW_DMA_BASE_OFFSET (0x1000)
/* Default DMA Burst length */
#ifndef CONFIG_DW_GMAC_DEFAULT_DMA_PBL
#define CONFIG_DW_GMAC_DEFAULT_DMA_PBL 8
#endif
/* Bus mode register definitions */
#define FIXEDBURST (1 << 16)
#define PRIORXTX_41 (3 << 14)
#define PRIORXTX_31 (2 << 14)
#define PRIORXTX_21 (1 << 14)
#define PRIORXTX_11 (0 << 14)
#define DMA_PBL (CONFIG_DW_GMAC_DEFAULT_DMA_PBL<<8)
#define RXHIGHPRIO (1 << 1)
#define DMAMAC_SRST (1 << 0)
/* Poll demand definitions */
#define POLL_DATA (0xFFFFFFFF)
/* Operation mode definitions */
#define STOREFORWARD (1 << 21)
#define FLUSHTXFIFO (1 << 20)
#define TXSTART (1 << 13)
#define TXSECONDFRAME (1 << 2)
#define RXSTART (1 << 1)
/* Descriptior related definitions */
#define MAC_MAX_FRAME_SZ (1600)
struct dmamacdescr {
u32 txrx_status;
u32 dmamac_cntl;
void *dmamac_addr;
struct dmamacdescr *dmamac_next;
} __aligned(ARCH_DMA_MINALIGN);
/*
* txrx_status definitions
*/
/* tx status bits definitions */
#if defined(CONFIG_DW_ALTDESCRIPTOR)
#define DESC_TXSTS_OWNBYDMA (1 << 31)
#define DESC_TXSTS_TXINT (1 << 30)
#define DESC_TXSTS_TXLAST (1 << 29)
#define DESC_TXSTS_TXFIRST (1 << 28)
#define DESC_TXSTS_TXCRCDIS (1 << 27)
#define DESC_TXSTS_TXPADDIS (1 << 26)
#define DESC_TXSTS_TXCHECKINSCTRL (3 << 22)
#define DESC_TXSTS_TXRINGEND (1 << 21)
#define DESC_TXSTS_TXCHAIN (1 << 20)
#define DESC_TXSTS_MSK (0x1FFFF << 0)
#else
#define DESC_TXSTS_OWNBYDMA (1 << 31)
#define DESC_TXSTS_MSK (0x1FFFF << 0)
#endif
/* rx status bits definitions */
#define DESC_RXSTS_OWNBYDMA (1 << 31)
#define DESC_RXSTS_DAFILTERFAIL (1 << 30)
#define DESC_RXSTS_FRMLENMSK (0x3FFF << 16)
#define DESC_RXSTS_FRMLENSHFT (16)
#define DESC_RXSTS_ERROR (1 << 15)
#define DESC_RXSTS_RXTRUNCATED (1 << 14)
#define DESC_RXSTS_SAFILTERFAIL (1 << 13)
#define DESC_RXSTS_RXIPC_GIANTFRAME (1 << 12)
#define DESC_RXSTS_RXDAMAGED (1 << 11)
#define DESC_RXSTS_RXVLANTAG (1 << 10)
#define DESC_RXSTS_RXFIRST (1 << 9)
#define DESC_RXSTS_RXLAST (1 << 8)
#define DESC_RXSTS_RXIPC_GIANT (1 << 7)
#define DESC_RXSTS_RXCOLLISION (1 << 6)
#define DESC_RXSTS_RXFRAMEETHER (1 << 5)
#define DESC_RXSTS_RXWATCHDOG (1 << 4)
#define DESC_RXSTS_RXMIIERROR (1 << 3)
#define DESC_RXSTS_RXDRIBBLING (1 << 2)
#define DESC_RXSTS_RXCRC (1 << 1)
/*
* dmamac_cntl definitions
*/
/* tx control bits definitions */
#if defined(CONFIG_DW_ALTDESCRIPTOR)
#define DESC_TXCTRL_SIZE1MASK (0x1FFF << 0)
#define DESC_TXCTRL_SIZE1SHFT (0)
#define DESC_TXCTRL_SIZE2MASK (0x1FFF << 16)
#define DESC_TXCTRL_SIZE2SHFT (16)
#else
#define DESC_TXCTRL_TXINT (1 << 31)
#define DESC_TXCTRL_TXLAST (1 << 30)
#define DESC_TXCTRL_TXFIRST (1 << 29)
#define DESC_TXCTRL_TXCHECKINSCTRL (3 << 27)
#define DESC_TXCTRL_TXCRCDIS (1 << 26)
#define DESC_TXCTRL_TXRINGEND (1 << 25)
#define DESC_TXCTRL_TXCHAIN (1 << 24)
#define DESC_TXCTRL_SIZE1MASK (0x7FF << 0)
#define DESC_TXCTRL_SIZE1SHFT (0)
#define DESC_TXCTRL_SIZE2MASK (0x7FF << 11)
#define DESC_TXCTRL_SIZE2SHFT (11)
#endif
/* rx control bits definitions */
#if defined(CONFIG_DW_ALTDESCRIPTOR)
#define DESC_RXCTRL_RXINTDIS (1 << 31)
#define DESC_RXCTRL_RXRINGEND (1 << 15)
#define DESC_RXCTRL_RXCHAIN (1 << 14)
#define DESC_RXCTRL_SIZE1MASK (0x1FFF << 0)
#define DESC_RXCTRL_SIZE1SHFT (0)
#define DESC_RXCTRL_SIZE2MASK (0x1FFF << 16)
#define DESC_RXCTRL_SIZE2SHFT (16)
#else
#define DESC_RXCTRL_RXINTDIS (1 << 31)
#define DESC_RXCTRL_RXRINGEND (1 << 25)
#define DESC_RXCTRL_RXCHAIN (1 << 24)
#define DESC_RXCTRL_SIZE1MASK (0x7FF << 0)
#define DESC_RXCTRL_SIZE1SHFT (0)
#define DESC_RXCTRL_SIZE2MASK (0x7FF << 11)
#define DESC_RXCTRL_SIZE2SHFT (11)
#endif
struct dw_eth_dev {
struct dmamacdescr tx_mac_descrtable[CONFIG_TX_DESCR_NUM];
struct dmamacdescr rx_mac_descrtable[CONFIG_RX_DESCR_NUM];
char txbuffs[TX_TOTAL_BUFSIZE] __aligned(ARCH_DMA_MINALIGN);
char rxbuffs[RX_TOTAL_BUFSIZE] __aligned(ARCH_DMA_MINALIGN);
u32 interface;
u32 tx_currdescnum;
u32 rx_currdescnum;
struct eth_mac_regs *mac_regs_p;
struct eth_dma_regs *dma_regs_p;
#ifndef CONFIG_DM_ETH
struct eth_device *dev;
#endif
struct phy_device *phydev;
struct mii_dev *bus;
};
#endif

View file

@ -0,0 +1,638 @@
/*
dm9000.c: Version 1.2 12/15/2003
A Davicom DM9000 ISA NIC fast Ethernet driver for Linux.
Copyright (C) 1997 Sten Wang
* SPDX-License-Identifier: GPL-2.0+
(C)Copyright 1997-1998 DAVICOM Semiconductor,Inc. All Rights Reserved.
V0.11 06/20/2001 REG_0A bit3=1, default enable BP with DA match
06/22/2001 Support DM9801 progrmming
E3: R25 = ((R24 + NF) & 0x00ff) | 0xf000
E4: R25 = ((R24 + NF) & 0x00ff) | 0xc200
R17 = (R17 & 0xfff0) | NF + 3
E5: R25 = ((R24 + NF - 3) & 0x00ff) | 0xc200
R17 = (R17 & 0xfff0) | NF
v1.00 modify by simon 2001.9.5
change for kernel 2.4.x
v1.1 11/09/2001 fix force mode bug
v1.2 03/18/2003 Weilun Huang <weilun_huang@davicom.com.tw>:
Fixed phy reset.
Added tx/rx 32 bit mode.
Cleaned up for kernel merge.
--------------------------------------
12/15/2003 Initial port to u-boot by
Sascha Hauer <saschahauer@web.de>
06/03/2008 Remy Bohmer <linux@bohmer.net>
- Fixed the driver to work with DM9000A.
(check on ISR receive status bit before reading the
FIFO as described in DM9000 programming guide and
application notes)
- Added autodetect of databus width.
- Made debug code compile again.
- Adapt eth_send such that it matches the DM9000*
application notes. Needed to make it work properly
for DM9000A.
- Adapted reset procedure to match DM9000 application
notes (i.e. double reset)
- some minor code cleanups
These changes are tested with DM9000{A,EP,E} together
with a 200MHz Atmel AT91SAM9261 core
TODO: external MII is not functional, only internal at the moment.
*/
#include <common.h>
#include <command.h>
#include <net.h>
#include <asm/io.h>
#include <dm9000.h>
#include "dm9000x.h"
/* Board/System/Debug information/definition ---------------- */
/* #define CONFIG_DM9000_DEBUG */
#ifdef CONFIG_DM9000_DEBUG
#define DM9000_DBG(fmt,args...) printf(fmt, ##args)
#define DM9000_DMP_PACKET(func,packet,length) \
do { \
int i; \
printf("%s: length: %d\n", func, length); \
for (i = 0; i < length; i++) { \
if (i % 8 == 0) \
printf("\n%s: %02x: ", func, i); \
printf("%02x ", ((unsigned char *) packet)[i]); \
} printf("\n"); \
} while(0)
#else
#define DM9000_DBG(fmt,args...)
#define DM9000_DMP_PACKET(func,packet,length)
#endif
/* Structure/enum declaration ------------------------------- */
typedef struct board_info {
u32 runt_length_counter; /* counter: RX length < 64byte */
u32 long_length_counter; /* counter: RX length > 1514byte */
u32 reset_counter; /* counter: RESET */
u32 reset_tx_timeout; /* RESET caused by TX Timeout */
u32 reset_rx_status; /* RESET caused by RX Statsus wrong */
u16 tx_pkt_cnt;
u16 queue_start_addr;
u16 dbug_cnt;
u8 phy_addr;
u8 device_wait_reset; /* device state */
unsigned char srom[128];
void (*outblk)(volatile void *data_ptr, int count);
void (*inblk)(void *data_ptr, int count);
void (*rx_status)(u16 *RxStatus, u16 *RxLen);
struct eth_device netdev;
} board_info_t;
static board_info_t dm9000_info;
/* function declaration ------------------------------------- */
static int dm9000_probe(void);
static u16 dm9000_phy_read(int);
static void dm9000_phy_write(int, u16);
static u8 DM9000_ior(int);
static void DM9000_iow(int reg, u8 value);
/* DM9000 network board routine ---------------------------- */
#ifndef CONFIG_DM9000_BYTE_SWAPPED
#define DM9000_outb(d,r) writeb(d, (volatile u8 *)(r))
#define DM9000_outw(d,r) writew(d, (volatile u16 *)(r))
#define DM9000_outl(d,r) writel(d, (volatile u32 *)(r))
#define DM9000_inb(r) readb((volatile u8 *)(r))
#define DM9000_inw(r) readw((volatile u16 *)(r))
#define DM9000_inl(r) readl((volatile u32 *)(r))
#else
#define DM9000_outb(d, r) __raw_writeb(d, r)
#define DM9000_outw(d, r) __raw_writew(d, r)
#define DM9000_outl(d, r) __raw_writel(d, r)
#define DM9000_inb(r) __raw_readb(r)
#define DM9000_inw(r) __raw_readw(r)
#define DM9000_inl(r) __raw_readl(r)
#endif
#ifdef CONFIG_DM9000_DEBUG
static void
dump_regs(void)
{
DM9000_DBG("\n");
DM9000_DBG("NCR (0x00): %02x\n", DM9000_ior(0));
DM9000_DBG("NSR (0x01): %02x\n", DM9000_ior(1));
DM9000_DBG("TCR (0x02): %02x\n", DM9000_ior(2));
DM9000_DBG("TSRI (0x03): %02x\n", DM9000_ior(3));
DM9000_DBG("TSRII (0x04): %02x\n", DM9000_ior(4));
DM9000_DBG("RCR (0x05): %02x\n", DM9000_ior(5));
DM9000_DBG("RSR (0x06): %02x\n", DM9000_ior(6));
DM9000_DBG("ISR (0xFE): %02x\n", DM9000_ior(DM9000_ISR));
DM9000_DBG("\n");
}
#endif
static void dm9000_outblk_8bit(volatile void *data_ptr, int count)
{
int i;
for (i = 0; i < count; i++)
DM9000_outb((((u8 *) data_ptr)[i] & 0xff), DM9000_DATA);
}
static void dm9000_outblk_16bit(volatile void *data_ptr, int count)
{
int i;
u32 tmplen = (count + 1) / 2;
for (i = 0; i < tmplen; i++)
DM9000_outw(((u16 *) data_ptr)[i], DM9000_DATA);
}
static void dm9000_outblk_32bit(volatile void *data_ptr, int count)
{
int i;
u32 tmplen = (count + 3) / 4;
for (i = 0; i < tmplen; i++)
DM9000_outl(((u32 *) data_ptr)[i], DM9000_DATA);
}
static void dm9000_inblk_8bit(void *data_ptr, int count)
{
int i;
for (i = 0; i < count; i++)
((u8 *) data_ptr)[i] = DM9000_inb(DM9000_DATA);
}
static void dm9000_inblk_16bit(void *data_ptr, int count)
{
int i;
u32 tmplen = (count + 1) / 2;
for (i = 0; i < tmplen; i++)
((u16 *) data_ptr)[i] = DM9000_inw(DM9000_DATA);
}
static void dm9000_inblk_32bit(void *data_ptr, int count)
{
int i;
u32 tmplen = (count + 3) / 4;
for (i = 0; i < tmplen; i++)
((u32 *) data_ptr)[i] = DM9000_inl(DM9000_DATA);
}
static void dm9000_rx_status_32bit(u16 *RxStatus, u16 *RxLen)
{
u32 tmpdata;
DM9000_outb(DM9000_MRCMD, DM9000_IO);
tmpdata = DM9000_inl(DM9000_DATA);
*RxStatus = __le16_to_cpu(tmpdata);
*RxLen = __le16_to_cpu(tmpdata >> 16);
}
static void dm9000_rx_status_16bit(u16 *RxStatus, u16 *RxLen)
{
DM9000_outb(DM9000_MRCMD, DM9000_IO);
*RxStatus = __le16_to_cpu(DM9000_inw(DM9000_DATA));
*RxLen = __le16_to_cpu(DM9000_inw(DM9000_DATA));
}
static void dm9000_rx_status_8bit(u16 *RxStatus, u16 *RxLen)
{
DM9000_outb(DM9000_MRCMD, DM9000_IO);
*RxStatus =
__le16_to_cpu(DM9000_inb(DM9000_DATA) +
(DM9000_inb(DM9000_DATA) << 8));
*RxLen =
__le16_to_cpu(DM9000_inb(DM9000_DATA) +
(DM9000_inb(DM9000_DATA) << 8));
}
/*
Search DM9000 board, allocate space and register it
*/
int
dm9000_probe(void)
{
u32 id_val;
id_val = DM9000_ior(DM9000_VIDL);
id_val |= DM9000_ior(DM9000_VIDH) << 8;
id_val |= DM9000_ior(DM9000_PIDL) << 16;
id_val |= DM9000_ior(DM9000_PIDH) << 24;
if (id_val == DM9000_ID) {
printf("dm9000 i/o: 0x%x, id: 0x%x \n", CONFIG_DM9000_BASE,
id_val);
return 0;
} else {
printf("dm9000 not found at 0x%08x id: 0x%08x\n",
CONFIG_DM9000_BASE, id_val);
return -1;
}
}
/* General Purpose dm9000 reset routine */
static void
dm9000_reset(void)
{
DM9000_DBG("resetting DM9000\n");
/* Reset DM9000,
see DM9000 Application Notes V1.22 Jun 11, 2004 page 29 */
/* DEBUG: Make all GPIO0 outputs, all others inputs */
DM9000_iow(DM9000_GPCR, GPCR_GPIO0_OUT);
/* Step 1: Power internal PHY by writing 0 to GPIO0 pin */
DM9000_iow(DM9000_GPR, 0);
/* Step 2: Software reset */
DM9000_iow(DM9000_NCR, (NCR_LBK_INT_MAC | NCR_RST));
do {
DM9000_DBG("resetting the DM9000, 1st reset\n");
udelay(25); /* Wait at least 20 us */
} while (DM9000_ior(DM9000_NCR) & 1);
DM9000_iow(DM9000_NCR, 0);
DM9000_iow(DM9000_NCR, (NCR_LBK_INT_MAC | NCR_RST)); /* Issue a second reset */
do {
DM9000_DBG("resetting the DM9000, 2nd reset\n");
udelay(25); /* Wait at least 20 us */
} while (DM9000_ior(DM9000_NCR) & 1);
/* Check whether the ethernet controller is present */
if ((DM9000_ior(DM9000_PIDL) != 0x0) ||
(DM9000_ior(DM9000_PIDH) != 0x90))
printf("ERROR: resetting DM9000 -> not responding\n");
}
/* Initialize dm9000 board
*/
static int dm9000_init(struct eth_device *dev, bd_t *bd)
{
int i, oft, lnk;
u8 io_mode;
struct board_info *db = &dm9000_info;
DM9000_DBG("%s\n", __func__);
/* RESET device */
dm9000_reset();
if (dm9000_probe() < 0)
return -1;
/* Auto-detect 8/16/32 bit mode, ISR Bit 6+7 indicate bus width */
io_mode = DM9000_ior(DM9000_ISR) >> 6;
switch (io_mode) {
case 0x0: /* 16-bit mode */
printf("DM9000: running in 16 bit mode\n");
db->outblk = dm9000_outblk_16bit;
db->inblk = dm9000_inblk_16bit;
db->rx_status = dm9000_rx_status_16bit;
break;
case 0x01: /* 32-bit mode */
printf("DM9000: running in 32 bit mode\n");
db->outblk = dm9000_outblk_32bit;
db->inblk = dm9000_inblk_32bit;
db->rx_status = dm9000_rx_status_32bit;
break;
case 0x02: /* 8 bit mode */
printf("DM9000: running in 8 bit mode\n");
db->outblk = dm9000_outblk_8bit;
db->inblk = dm9000_inblk_8bit;
db->rx_status = dm9000_rx_status_8bit;
break;
default:
/* Assume 8 bit mode, will probably not work anyway */
printf("DM9000: Undefined IO-mode:0x%x\n", io_mode);
db->outblk = dm9000_outblk_8bit;
db->inblk = dm9000_inblk_8bit;
db->rx_status = dm9000_rx_status_8bit;
break;
}
/* Program operating register, only internal phy supported */
DM9000_iow(DM9000_NCR, 0x0);
/* TX Polling clear */
DM9000_iow(DM9000_TCR, 0);
/* Less 3Kb, 200us */
DM9000_iow(DM9000_BPTR, BPTR_BPHW(3) | BPTR_JPT_600US);
/* Flow Control : High/Low Water */
DM9000_iow(DM9000_FCTR, FCTR_HWOT(3) | FCTR_LWOT(8));
/* SH FIXME: This looks strange! Flow Control */
DM9000_iow(DM9000_FCR, 0x0);
/* Special Mode */
DM9000_iow(DM9000_SMCR, 0);
/* clear TX status */
DM9000_iow(DM9000_NSR, NSR_WAKEST | NSR_TX2END | NSR_TX1END);
/* Clear interrupt status */
DM9000_iow(DM9000_ISR, ISR_ROOS | ISR_ROS | ISR_PTS | ISR_PRS);
printf("MAC: %pM\n", dev->enetaddr);
if (!is_valid_ethaddr(dev->enetaddr)) {
printf("WARNING: Bad MAC address (uninitialized EEPROM?)\n");
}
/* fill device MAC address registers */
for (i = 0, oft = DM9000_PAR; i < 6; i++, oft++)
DM9000_iow(oft, dev->enetaddr[i]);
for (i = 0, oft = 0x16; i < 8; i++, oft++)
DM9000_iow(oft, 0xff);
/* read back mac, just to be sure */
for (i = 0, oft = 0x10; i < 6; i++, oft++)
DM9000_DBG("%02x:", DM9000_ior(oft));
DM9000_DBG("\n");
/* Activate DM9000 */
/* RX enable */
DM9000_iow(DM9000_RCR, RCR_DIS_LONG | RCR_DIS_CRC | RCR_RXEN);
/* Enable TX/RX interrupt mask */
DM9000_iow(DM9000_IMR, IMR_PAR);
i = 0;
while (!(dm9000_phy_read(1) & 0x20)) { /* autonegation complete bit */
udelay(1000);
i++;
if (i == 10000) {
printf("could not establish link\n");
return 0;
}
}
/* see what we've got */
lnk = dm9000_phy_read(17) >> 12;
printf("operating at ");
switch (lnk) {
case 1:
printf("10M half duplex ");
break;
case 2:
printf("10M full duplex ");
break;
case 4:
printf("100M half duplex ");
break;
case 8:
printf("100M full duplex ");
break;
default:
printf("unknown: %d ", lnk);
break;
}
printf("mode\n");
return 0;
}
/*
Hardware start transmission.
Send a packet to media from the upper layer.
*/
static int dm9000_send(struct eth_device *netdev, void *packet, int length)
{
int tmo;
struct board_info *db = &dm9000_info;
DM9000_DMP_PACKET(__func__ , packet, length);
DM9000_iow(DM9000_ISR, IMR_PTM); /* Clear Tx bit in ISR */
/* Move data to DM9000 TX RAM */
DM9000_outb(DM9000_MWCMD, DM9000_IO); /* Prepare for TX-data */
/* push the data to the TX-fifo */
(db->outblk)(packet, length);
/* Set TX length to DM9000 */
DM9000_iow(DM9000_TXPLL, length & 0xff);
DM9000_iow(DM9000_TXPLH, (length >> 8) & 0xff);
/* Issue TX polling command */
DM9000_iow(DM9000_TCR, TCR_TXREQ); /* Cleared after TX complete */
/* wait for end of transmission */
tmo = get_timer(0) + 5 * CONFIG_SYS_HZ;
while ( !(DM9000_ior(DM9000_NSR) & (NSR_TX1END | NSR_TX2END)) ||
!(DM9000_ior(DM9000_ISR) & IMR_PTM) ) {
if (get_timer(0) >= tmo) {
printf("transmission timeout\n");
break;
}
}
DM9000_iow(DM9000_ISR, IMR_PTM); /* Clear Tx bit in ISR */
DM9000_DBG("transmit done\n\n");
return 0;
}
/*
Stop the interface.
The interface is stopped when it is brought.
*/
static void dm9000_halt(struct eth_device *netdev)
{
DM9000_DBG("%s\n", __func__);
/* RESET devie */
dm9000_phy_write(0, 0x8000); /* PHY RESET */
DM9000_iow(DM9000_GPR, 0x01); /* Power-Down PHY */
DM9000_iow(DM9000_IMR, 0x80); /* Disable all interrupt */
DM9000_iow(DM9000_RCR, 0x00); /* Disable RX */
}
/*
Received a packet and pass to upper layer
*/
static int dm9000_rx(struct eth_device *netdev)
{
u8 rxbyte;
u8 *rdptr = (u8 *)net_rx_packets[0];
u16 RxStatus, RxLen = 0;
struct board_info *db = &dm9000_info;
/* Check packet ready or not, we must check
the ISR status first for DM9000A */
if (!(DM9000_ior(DM9000_ISR) & 0x01)) /* Rx-ISR bit must be set. */
return 0;
DM9000_iow(DM9000_ISR, 0x01); /* clear PR status latched in bit 0 */
/* There is _at least_ 1 package in the fifo, read them all */
for (;;) {
DM9000_ior(DM9000_MRCMDX); /* Dummy read */
/* Get most updated data,
only look at bits 0:1, See application notes DM9000 */
rxbyte = DM9000_inb(DM9000_DATA) & 0x03;
/* Status check: this byte must be 0 or 1 */
if (rxbyte > DM9000_PKT_RDY) {
DM9000_iow(DM9000_RCR, 0x00); /* Stop Device */
DM9000_iow(DM9000_ISR, 0x80); /* Stop INT request */
printf("DM9000 error: status check fail: 0x%x\n",
rxbyte);
return 0;
}
if (rxbyte != DM9000_PKT_RDY)
return 0; /* No packet received, ignore */
DM9000_DBG("receiving packet\n");
/* A packet ready now & Get status/length */
(db->rx_status)(&RxStatus, &RxLen);
DM9000_DBG("rx status: 0x%04x rx len: %d\n", RxStatus, RxLen);
/* Move data from DM9000 */
/* Read received packet from RX SRAM */
(db->inblk)(rdptr, RxLen);
if ((RxStatus & 0xbf00) || (RxLen < 0x40)
|| (RxLen > DM9000_PKT_MAX)) {
if (RxStatus & 0x100) {
printf("rx fifo error\n");
}
if (RxStatus & 0x200) {
printf("rx crc error\n");
}
if (RxStatus & 0x8000) {
printf("rx length error\n");
}
if (RxLen > DM9000_PKT_MAX) {
printf("rx length too big\n");
dm9000_reset();
}
} else {
DM9000_DMP_PACKET(__func__ , rdptr, RxLen);
DM9000_DBG("passing packet to upper layer\n");
net_process_received_packet(net_rx_packets[0], RxLen);
}
}
return 0;
}
/*
Read a word data from SROM
*/
#if !defined(CONFIG_DM9000_NO_SROM)
void dm9000_read_srom_word(int offset, u8 *to)
{
DM9000_iow(DM9000_EPAR, offset);
DM9000_iow(DM9000_EPCR, 0x4);
udelay(8000);
DM9000_iow(DM9000_EPCR, 0x0);
to[0] = DM9000_ior(DM9000_EPDRL);
to[1] = DM9000_ior(DM9000_EPDRH);
}
void dm9000_write_srom_word(int offset, u16 val)
{
DM9000_iow(DM9000_EPAR, offset);
DM9000_iow(DM9000_EPDRH, ((val >> 8) & 0xff));
DM9000_iow(DM9000_EPDRL, (val & 0xff));
DM9000_iow(DM9000_EPCR, 0x12);
udelay(8000);
DM9000_iow(DM9000_EPCR, 0);
}
#endif
static void dm9000_get_enetaddr(struct eth_device *dev)
{
#if !defined(CONFIG_DM9000_NO_SROM)
int i;
for (i = 0; i < 3; i++)
dm9000_read_srom_word(i, dev->enetaddr + (2 * i));
#endif
}
/*
Read a byte from I/O port
*/
static u8
DM9000_ior(int reg)
{
DM9000_outb(reg, DM9000_IO);
return DM9000_inb(DM9000_DATA);
}
/*
Write a byte to I/O port
*/
static void
DM9000_iow(int reg, u8 value)
{
DM9000_outb(reg, DM9000_IO);
DM9000_outb(value, DM9000_DATA);
}
/*
Read a word from phyxcer
*/
static u16
dm9000_phy_read(int reg)
{
u16 val;
/* Fill the phyxcer register into REG_0C */
DM9000_iow(DM9000_EPAR, DM9000_PHY | reg);
DM9000_iow(DM9000_EPCR, 0xc); /* Issue phyxcer read command */
udelay(100); /* Wait read complete */
DM9000_iow(DM9000_EPCR, 0x0); /* Clear phyxcer read command */
val = (DM9000_ior(DM9000_EPDRH) << 8) | DM9000_ior(DM9000_EPDRL);
/* The read data keeps on REG_0D & REG_0E */
DM9000_DBG("dm9000_phy_read(0x%x): 0x%x\n", reg, val);
return val;
}
/*
Write a word to phyxcer
*/
static void
dm9000_phy_write(int reg, u16 value)
{
/* Fill the phyxcer register into REG_0C */
DM9000_iow(DM9000_EPAR, DM9000_PHY | reg);
/* Fill the written data into REG_0D & REG_0E */
DM9000_iow(DM9000_EPDRL, (value & 0xff));
DM9000_iow(DM9000_EPDRH, ((value >> 8) & 0xff));
DM9000_iow(DM9000_EPCR, 0xa); /* Issue phyxcer write command */
udelay(500); /* Wait write complete */
DM9000_iow(DM9000_EPCR, 0x0); /* Clear phyxcer write command */
DM9000_DBG("dm9000_phy_write(reg:0x%x, value:0x%x)\n", reg, value);
}
int dm9000_initialize(bd_t *bis)
{
struct eth_device *dev = &(dm9000_info.netdev);
/* Load MAC address from EEPROM */
dm9000_get_enetaddr(dev);
dev->init = dm9000_init;
dev->halt = dm9000_halt;
dev->send = dm9000_send;
dev->recv = dm9000_rx;
sprintf(dev->name, "dm9000");
eth_register(dev);
return 0;
}

View file

@ -0,0 +1,140 @@
/*
* dm9000 Ethernet
*/
#ifdef CONFIG_DRIVER_DM9000
#define DM9000_ID 0x90000A46
#define DM9000_PKT_MAX 1536 /* Received packet max size */
#define DM9000_PKT_RDY 0x01 /* Packet ready to receive */
/* although the registers are 16 bit, they are 32-bit aligned.
*/
#define DM9000_NCR 0x00
#define DM9000_NSR 0x01
#define DM9000_TCR 0x02
#define DM9000_TSR1 0x03
#define DM9000_TSR2 0x04
#define DM9000_RCR 0x05
#define DM9000_RSR 0x06
#define DM9000_ROCR 0x07
#define DM9000_BPTR 0x08
#define DM9000_FCTR 0x09
#define DM9000_FCR 0x0A
#define DM9000_EPCR 0x0B
#define DM9000_EPAR 0x0C
#define DM9000_EPDRL 0x0D
#define DM9000_EPDRH 0x0E
#define DM9000_WCR 0x0F
#define DM9000_PAR 0x10
#define DM9000_MAR 0x16
#define DM9000_GPCR 0x1e
#define DM9000_GPR 0x1f
#define DM9000_TRPAL 0x22
#define DM9000_TRPAH 0x23
#define DM9000_RWPAL 0x24
#define DM9000_RWPAH 0x25
#define DM9000_VIDL 0x28
#define DM9000_VIDH 0x29
#define DM9000_PIDL 0x2A
#define DM9000_PIDH 0x2B
#define DM9000_CHIPR 0x2C
#define DM9000_SMCR 0x2F
#define DM9000_PHY 0x40 /* PHY address 0x01 */
#define DM9000_MRCMDX 0xF0
#define DM9000_MRCMD 0xF2
#define DM9000_MRRL 0xF4
#define DM9000_MRRH 0xF5
#define DM9000_MWCMDX 0xF6
#define DM9000_MWCMD 0xF8
#define DM9000_MWRL 0xFA
#define DM9000_MWRH 0xFB
#define DM9000_TXPLL 0xFC
#define DM9000_TXPLH 0xFD
#define DM9000_ISR 0xFE
#define DM9000_IMR 0xFF
#define NCR_EXT_PHY (1<<7)
#define NCR_WAKEEN (1<<6)
#define NCR_FCOL (1<<4)
#define NCR_FDX (1<<3)
#define NCR_LBK (3<<1)
#define NCR_LBK_INT_MAC (1<<1)
#define NCR_LBK_INT_PHY (2<<1)
#define NCR_RST (1<<0)
#define NSR_SPEED (1<<7)
#define NSR_LINKST (1<<6)
#define NSR_WAKEST (1<<5)
#define NSR_TX2END (1<<3)
#define NSR_TX1END (1<<2)
#define NSR_RXOV (1<<1)
#define TCR_TJDIS (1<<6)
#define TCR_EXCECM (1<<5)
#define TCR_PAD_DIS2 (1<<4)
#define TCR_CRC_DIS2 (1<<3)
#define TCR_PAD_DIS1 (1<<2)
#define TCR_CRC_DIS1 (1<<1)
#define TCR_TXREQ (1<<0)
#define TSR_TJTO (1<<7)
#define TSR_LC (1<<6)
#define TSR_NC (1<<5)
#define TSR_LCOL (1<<4)
#define TSR_COL (1<<3)
#define TSR_EC (1<<2)
#define RCR_WTDIS (1<<6)
#define RCR_DIS_LONG (1<<5)
#define RCR_DIS_CRC (1<<4)
#define RCR_ALL (1<<3)
#define RCR_RUNT (1<<2)
#define RCR_PRMSC (1<<1)
#define RCR_RXEN (1<<0)
#define RSR_RF (1<<7)
#define RSR_MF (1<<6)
#define RSR_LCS (1<<5)
#define RSR_RWTO (1<<4)
#define RSR_PLE (1<<3)
#define RSR_AE (1<<2)
#define RSR_CE (1<<1)
#define RSR_FOE (1<<0)
#define EPCR_EPOS_PHY (1<<3)
#define EPCR_EPOS_EE (0<<3)
#define EPCR_ERPRR (1<<2)
#define EPCR_ERPRW (1<<1)
#define EPCR_ERRE (1<<0)
#define FCTR_HWOT(ot) (( ot & 0xf ) << 4 )
#define FCTR_LWOT(ot) ( ot & 0xf )
#define BPTR_BPHW(x) ((x) << 4)
#define BPTR_JPT_200US (0x07)
#define BPTR_JPT_600US (0x0f)
#define IMR_PAR (1<<7)
#define IMR_ROOM (1<<3)
#define IMR_ROM (1<<2)
#define IMR_PTM (1<<1)
#define IMR_PRM (1<<0)
#define ISR_ROOS (1<<3)
#define ISR_ROS (1<<2)
#define ISR_PTS (1<<1)
#define ISR_PRS (1<<0)
#define GPCR_GPIO0_OUT (1<<0)
#define GPR_PHY_PWROFF (1<<0)
#endif

View file

@ -0,0 +1,393 @@
/*
* Dave Ethernet Controller driver
*
* Copyright (C) 2008 Dave S.r.l. <www.dave.eu>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <common.h>
#ifndef CONFIG_DNET_AUTONEG_TIMEOUT
#define CONFIG_DNET_AUTONEG_TIMEOUT 5000000 /* default value */
#endif
#include <net.h>
#include <malloc.h>
#include <linux/mii.h>
#include <miiphy.h>
#include <asm/io.h>
#include <asm/unaligned.h>
#include "dnet.h"
struct dnet_device {
struct dnet_registers *regs;
const struct device *dev;
struct eth_device netdev;
unsigned short phy_addr;
};
/* get struct dnet_device from given struct netdev */
#define to_dnet(_nd) container_of(_nd, struct dnet_device, netdev)
/* function for reading internal MAC register */
u16 dnet_readw_mac(struct dnet_device *dnet, u16 reg)
{
u16 data_read;
/* issue a read */
writel(reg, &dnet->regs->MACREG_ADDR);
/* since a read/write op to the MAC is very slow,
* we must wait before reading the data */
udelay(1);
/* read data read from the MAC register */
data_read = readl(&dnet->regs->MACREG_DATA);
/* all done */
return data_read;
}
/* function for writing internal MAC register */
void dnet_writew_mac(struct dnet_device *dnet, u16 reg, u16 val)
{
/* load data to write */
writel(val, &dnet->regs->MACREG_DATA);
/* issue a write */
writel(reg | DNET_INTERNAL_WRITE, &dnet->regs->MACREG_ADDR);
/* since a read/write op to the MAC is very slow,
* we must wait before exiting */
udelay(1);
}
static void dnet_mdio_write(struct dnet_device *dnet, u8 reg, u16 value)
{
u16 tmp;
debug(DRIVERNAME "dnet_mdio_write %02x:%02x <- %04x\n",
dnet->phy_addr, reg, value);
while (!(dnet_readw_mac(dnet, DNET_INTERNAL_GMII_MNG_CTL_REG) &
DNET_INTERNAL_GMII_MNG_CMD_FIN))
;
/* prepare for a write operation */
tmp = (1 << 13);
/* only 5 bits allowed for register offset */
reg &= 0x1f;
/* prepare reg_value for a write */
tmp |= (dnet->phy_addr << 8);
tmp |= reg;
/* write data to write first */
dnet_writew_mac(dnet, DNET_INTERNAL_GMII_MNG_DAT_REG, value);
/* write control word */
dnet_writew_mac(dnet, DNET_INTERNAL_GMII_MNG_CTL_REG, tmp);
while (!(dnet_readw_mac(dnet, DNET_INTERNAL_GMII_MNG_CTL_REG) &
DNET_INTERNAL_GMII_MNG_CMD_FIN))
;
}
static u16 dnet_mdio_read(struct dnet_device *dnet, u8 reg)
{
u16 value;
while (!(dnet_readw_mac(dnet, DNET_INTERNAL_GMII_MNG_CTL_REG) &
DNET_INTERNAL_GMII_MNG_CMD_FIN))
;
/* only 5 bits allowed for register offset*/
reg &= 0x1f;
/* prepare reg_value for a read */
value = (dnet->phy_addr << 8);
value |= reg;
/* write control word */
dnet_writew_mac(dnet, DNET_INTERNAL_GMII_MNG_CTL_REG, value);
/* wait for end of transfer */
while (!(dnet_readw_mac(dnet, DNET_INTERNAL_GMII_MNG_CTL_REG) &
DNET_INTERNAL_GMII_MNG_CMD_FIN))
;
value = dnet_readw_mac(dnet, DNET_INTERNAL_GMII_MNG_DAT_REG);
debug(DRIVERNAME "dnet_mdio_read %02x:%02x <- %04x\n",
dnet->phy_addr, reg, value);
return value;
}
static int dnet_send(struct eth_device *netdev, void *packet, int length)
{
struct dnet_device *dnet = to_dnet(netdev);
int i, wrsz;
unsigned int *bufp;
unsigned int tx_cmd;
debug(DRIVERNAME "[%s] Sending %u bytes\n", __func__, length);
bufp = (unsigned int *) (((u32)packet) & 0xFFFFFFFC);
wrsz = (u32)length + 3;
wrsz += ((u32)packet) & 0x3;
wrsz >>= 2;
tx_cmd = ((((unsigned int)(packet)) & 0x03) << 16) | (u32)length;
/* check if there is enough room for the current frame */
if (wrsz < (DNET_FIFO_SIZE - readl(&dnet->regs->TX_FIFO_WCNT))) {
for (i = 0; i < wrsz; i++)
writel(*bufp++, &dnet->regs->TX_DATA_FIFO);
/*
* inform MAC that a packet's written and ready
* to be shipped out
*/
writel(tx_cmd, &dnet->regs->TX_LEN_FIFO);
} else {
printf(DRIVERNAME "No free space (actual %d, required %d "
"(words))\n", DNET_FIFO_SIZE -
readl(&dnet->regs->TX_FIFO_WCNT), wrsz);
}
/* No one cares anyway */
return 0;
}
static int dnet_recv(struct eth_device *netdev)
{
struct dnet_device *dnet = to_dnet(netdev);
unsigned int *data_ptr;
int pkt_len, poll, i;
u32 cmd_word;
debug("Waiting for pkt (polling)\n");
poll = 50;
while ((readl(&dnet->regs->RX_FIFO_WCNT) >> 16) == 0) {
udelay(10); /* wait 10 usec */
if (--poll == 0)
return 0; /* no pkt available */
}
cmd_word = readl(&dnet->regs->RX_LEN_FIFO);
pkt_len = cmd_word & 0xFFFF;
debug("Got pkt with size %d bytes\n", pkt_len);
if (cmd_word & 0xDF180000)
printf("%s packet receive error %x\n", __func__, cmd_word);
data_ptr = (unsigned int *)net_rx_packets[0];
for (i = 0; i < (pkt_len + 3) >> 2; i++)
*data_ptr++ = readl(&dnet->regs->RX_DATA_FIFO);
/* ok + 5 ?? */
net_process_received_packet(net_rx_packets[0], pkt_len + 5);
return 0;
}
static void dnet_set_hwaddr(struct eth_device *netdev)
{
struct dnet_device *dnet = to_dnet(netdev);
u16 tmp;
tmp = get_unaligned_be16(netdev->enetaddr);
dnet_writew_mac(dnet, DNET_INTERNAL_MAC_ADDR_0_REG, tmp);
tmp = get_unaligned_be16(&netdev->enetaddr[2]);
dnet_writew_mac(dnet, DNET_INTERNAL_MAC_ADDR_1_REG, tmp);
tmp = get_unaligned_be16(&netdev->enetaddr[4]);
dnet_writew_mac(dnet, DNET_INTERNAL_MAC_ADDR_2_REG, tmp);
}
static void dnet_phy_reset(struct dnet_device *dnet)
{
struct eth_device *netdev = &dnet->netdev;
int i;
u16 status, adv;
adv = ADVERTISE_CSMA | ADVERTISE_ALL;
dnet_mdio_write(dnet, MII_ADVERTISE, adv);
printf("%s: Starting autonegotiation...\n", netdev->name);
dnet_mdio_write(dnet, MII_BMCR, (BMCR_ANENABLE
| BMCR_ANRESTART));
for (i = 0; i < CONFIG_DNET_AUTONEG_TIMEOUT / 100; i++) {
status = dnet_mdio_read(dnet, MII_BMSR);
if (status & BMSR_ANEGCOMPLETE)
break;
udelay(100);
}
if (status & BMSR_ANEGCOMPLETE)
printf("%s: Autonegotiation complete\n", netdev->name);
else
printf("%s: Autonegotiation timed out (status=0x%04x)\n",
netdev->name, status);
}
static int dnet_phy_init(struct dnet_device *dnet)
{
struct eth_device *netdev = &dnet->netdev;
u16 phy_id, status, adv, lpa;
int media, speed, duplex;
int i;
u32 ctl_reg;
/* Find a PHY */
for (i = 0; i < 32; i++) {
dnet->phy_addr = i;
phy_id = dnet_mdio_read(dnet, MII_PHYSID1);
if (phy_id != 0xffff) {
/* ok we found it */
printf("Found PHY at address %d PHYID (%04x:%04x)\n",
i, phy_id,
dnet_mdio_read(dnet, MII_PHYSID2));
break;
}
}
/* Check if the PHY is up to snuff... */
phy_id = dnet_mdio_read(dnet, MII_PHYSID1);
if (phy_id == 0xffff) {
printf("%s: No PHY present\n", netdev->name);
return -1;
}
status = dnet_mdio_read(dnet, MII_BMSR);
if (!(status & BMSR_LSTATUS)) {
/* Try to re-negotiate if we don't have link already. */
dnet_phy_reset(dnet);
for (i = 0; i < CONFIG_DNET_AUTONEG_TIMEOUT / 100; i++) {
status = dnet_mdio_read(dnet, MII_BMSR);
if (status & BMSR_LSTATUS)
break;
udelay(100);
}
}
if (!(status & BMSR_LSTATUS)) {
printf("%s: link down (status: 0x%04x)\n",
netdev->name, status);
return -1;
} else {
adv = dnet_mdio_read(dnet, MII_ADVERTISE);
lpa = dnet_mdio_read(dnet, MII_LPA);
media = mii_nway_result(lpa & adv);
speed = (media & (ADVERTISE_100FULL | ADVERTISE_100HALF)
? 1 : 0);
duplex = (media & ADVERTISE_FULL) ? 1 : 0;
/* 1000BaseT ethernet is not supported */
printf("%s: link up, %sMbps %s-duplex (lpa: 0x%04x)\n",
netdev->name,
speed ? "100" : "10",
duplex ? "full" : "half",
lpa);
ctl_reg = dnet_readw_mac(dnet, DNET_INTERNAL_RXTX_CONTROL_REG);
if (duplex)
ctl_reg &= ~(DNET_INTERNAL_RXTX_CONTROL_ENABLEHALFDUP);
else
ctl_reg |= DNET_INTERNAL_RXTX_CONTROL_ENABLEHALFDUP;
dnet_writew_mac(dnet, DNET_INTERNAL_RXTX_CONTROL_REG, ctl_reg);
return 0;
}
}
static int dnet_init(struct eth_device *netdev, bd_t *bd)
{
struct dnet_device *dnet = to_dnet(netdev);
u32 config;
/*
* dnet_halt should have been called at some point before now,
* so we'll assume the controller is idle.
*/
/* set hardware address */
dnet_set_hwaddr(netdev);
if (dnet_phy_init(dnet) < 0)
return -1;
/* flush rx/tx fifos */
writel(DNET_SYS_CTL_RXFIFOFLUSH | DNET_SYS_CTL_TXFIFOFLUSH,
&dnet->regs->SYS_CTL);
udelay(1000);
writel(0, &dnet->regs->SYS_CTL);
config = dnet_readw_mac(dnet, DNET_INTERNAL_RXTX_CONTROL_REG);
config |= DNET_INTERNAL_RXTX_CONTROL_RXPAUSE |
DNET_INTERNAL_RXTX_CONTROL_RXBROADCAST |
DNET_INTERNAL_RXTX_CONTROL_DROPCONTROL |
DNET_INTERNAL_RXTX_CONTROL_DISCFXFCS;
dnet_writew_mac(dnet, DNET_INTERNAL_RXTX_CONTROL_REG, config);
/* Enable TX and RX */
dnet_writew_mac(dnet, DNET_INTERNAL_MODE_REG,
DNET_INTERNAL_MODE_RXEN | DNET_INTERNAL_MODE_TXEN);
return 0;
}
static void dnet_halt(struct eth_device *netdev)
{
struct dnet_device *dnet = to_dnet(netdev);
/* Disable TX and RX */
dnet_writew_mac(dnet, DNET_INTERNAL_MODE_REG, 0);
}
int dnet_eth_initialize(int id, void *regs, unsigned int phy_addr)
{
struct dnet_device *dnet;
struct eth_device *netdev;
unsigned int dev_capa;
dnet = malloc(sizeof(struct dnet_device));
if (!dnet) {
printf("Error: Failed to allocate memory for DNET%d\n", id);
return -1;
}
memset(dnet, 0, sizeof(struct dnet_device));
netdev = &dnet->netdev;
dnet->regs = (struct dnet_registers *)regs;
dnet->phy_addr = phy_addr;
sprintf(netdev->name, "dnet%d", id);
netdev->init = dnet_init;
netdev->halt = dnet_halt;
netdev->send = dnet_send;
netdev->recv = dnet_recv;
dev_capa = readl(&dnet->regs->VERCAPS) & 0xFFFF;
debug("%s: has %smdio, %sirq, %sgigabit, %sdma \n", netdev->name,
(dev_capa & DNET_HAS_MDIO) ? "" : "no ",
(dev_capa & DNET_HAS_IRQ) ? "" : "no ",
(dev_capa & DNET_HAS_GIGABIT) ? "" : "no ",
(dev_capa & DNET_HAS_DMA) ? "" : "no ");
eth_register(netdev);
return 0;
}

View file

@ -0,0 +1,166 @@
/*
* Dave Ethernet Controller driver
*
* Copyright (C) 2008 Dave S.r.l. <www.dave.eu>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#ifndef __DRIVERS_DNET_H__
#define __DRIVERS_DNET_H__
#define DRIVERNAME "dnet"
struct dnet_registers {
/* ALL DNET FIFO REGISTERS */
u32 RX_LEN_FIFO;
u32 RX_DATA_FIFO;
u32 TX_LEN_FIFO;
u32 TX_DATA_FIFO;
u32 pad1[0x3c];
/* ALL DNET CONTROL/STATUS REGISTERS */
u32 VERCAPS;
u32 INTR_SRC;
u32 INTR_ENB;
u32 RX_STATUS;
u32 TX_STATUS;
u32 RX_FRAMES_CNT;
u32 TX_FRAMES_CNT;
u32 RX_FIFO_TH;
u32 TX_FIFO_TH;
u32 SYS_CTL;
u32 PAUSE_TMR;
u32 RX_FIFO_WCNT;
u32 TX_FIFO_WCNT;
u32 pad2[0x33];
/* ALL DNET MAC REGISTERS */
u32 MACREG_DATA; /* Mac-Reg Data */
u32 MACREG_ADDR; /* Mac-Reg Addr */
u32 pad3[0x3e];
/* ALL DNET RX STATISTICS COUNTERS */
u32 RX_PKT_IGNR_CNT;
u32 RX_LEN_CHK_ERR_CNT;
u32 RX_LNG_FRM_CNT;
u32 RX_SHRT_FRM_CNT;
u32 RX_IPG_VIOL_CNT;
u32 RX_CRC_ERR_CNT;
u32 RX_OK_PKT_CNT;
u32 RX_CTL_FRM_CNT;
u32 RX_PAUSE_FRM_CNT;
u32 RX_MULTICAST_CNT;
u32 RX_BROADCAST_CNT;
u32 RX_VLAN_TAG_CNT;
u32 RX_PRE_SHRINK_CNT;
u32 RX_DRIB_NIB_CNT;
u32 RX_UNSUP_OPCD_CNT;
u32 RX_BYTE_CNT;
u32 pad4[0x30];
/* DNET TX STATISTICS COUNTERS */
u32 TX_UNICAST_CNT;
u32 TX_PAUSE_FRM_CNT;
u32 TX_MULTICAST_CNT;
u32 TX_BRDCAST_CNT;
u32 TX_VLAN_TAG_CNT;
u32 TX_BAD_FCS_CNT;
u32 TX_JUMBO_CNT;
u32 TX_BYTE_CNT;
};
/* SOME INTERNAL MAC-CORE REGISTER */
#define DNET_INTERNAL_MODE_REG 0x0
#define DNET_INTERNAL_RXTX_CONTROL_REG 0x2
#define DNET_INTERNAL_MAX_PKT_SIZE_REG 0x4
#define DNET_INTERNAL_IGP_REG 0x8
#define DNET_INTERNAL_MAC_ADDR_0_REG 0xa
#define DNET_INTERNAL_MAC_ADDR_1_REG 0xc
#define DNET_INTERNAL_MAC_ADDR_2_REG 0xe
#define DNET_INTERNAL_TX_RX_STS_REG 0x12
#define DNET_INTERNAL_GMII_MNG_CTL_REG 0x14
#define DNET_INTERNAL_GMII_MNG_DAT_REG 0x16
#define DNET_INTERNAL_GMII_MNG_CMD_FIN (1 << 14)
#define DNET_INTERNAL_WRITE (1 << 31)
/* MAC-CORE REGISTER FIELDS */
/* MAC-CORE MODE REGISTER FIELDS */
#define DNET_INTERNAL_MODE_GBITEN (1 << 0)
#define DNET_INTERNAL_MODE_FCEN (1 << 1)
#define DNET_INTERNAL_MODE_RXEN (1 << 2)
#define DNET_INTERNAL_MODE_TXEN (1 << 3)
/* MAC-CORE RXTX CONTROL REGISTER FIELDS */
#define DNET_INTERNAL_RXTX_CONTROL_RXSHORTFRAME (1 << 8)
#define DNET_INTERNAL_RXTX_CONTROL_RXBROADCAST (1 << 7)
#define DNET_INTERNAL_RXTX_CONTROL_RXMULTICAST (1 << 4)
#define DNET_INTERNAL_RXTX_CONTROL_RXPAUSE (1 << 3)
#define DNET_INTERNAL_RXTX_CONTROL_DISTXFCS (1 << 2)
#define DNET_INTERNAL_RXTX_CONTROL_DISCFXFCS (1 << 1)
#define DNET_INTERNAL_RXTX_CONTROL_ENPROMISC (1 << 0)
#define DNET_INTERNAL_RXTX_CONTROL_DROPCONTROL (1 << 6)
#define DNET_INTERNAL_RXTX_CONTROL_ENABLEHALFDUP (1 << 5)
/* SYSTEM CONTROL REGISTER FIELDS */
#define DNET_SYS_CTL_IGNORENEXTPKT (1 << 0)
#define DNET_SYS_CTL_SENDPAUSE (1 << 2)
#define DNET_SYS_CTL_RXFIFOFLUSH (1 << 3)
#define DNET_SYS_CTL_TXFIFOFLUSH (1 << 4)
/* TX STATUS REGISTER FIELDS */
#define DNET_TX_STATUS_FIFO_ALMOST_EMPTY (1 << 2)
#define DNET_TX_STATUS_FIFO_ALMOST_FULL (1 << 1)
/* INTERRUPT SOURCE REGISTER FIELDS */
#define DNET_INTR_SRC_TX_PKTSENT (1 << 0)
#define DNET_INTR_SRC_TX_FIFOAF (1 << 1)
#define DNET_INTR_SRC_TX_FIFOAE (1 << 2)
#define DNET_INTR_SRC_TX_DISCFRM (1 << 3)
#define DNET_INTR_SRC_TX_FIFOFULL (1 << 4)
#define DNET_INTR_SRC_RX_CMDFIFOAF (1 << 8)
#define DNET_INTR_SRC_RX_CMDFIFOFF (1 << 9)
#define DNET_INTR_SRC_RX_DATAFIFOFF (1 << 10)
#define DNET_INTR_SRC_TX_SUMMARY (1 << 16)
#define DNET_INTR_SRC_RX_SUMMARY (1 << 17)
#define DNET_INTR_SRC_PHY (1 << 19)
/* INTERRUPT ENABLE REGISTER FIELDS */
#define DNET_INTR_ENB_TX_PKTSENT (1 << 0)
#define DNET_INTR_ENB_TX_FIFOAF (1 << 1)
#define DNET_INTR_ENB_TX_FIFOAE (1 << 2)
#define DNET_INTR_ENB_TX_DISCFRM (1 << 3)
#define DNET_INTR_ENB_TX_FIFOFULL (1 << 4)
#define DNET_INTR_ENB_RX_PKTRDY (1 << 8)
#define DNET_INTR_ENB_RX_FIFOAF (1 << 9)
#define DNET_INTR_ENB_RX_FIFOERR (1 << 10)
#define DNET_INTR_ENB_RX_ERROR (1 << 11)
#define DNET_INTR_ENB_RX_FIFOFULL (1 << 12)
#define DNET_INTR_ENB_RX_FIFOAE (1 << 13)
#define DNET_INTR_ENB_TX_SUMMARY (1 << 16)
#define DNET_INTR_ENB_RX_SUMMARY (1 << 17)
#define DNET_INTR_ENB_GLOBAL_ENABLE (1 << 18)
/*
* Capabilities. Used by the driver to know the capabilities that
* the ethernet controller inside the FPGA have.
*/
#define DNET_HAS_MDIO (1 << 0)
#define DNET_HAS_IRQ (1 << 1)
#define DNET_HAS_GIGABIT (1 << 2)
#define DNET_HAS_DMA (1 << 3)
#define DNET_HAS_MII (1 << 4) /* or GMII */
#define DNET_HAS_RMII (1 << 5) /* or RGMII */
#define DNET_CAPS_MASK 0xFFFF
#define DNET_FIFO_SIZE 2048 /* 2K x 32 bit */
#define DNET_FIFO_TX_DATA_AF_TH (DNET_FIFO_SIZE - 384) /* 384 = 1536 / 4 */
#define DNET_FIFO_TX_DATA_AE_TH (384)
#define DNET_FIFO_RX_CMD_AF_TH (1 << 16) /* just one frame inside the FIFO */
#endif

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,579 @@
#include <common.h>
#include <console.h>
#include "e1000.h"
#include <linux/compiler.h>
/*-----------------------------------------------------------------------
* SPI transfer
*
* This writes "bitlen" bits out the SPI MOSI port and simultaneously clocks
* "bitlen" bits in the SPI MISO port. That's just the way SPI works.
*
* The source of the outgoing bits is the "dout" parameter and the
* destination of the input bits is the "din" parameter. Note that "dout"
* and "din" can point to the same memory location, in which case the
* input data overwrites the output data (since both are buffered by
* temporary variables, this is OK).
*
* This may be interrupted with Ctrl-C if "intr" is true, otherwise it will
* never return an error.
*/
static int e1000_spi_xfer(struct e1000_hw *hw, unsigned int bitlen,
const void *dout_mem, void *din_mem, bool intr)
{
const uint8_t *dout = dout_mem;
uint8_t *din = din_mem;
uint8_t mask = 0;
uint32_t eecd;
unsigned long i;
/* Pre-read the control register */
eecd = E1000_READ_REG(hw, EECD);
/* Iterate over each bit */
for (i = 0, mask = 0x80; i < bitlen; i++, mask = (mask >> 1)?:0x80) {
/* Check for interrupt */
if (intr && ctrlc())
return -1;
/* Determine the output bit */
if (dout && dout[i >> 3] & mask)
eecd |= E1000_EECD_DI;
else
eecd &= ~E1000_EECD_DI;
/* Write the output bit and wait 50us */
E1000_WRITE_REG(hw, EECD, eecd);
E1000_WRITE_FLUSH(hw);
udelay(50);
/* Poke the clock (waits 50us) */
e1000_raise_ee_clk(hw, &eecd);
/* Now read the input bit */
eecd = E1000_READ_REG(hw, EECD);
if (din) {
if (eecd & E1000_EECD_DO)
din[i >> 3] |= mask;
else
din[i >> 3] &= ~mask;
}
/* Poke the clock again (waits 50us) */
e1000_lower_ee_clk(hw, &eecd);
}
/* Now clear any remaining bits of the input */
if (din && (i & 7))
din[i >> 3] &= ~((mask << 1) - 1);
return 0;
}
#ifdef CONFIG_E1000_SPI_GENERIC
static inline struct e1000_hw *e1000_hw_from_spi(struct spi_slave *spi)
{
return container_of(spi, struct e1000_hw, spi);
}
/* Not sure why all of these are necessary */
void spi_init_r(void) { /* Nothing to do */ }
void spi_init_f(void) { /* Nothing to do */ }
void spi_init(void) { /* Nothing to do */ }
struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs,
unsigned int max_hz, unsigned int mode)
{
/* Find the right PCI device */
struct e1000_hw *hw = e1000_find_card(bus);
if (!hw) {
printf("ERROR: No such e1000 device: e1000#%u\n", bus);
return NULL;
}
/* Make sure it has an SPI chip */
if (hw->eeprom.type != e1000_eeprom_spi) {
E1000_ERR(hw->nic, "No attached SPI EEPROM found!\n");
return NULL;
}
/* Argument sanity checks */
if (cs != 0) {
E1000_ERR(hw->nic, "No such SPI chip: %u\n", cs);
return NULL;
}
if (mode != SPI_MODE_0) {
E1000_ERR(hw->nic, "Only SPI MODE-0 is supported!\n");
return NULL;
}
/* TODO: Use max_hz somehow */
E1000_DBG(hw->nic, "EEPROM SPI access requested\n");
return &hw->spi;
}
void spi_free_slave(struct spi_slave *spi)
{
__maybe_unused struct e1000_hw *hw = e1000_hw_from_spi(spi);
E1000_DBG(hw->nic, "EEPROM SPI access released\n");
}
int spi_claim_bus(struct spi_slave *spi)
{
struct e1000_hw *hw = e1000_hw_from_spi(spi);
if (e1000_acquire_eeprom(hw)) {
E1000_ERR(hw->nic, "EEPROM SPI cannot be acquired!\n");
return -1;
}
return 0;
}
void spi_release_bus(struct spi_slave *spi)
{
struct e1000_hw *hw = e1000_hw_from_spi(spi);
e1000_release_eeprom(hw);
}
/* Skinny wrapper around e1000_spi_xfer */
int spi_xfer(struct spi_slave *spi, unsigned int bitlen,
const void *dout_mem, void *din_mem, unsigned long flags)
{
struct e1000_hw *hw = e1000_hw_from_spi(spi);
int ret;
if (flags & SPI_XFER_BEGIN)
e1000_standby_eeprom(hw);
ret = e1000_spi_xfer(hw, bitlen, dout_mem, din_mem, true);
if (flags & SPI_XFER_END)
e1000_standby_eeprom(hw);
return ret;
}
#endif /* not CONFIG_E1000_SPI_GENERIC */
#ifdef CONFIG_CMD_E1000
/* The EEPROM opcodes */
#define SPI_EEPROM_ENABLE_WR 0x06
#define SPI_EEPROM_DISABLE_WR 0x04
#define SPI_EEPROM_WRITE_STATUS 0x01
#define SPI_EEPROM_READ_STATUS 0x05
#define SPI_EEPROM_WRITE_PAGE 0x02
#define SPI_EEPROM_READ_PAGE 0x03
/* The EEPROM status bits */
#define SPI_EEPROM_STATUS_BUSY 0x01
#define SPI_EEPROM_STATUS_WREN 0x02
static int e1000_spi_eeprom_enable_wr(struct e1000_hw *hw, bool intr)
{
u8 op[] = { SPI_EEPROM_ENABLE_WR };
e1000_standby_eeprom(hw);
return e1000_spi_xfer(hw, 8*sizeof(op), op, NULL, intr);
}
/*
* These have been tested to perform correctly, but they are not used by any
* of the EEPROM commands at this time.
*/
#if 0
static int e1000_spi_eeprom_disable_wr(struct e1000_hw *hw, bool intr)
{
u8 op[] = { SPI_EEPROM_DISABLE_WR };
e1000_standby_eeprom(hw);
return e1000_spi_xfer(hw, 8*sizeof(op), op, NULL, intr);
}
static int e1000_spi_eeprom_write_status(struct e1000_hw *hw,
u8 status, bool intr)
{
u8 op[] = { SPI_EEPROM_WRITE_STATUS, status };
e1000_standby_eeprom(hw);
return e1000_spi_xfer(hw, 8*sizeof(op), op, NULL, intr);
}
#endif
static int e1000_spi_eeprom_read_status(struct e1000_hw *hw, bool intr)
{
u8 op[] = { SPI_EEPROM_READ_STATUS, 0 };
e1000_standby_eeprom(hw);
if (e1000_spi_xfer(hw, 8*sizeof(op), op, op, intr))
return -1;
return op[1];
}
static int e1000_spi_eeprom_write_page(struct e1000_hw *hw,
const void *data, u16 off, u16 len, bool intr)
{
u8 op[] = {
SPI_EEPROM_WRITE_PAGE,
(off >> (hw->eeprom.address_bits - 8)) & 0xff, off & 0xff
};
e1000_standby_eeprom(hw);
if (e1000_spi_xfer(hw, 8 + hw->eeprom.address_bits, op, NULL, intr))
return -1;
if (e1000_spi_xfer(hw, len << 3, data, NULL, intr))
return -1;
return 0;
}
static int e1000_spi_eeprom_read_page(struct e1000_hw *hw,
void *data, u16 off, u16 len, bool intr)
{
u8 op[] = {
SPI_EEPROM_READ_PAGE,
(off >> (hw->eeprom.address_bits - 8)) & 0xff, off & 0xff
};
e1000_standby_eeprom(hw);
if (e1000_spi_xfer(hw, 8 + hw->eeprom.address_bits, op, NULL, intr))
return -1;
if (e1000_spi_xfer(hw, len << 3, NULL, data, intr))
return -1;
return 0;
}
static int e1000_spi_eeprom_poll_ready(struct e1000_hw *hw, bool intr)
{
int status;
while ((status = e1000_spi_eeprom_read_status(hw, intr)) >= 0) {
if (!(status & SPI_EEPROM_STATUS_BUSY))
return 0;
}
return -1;
}
static int e1000_spi_eeprom_dump(struct e1000_hw *hw,
void *data, u16 off, unsigned int len, bool intr)
{
/* Interruptibly wait for the EEPROM to be ready */
if (e1000_spi_eeprom_poll_ready(hw, intr))
return -1;
/* Dump each page in sequence */
while (len) {
/* Calculate the data bytes on this page */
u16 pg_off = off & (hw->eeprom.page_size - 1);
u16 pg_len = hw->eeprom.page_size - pg_off;
if (pg_len > len)
pg_len = len;
/* Now dump the page */
if (e1000_spi_eeprom_read_page(hw, data, off, pg_len, intr))
return -1;
/* Otherwise go on to the next page */
len -= pg_len;
off += pg_len;
data += pg_len;
}
/* We're done! */
return 0;
}
static int e1000_spi_eeprom_program(struct e1000_hw *hw,
const void *data, u16 off, u16 len, bool intr)
{
/* Program each page in sequence */
while (len) {
/* Calculate the data bytes on this page */
u16 pg_off = off & (hw->eeprom.page_size - 1);
u16 pg_len = hw->eeprom.page_size - pg_off;
if (pg_len > len)
pg_len = len;
/* Interruptibly wait for the EEPROM to be ready */
if (e1000_spi_eeprom_poll_ready(hw, intr))
return -1;
/* Enable write access */
if (e1000_spi_eeprom_enable_wr(hw, intr))
return -1;
/* Now program the page */
if (e1000_spi_eeprom_write_page(hw, data, off, pg_len, intr))
return -1;
/* Otherwise go on to the next page */
len -= pg_len;
off += pg_len;
data += pg_len;
}
/* Wait for the last write to complete */
if (e1000_spi_eeprom_poll_ready(hw, intr))
return -1;
/* We're done! */
return 0;
}
static int do_e1000_spi_show(cmd_tbl_t *cmdtp, struct e1000_hw *hw,
int argc, char * const argv[])
{
unsigned int length = 0;
u16 i, offset = 0;
u8 *buffer;
int err;
if (argc > 2) {
cmd_usage(cmdtp);
return 1;
}
/* Parse the offset and length */
if (argc >= 1)
offset = simple_strtoul(argv[0], NULL, 0);
if (argc == 2)
length = simple_strtoul(argv[1], NULL, 0);
else if (offset < (hw->eeprom.word_size << 1))
length = (hw->eeprom.word_size << 1) - offset;
/* Extra sanity checks */
if (!length) {
E1000_ERR(hw->nic, "Requested zero-sized dump!\n");
return 1;
}
if ((0x10000 < length) || (0x10000 - length < offset)) {
E1000_ERR(hw->nic, "Can't dump past 0xFFFF!\n");
return 1;
}
/* Allocate a buffer to hold stuff */
buffer = malloc(length);
if (!buffer) {
E1000_ERR(hw->nic, "Out of Memory!\n");
return 1;
}
/* Acquire the EEPROM and perform the dump */
if (e1000_acquire_eeprom(hw)) {
E1000_ERR(hw->nic, "EEPROM SPI cannot be acquired!\n");
free(buffer);
return 1;
}
err = e1000_spi_eeprom_dump(hw, buffer, offset, length, true);
e1000_release_eeprom(hw);
if (err) {
E1000_ERR(hw->nic, "Interrupted!\n");
free(buffer);
return 1;
}
/* Now hexdump the result */
printf("%s: ===== Intel e1000 EEPROM (0x%04hX - 0x%04hX) =====",
hw->nic->name, offset, offset + length - 1);
for (i = 0; i < length; i++) {
if ((i & 0xF) == 0)
printf("\n%s: %04hX: ", hw->nic->name, offset + i);
else if ((i & 0xF) == 0x8)
printf(" ");
printf(" %02hx", buffer[i]);
}
printf("\n");
/* Success! */
free(buffer);
return 0;
}
static int do_e1000_spi_dump(cmd_tbl_t *cmdtp, struct e1000_hw *hw,
int argc, char * const argv[])
{
unsigned int length;
u16 offset;
void *dest;
if (argc != 3) {
cmd_usage(cmdtp);
return 1;
}
/* Parse the arguments */
dest = (void *)simple_strtoul(argv[0], NULL, 16);
offset = simple_strtoul(argv[1], NULL, 0);
length = simple_strtoul(argv[2], NULL, 0);
/* Extra sanity checks */
if (!length) {
E1000_ERR(hw->nic, "Requested zero-sized dump!\n");
return 1;
}
if ((0x10000 < length) || (0x10000 - length < offset)) {
E1000_ERR(hw->nic, "Can't dump past 0xFFFF!\n");
return 1;
}
/* Acquire the EEPROM */
if (e1000_acquire_eeprom(hw)) {
E1000_ERR(hw->nic, "EEPROM SPI cannot be acquired!\n");
return 1;
}
/* Perform the programming operation */
if (e1000_spi_eeprom_dump(hw, dest, offset, length, true) < 0) {
E1000_ERR(hw->nic, "Interrupted!\n");
e1000_release_eeprom(hw);
return 1;
}
e1000_release_eeprom(hw);
printf("%s: ===== EEPROM DUMP COMPLETE =====\n", hw->nic->name);
return 0;
}
static int do_e1000_spi_program(cmd_tbl_t *cmdtp, struct e1000_hw *hw,
int argc, char * const argv[])
{
unsigned int length;
const void *source;
u16 offset;
if (argc != 3) {
cmd_usage(cmdtp);
return 1;
}
/* Parse the arguments */
source = (const void *)simple_strtoul(argv[0], NULL, 16);
offset = simple_strtoul(argv[1], NULL, 0);
length = simple_strtoul(argv[2], NULL, 0);
/* Acquire the EEPROM */
if (e1000_acquire_eeprom(hw)) {
E1000_ERR(hw->nic, "EEPROM SPI cannot be acquired!\n");
return 1;
}
/* Perform the programming operation */
if (e1000_spi_eeprom_program(hw, source, offset, length, true) < 0) {
E1000_ERR(hw->nic, "Interrupted!\n");
e1000_release_eeprom(hw);
return 1;
}
e1000_release_eeprom(hw);
printf("%s: ===== EEPROM PROGRAMMED =====\n", hw->nic->name);
return 0;
}
static int do_e1000_spi_checksum(cmd_tbl_t *cmdtp, struct e1000_hw *hw,
int argc, char * const argv[])
{
uint16_t i, length, checksum = 0, checksum_reg;
uint16_t *buffer;
bool upd;
if (argc == 0)
upd = 0;
else if ((argc == 1) && !strcmp(argv[0], "update"))
upd = 1;
else {
cmd_usage(cmdtp);
return 1;
}
/* Allocate a temporary buffer */
length = sizeof(uint16_t) * (EEPROM_CHECKSUM_REG + 1);
buffer = malloc(length);
if (!buffer) {
E1000_ERR(hw->nic, "Unable to allocate EEPROM buffer!\n");
return 1;
}
/* Acquire the EEPROM */
if (e1000_acquire_eeprom(hw)) {
E1000_ERR(hw->nic, "EEPROM SPI cannot be acquired!\n");
return 1;
}
/* Read the EEPROM */
if (e1000_spi_eeprom_dump(hw, buffer, 0, length, true) < 0) {
E1000_ERR(hw->nic, "Interrupted!\n");
e1000_release_eeprom(hw);
return 1;
}
/* Compute the checksum and read the expected value */
for (i = 0; i < EEPROM_CHECKSUM_REG; i++)
checksum += le16_to_cpu(buffer[i]);
checksum = ((uint16_t)EEPROM_SUM) - checksum;
checksum_reg = le16_to_cpu(buffer[i]);
/* Verify it! */
if (checksum_reg == checksum) {
printf("%s: INFO: EEPROM checksum is correct! (0x%04hx)\n",
hw->nic->name, checksum);
e1000_release_eeprom(hw);
return 0;
}
/* Hrm, verification failed, print an error */
E1000_ERR(hw->nic, "EEPROM checksum is incorrect!\n");
E1000_ERR(hw->nic, " ...register was 0x%04hx, calculated 0x%04hx\n",
checksum_reg, checksum);
/* If they didn't ask us to update it, just return an error */
if (!upd) {
e1000_release_eeprom(hw);
return 1;
}
/* Ok, correct it! */
printf("%s: Reprogramming the EEPROM checksum...\n", hw->nic->name);
buffer[i] = cpu_to_le16(checksum);
if (e1000_spi_eeprom_program(hw, &buffer[i], i * sizeof(uint16_t),
sizeof(uint16_t), true)) {
E1000_ERR(hw->nic, "Interrupted!\n");
e1000_release_eeprom(hw);
return 1;
}
e1000_release_eeprom(hw);
return 0;
}
int do_e1000_spi(cmd_tbl_t *cmdtp, struct e1000_hw *hw,
int argc, char * const argv[])
{
if (argc < 1) {
cmd_usage(cmdtp);
return 1;
}
/* Make sure it has an SPI chip */
if (hw->eeprom.type != e1000_eeprom_spi) {
E1000_ERR(hw->nic, "No attached SPI EEPROM found!\n");
return 1;
}
/* Check the eeprom sub-sub-command arguments */
if (!strcmp(argv[0], "show"))
return do_e1000_spi_show(cmdtp, hw, argc - 1, argv + 1);
if (!strcmp(argv[0], "dump"))
return do_e1000_spi_dump(cmdtp, hw, argc - 1, argv + 1);
if (!strcmp(argv[0], "program"))
return do_e1000_spi_program(cmdtp, hw, argc - 1, argv + 1);
if (!strcmp(argv[0], "checksum"))
return do_e1000_spi_checksum(cmdtp, hw, argc - 1, argv + 1);
cmd_usage(cmdtp);
return 1;
}
#endif /* not CONFIG_CMD_E1000 */

View file

@ -0,0 +1,932 @@
/*
* (C) Copyright 2002
* Wolfgang Denk, DENX Software Engineering, wd@denx.de.
*
* SPDX-License-Identifier: GPL-2.0+
*/
#include <common.h>
#include <malloc.h>
#include <net.h>
#include <netdev.h>
#include <asm/io.h>
#include <pci.h>
#include <miiphy.h>
#undef DEBUG
/* Ethernet chip registers.
*/
#define SCBStatus 0 /* Rx/Command Unit Status *Word* */
#define SCBIntAckByte 1 /* Rx/Command Unit STAT/ACK byte */
#define SCBCmd 2 /* Rx/Command Unit Command *Word* */
#define SCBIntrCtlByte 3 /* Rx/Command Unit Intr.Control Byte */
#define SCBPointer 4 /* General purpose pointer. */
#define SCBPort 8 /* Misc. commands and operands. */
#define SCBflash 12 /* Flash memory control. */
#define SCBeeprom 14 /* EEPROM memory control. */
#define SCBCtrlMDI 16 /* MDI interface control. */
#define SCBEarlyRx 20 /* Early receive byte count. */
#define SCBGenControl 28 /* 82559 General Control Register */
#define SCBGenStatus 29 /* 82559 General Status register */
/* 82559 SCB status word defnitions
*/
#define SCB_STATUS_CX 0x8000 /* CU finished command (transmit) */
#define SCB_STATUS_FR 0x4000 /* frame received */
#define SCB_STATUS_CNA 0x2000 /* CU left active state */
#define SCB_STATUS_RNR 0x1000 /* receiver left ready state */
#define SCB_STATUS_MDI 0x0800 /* MDI read/write cycle done */
#define SCB_STATUS_SWI 0x0400 /* software generated interrupt */
#define SCB_STATUS_FCP 0x0100 /* flow control pause interrupt */
#define SCB_INTACK_MASK 0xFD00 /* all the above */
#define SCB_INTACK_TX (SCB_STATUS_CX | SCB_STATUS_CNA)
#define SCB_INTACK_RX (SCB_STATUS_FR | SCB_STATUS_RNR)
/* System control block commands
*/
/* CU Commands */
#define CU_NOP 0x0000
#define CU_START 0x0010
#define CU_RESUME 0x0020
#define CU_STATSADDR 0x0040 /* Load Dump Statistics ctrs addr */
#define CU_SHOWSTATS 0x0050 /* Dump statistics counters. */
#define CU_ADDR_LOAD 0x0060 /* Base address to add to CU commands */
#define CU_DUMPSTATS 0x0070 /* Dump then reset stats counters. */
/* RUC Commands */
#define RUC_NOP 0x0000
#define RUC_START 0x0001
#define RUC_RESUME 0x0002
#define RUC_ABORT 0x0004
#define RUC_ADDR_LOAD 0x0006 /* (seems not to clear on acceptance) */
#define RUC_RESUMENR 0x0007
#define CU_CMD_MASK 0x00f0
#define RU_CMD_MASK 0x0007
#define SCB_M 0x0100 /* 0 = enable interrupt, 1 = disable */
#define SCB_SWI 0x0200 /* 1 - cause device to interrupt */
#define CU_STATUS_MASK 0x00C0
#define RU_STATUS_MASK 0x003C
#define RU_STATUS_IDLE (0<<2)
#define RU_STATUS_SUS (1<<2)
#define RU_STATUS_NORES (2<<2)
#define RU_STATUS_READY (4<<2)
#define RU_STATUS_NO_RBDS_SUS ((1<<2)|(8<<2))
#define RU_STATUS_NO_RBDS_NORES ((2<<2)|(8<<2))
#define RU_STATUS_NO_RBDS_READY ((4<<2)|(8<<2))
/* 82559 Port interface commands.
*/
#define I82559_RESET 0x00000000 /* Software reset */
#define I82559_SELFTEST 0x00000001 /* 82559 Selftest command */
#define I82559_SELECTIVE_RESET 0x00000002
#define I82559_DUMP 0x00000003
#define I82559_DUMP_WAKEUP 0x00000007
/* 82559 Eeprom interface.
*/
#define EE_SHIFT_CLK 0x01 /* EEPROM shift clock. */
#define EE_CS 0x02 /* EEPROM chip select. */
#define EE_DATA_WRITE 0x04 /* EEPROM chip data in. */
#define EE_WRITE_0 0x01
#define EE_WRITE_1 0x05
#define EE_DATA_READ 0x08 /* EEPROM chip data out. */
#define EE_ENB (0x4800 | EE_CS)
#define EE_CMD_BITS 3
#define EE_DATA_BITS 16
/* The EEPROM commands include the alway-set leading bit.
*/
#define EE_EWENB_CMD (4 << addr_len)
#define EE_WRITE_CMD (5 << addr_len)
#define EE_READ_CMD (6 << addr_len)
#define EE_ERASE_CMD (7 << addr_len)
/* Receive frame descriptors.
*/
struct RxFD {
volatile u16 status;
volatile u16 control;
volatile u32 link; /* struct RxFD * */
volatile u32 rx_buf_addr; /* void * */
volatile u32 count;
volatile u8 data[PKTSIZE_ALIGN];
};
#define RFD_STATUS_C 0x8000 /* completion of received frame */
#define RFD_STATUS_OK 0x2000 /* frame received with no errors */
#define RFD_CONTROL_EL 0x8000 /* 1=last RFD in RFA */
#define RFD_CONTROL_S 0x4000 /* 1=suspend RU after receiving frame */
#define RFD_CONTROL_H 0x0010 /* 1=RFD is a header RFD */
#define RFD_CONTROL_SF 0x0008 /* 0=simplified, 1=flexible mode */
#define RFD_COUNT_MASK 0x3fff
#define RFD_COUNT_F 0x4000
#define RFD_COUNT_EOF 0x8000
#define RFD_RX_CRC 0x0800 /* crc error */
#define RFD_RX_ALIGNMENT 0x0400 /* alignment error */
#define RFD_RX_RESOURCE 0x0200 /* out of space, no resources */
#define RFD_RX_DMA_OVER 0x0100 /* DMA overrun */
#define RFD_RX_SHORT 0x0080 /* short frame error */
#define RFD_RX_LENGTH 0x0020
#define RFD_RX_ERROR 0x0010 /* receive error */
#define RFD_RX_NO_ADR_MATCH 0x0004 /* no address match */
#define RFD_RX_IA_MATCH 0x0002 /* individual address does not match */
#define RFD_RX_TCO 0x0001 /* TCO indication */
/* Transmit frame descriptors
*/
struct TxFD { /* Transmit frame descriptor set. */
volatile u16 status;
volatile u16 command;
volatile u32 link; /* void * */
volatile u32 tx_desc_addr; /* Always points to the tx_buf_addr element. */
volatile s32 count;
volatile u32 tx_buf_addr0; /* void *, frame to be transmitted. */
volatile s32 tx_buf_size0; /* Length of Tx frame. */
volatile u32 tx_buf_addr1; /* void *, frame to be transmitted. */
volatile s32 tx_buf_size1; /* Length of Tx frame. */
};
#define TxCB_CMD_TRANSMIT 0x0004 /* transmit command */
#define TxCB_CMD_SF 0x0008 /* 0=simplified, 1=flexible mode */
#define TxCB_CMD_NC 0x0010 /* 0=CRC insert by controller */
#define TxCB_CMD_I 0x2000 /* generate interrupt on completion */
#define TxCB_CMD_S 0x4000 /* suspend on completion */
#define TxCB_CMD_EL 0x8000 /* last command block in CBL */
#define TxCB_COUNT_MASK 0x3fff
#define TxCB_COUNT_EOF 0x8000
/* The Speedo3 Rx and Tx frame/buffer descriptors.
*/
struct descriptor { /* A generic descriptor. */
volatile u16 status;
volatile u16 command;
volatile u32 link; /* struct descriptor * */
unsigned char params[0];
};
#define CONFIG_SYS_CMD_EL 0x8000
#define CONFIG_SYS_CMD_SUSPEND 0x4000
#define CONFIG_SYS_CMD_INT 0x2000
#define CONFIG_SYS_CMD_IAS 0x0001 /* individual address setup */
#define CONFIG_SYS_CMD_CONFIGURE 0x0002 /* configure */
#define CONFIG_SYS_STATUS_C 0x8000
#define CONFIG_SYS_STATUS_OK 0x2000
/* Misc.
*/
#define NUM_RX_DESC PKTBUFSRX
#define NUM_TX_DESC 1 /* Number of TX descriptors */
#define TOUT_LOOP 1000000
#define ETH_ALEN 6
static struct RxFD rx_ring[NUM_RX_DESC]; /* RX descriptor ring */
static struct TxFD tx_ring[NUM_TX_DESC]; /* TX descriptor ring */
static int rx_next; /* RX descriptor ring pointer */
static int tx_next; /* TX descriptor ring pointer */
static int tx_threshold;
/*
* The parameters for a CmdConfigure operation.
* There are so many options that it would be difficult to document
* each bit. We mostly use the default or recommended settings.
*/
static const char i82557_config_cmd[] = {
22, 0x08, 0, 0, 0, 0, 0x32, 0x03, 1, /* 1=Use MII 0=Use AUI */
0, 0x2E, 0, 0x60, 0,
0xf2, 0x48, 0, 0x40, 0xf2, 0x80, /* 0x40=Force full-duplex */
0x3f, 0x05,
};
static const char i82558_config_cmd[] = {
22, 0x08, 0, 1, 0, 0, 0x22, 0x03, 1, /* 1=Use MII 0=Use AUI */
0, 0x2E, 0, 0x60, 0x08, 0x88,
0x68, 0, 0x40, 0xf2, 0x84, /* Disable FC */
0x31, 0x05,
};
static void init_rx_ring (struct eth_device *dev);
static void purge_tx_ring (struct eth_device *dev);
static void read_hw_addr (struct eth_device *dev, bd_t * bis);
static int eepro100_init (struct eth_device *dev, bd_t * bis);
static int eepro100_send(struct eth_device *dev, void *packet, int length);
static int eepro100_recv (struct eth_device *dev);
static void eepro100_halt (struct eth_device *dev);
#if defined(CONFIG_E500)
#define bus_to_phys(a) (a)
#define phys_to_bus(a) (a)
#else
#define bus_to_phys(a) pci_mem_to_phys((pci_dev_t)dev->priv, a)
#define phys_to_bus(a) pci_phys_to_mem((pci_dev_t)dev->priv, a)
#endif
static inline int INW (struct eth_device *dev, u_long addr)
{
return le16_to_cpu (*(volatile u16 *) (addr + dev->iobase));
}
static inline void OUTW (struct eth_device *dev, int command, u_long addr)
{
*(volatile u16 *) ((addr + dev->iobase)) = cpu_to_le16 (command);
}
static inline void OUTL (struct eth_device *dev, int command, u_long addr)
{
*(volatile u32 *) ((addr + dev->iobase)) = cpu_to_le32 (command);
}
#if defined(CONFIG_MII) || defined(CONFIG_CMD_MII)
static inline int INL (struct eth_device *dev, u_long addr)
{
return le32_to_cpu (*(volatile u32 *) (addr + dev->iobase));
}
static int get_phyreg (struct eth_device *dev, unsigned char addr,
unsigned char reg, unsigned short *value)
{
int cmd;
int timeout = 50;
/* read requested data */
cmd = (2 << 26) | ((addr & 0x1f) << 21) | ((reg & 0x1f) << 16);
OUTL (dev, cmd, SCBCtrlMDI);
do {
udelay(1000);
cmd = INL (dev, SCBCtrlMDI);
} while (!(cmd & (1 << 28)) && (--timeout));
if (timeout == 0)
return -1;
*value = (unsigned short) (cmd & 0xffff);
return 0;
}
static int set_phyreg (struct eth_device *dev, unsigned char addr,
unsigned char reg, unsigned short value)
{
int cmd;
int timeout = 50;
/* write requested data */
cmd = (1 << 26) | ((addr & 0x1f) << 21) | ((reg & 0x1f) << 16);
OUTL (dev, cmd | value, SCBCtrlMDI);
while (!(INL (dev, SCBCtrlMDI) & (1 << 28)) && (--timeout))
udelay(1000);
if (timeout == 0)
return -1;
return 0;
}
/* Check if given phyaddr is valid, i.e. there is a PHY connected.
* Do this by checking model value field from ID2 register.
*/
static struct eth_device* verify_phyaddr (const char *devname,
unsigned char addr)
{
struct eth_device *dev;
unsigned short value;
unsigned char model;
dev = eth_get_dev_by_name(devname);
if (dev == NULL) {
printf("%s: no such device\n", devname);
return NULL;
}
/* read id2 register */
if (get_phyreg(dev, addr, MII_PHYSID2, &value) != 0) {
printf("%s: mii read timeout!\n", devname);
return NULL;
}
/* get model */
model = (unsigned char)((value >> 4) & 0x003f);
if (model == 0) {
printf("%s: no PHY at address %d\n", devname, addr);
return NULL;
}
return dev;
}
static int eepro100_miiphy_read(const char *devname, unsigned char addr,
unsigned char reg, unsigned short *value)
{
struct eth_device *dev;
dev = verify_phyaddr(devname, addr);
if (dev == NULL)
return -1;
if (get_phyreg(dev, addr, reg, value) != 0) {
printf("%s: mii read timeout!\n", devname);
return -1;
}
return 0;
}
static int eepro100_miiphy_write(const char *devname, unsigned char addr,
unsigned char reg, unsigned short value)
{
struct eth_device *dev;
dev = verify_phyaddr(devname, addr);
if (dev == NULL)
return -1;
if (set_phyreg(dev, addr, reg, value) != 0) {
printf("%s: mii write timeout!\n", devname);
return -1;
}
return 0;
}
#endif
/* Wait for the chip get the command.
*/
static int wait_for_eepro100 (struct eth_device *dev)
{
int i;
for (i = 0; INW (dev, SCBCmd) & (CU_CMD_MASK | RU_CMD_MASK); i++) {
if (i >= TOUT_LOOP) {
return 0;
}
}
return 1;
}
static struct pci_device_id supported[] = {
{PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82557},
{PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82559},
{PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82559ER},
{}
};
int eepro100_initialize (bd_t * bis)
{
pci_dev_t devno;
int card_number = 0;
struct eth_device *dev;
u32 iobase, status;
int idx = 0;
while (1) {
/* Find PCI device
*/
if ((devno = pci_find_devices (supported, idx++)) < 0) {
break;
}
pci_read_config_dword (devno, PCI_BASE_ADDRESS_0, &iobase);
iobase &= ~0xf;
#ifdef DEBUG
printf ("eepro100: Intel i82559 PCI EtherExpressPro @0x%x\n",
iobase);
#endif
pci_write_config_dword (devno,
PCI_COMMAND,
PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER);
/* Check if I/O accesses and Bus Mastering are enabled.
*/
pci_read_config_dword (devno, PCI_COMMAND, &status);
if (!(status & PCI_COMMAND_MEMORY)) {
printf ("Error: Can not enable MEM access.\n");
continue;
}
if (!(status & PCI_COMMAND_MASTER)) {
printf ("Error: Can not enable Bus Mastering.\n");
continue;
}
dev = (struct eth_device *) malloc (sizeof *dev);
if (!dev) {
printf("eepro100: Can not allocate memory\n");
break;
}
memset(dev, 0, sizeof(*dev));
sprintf (dev->name, "i82559#%d", card_number);
dev->priv = (void *) devno; /* this have to come before bus_to_phys() */
dev->iobase = bus_to_phys (iobase);
dev->init = eepro100_init;
dev->halt = eepro100_halt;
dev->send = eepro100_send;
dev->recv = eepro100_recv;
eth_register (dev);
#if defined (CONFIG_MII) || defined(CONFIG_CMD_MII)
/* register mii command access routines */
miiphy_register(dev->name,
eepro100_miiphy_read, eepro100_miiphy_write);
#endif
card_number++;
/* Set the latency timer for value.
*/
pci_write_config_byte (devno, PCI_LATENCY_TIMER, 0x20);
udelay (10 * 1000);
read_hw_addr (dev, bis);
}
return card_number;
}
static int eepro100_init (struct eth_device *dev, bd_t * bis)
{
int i, status = -1;
int tx_cur;
struct descriptor *ias_cmd, *cfg_cmd;
/* Reset the ethernet controller
*/
OUTL (dev, I82559_SELECTIVE_RESET, SCBPort);
udelay (20);
OUTL (dev, I82559_RESET, SCBPort);
udelay (20);
if (!wait_for_eepro100 (dev)) {
printf ("Error: Can not reset ethernet controller.\n");
goto Done;
}
OUTL (dev, 0, SCBPointer);
OUTW (dev, SCB_M | RUC_ADDR_LOAD, SCBCmd);
if (!wait_for_eepro100 (dev)) {
printf ("Error: Can not reset ethernet controller.\n");
goto Done;
}
OUTL (dev, 0, SCBPointer);
OUTW (dev, SCB_M | CU_ADDR_LOAD, SCBCmd);
/* Initialize Rx and Tx rings.
*/
init_rx_ring (dev);
purge_tx_ring (dev);
/* Tell the adapter where the RX ring is located.
*/
if (!wait_for_eepro100 (dev)) {
printf ("Error: Can not reset ethernet controller.\n");
goto Done;
}
OUTL (dev, phys_to_bus ((u32) & rx_ring[rx_next]), SCBPointer);
OUTW (dev, SCB_M | RUC_START, SCBCmd);
/* Send the Configure frame */
tx_cur = tx_next;
tx_next = ((tx_next + 1) % NUM_TX_DESC);
cfg_cmd = (struct descriptor *) &tx_ring[tx_cur];
cfg_cmd->command = cpu_to_le16 ((CONFIG_SYS_CMD_SUSPEND | CONFIG_SYS_CMD_CONFIGURE));
cfg_cmd->status = 0;
cfg_cmd->link = cpu_to_le32 (phys_to_bus ((u32) & tx_ring[tx_next]));
memcpy (cfg_cmd->params, i82558_config_cmd,
sizeof (i82558_config_cmd));
if (!wait_for_eepro100 (dev)) {
printf ("Error---CONFIG_SYS_CMD_CONFIGURE: Can not reset ethernet controller.\n");
goto Done;
}
OUTL (dev, phys_to_bus ((u32) & tx_ring[tx_cur]), SCBPointer);
OUTW (dev, SCB_M | CU_START, SCBCmd);
for (i = 0;
!(le16_to_cpu (tx_ring[tx_cur].status) & CONFIG_SYS_STATUS_C);
i++) {
if (i >= TOUT_LOOP) {
printf ("%s: Tx error buffer not ready\n", dev->name);
goto Done;
}
}
if (!(le16_to_cpu (tx_ring[tx_cur].status) & CONFIG_SYS_STATUS_OK)) {
printf ("TX error status = 0x%08X\n",
le16_to_cpu (tx_ring[tx_cur].status));
goto Done;
}
/* Send the Individual Address Setup frame
*/
tx_cur = tx_next;
tx_next = ((tx_next + 1) % NUM_TX_DESC);
ias_cmd = (struct descriptor *) &tx_ring[tx_cur];
ias_cmd->command = cpu_to_le16 ((CONFIG_SYS_CMD_SUSPEND | CONFIG_SYS_CMD_IAS));
ias_cmd->status = 0;
ias_cmd->link = cpu_to_le32 (phys_to_bus ((u32) & tx_ring[tx_next]));
memcpy (ias_cmd->params, dev->enetaddr, 6);
/* Tell the adapter where the TX ring is located.
*/
if (!wait_for_eepro100 (dev)) {
printf ("Error: Can not reset ethernet controller.\n");
goto Done;
}
OUTL (dev, phys_to_bus ((u32) & tx_ring[tx_cur]), SCBPointer);
OUTW (dev, SCB_M | CU_START, SCBCmd);
for (i = 0; !(le16_to_cpu (tx_ring[tx_cur].status) & CONFIG_SYS_STATUS_C);
i++) {
if (i >= TOUT_LOOP) {
printf ("%s: Tx error buffer not ready\n",
dev->name);
goto Done;
}
}
if (!(le16_to_cpu (tx_ring[tx_cur].status) & CONFIG_SYS_STATUS_OK)) {
printf ("TX error status = 0x%08X\n",
le16_to_cpu (tx_ring[tx_cur].status));
goto Done;
}
status = 0;
Done:
return status;
}
static int eepro100_send(struct eth_device *dev, void *packet, int length)
{
int i, status = -1;
int tx_cur;
if (length <= 0) {
printf ("%s: bad packet size: %d\n", dev->name, length);
goto Done;
}
tx_cur = tx_next;
tx_next = (tx_next + 1) % NUM_TX_DESC;
tx_ring[tx_cur].command = cpu_to_le16 ( TxCB_CMD_TRANSMIT |
TxCB_CMD_SF |
TxCB_CMD_S |
TxCB_CMD_EL );
tx_ring[tx_cur].status = 0;
tx_ring[tx_cur].count = cpu_to_le32 (tx_threshold);
tx_ring[tx_cur].link =
cpu_to_le32 (phys_to_bus ((u32) & tx_ring[tx_next]));
tx_ring[tx_cur].tx_desc_addr =
cpu_to_le32 (phys_to_bus ((u32) & tx_ring[tx_cur].tx_buf_addr0));
tx_ring[tx_cur].tx_buf_addr0 =
cpu_to_le32 (phys_to_bus ((u_long) packet));
tx_ring[tx_cur].tx_buf_size0 = cpu_to_le32 (length);
if (!wait_for_eepro100 (dev)) {
printf ("%s: Tx error ethernet controller not ready.\n",
dev->name);
goto Done;
}
/* Send the packet.
*/
OUTL (dev, phys_to_bus ((u32) & tx_ring[tx_cur]), SCBPointer);
OUTW (dev, SCB_M | CU_START, SCBCmd);
for (i = 0; !(le16_to_cpu (tx_ring[tx_cur].status) & CONFIG_SYS_STATUS_C);
i++) {
if (i >= TOUT_LOOP) {
printf ("%s: Tx error buffer not ready\n", dev->name);
goto Done;
}
}
if (!(le16_to_cpu (tx_ring[tx_cur].status) & CONFIG_SYS_STATUS_OK)) {
printf ("TX error status = 0x%08X\n",
le16_to_cpu (tx_ring[tx_cur].status));
goto Done;
}
status = length;
Done:
return status;
}
static int eepro100_recv (struct eth_device *dev)
{
u16 status, stat;
int rx_prev, length = 0;
stat = INW (dev, SCBStatus);
OUTW (dev, stat & SCB_STATUS_RNR, SCBStatus);
for (;;) {
status = le16_to_cpu (rx_ring[rx_next].status);
if (!(status & RFD_STATUS_C)) {
break;
}
/* Valid frame status.
*/
if ((status & RFD_STATUS_OK)) {
/* A valid frame received.
*/
length = le32_to_cpu (rx_ring[rx_next].count) & 0x3fff;
/* Pass the packet up to the protocol
* layers.
*/
net_process_received_packet((u8 *)rx_ring[rx_next].data,
length);
} else {
/* There was an error.
*/
printf ("RX error status = 0x%08X\n", status);
}
rx_ring[rx_next].control = cpu_to_le16 (RFD_CONTROL_S);
rx_ring[rx_next].status = 0;
rx_ring[rx_next].count = cpu_to_le32 (PKTSIZE_ALIGN << 16);
rx_prev = (rx_next + NUM_RX_DESC - 1) % NUM_RX_DESC;
rx_ring[rx_prev].control = 0;
/* Update entry information.
*/
rx_next = (rx_next + 1) % NUM_RX_DESC;
}
if (stat & SCB_STATUS_RNR) {
printf ("%s: Receiver is not ready, restart it !\n", dev->name);
/* Reinitialize Rx ring.
*/
init_rx_ring (dev);
if (!wait_for_eepro100 (dev)) {
printf ("Error: Can not restart ethernet controller.\n");
goto Done;
}
OUTL (dev, phys_to_bus ((u32) & rx_ring[rx_next]), SCBPointer);
OUTW (dev, SCB_M | RUC_START, SCBCmd);
}
Done:
return length;
}
static void eepro100_halt (struct eth_device *dev)
{
/* Reset the ethernet controller
*/
OUTL (dev, I82559_SELECTIVE_RESET, SCBPort);
udelay (20);
OUTL (dev, I82559_RESET, SCBPort);
udelay (20);
if (!wait_for_eepro100 (dev)) {
printf ("Error: Can not reset ethernet controller.\n");
goto Done;
}
OUTL (dev, 0, SCBPointer);
OUTW (dev, SCB_M | RUC_ADDR_LOAD, SCBCmd);
if (!wait_for_eepro100 (dev)) {
printf ("Error: Can not reset ethernet controller.\n");
goto Done;
}
OUTL (dev, 0, SCBPointer);
OUTW (dev, SCB_M | CU_ADDR_LOAD, SCBCmd);
Done:
return;
}
/* SROM Read.
*/
static int read_eeprom (struct eth_device *dev, int location, int addr_len)
{
unsigned short retval = 0;
int read_cmd = location | EE_READ_CMD;
int i;
OUTW (dev, EE_ENB & ~EE_CS, SCBeeprom);
OUTW (dev, EE_ENB, SCBeeprom);
/* Shift the read command bits out. */
for (i = 12; i >= 0; i--) {
short dataval = (read_cmd & (1 << i)) ? EE_DATA_WRITE : 0;
OUTW (dev, EE_ENB | dataval, SCBeeprom);
udelay (1);
OUTW (dev, EE_ENB | dataval | EE_SHIFT_CLK, SCBeeprom);
udelay (1);
}
OUTW (dev, EE_ENB, SCBeeprom);
for (i = 15; i >= 0; i--) {
OUTW (dev, EE_ENB | EE_SHIFT_CLK, SCBeeprom);
udelay (1);
retval = (retval << 1) |
((INW (dev, SCBeeprom) & EE_DATA_READ) ? 1 : 0);
OUTW (dev, EE_ENB, SCBeeprom);
udelay (1);
}
/* Terminate the EEPROM access. */
OUTW (dev, EE_ENB & ~EE_CS, SCBeeprom);
return retval;
}
#ifdef CONFIG_EEPRO100_SROM_WRITE
int eepro100_write_eeprom (struct eth_device* dev, int location, int addr_len, unsigned short data)
{
unsigned short dataval;
int enable_cmd = 0x3f | EE_EWENB_CMD;
int write_cmd = location | EE_WRITE_CMD;
int i;
unsigned long datalong, tmplong;
OUTW(dev, EE_ENB & ~EE_CS, SCBeeprom);
udelay(1);
OUTW(dev, EE_ENB, SCBeeprom);
/* Shift the enable command bits out. */
for (i = (addr_len+EE_CMD_BITS-1); i >= 0; i--)
{
dataval = (enable_cmd & (1 << i)) ? EE_DATA_WRITE : 0;
OUTW(dev, EE_ENB | dataval, SCBeeprom);
udelay(1);
OUTW(dev, EE_ENB | dataval | EE_SHIFT_CLK, SCBeeprom);
udelay(1);
}
OUTW(dev, EE_ENB, SCBeeprom);
udelay(1);
OUTW(dev, EE_ENB & ~EE_CS, SCBeeprom);
udelay(1);
OUTW(dev, EE_ENB, SCBeeprom);
/* Shift the write command bits out. */
for (i = (addr_len+EE_CMD_BITS-1); i >= 0; i--)
{
dataval = (write_cmd & (1 << i)) ? EE_DATA_WRITE : 0;
OUTW(dev, EE_ENB | dataval, SCBeeprom);
udelay(1);
OUTW(dev, EE_ENB | dataval | EE_SHIFT_CLK, SCBeeprom);
udelay(1);
}
/* Write the data */
datalong= (unsigned long) ((((data) & 0x00ff) << 8) | ( (data) >> 8));
for (i = 0; i< EE_DATA_BITS; i++)
{
/* Extract and move data bit to bit DI */
dataval = ((datalong & 0x8000)>>13) ? EE_DATA_WRITE : 0;
OUTW(dev, EE_ENB | dataval, SCBeeprom);
udelay(1);
OUTW(dev, EE_ENB | dataval | EE_SHIFT_CLK, SCBeeprom);
udelay(1);
OUTW(dev, EE_ENB | dataval, SCBeeprom);
udelay(1);
datalong = datalong << 1; /* Adjust significant data bit*/
}
/* Finish up command (toggle CS) */
OUTW(dev, EE_ENB & ~EE_CS, SCBeeprom);
udelay(1); /* delay for more than 250 ns */
OUTW(dev, EE_ENB, SCBeeprom);
/* Wait for programming ready (D0 = 1) */
tmplong = 10;
do
{
dataval = INW(dev, SCBeeprom);
if (dataval & EE_DATA_READ)
break;
udelay(10000);
}
while (-- tmplong);
if (tmplong == 0)
{
printf ("Write i82559 eeprom timed out (100 ms waiting for data ready.\n");
return -1;
}
/* Terminate the EEPROM access. */
OUTW(dev, EE_ENB & ~EE_CS, SCBeeprom);
return 0;
}
#endif
static void init_rx_ring (struct eth_device *dev)
{
int i;
for (i = 0; i < NUM_RX_DESC; i++) {
rx_ring[i].status = 0;
rx_ring[i].control =
(i == NUM_RX_DESC - 1) ? cpu_to_le16 (RFD_CONTROL_S) : 0;
rx_ring[i].link =
cpu_to_le32 (phys_to_bus
((u32) & rx_ring[(i + 1) % NUM_RX_DESC]));
rx_ring[i].rx_buf_addr = 0xffffffff;
rx_ring[i].count = cpu_to_le32 (PKTSIZE_ALIGN << 16);
}
rx_next = 0;
}
static void purge_tx_ring (struct eth_device *dev)
{
int i;
tx_next = 0;
tx_threshold = 0x01208000;
for (i = 0; i < NUM_TX_DESC; i++) {
tx_ring[i].status = 0;
tx_ring[i].command = 0;
tx_ring[i].link = 0;
tx_ring[i].tx_desc_addr = 0;
tx_ring[i].count = 0;
tx_ring[i].tx_buf_addr0 = 0;
tx_ring[i].tx_buf_size0 = 0;
tx_ring[i].tx_buf_addr1 = 0;
tx_ring[i].tx_buf_size1 = 0;
}
}
static void read_hw_addr (struct eth_device *dev, bd_t * bis)
{
u16 sum = 0;
int i, j;
int addr_len = read_eeprom (dev, 0, 6) == 0xffff ? 8 : 6;
for (j = 0, i = 0; i < 0x40; i++) {
u16 value = read_eeprom (dev, i, addr_len);
sum += value;
if (i < 3) {
dev->enetaddr[j++] = value;
dev->enetaddr[j++] = value >> 8;
}
}
if (sum != 0xBABA) {
memset (dev->enetaddr, 0, ETH_ALEN);
#ifdef DEBUG
printf ("%s: Invalid EEPROM checksum %#4.4x, "
"check settings before activating this device!\n",
dev->name, sum);
#endif
}
}

View file

@ -0,0 +1,964 @@
/*
* (C) Copyright 2010
* Reinhard Meyer, EMK Elektronik, reinhard.meyer@emk-elektronik.de
* Martin Krause, Martin.Krause@tqs.de
* reworked original enc28j60.c
*
* SPDX-License-Identifier: GPL-2.0+
*/
#include <common.h>
#include <net.h>
#include <spi.h>
#include <malloc.h>
#include <netdev.h>
#include <miiphy.h>
#include "enc28j60.h"
/*
* IMPORTANT: spi_claim_bus() and spi_release_bus()
* are called at begin and end of each of the following functions:
* enc_miiphy_read(), enc_miiphy_write(), enc_write_hwaddr(),
* enc_init(), enc_recv(), enc_send(), enc_halt()
* ALL other functions assume that the bus has already been claimed!
* Since net_process_received_packet() might call enc_send() in return, the bus
* must be released, net_process_received_packet() called and claimed again.
*/
/*
* Controller memory layout.
* We only allow 1 frame for transmission and reserve the rest
* for reception to handle as many broadcast packets as possible.
* Also use the memory from 0x0000 for receiver buffer. See errata pt. 5
* 0x0000 - 0x19ff 6656 bytes receive buffer
* 0x1a00 - 0x1fff 1536 bytes transmit buffer =
* control(1)+frame(1518)+status(7)+reserve(10).
*/
#define ENC_RX_BUF_START 0x0000
#define ENC_RX_BUF_END 0x19ff
#define ENC_TX_BUF_START 0x1a00
#define ENC_TX_BUF_END 0x1fff
#define ENC_MAX_FRM_LEN 1518
#define RX_RESET_COUNTER 1000
/*
* For non data transfer functions, like phy read/write, set hwaddr, init
* we do not need a full, time consuming init including link ready wait.
* This enum helps to bring the chip through the minimum necessary inits.
*/
enum enc_initstate {none=0, setupdone, linkready};
typedef struct enc_device {
struct eth_device *dev; /* back pointer */
struct spi_slave *slave;
int rx_reset_counter;
u16 next_pointer;
u8 bank; /* current bank in enc28j60 */
enum enc_initstate initstate;
} enc_dev_t;
/*
* enc_bset: set bits in a common register
* enc_bclr: clear bits in a common register
*
* making the reg parameter u8 will give a compile time warning if the
* functions are called with a register not accessible in all Banks
*/
static void enc_bset(enc_dev_t *enc, const u8 reg, const u8 data)
{
u8 dout[2];
dout[0] = CMD_BFS(reg);
dout[1] = data;
spi_xfer(enc->slave, 2 * 8, dout, NULL,
SPI_XFER_BEGIN | SPI_XFER_END);
}
static void enc_bclr(enc_dev_t *enc, const u8 reg, const u8 data)
{
u8 dout[2];
dout[0] = CMD_BFC(reg);
dout[1] = data;
spi_xfer(enc->slave, 2 * 8, dout, NULL,
SPI_XFER_BEGIN | SPI_XFER_END);
}
/*
* high byte of the register contains bank number:
* 0: no bank switch necessary
* 1: switch to bank 0
* 2: switch to bank 1
* 3: switch to bank 2
* 4: switch to bank 3
*/
static void enc_set_bank(enc_dev_t *enc, const u16 reg)
{
u8 newbank = reg >> 8;
if (newbank == 0 || newbank == enc->bank)
return;
switch (newbank) {
case 1:
enc_bclr(enc, CTL_REG_ECON1,
ENC_ECON1_BSEL0 | ENC_ECON1_BSEL1);
break;
case 2:
enc_bset(enc, CTL_REG_ECON1, ENC_ECON1_BSEL0);
enc_bclr(enc, CTL_REG_ECON1, ENC_ECON1_BSEL1);
break;
case 3:
enc_bclr(enc, CTL_REG_ECON1, ENC_ECON1_BSEL0);
enc_bset(enc, CTL_REG_ECON1, ENC_ECON1_BSEL1);
break;
case 4:
enc_bset(enc, CTL_REG_ECON1,
ENC_ECON1_BSEL0 | ENC_ECON1_BSEL1);
break;
}
enc->bank = newbank;
}
/*
* local functions to access SPI
*
* reg: register inside ENC28J60
* data: 8/16 bits to write
* c: number of retries
*
* enc_r8: read 8 bits
* enc_r16: read 16 bits
* enc_w8: write 8 bits
* enc_w16: write 16 bits
* enc_w8_retry: write 8 bits, verify and retry
* enc_rbuf: read from ENC28J60 into buffer
* enc_wbuf: write from buffer into ENC28J60
*/
/*
* MAC and MII registers need a 3 byte SPI transfer to read,
* all other registers need a 2 byte SPI transfer.
*/
static int enc_reg2nbytes(const u16 reg)
{
/* check if MAC or MII register */
return ((reg >= CTL_REG_MACON1 && reg <= CTL_REG_MIRDH) ||
(reg >= CTL_REG_MAADR1 && reg <= CTL_REG_MAADR4) ||
(reg == CTL_REG_MISTAT)) ? 3 : 2;
}
/*
* Read a byte register
*/
static u8 enc_r8(enc_dev_t *enc, const u16 reg)
{
u8 dout[3];
u8 din[3];
int nbytes = enc_reg2nbytes(reg);
enc_set_bank(enc, reg);
dout[0] = CMD_RCR(reg);
spi_xfer(enc->slave, nbytes * 8, dout, din,
SPI_XFER_BEGIN | SPI_XFER_END);
return din[nbytes-1];
}
/*
* Read a L/H register pair and return a word.
* Must be called with the L register's address.
*/
static u16 enc_r16(enc_dev_t *enc, const u16 reg)
{
u8 dout[3];
u8 din[3];
u16 result;
int nbytes = enc_reg2nbytes(reg);
enc_set_bank(enc, reg);
dout[0] = CMD_RCR(reg);
spi_xfer(enc->slave, nbytes * 8, dout, din,
SPI_XFER_BEGIN | SPI_XFER_END);
result = din[nbytes-1];
dout[0]++; /* next register */
spi_xfer(enc->slave, nbytes * 8, dout, din,
SPI_XFER_BEGIN | SPI_XFER_END);
result |= din[nbytes-1] << 8;
return result;
}
/*
* Write a byte register
*/
static void enc_w8(enc_dev_t *enc, const u16 reg, const u8 data)
{
u8 dout[2];
enc_set_bank(enc, reg);
dout[0] = CMD_WCR(reg);
dout[1] = data;
spi_xfer(enc->slave, 2 * 8, dout, NULL,
SPI_XFER_BEGIN | SPI_XFER_END);
}
/*
* Write a L/H register pair.
* Must be called with the L register's address.
*/
static void enc_w16(enc_dev_t *enc, const u16 reg, const u16 data)
{
u8 dout[2];
enc_set_bank(enc, reg);
dout[0] = CMD_WCR(reg);
dout[1] = data;
spi_xfer(enc->slave, 2 * 8, dout, NULL,
SPI_XFER_BEGIN | SPI_XFER_END);
dout[0]++; /* next register */
dout[1] = data >> 8;
spi_xfer(enc->slave, 2 * 8, dout, NULL,
SPI_XFER_BEGIN | SPI_XFER_END);
}
/*
* Write a byte register, verify and retry
*/
static void enc_w8_retry(enc_dev_t *enc, const u16 reg, const u8 data, const int c)
{
u8 dout[2];
u8 readback;
int i;
enc_set_bank(enc, reg);
for (i = 0; i < c; i++) {
dout[0] = CMD_WCR(reg);
dout[1] = data;
spi_xfer(enc->slave, 2 * 8, dout, NULL,
SPI_XFER_BEGIN | SPI_XFER_END);
readback = enc_r8(enc, reg);
if (readback == data)
break;
/* wait 1ms */
udelay(1000);
}
if (i == c) {
printf("%s: write reg 0x%03x failed\n", enc->dev->name, reg);
}
}
/*
* Read ENC RAM into buffer
*/
static void enc_rbuf(enc_dev_t *enc, const u16 length, u8 *buf)
{
u8 dout[1];
dout[0] = CMD_RBM;
spi_xfer(enc->slave, 8, dout, NULL, SPI_XFER_BEGIN);
spi_xfer(enc->slave, length * 8, NULL, buf, SPI_XFER_END);
#ifdef DEBUG
puts("Rx:\n");
print_buffer(0, buf, 1, length, 0);
#endif
}
/*
* Write buffer into ENC RAM
*/
static void enc_wbuf(enc_dev_t *enc, const u16 length, const u8 *buf, const u8 control)
{
u8 dout[2];
dout[0] = CMD_WBM;
dout[1] = control;
spi_xfer(enc->slave, 2 * 8, dout, NULL, SPI_XFER_BEGIN);
spi_xfer(enc->slave, length * 8, buf, NULL, SPI_XFER_END);
#ifdef DEBUG
puts("Tx:\n");
print_buffer(0, buf, 1, length, 0);
#endif
}
/*
* Try to claim the SPI bus.
* Print error message on failure.
*/
static int enc_claim_bus(enc_dev_t *enc)
{
int rc = spi_claim_bus(enc->slave);
if (rc)
printf("%s: failed to claim SPI bus\n", enc->dev->name);
return rc;
}
/*
* Release previously claimed SPI bus.
* This function is mainly for symmetry to enc_claim_bus().
* Let the toolchain decide to inline it...
*/
static void enc_release_bus(enc_dev_t *enc)
{
spi_release_bus(enc->slave);
}
/*
* Read PHY register
*/
static u16 enc_phy_read(enc_dev_t *enc, const u8 addr)
{
uint64_t etime;
u8 status;
enc_w8(enc, CTL_REG_MIREGADR, addr);
enc_w8(enc, CTL_REG_MICMD, ENC_MICMD_MIIRD);
/* 1 second timeout - only happens on hardware problem */
etime = get_ticks() + get_tbclk();
/* poll MISTAT.BUSY bit until operation is complete */
do
{
status = enc_r8(enc, CTL_REG_MISTAT);
} while (get_ticks() <= etime && (status & ENC_MISTAT_BUSY));
if (status & ENC_MISTAT_BUSY) {
printf("%s: timeout reading phy\n", enc->dev->name);
return 0;
}
enc_w8(enc, CTL_REG_MICMD, 0);
return enc_r16(enc, CTL_REG_MIRDL);
}
/*
* Write PHY register
*/
static void enc_phy_write(enc_dev_t *enc, const u8 addr, const u16 data)
{
uint64_t etime;
u8 status;
enc_w8(enc, CTL_REG_MIREGADR, addr);
enc_w16(enc, CTL_REG_MIWRL, data);
/* 1 second timeout - only happens on hardware problem */
etime = get_ticks() + get_tbclk();
/* poll MISTAT.BUSY bit until operation is complete */
do
{
status = enc_r8(enc, CTL_REG_MISTAT);
} while (get_ticks() <= etime && (status & ENC_MISTAT_BUSY));
if (status & ENC_MISTAT_BUSY) {
printf("%s: timeout writing phy\n", enc->dev->name);
return;
}
}
/*
* Verify link status, wait if necessary
*
* Note: with a 10 MBit/s only PHY there is no autonegotiation possible,
* half/full duplex is a pure setup matter. For the time being, this driver
* will setup in half duplex mode only.
*/
static int enc_phy_link_wait(enc_dev_t *enc)
{
u16 status;
int duplex;
uint64_t etime;
#ifdef CONFIG_ENC_SILENTLINK
/* check if we have a link, then just return */
status = enc_phy_read(enc, PHY_REG_PHSTAT1);
if (status & ENC_PHSTAT1_LLSTAT)
return 0;
#endif
/* wait for link with 1 second timeout */
etime = get_ticks() + get_tbclk();
while (get_ticks() <= etime) {
status = enc_phy_read(enc, PHY_REG_PHSTAT1);
if (status & ENC_PHSTAT1_LLSTAT) {
/* now we have a link */
status = enc_phy_read(enc, PHY_REG_PHSTAT2);
duplex = (status & ENC_PHSTAT2_DPXSTAT) ? 1 : 0;
printf("%s: link up, 10Mbps %s-duplex\n",
enc->dev->name, duplex ? "full" : "half");
return 0;
}
udelay(1000);
}
/* timeout occured */
printf("%s: link down\n", enc->dev->name);
return 1;
}
/*
* This function resets the receiver only.
*/
static void enc_reset_rx(enc_dev_t *enc)
{
u8 econ1;
econ1 = enc_r8(enc, CTL_REG_ECON1);
if ((econ1 & ENC_ECON1_RXRST) == 0) {
enc_bset(enc, CTL_REG_ECON1, ENC_ECON1_RXRST);
enc->rx_reset_counter = RX_RESET_COUNTER;
}
}
/*
* Reset receiver and reenable it.
*/
static void enc_reset_rx_call(enc_dev_t *enc)
{
enc_bclr(enc, CTL_REG_ECON1, ENC_ECON1_RXRST);
enc_bset(enc, CTL_REG_ECON1, ENC_ECON1_RXEN);
}
/*
* Copy a packet from the receive ring and forward it to
* the protocol stack.
*/
static void enc_receive(enc_dev_t *enc)
{
u8 *packet = (u8 *)net_rx_packets[0];
u16 pkt_len;
u16 copy_len;
u16 status;
u8 pkt_cnt = 0;
u16 rxbuf_rdpt;
u8 hbuf[6];
enc_w16(enc, CTL_REG_ERDPTL, enc->next_pointer);
do {
enc_rbuf(enc, 6, hbuf);
enc->next_pointer = hbuf[0] | (hbuf[1] << 8);
pkt_len = hbuf[2] | (hbuf[3] << 8);
status = hbuf[4] | (hbuf[5] << 8);
debug("next_pointer=$%04x pkt_len=%u status=$%04x\n",
enc->next_pointer, pkt_len, status);
if (pkt_len <= ENC_MAX_FRM_LEN)
copy_len = pkt_len;
else
copy_len = 0;
if ((status & (1L << 7)) == 0) /* check Received Ok bit */
copy_len = 0;
/* check if next pointer is resonable */
if (enc->next_pointer >= ENC_TX_BUF_START)
copy_len = 0;
if (copy_len > 0) {
enc_rbuf(enc, copy_len, packet);
}
/* advance read pointer to next pointer */
enc_w16(enc, CTL_REG_ERDPTL, enc->next_pointer);
/* decrease packet counter */
enc_bset(enc, CTL_REG_ECON2, ENC_ECON2_PKTDEC);
/*
* Only odd values should be written to ERXRDPTL,
* see errata B4 pt.13
*/
rxbuf_rdpt = enc->next_pointer - 1;
if ((rxbuf_rdpt < enc_r16(enc, CTL_REG_ERXSTL)) ||
(rxbuf_rdpt > enc_r16(enc, CTL_REG_ERXNDL))) {
enc_w16(enc, CTL_REG_ERXRDPTL,
enc_r16(enc, CTL_REG_ERXNDL));
} else {
enc_w16(enc, CTL_REG_ERXRDPTL, rxbuf_rdpt);
}
/* read pktcnt */
pkt_cnt = enc_r8(enc, CTL_REG_EPKTCNT);
if (copy_len == 0) {
(void)enc_r8(enc, CTL_REG_EIR);
enc_reset_rx(enc);
printf("%s: receive copy_len=0\n", enc->dev->name);
continue;
}
/*
* Because net_process_received_packet() might call enc_send(),
* we need to release the SPI bus, call
* net_process_received_packet(), reclaim the bus.
*/
enc_release_bus(enc);
net_process_received_packet(packet, pkt_len);
if (enc_claim_bus(enc))
return;
(void)enc_r8(enc, CTL_REG_EIR);
} while (pkt_cnt);
/* Use EPKTCNT not EIR.PKTIF flag, see errata pt. 6 */
}
/*
* Poll for completely received packets.
*/
static void enc_poll(enc_dev_t *enc)
{
u8 eir_reg;
u8 pkt_cnt;
#ifdef CONFIG_USE_IRQ
/* clear global interrupt enable bit in enc28j60 */
enc_bclr(enc, CTL_REG_EIE, ENC_EIE_INTIE);
#endif
(void)enc_r8(enc, CTL_REG_ESTAT);
eir_reg = enc_r8(enc, CTL_REG_EIR);
if (eir_reg & ENC_EIR_TXIF) {
/* clear TXIF bit in EIR */
enc_bclr(enc, CTL_REG_EIR, ENC_EIR_TXIF);
}
/* We have to use pktcnt and not pktif bit, see errata pt. 6 */
pkt_cnt = enc_r8(enc, CTL_REG_EPKTCNT);
if (pkt_cnt > 0) {
if ((eir_reg & ENC_EIR_PKTIF) == 0) {
debug("enc_poll: pkt cnt > 0, but pktif not set\n");
}
enc_receive(enc);
/*
* clear PKTIF bit in EIR, this should not need to be done
* but it seems like we get problems if we do not
*/
enc_bclr(enc, CTL_REG_EIR, ENC_EIR_PKTIF);
}
if (eir_reg & ENC_EIR_RXERIF) {
printf("%s: rx error\n", enc->dev->name);
enc_bclr(enc, CTL_REG_EIR, ENC_EIR_RXERIF);
}
if (eir_reg & ENC_EIR_TXERIF) {
printf("%s: tx error\n", enc->dev->name);
enc_bclr(enc, CTL_REG_EIR, ENC_EIR_TXERIF);
}
#ifdef CONFIG_USE_IRQ
/* set global interrupt enable bit in enc28j60 */
enc_bset(enc, CTL_REG_EIE, ENC_EIE_INTIE);
#endif
}
/*
* Completely Reset the ENC
*/
static void enc_reset(enc_dev_t *enc)
{
u8 dout[1];
dout[0] = CMD_SRC;
spi_xfer(enc->slave, 8, dout, NULL,
SPI_XFER_BEGIN | SPI_XFER_END);
/* sleep 1 ms. See errata pt. 2 */
udelay(1000);
}
/*
* Initialisation data for most of the ENC registers
*/
static const u16 enc_initdata[] = {
/*
* Setup the buffer space. The reset values are valid for the
* other pointers.
*
* We shall not write to ERXST, see errata pt. 5. Instead we
* have to make sure that ENC_RX_BUS_START is 0.
*/
CTL_REG_ERXSTL, ENC_RX_BUF_START,
CTL_REG_ERXSTH, ENC_RX_BUF_START >> 8,
CTL_REG_ERXNDL, ENC_RX_BUF_END,
CTL_REG_ERXNDH, ENC_RX_BUF_END >> 8,
CTL_REG_ERDPTL, ENC_RX_BUF_START,
CTL_REG_ERDPTH, ENC_RX_BUF_START >> 8,
/*
* Set the filter to receive only good-CRC, unicast and broadcast
* frames.
* Note: some DHCP servers return their answers as broadcasts!
* So its unwise to remove broadcast from this. This driver
* might incur receiver overruns with packet loss on a broadcast
* flooded network.
*/
CTL_REG_ERXFCON, ENC_RFR_BCEN | ENC_RFR_UCEN | ENC_RFR_CRCEN,
/* enable MAC to receive frames */
CTL_REG_MACON1,
ENC_MACON1_MARXEN | ENC_MACON1_TXPAUS | ENC_MACON1_RXPAUS,
/* configure pad, tx-crc and duplex */
CTL_REG_MACON3,
ENC_MACON3_PADCFG0 | ENC_MACON3_TXCRCEN |
ENC_MACON3_FRMLNEN,
/* Allow infinite deferals if the medium is continously busy */
CTL_REG_MACON4, ENC_MACON4_DEFER,
/* Late collisions occur beyond 63 bytes */
CTL_REG_MACLCON2, 63,
/*
* Set (low byte) Non-Back-to_Back Inter-Packet Gap.
* Recommended 0x12
*/
CTL_REG_MAIPGL, 0x12,
/*
* Set (high byte) Non-Back-to_Back Inter-Packet Gap.
* Recommended 0x0c for half-duplex. Nothing for full-duplex
*/
CTL_REG_MAIPGH, 0x0C,
/* set maximum frame length */
CTL_REG_MAMXFLL, ENC_MAX_FRM_LEN,
CTL_REG_MAMXFLH, ENC_MAX_FRM_LEN >> 8,
/*
* Set MAC back-to-back inter-packet gap.
* Recommended 0x12 for half duplex
* and 0x15 for full duplex.
*/
CTL_REG_MABBIPG, 0x12,
/* end of table */
0xffff
};
/*
* Wait for the XTAL oscillator to become ready
*/
static int enc_clock_wait(enc_dev_t *enc)
{
uint64_t etime;
/* one second timeout */
etime = get_ticks() + get_tbclk();
/*
* Wait for CLKRDY to become set (i.e., check that we can
* communicate with the ENC)
*/
do
{
if (enc_r8(enc, CTL_REG_ESTAT) & ENC_ESTAT_CLKRDY)
return 0;
} while (get_ticks() <= etime);
printf("%s: timeout waiting for CLKRDY\n", enc->dev->name);
return -1;
}
/*
* Write the MAC address into the ENC
*/
static int enc_write_macaddr(enc_dev_t *enc)
{
unsigned char *p = enc->dev->enetaddr;
enc_w8_retry(enc, CTL_REG_MAADR5, *p++, 5);
enc_w8_retry(enc, CTL_REG_MAADR4, *p++, 5);
enc_w8_retry(enc, CTL_REG_MAADR3, *p++, 5);
enc_w8_retry(enc, CTL_REG_MAADR2, *p++, 5);
enc_w8_retry(enc, CTL_REG_MAADR1, *p++, 5);
enc_w8_retry(enc, CTL_REG_MAADR0, *p, 5);
return 0;
}
/*
* Setup most of the ENC registers
*/
static int enc_setup(enc_dev_t *enc)
{
u16 phid1 = 0;
u16 phid2 = 0;
const u16 *tp;
/* reset enc struct values */
enc->next_pointer = ENC_RX_BUF_START;
enc->rx_reset_counter = RX_RESET_COUNTER;
enc->bank = 0xff; /* invalidate current bank in enc28j60 */
/* verify PHY identification */
phid1 = enc_phy_read(enc, PHY_REG_PHID1);
phid2 = enc_phy_read(enc, PHY_REG_PHID2) & ENC_PHID2_MASK;
if (phid1 != ENC_PHID1_VALUE || phid2 != ENC_PHID2_VALUE) {
printf("%s: failed to identify PHY. Found %04x:%04x\n",
enc->dev->name, phid1, phid2);
return -1;
}
/* now program registers */
for (tp = enc_initdata; *tp != 0xffff; tp += 2)
enc_w8_retry(enc, tp[0], tp[1], 10);
/*
* Prevent automatic loopback of data beeing transmitted by setting
* ENC_PHCON2_HDLDIS
*/
enc_phy_write(enc, PHY_REG_PHCON2, (1<<8));
/*
* LEDs configuration
* LEDA: LACFG = 0100 -> display link status
* LEDB: LBCFG = 0111 -> display TX & RX activity
* STRCH = 1 -> LED pulses
*/
enc_phy_write(enc, PHY_REG_PHLCON, 0x0472);
/* Reset PDPXMD-bit => half duplex */
enc_phy_write(enc, PHY_REG_PHCON1, 0);
#ifdef CONFIG_USE_IRQ
/* enable interrupts */
enc_bset(enc, CTL_REG_EIE, ENC_EIE_PKTIE);
enc_bset(enc, CTL_REG_EIE, ENC_EIE_TXIE);
enc_bset(enc, CTL_REG_EIE, ENC_EIE_RXERIE);
enc_bset(enc, CTL_REG_EIE, ENC_EIE_TXERIE);
enc_bset(enc, CTL_REG_EIE, ENC_EIE_INTIE);
#endif
return 0;
}
/*
* Check if ENC has been initialized.
* If not, try to initialize it.
* Remember initialized state in struct.
*/
static int enc_initcheck(enc_dev_t *enc, const enum enc_initstate requiredstate)
{
if (enc->initstate >= requiredstate)
return 0;
if (enc->initstate < setupdone) {
/* Initialize the ENC only */
enc_reset(enc);
/* if any of functions fails, skip the rest and return an error */
if (enc_clock_wait(enc) || enc_setup(enc) || enc_write_macaddr(enc)) {
return -1;
}
enc->initstate = setupdone;
}
/* if that's all we need, return here */
if (enc->initstate >= requiredstate)
return 0;
/* now wait for link ready condition */
if (enc_phy_link_wait(enc)) {
return -1;
}
enc->initstate = linkready;
return 0;
}
#if defined(CONFIG_CMD_MII)
/*
* Read a PHY register.
*
* This function is registered with miiphy_register().
*/
int enc_miiphy_read(const char *devname, u8 phy_adr, u8 reg, u16 *value)
{
struct eth_device *dev = eth_get_dev_by_name(devname);
enc_dev_t *enc;
if (!dev || phy_adr != 0)
return -1;
enc = dev->priv;
if (enc_claim_bus(enc))
return -1;
if (enc_initcheck(enc, setupdone)) {
enc_release_bus(enc);
return -1;
}
*value = enc_phy_read(enc, reg);
enc_release_bus(enc);
return 0;
}
/*
* Write a PHY register.
*
* This function is registered with miiphy_register().
*/
int enc_miiphy_write(const char *devname, u8 phy_adr, u8 reg, u16 value)
{
struct eth_device *dev = eth_get_dev_by_name(devname);
enc_dev_t *enc;
if (!dev || phy_adr != 0)
return -1;
enc = dev->priv;
if (enc_claim_bus(enc))
return -1;
if (enc_initcheck(enc, setupdone)) {
enc_release_bus(enc);
return -1;
}
enc_phy_write(enc, reg, value);
enc_release_bus(enc);
return 0;
}
#endif
/*
* Write hardware (MAC) address.
*
* This function entered into eth_device structure.
*/
static int enc_write_hwaddr(struct eth_device *dev)
{
enc_dev_t *enc = dev->priv;
if (enc_claim_bus(enc))
return -1;
if (enc_initcheck(enc, setupdone)) {
enc_release_bus(enc);
return -1;
}
enc_release_bus(enc);
return 0;
}
/*
* Initialize ENC28J60 for use.
*
* This function entered into eth_device structure.
*/
static int enc_init(struct eth_device *dev, bd_t *bis)
{
enc_dev_t *enc = dev->priv;
if (enc_claim_bus(enc))
return -1;
if (enc_initcheck(enc, linkready)) {
enc_release_bus(enc);
return -1;
}
/* enable receive */
enc_bset(enc, CTL_REG_ECON1, ENC_ECON1_RXEN);
enc_release_bus(enc);
return 0;
}
/*
* Check for received packets.
*
* This function entered into eth_device structure.
*/
static int enc_recv(struct eth_device *dev)
{
enc_dev_t *enc = dev->priv;
if (enc_claim_bus(enc))
return -1;
if (enc_initcheck(enc, linkready)) {
enc_release_bus(enc);
return -1;
}
/* Check for dead receiver */
if (enc->rx_reset_counter > 0)
enc->rx_reset_counter--;
else
enc_reset_rx_call(enc);
enc_poll(enc);
enc_release_bus(enc);
return 0;
}
/*
* Send a packet.
*
* This function entered into eth_device structure.
*
* Should we wait here until we have a Link? Or shall we leave that to
* protocol retries?
*/
static int enc_send(
struct eth_device *dev,
void *packet,
int length)
{
enc_dev_t *enc = dev->priv;
if (enc_claim_bus(enc))
return -1;
if (enc_initcheck(enc, linkready)) {
enc_release_bus(enc);
return -1;
}
/* setup transmit pointers */
enc_w16(enc, CTL_REG_EWRPTL, ENC_TX_BUF_START);
enc_w16(enc, CTL_REG_ETXNDL, length + ENC_TX_BUF_START);
enc_w16(enc, CTL_REG_ETXSTL, ENC_TX_BUF_START);
/* write packet to ENC */
enc_wbuf(enc, length, (u8 *) packet, 0x00);
/*
* Check that the internal transmit logic has not been altered
* by excessive collisions. Reset transmitter if so.
* See Errata B4 12 and 14.
*/
if (enc_r8(enc, CTL_REG_EIR) & ENC_EIR_TXERIF) {
enc_bset(enc, CTL_REG_ECON1, ENC_ECON1_TXRST);
enc_bclr(enc, CTL_REG_ECON1, ENC_ECON1_TXRST);
}
enc_bclr(enc, CTL_REG_EIR, (ENC_EIR_TXERIF | ENC_EIR_TXIF));
/* start transmitting */
enc_bset(enc, CTL_REG_ECON1, ENC_ECON1_TXRTS);
enc_release_bus(enc);
return 0;
}
/*
* Finish use of ENC.
*
* This function entered into eth_device structure.
*/
static void enc_halt(struct eth_device *dev)
{
enc_dev_t *enc = dev->priv;
if (enc_claim_bus(enc))
return;
/* Just disable receiver */
enc_bclr(enc, CTL_REG_ECON1, ENC_ECON1_RXEN);
enc_release_bus(enc);
}
/*
* This is the only exported function.
*
* It may be called several times with different bus:cs combinations.
*/
int enc28j60_initialize(unsigned int bus, unsigned int cs,
unsigned int max_hz, unsigned int mode)
{
struct eth_device *dev;
enc_dev_t *enc;
/* try to allocate, check and clear eth_device object */
dev = malloc(sizeof(*dev));
if (!dev) {
return -1;
}
memset(dev, 0, sizeof(*dev));
/* try to allocate, check and clear enc_dev_t object */
enc = malloc(sizeof(*enc));
if (!enc) {
free(dev);
return -1;
}
memset(enc, 0, sizeof(*enc));
/* try to setup the SPI slave */
enc->slave = spi_setup_slave(bus, cs, max_hz, mode);
if (!enc->slave) {
printf("enc28j60: invalid SPI device %i:%i\n", bus, cs);
free(enc);
free(dev);
return -1;
}
enc->dev = dev;
/* now fill the eth_device object */
dev->priv = enc;
dev->init = enc_init;
dev->halt = enc_halt;
dev->send = enc_send;
dev->recv = enc_recv;
dev->write_hwaddr = enc_write_hwaddr;
sprintf(dev->name, "enc%i.%i", bus, cs);
eth_register(dev);
#if defined(CONFIG_CMD_MII)
miiphy_register(dev->name, enc_miiphy_read, enc_miiphy_write);
#endif
return 0;
}

View file

@ -0,0 +1,238 @@
/*
* (X) extracted from enc28j60.c
* Reinhard Meyer, EMK Elektronik, reinhard.meyer@emk-elektronik.de
*
* SPDX-License-Identifier: GPL-2.0+
*/
#ifndef _enc28j60_h
#define _enc28j60_h
/*
* SPI Commands
*
* Bits 7-5: Command
* Bits 4-0: Register
*/
#define CMD_RCR(x) (0x00+((x)&0x1f)) /* Read Control Register */
#define CMD_RBM 0x3a /* Read Buffer Memory */
#define CMD_WCR(x) (0x40+((x)&0x1f)) /* Write Control Register */
#define CMD_WBM 0x7a /* Write Buffer Memory */
#define CMD_BFS(x) (0x80+((x)&0x1f)) /* Bit Field Set */
#define CMD_BFC(x) (0xa0+((x)&0x1f)) /* Bit Field Clear */
#define CMD_SRC 0xff /* System Reset Command */
/* NEW: encode (bank number+1) in upper byte */
/* Common Control Registers accessible in all Banks */
#define CTL_REG_EIE 0x01B
#define CTL_REG_EIR 0x01C
#define CTL_REG_ESTAT 0x01D
#define CTL_REG_ECON2 0x01E
#define CTL_REG_ECON1 0x01F
/* Control Registers accessible in Bank 0 */
#define CTL_REG_ERDPTL 0x100
#define CTL_REG_ERDPTH 0x101
#define CTL_REG_EWRPTL 0x102
#define CTL_REG_EWRPTH 0x103
#define CTL_REG_ETXSTL 0x104
#define CTL_REG_ETXSTH 0x105
#define CTL_REG_ETXNDL 0x106
#define CTL_REG_ETXNDH 0x107
#define CTL_REG_ERXSTL 0x108
#define CTL_REG_ERXSTH 0x109
#define CTL_REG_ERXNDL 0x10A
#define CTL_REG_ERXNDH 0x10B
#define CTL_REG_ERXRDPTL 0x10C
#define CTL_REG_ERXRDPTH 0x10D
#define CTL_REG_ERXWRPTL 0x10E
#define CTL_REG_ERXWRPTH 0x10F
#define CTL_REG_EDMASTL 0x110
#define CTL_REG_EDMASTH 0x111
#define CTL_REG_EDMANDL 0x112
#define CTL_REG_EDMANDH 0x113
#define CTL_REG_EDMADSTL 0x114
#define CTL_REG_EDMADSTH 0x115
#define CTL_REG_EDMACSL 0x116
#define CTL_REG_EDMACSH 0x117
/* Control Registers accessible in Bank 1 */
#define CTL_REG_EHT0 0x200
#define CTL_REG_EHT1 0x201
#define CTL_REG_EHT2 0x202
#define CTL_REG_EHT3 0x203
#define CTL_REG_EHT4 0x204
#define CTL_REG_EHT5 0x205
#define CTL_REG_EHT6 0x206
#define CTL_REG_EHT7 0x207
#define CTL_REG_EPMM0 0x208
#define CTL_REG_EPMM1 0x209
#define CTL_REG_EPMM2 0x20A
#define CTL_REG_EPMM3 0x20B
#define CTL_REG_EPMM4 0x20C
#define CTL_REG_EPMM5 0x20D
#define CTL_REG_EPMM6 0x20E
#define CTL_REG_EPMM7 0x20F
#define CTL_REG_EPMCSL 0x210
#define CTL_REG_EPMCSH 0x211
#define CTL_REG_EPMOL 0x214
#define CTL_REG_EPMOH 0x215
#define CTL_REG_EWOLIE 0x216
#define CTL_REG_EWOLIR 0x217
#define CTL_REG_ERXFCON 0x218
#define CTL_REG_EPKTCNT 0x219
/* Control Registers accessible in Bank 2 */
#define CTL_REG_MACON1 0x300
#define CTL_REG_MACON2 0x301
#define CTL_REG_MACON3 0x302
#define CTL_REG_MACON4 0x303
#define CTL_REG_MABBIPG 0x304
#define CTL_REG_MAIPGL 0x306
#define CTL_REG_MAIPGH 0x307
#define CTL_REG_MACLCON1 0x308
#define CTL_REG_MACLCON2 0x309
#define CTL_REG_MAMXFLL 0x30A
#define CTL_REG_MAMXFLH 0x30B
#define CTL_REG_MAPHSUP 0x30D
#define CTL_REG_MICON 0x311
#define CTL_REG_MICMD 0x312
#define CTL_REG_MIREGADR 0x314
#define CTL_REG_MIWRL 0x316
#define CTL_REG_MIWRH 0x317
#define CTL_REG_MIRDL 0x318
#define CTL_REG_MIRDH 0x319
/* Control Registers accessible in Bank 3 */
#define CTL_REG_MAADR1 0x400
#define CTL_REG_MAADR0 0x401
#define CTL_REG_MAADR3 0x402
#define CTL_REG_MAADR2 0x403
#define CTL_REG_MAADR5 0x404
#define CTL_REG_MAADR4 0x405
#define CTL_REG_EBSTSD 0x406
#define CTL_REG_EBSTCON 0x407
#define CTL_REG_EBSTCSL 0x408
#define CTL_REG_EBSTCSH 0x409
#define CTL_REG_MISTAT 0x40A
#define CTL_REG_EREVID 0x412
#define CTL_REG_ECOCON 0x415
#define CTL_REG_EFLOCON 0x417
#define CTL_REG_EPAUSL 0x418
#define CTL_REG_EPAUSH 0x419
/* PHY Register */
#define PHY_REG_PHCON1 0x00
#define PHY_REG_PHSTAT1 0x01
#define PHY_REG_PHID1 0x02
#define PHY_REG_PHID2 0x03
#define PHY_REG_PHCON2 0x10
#define PHY_REG_PHSTAT2 0x11
#define PHY_REG_PHLCON 0x14
/* Receive Filter Register (ERXFCON) bits */
#define ENC_RFR_UCEN 0x80
#define ENC_RFR_ANDOR 0x40
#define ENC_RFR_CRCEN 0x20
#define ENC_RFR_PMEN 0x10
#define ENC_RFR_MPEN 0x08
#define ENC_RFR_HTEN 0x04
#define ENC_RFR_MCEN 0x02
#define ENC_RFR_BCEN 0x01
/* ECON1 Register Bits */
#define ENC_ECON1_TXRST 0x80
#define ENC_ECON1_RXRST 0x40
#define ENC_ECON1_DMAST 0x20
#define ENC_ECON1_CSUMEN 0x10
#define ENC_ECON1_TXRTS 0x08
#define ENC_ECON1_RXEN 0x04
#define ENC_ECON1_BSEL1 0x02
#define ENC_ECON1_BSEL0 0x01
/* ECON2 Register Bits */
#define ENC_ECON2_AUTOINC 0x80
#define ENC_ECON2_PKTDEC 0x40
#define ENC_ECON2_PWRSV 0x20
#define ENC_ECON2_VRPS 0x08
/* EIR Register Bits */
#define ENC_EIR_PKTIF 0x40
#define ENC_EIR_DMAIF 0x20
#define ENC_EIR_LINKIF 0x10
#define ENC_EIR_TXIF 0x08
#define ENC_EIR_WOLIF 0x04
#define ENC_EIR_TXERIF 0x02
#define ENC_EIR_RXERIF 0x01
/* ESTAT Register Bits */
#define ENC_ESTAT_INT 0x80
#define ENC_ESTAT_LATECOL 0x10
#define ENC_ESTAT_RXBUSY 0x04
#define ENC_ESTAT_TXABRT 0x02
#define ENC_ESTAT_CLKRDY 0x01
/* EIE Register Bits */
#define ENC_EIE_INTIE 0x80
#define ENC_EIE_PKTIE 0x40
#define ENC_EIE_DMAIE 0x20
#define ENC_EIE_LINKIE 0x10
#define ENC_EIE_TXIE 0x08
#define ENC_EIE_WOLIE 0x04
#define ENC_EIE_TXERIE 0x02
#define ENC_EIE_RXERIE 0x01
/* MACON1 Register Bits */
#define ENC_MACON1_LOOPBK 0x10
#define ENC_MACON1_TXPAUS 0x08
#define ENC_MACON1_RXPAUS 0x04
#define ENC_MACON1_PASSALL 0x02
#define ENC_MACON1_MARXEN 0x01
/* MACON2 Register Bits */
#define ENC_MACON2_MARST 0x80
#define ENC_MACON2_RNDRST 0x40
#define ENC_MACON2_MARXRST 0x08
#define ENC_MACON2_RFUNRST 0x04
#define ENC_MACON2_MATXRST 0x02
#define ENC_MACON2_TFUNRST 0x01
/* MACON3 Register Bits */
#define ENC_MACON3_PADCFG2 0x80
#define ENC_MACON3_PADCFG1 0x40
#define ENC_MACON3_PADCFG0 0x20
#define ENC_MACON3_TXCRCEN 0x10
#define ENC_MACON3_PHDRLEN 0x08
#define ENC_MACON3_HFRMEN 0x04
#define ENC_MACON3_FRMLNEN 0x02
#define ENC_MACON3_FULDPX 0x01
/* MACON4 Register Bits */
#define ENC_MACON4_DEFER 0x40
/* MICMD Register Bits */
#define ENC_MICMD_MIISCAN 0x02
#define ENC_MICMD_MIIRD 0x01
/* MISTAT Register Bits */
#define ENC_MISTAT_NVALID 0x04
#define ENC_MISTAT_SCAN 0x02
#define ENC_MISTAT_BUSY 0x01
/* PHID1 and PHID2 values */
#define ENC_PHID1_VALUE 0x0083
#define ENC_PHID2_VALUE 0x1400
#define ENC_PHID2_MASK 0xFC00
/* PHCON1 values */
#define ENC_PHCON1_PDPXMD 0x0100
/* PHSTAT1 values */
#define ENC_PHSTAT1_LLSTAT 0x0004
/* PHSTAT2 values */
#define ENC_PHSTAT2_LSTAT 0x0400
#define ENC_PHSTAT2_DPXSTAT 0x0200
#endif

View file

@ -0,0 +1,640 @@
/*
* Cirrus Logic EP93xx ethernet MAC / MII driver.
*
* Copyright (C) 2010, 2009
* Matthias Kaehlcke <matthias@kaehlcke.net>
*
* Copyright (C) 2004, 2005
* Cory T. Tusar, Videon Central, Inc., <ctusar@videon-central.com>
*
* Based on the original eth.[ch] Cirrus Logic EP93xx Rev D. Ethernet Driver,
* which is
*
* (C) Copyright 2002 2003
* Adam Bezanson, Network Audio Technologies, Inc.
* <bezanson@netaudiotech.com>
*
* SPDX-License-Identifier: GPL-2.0+
*/
#include <command.h>
#include <common.h>
#include <asm/arch/ep93xx.h>
#include <asm/io.h>
#include <malloc.h>
#include <miiphy.h>
#include <linux/types.h>
#include "ep93xx_eth.h"
#define GET_PRIV(eth_dev) ((struct ep93xx_priv *)(eth_dev)->priv)
#define GET_REGS(eth_dev) (GET_PRIV(eth_dev)->regs)
/* ep93xx_miiphy ops forward declarations */
static int ep93xx_miiphy_read(const char * const dev, unsigned char const addr,
unsigned char const reg, unsigned short * const value);
static int ep93xx_miiphy_write(const char * const dev, unsigned char const addr,
unsigned char const reg, unsigned short const value);
#if defined(EP93XX_MAC_DEBUG)
/**
* Dump ep93xx_mac values to the terminal.
*/
static void dump_dev(struct eth_device *dev)
{
struct ep93xx_priv *priv = GET_PRIV(dev);
int i;
printf("\ndump_dev()\n");
printf(" rx_dq.base %p\n", priv->rx_dq.base);
printf(" rx_dq.current %p\n", priv->rx_dq.current);
printf(" rx_dq.end %p\n", priv->rx_dq.end);
printf(" rx_sq.base %p\n", priv->rx_sq.base);
printf(" rx_sq.current %p\n", priv->rx_sq.current);
printf(" rx_sq.end %p\n", priv->rx_sq.end);
for (i = 0; i < NUMRXDESC; i++)
printf(" rx_buffer[%2.d] %p\n", i, net_rx_packets[i]);
printf(" tx_dq.base %p\n", priv->tx_dq.base);
printf(" tx_dq.current %p\n", priv->tx_dq.current);
printf(" tx_dq.end %p\n", priv->tx_dq.end);
printf(" tx_sq.base %p\n", priv->tx_sq.base);
printf(" tx_sq.current %p\n", priv->tx_sq.current);
printf(" tx_sq.end %p\n", priv->tx_sq.end);
}
/**
* Dump all RX status queue entries to the terminal.
*/
static void dump_rx_status_queue(struct eth_device *dev)
{
struct ep93xx_priv *priv = GET_PRIV(dev);
int i;
printf("\ndump_rx_status_queue()\n");
printf(" descriptor address word1 word2\n");
for (i = 0; i < NUMRXDESC; i++) {
printf(" [ %p ] %08X %08X\n",
priv->rx_sq.base + i,
(priv->rx_sq.base + i)->word1,
(priv->rx_sq.base + i)->word2);
}
}
/**
* Dump all RX descriptor queue entries to the terminal.
*/
static void dump_rx_descriptor_queue(struct eth_device *dev)
{
struct ep93xx_priv *priv = GET_PRIV(dev);
int i;
printf("\ndump_rx_descriptor_queue()\n");
printf(" descriptor address word1 word2\n");
for (i = 0; i < NUMRXDESC; i++) {
printf(" [ %p ] %08X %08X\n",
priv->rx_dq.base + i,
(priv->rx_dq.base + i)->word1,
(priv->rx_dq.base + i)->word2);
}
}
/**
* Dump all TX descriptor queue entries to the terminal.
*/
static void dump_tx_descriptor_queue(struct eth_device *dev)
{
struct ep93xx_priv *priv = GET_PRIV(dev);
int i;
printf("\ndump_tx_descriptor_queue()\n");
printf(" descriptor address word1 word2\n");
for (i = 0; i < NUMTXDESC; i++) {
printf(" [ %p ] %08X %08X\n",
priv->tx_dq.base + i,
(priv->tx_dq.base + i)->word1,
(priv->tx_dq.base + i)->word2);
}
}
/**
* Dump all TX status queue entries to the terminal.
*/
static void dump_tx_status_queue(struct eth_device *dev)
{
struct ep93xx_priv *priv = GET_PRIV(dev);
int i;
printf("\ndump_tx_status_queue()\n");
printf(" descriptor address word1\n");
for (i = 0; i < NUMTXDESC; i++) {
printf(" [ %p ] %08X\n",
priv->rx_sq.base + i,
(priv->rx_sq.base + i)->word1);
}
}
#else
#define dump_dev(x)
#define dump_rx_descriptor_queue(x)
#define dump_rx_status_queue(x)
#define dump_tx_descriptor_queue(x)
#define dump_tx_status_queue(x)
#endif /* defined(EP93XX_MAC_DEBUG) */
/**
* Reset the EP93xx MAC by twiddling the soft reset bit and spinning until
* it's cleared.
*/
static void ep93xx_mac_reset(struct eth_device *dev)
{
struct mac_regs *mac = GET_REGS(dev);
uint32_t value;
debug("+ep93xx_mac_reset");
value = readl(&mac->selfctl);
value |= SELFCTL_RESET;
writel(value, &mac->selfctl);
while (readl(&mac->selfctl) & SELFCTL_RESET)
; /* noop */
debug("-ep93xx_mac_reset");
}
/* Eth device open */
static int ep93xx_eth_open(struct eth_device *dev, bd_t *bd)
{
struct ep93xx_priv *priv = GET_PRIV(dev);
struct mac_regs *mac = GET_REGS(dev);
uchar *mac_addr = dev->enetaddr;
int i;
debug("+ep93xx_eth_open");
/* Reset the MAC */
ep93xx_mac_reset(dev);
/* Reset the descriptor queues' current and end address values */
priv->tx_dq.current = priv->tx_dq.base;
priv->tx_dq.end = (priv->tx_dq.base + NUMTXDESC);
priv->tx_sq.current = priv->tx_sq.base;
priv->tx_sq.end = (priv->tx_sq.base + NUMTXDESC);
priv->rx_dq.current = priv->rx_dq.base;
priv->rx_dq.end = (priv->rx_dq.base + NUMRXDESC);
priv->rx_sq.current = priv->rx_sq.base;
priv->rx_sq.end = (priv->rx_sq.base + NUMRXDESC);
/*
* Set the transmit descriptor and status queues' base address,
* current address, and length registers. Set the maximum frame
* length and threshold. Enable the transmit descriptor processor.
*/
writel((uint32_t)priv->tx_dq.base, &mac->txdq.badd);
writel((uint32_t)priv->tx_dq.base, &mac->txdq.curadd);
writel(sizeof(struct tx_descriptor) * NUMTXDESC, &mac->txdq.blen);
writel((uint32_t)priv->tx_sq.base, &mac->txstsq.badd);
writel((uint32_t)priv->tx_sq.base, &mac->txstsq.curadd);
writel(sizeof(struct tx_status) * NUMTXDESC, &mac->txstsq.blen);
writel(0x00040000, &mac->txdthrshld);
writel(0x00040000, &mac->txststhrshld);
writel((TXSTARTMAX << 0) | (PKTSIZE_ALIGN << 16), &mac->maxfrmlen);
writel(BMCTL_TXEN, &mac->bmctl);
/*
* Set the receive descriptor and status queues' base address,
* current address, and length registers. Enable the receive
* descriptor processor.
*/
writel((uint32_t)priv->rx_dq.base, &mac->rxdq.badd);
writel((uint32_t)priv->rx_dq.base, &mac->rxdq.curadd);
writel(sizeof(struct rx_descriptor) * NUMRXDESC, &mac->rxdq.blen);
writel((uint32_t)priv->rx_sq.base, &mac->rxstsq.badd);
writel((uint32_t)priv->rx_sq.base, &mac->rxstsq.curadd);
writel(sizeof(struct rx_status) * NUMRXDESC, &mac->rxstsq.blen);
writel(0x00040000, &mac->rxdthrshld);
writel(BMCTL_RXEN, &mac->bmctl);
writel(0x00040000, &mac->rxststhrshld);
/* Wait until the receive descriptor processor is active */
while (!(readl(&mac->bmsts) & BMSTS_RXACT))
; /* noop */
/*
* Initialize the RX descriptor queue. Clear the TX descriptor queue.
* Clear the RX and TX status queues. Enqueue the RX descriptor and
* status entries to the MAC.
*/
for (i = 0; i < NUMRXDESC; i++) {
/* set buffer address */
(priv->rx_dq.base + i)->word1 = (uint32_t)net_rx_packets[i];
/* set buffer length, clear buffer index and NSOF */
(priv->rx_dq.base + i)->word2 = PKTSIZE_ALIGN;
}
memset(priv->tx_dq.base, 0,
(sizeof(struct tx_descriptor) * NUMTXDESC));
memset(priv->rx_sq.base, 0,
(sizeof(struct rx_status) * NUMRXDESC));
memset(priv->tx_sq.base, 0,
(sizeof(struct tx_status) * NUMTXDESC));
writel(NUMRXDESC, &mac->rxdqenq);
writel(NUMRXDESC, &mac->rxstsqenq);
/* Set the primary MAC address */
writel(AFP_IAPRIMARY, &mac->afp);
writel(mac_addr[0] | (mac_addr[1] << 8) |
(mac_addr[2] << 16) | (mac_addr[3] << 24),
&mac->indad);
writel(mac_addr[4] | (mac_addr[5] << 8), &mac->indad_upper);
/* Turn on RX and TX */
writel(RXCTL_IA0 | RXCTL_BA | RXCTL_SRXON |
RXCTL_RCRCA | RXCTL_MA, &mac->rxctl);
writel(TXCTL_STXON, &mac->txctl);
/* Dump data structures if we're debugging */
dump_dev(dev);
dump_rx_descriptor_queue(dev);
dump_rx_status_queue(dev);
dump_tx_descriptor_queue(dev);
dump_tx_status_queue(dev);
debug("-ep93xx_eth_open");
return 1;
}
/**
* Halt EP93xx MAC transmit and receive by clearing the TxCTL and RxCTL
* registers.
*/
static void ep93xx_eth_close(struct eth_device *dev)
{
struct mac_regs *mac = GET_REGS(dev);
debug("+ep93xx_eth_close");
writel(0x00000000, &mac->rxctl);
writel(0x00000000, &mac->txctl);
debug("-ep93xx_eth_close");
}
/**
* Copy a frame of data from the MAC into the protocol layer for further
* processing.
*/
static int ep93xx_eth_rcv_packet(struct eth_device *dev)
{
struct mac_regs *mac = GET_REGS(dev);
struct ep93xx_priv *priv = GET_PRIV(dev);
int len = -1;
debug("+ep93xx_eth_rcv_packet");
if (RX_STATUS_RFP(priv->rx_sq.current)) {
if (RX_STATUS_RWE(priv->rx_sq.current)) {
/*
* We have a good frame. Extract the frame's length
* from the current rx_status_queue entry, and copy
* the frame's data into net_rx_packets[] of the
* protocol stack. We track the total number of
* bytes in the frame (nbytes_frame) which will be
* used when we pass the data off to the protocol
* layer via net_process_received_packet().
*/
len = RX_STATUS_FRAME_LEN(priv->rx_sq.current);
net_process_received_packet(
(uchar *)priv->rx_dq.current->word1, len);
debug("reporting %d bytes...\n", len);
} else {
/* Do we have an erroneous packet? */
error("packet rx error, status %08X %08X",
priv->rx_sq.current->word1,
priv->rx_sq.current->word2);
dump_rx_descriptor_queue(dev);
dump_rx_status_queue(dev);
}
/*
* Clear the associated status queue entry, and
* increment our current pointers to the next RX
* descriptor and status queue entries (making sure
* we wrap properly).
*/
memset((void *)priv->rx_sq.current, 0,
sizeof(struct rx_status));
priv->rx_sq.current++;
if (priv->rx_sq.current >= priv->rx_sq.end)
priv->rx_sq.current = priv->rx_sq.base;
priv->rx_dq.current++;
if (priv->rx_dq.current >= priv->rx_dq.end)
priv->rx_dq.current = priv->rx_dq.base;
/*
* Finally, return the RX descriptor and status entries
* back to the MAC engine, and loop again, checking for
* more descriptors to process.
*/
writel(1, &mac->rxdqenq);
writel(1, &mac->rxstsqenq);
} else {
len = 0;
}
debug("-ep93xx_eth_rcv_packet %d", len);
return len;
}
/**
* Send a block of data via ethernet.
*/
static int ep93xx_eth_send_packet(struct eth_device *dev,
void * const packet, int const length)
{
struct mac_regs *mac = GET_REGS(dev);
struct ep93xx_priv *priv = GET_PRIV(dev);
int ret = -1;
debug("+ep93xx_eth_send_packet");
/* Parameter check */
BUG_ON(packet == NULL);
/*
* Initialize the TX descriptor queue with the new packet's info.
* Clear the associated status queue entry. Enqueue the packet
* to the MAC for transmission.
*/
/* set buffer address */
priv->tx_dq.current->word1 = (uint32_t)packet;
/* set buffer length and EOF bit */
priv->tx_dq.current->word2 = length | TX_DESC_EOF;
/* clear tx status */
priv->tx_sq.current->word1 = 0;
/* enqueue the TX descriptor */
writel(1, &mac->txdqenq);
/* wait for the frame to become processed */
while (!TX_STATUS_TXFP(priv->tx_sq.current))
; /* noop */
if (!TX_STATUS_TXWE(priv->tx_sq.current)) {
error("packet tx error, status %08X",
priv->tx_sq.current->word1);
dump_tx_descriptor_queue(dev);
dump_tx_status_queue(dev);
/* TODO: Add better error handling? */
goto eth_send_out;
}
ret = 0;
/* Fall through */
eth_send_out:
debug("-ep93xx_eth_send_packet %d", ret);
return ret;
}
#if defined(CONFIG_MII)
int ep93xx_miiphy_initialize(bd_t * const bd)
{
miiphy_register("ep93xx_eth0", ep93xx_miiphy_read, ep93xx_miiphy_write);
return 0;
}
#endif
/**
* Initialize the EP93xx MAC. The MAC hardware is reset. Buffers are
* allocated, if necessary, for the TX and RX descriptor and status queues,
* as well as for received packets. The EP93XX MAC hardware is initialized.
* Transmit and receive operations are enabled.
*/
int ep93xx_eth_initialize(u8 dev_num, int base_addr)
{
int ret = -1;
struct eth_device *dev;
struct ep93xx_priv *priv;
debug("+ep93xx_eth_initialize");
priv = malloc(sizeof(*priv));
if (!priv) {
error("malloc() failed");
goto eth_init_failed_0;
}
memset(priv, 0, sizeof(*priv));
priv->regs = (struct mac_regs *)base_addr;
priv->tx_dq.base = calloc(NUMTXDESC,
sizeof(struct tx_descriptor));
if (priv->tx_dq.base == NULL) {
error("calloc() failed");
goto eth_init_failed_1;
}
priv->tx_sq.base = calloc(NUMTXDESC,
sizeof(struct tx_status));
if (priv->tx_sq.base == NULL) {
error("calloc() failed");
goto eth_init_failed_2;
}
priv->rx_dq.base = calloc(NUMRXDESC,
sizeof(struct rx_descriptor));
if (priv->rx_dq.base == NULL) {
error("calloc() failed");
goto eth_init_failed_3;
}
priv->rx_sq.base = calloc(NUMRXDESC,
sizeof(struct rx_status));
if (priv->rx_sq.base == NULL) {
error("calloc() failed");
goto eth_init_failed_4;
}
dev = malloc(sizeof *dev);
if (dev == NULL) {
error("malloc() failed");
goto eth_init_failed_5;
}
memset(dev, 0, sizeof *dev);
dev->iobase = base_addr;
dev->priv = priv;
dev->init = ep93xx_eth_open;
dev->halt = ep93xx_eth_close;
dev->send = ep93xx_eth_send_packet;
dev->recv = ep93xx_eth_rcv_packet;
sprintf(dev->name, "ep93xx_eth-%hu", dev_num);
eth_register(dev);
/* Done! */
ret = 1;
goto eth_init_done;
eth_init_failed_5:
free(priv->rx_sq.base);
/* Fall through */
eth_init_failed_4:
free(priv->rx_dq.base);
/* Fall through */
eth_init_failed_3:
free(priv->tx_sq.base);
/* Fall through */
eth_init_failed_2:
free(priv->tx_dq.base);
/* Fall through */
eth_init_failed_1:
free(priv);
/* Fall through */
eth_init_failed_0:
/* Fall through */
eth_init_done:
debug("-ep93xx_eth_initialize %d", ret);
return ret;
}
#if defined(CONFIG_MII)
/**
* Maximum MII address we support
*/
#define MII_ADDRESS_MAX 31
/**
* Maximum MII register address we support
*/
#define MII_REGISTER_MAX 31
/**
* Read a 16-bit value from an MII register.
*/
static int ep93xx_miiphy_read(const char * const dev, unsigned char const addr,
unsigned char const reg, unsigned short * const value)
{
struct mac_regs *mac = (struct mac_regs *)MAC_BASE;
int ret = -1;
uint32_t self_ctl;
debug("+ep93xx_miiphy_read");
/* Parameter checks */
BUG_ON(dev == NULL);
BUG_ON(addr > MII_ADDRESS_MAX);
BUG_ON(reg > MII_REGISTER_MAX);
BUG_ON(value == NULL);
/*
* Save the current SelfCTL register value. Set MAC to suppress
* preamble bits. Wait for any previous MII command to complete
* before issuing the new command.
*/
self_ctl = readl(&mac->selfctl);
#if defined(CONFIG_MII_SUPPRESS_PREAMBLE)
writel(self_ctl & ~(1 << 8), &mac->selfctl);
#endif /* defined(CONFIG_MII_SUPPRESS_PREAMBLE) */
while (readl(&mac->miists) & MIISTS_BUSY)
; /* noop */
/*
* Issue the MII 'read' command. Wait for the command to complete.
* Read the MII data value.
*/
writel(MIICMD_OPCODE_READ | ((uint32_t)addr << 5) | (uint32_t)reg,
&mac->miicmd);
while (readl(&mac->miists) & MIISTS_BUSY)
; /* noop */
*value = (unsigned short)readl(&mac->miidata);
/* Restore the saved SelfCTL value and return. */
writel(self_ctl, &mac->selfctl);
ret = 0;
/* Fall through */
debug("-ep93xx_miiphy_read");
return ret;
}
/**
* Write a 16-bit value to an MII register.
*/
static int ep93xx_miiphy_write(const char * const dev, unsigned char const addr,
unsigned char const reg, unsigned short const value)
{
struct mac_regs *mac = (struct mac_regs *)MAC_BASE;
int ret = -1;
uint32_t self_ctl;
debug("+ep93xx_miiphy_write");
/* Parameter checks */
BUG_ON(dev == NULL);
BUG_ON(addr > MII_ADDRESS_MAX);
BUG_ON(reg > MII_REGISTER_MAX);
/*
* Save the current SelfCTL register value. Set MAC to suppress
* preamble bits. Wait for any previous MII command to complete
* before issuing the new command.
*/
self_ctl = readl(&mac->selfctl);
#if defined(CONFIG_MII_SUPPRESS_PREAMBLE)
writel(self_ctl & ~(1 << 8), &mac->selfctl);
#endif /* defined(CONFIG_MII_SUPPRESS_PREAMBLE) */
while (readl(&mac->miists) & MIISTS_BUSY)
; /* noop */
/* Issue the MII 'write' command. Wait for the command to complete. */
writel((uint32_t)value, &mac->miidata);
writel(MIICMD_OPCODE_WRITE | ((uint32_t)addr << 5) | (uint32_t)reg,
&mac->miicmd);
while (readl(&mac->miists) & MIISTS_BUSY)
; /* noop */
/* Restore the saved SelfCTL value and return. */
writel(self_ctl, &mac->selfctl);
ret = 0;
/* Fall through */
debug("-ep93xx_miiphy_write");
return ret;
}
#endif /* defined(CONFIG_MII) */

View file

@ -0,0 +1,127 @@
/*
* Copyright (C) 2009 Matthias Kaehlcke <matthias@kaehlcke.net>
*
* Copyright (C) 2004, 2005
* Cory T. Tusar, Videon Central, Inc., <ctusar@videon-central.com>
*
* SPDX-License-Identifier: GPL-2.0+
*/
#ifndef _EP93XX_ETH_H
#define _EP93XX_ETH_H
#include <net.h>
/**
* #define this to dump device status and queue info during initialization and
* following errors.
*/
#undef EP93XX_MAC_DEBUG
/**
* Number of descriptor and status entries in our RX queues.
* It must be power of 2 !
*/
#define NUMRXDESC PKTBUFSRX
/**
* Number of descriptor and status entries in our TX queues.
*/
#define NUMTXDESC 1
/**
* 944 = (1024 - 64) - 16, Fifo size - Minframesize - 16 (Chip FACT)
*/
#define TXSTARTMAX 944
/**
* Receive descriptor queue entry
*/
struct rx_descriptor {
uint32_t word1;
uint32_t word2;
};
/**
* Receive status queue entry
*/
struct rx_status {
uint32_t word1;
uint32_t word2;
};
#define RX_STATUS_RWE(rx_status) ((rx_status->word1 >> 30) & 0x01)
#define RX_STATUS_RFP(rx_status) ((rx_status->word1 >> 31) & 0x01)
#define RX_STATUS_FRAME_LEN(rx_status) (rx_status->word2 & 0xFFFF)
/**
* Transmit descriptor queue entry
*/
struct tx_descriptor {
uint32_t word1;
uint32_t word2;
};
#define TX_DESC_EOF (1 << 31)
/**
* Transmit status queue entry
*/
struct tx_status {
uint32_t word1;
};
#define TX_STATUS_TXWE(tx_status) (((tx_status)->word1 >> 30) & 0x01)
#define TX_STATUS_TXFP(tx_status) (((tx_status)->word1 >> 31) & 0x01)
/**
* Transmit descriptor queue
*/
struct tx_descriptor_queue {
struct tx_descriptor *base;
struct tx_descriptor *current;
struct tx_descriptor *end;
};
/**
* Transmit status queue
*/
struct tx_status_queue {
struct tx_status *base;
volatile struct tx_status *current;
struct tx_status *end;
};
/**
* Receive descriptor queue
*/
struct rx_descriptor_queue {
struct rx_descriptor *base;
struct rx_descriptor *current;
struct rx_descriptor *end;
};
/**
* Receive status queue
*/
struct rx_status_queue {
struct rx_status *base;
volatile struct rx_status *current;
struct rx_status *end;
};
/**
* EP93xx MAC private data structure
*/
struct ep93xx_priv {
struct rx_descriptor_queue rx_dq;
struct rx_status_queue rx_sq;
void *rx_buffer[NUMRXDESC];
struct tx_descriptor_queue tx_dq;
struct tx_status_queue tx_sq;
struct mac_regs *regs;
};
#endif

View file

@ -0,0 +1,511 @@
/*
* Opencore 10/100 ethernet mac driver
*
* Copyright (C) 2007-2008 Avionic Design Development GmbH
* Copyright (C) 2008-2009 Avionic Design GmbH
* Thierry Reding <thierry.reding@avionic-design.de>
* Copyright (C) 2010 Thomas Chou <thomas@wytron.com.tw>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <common.h>
#include <command.h>
#include <malloc.h>
#include <net.h>
#include <miiphy.h>
#include <asm/io.h>
#include <asm/cache.h>
/* register offsets */
#define MODER 0x00
#define INT_SOURCE 0x04
#define INT_MASK 0x08
#define IPGT 0x0c
#define IPGR1 0x10
#define IPGR2 0x14
#define PACKETLEN 0x18
#define COLLCONF 0x1c
#define TX_BD_NUM 0x20
#define CTRLMODER 0x24
#define MIIMODER 0x28
#define MIICOMMAND 0x2c
#define MIIADDRESS 0x30
#define MIITX_DATA 0x34
#define MIIRX_DATA 0x38
#define MIISTATUS 0x3c
#define MAC_ADDR0 0x40
#define MAC_ADDR1 0x44
#define ETH_HASH0 0x48
#define ETH_HASH1 0x4c
#define ETH_TXCTRL 0x50
/* mode register */
#define MODER_RXEN (1 << 0) /* receive enable */
#define MODER_TXEN (1 << 1) /* transmit enable */
#define MODER_NOPRE (1 << 2) /* no preamble */
#define MODER_BRO (1 << 3) /* broadcast address */
#define MODER_IAM (1 << 4) /* individual address mode */
#define MODER_PRO (1 << 5) /* promiscuous mode */
#define MODER_IFG (1 << 6) /* interframe gap for incoming frames */
#define MODER_LOOP (1 << 7) /* loopback */
#define MODER_NBO (1 << 8) /* no back-off */
#define MODER_EDE (1 << 9) /* excess defer enable */
#define MODER_FULLD (1 << 10) /* full duplex */
#define MODER_RESET (1 << 11) /* FIXME: reset (undocumented) */
#define MODER_DCRC (1 << 12) /* delayed CRC enable */
#define MODER_CRC (1 << 13) /* CRC enable */
#define MODER_HUGE (1 << 14) /* huge packets enable */
#define MODER_PAD (1 << 15) /* padding enabled */
#define MODER_RSM (1 << 16) /* receive small packets */
/* interrupt source and mask registers */
#define INT_MASK_TXF (1 << 0) /* transmit frame */
#define INT_MASK_TXE (1 << 1) /* transmit error */
#define INT_MASK_RXF (1 << 2) /* receive frame */
#define INT_MASK_RXE (1 << 3) /* receive error */
#define INT_MASK_BUSY (1 << 4)
#define INT_MASK_TXC (1 << 5) /* transmit control frame */
#define INT_MASK_RXC (1 << 6) /* receive control frame */
#define INT_MASK_TX (INT_MASK_TXF | INT_MASK_TXE)
#define INT_MASK_RX (INT_MASK_RXF | INT_MASK_RXE)
#define INT_MASK_ALL ( \
INT_MASK_TXF | INT_MASK_TXE | \
INT_MASK_RXF | INT_MASK_RXE | \
INT_MASK_TXC | INT_MASK_RXC | \
INT_MASK_BUSY \
)
/* packet length register */
#define PACKETLEN_MIN(min) (((min) & 0xffff) << 16)
#define PACKETLEN_MAX(max) (((max) & 0xffff) << 0)
#define PACKETLEN_MIN_MAX(min, max) (PACKETLEN_MIN(min) | \
PACKETLEN_MAX(max))
/* transmit buffer number register */
#define TX_BD_NUM_VAL(x) (((x) <= 0x80) ? (x) : 0x80)
/* control module mode register */
#define CTRLMODER_PASSALL (1 << 0) /* pass all receive frames */
#define CTRLMODER_RXFLOW (1 << 1) /* receive control flow */
#define CTRLMODER_TXFLOW (1 << 2) /* transmit control flow */
/* MII mode register */
#define MIIMODER_CLKDIV(x) ((x) & 0xfe) /* needs to be an even number */
#define MIIMODER_NOPRE (1 << 8) /* no preamble */
/* MII command register */
#define MIICOMMAND_SCAN (1 << 0) /* scan status */
#define MIICOMMAND_READ (1 << 1) /* read status */
#define MIICOMMAND_WRITE (1 << 2) /* write control data */
/* MII address register */
#define MIIADDRESS_FIAD(x) (((x) & 0x1f) << 0)
#define MIIADDRESS_RGAD(x) (((x) & 0x1f) << 8)
#define MIIADDRESS_ADDR(phy, reg) (MIIADDRESS_FIAD(phy) | \
MIIADDRESS_RGAD(reg))
/* MII transmit data register */
#define MIITX_DATA_VAL(x) ((x) & 0xffff)
/* MII receive data register */
#define MIIRX_DATA_VAL(x) ((x) & 0xffff)
/* MII status register */
#define MIISTATUS_LINKFAIL (1 << 0)
#define MIISTATUS_BUSY (1 << 1)
#define MIISTATUS_INVALID (1 << 2)
/* TX buffer descriptor */
#define TX_BD_CS (1 << 0) /* carrier sense lost */
#define TX_BD_DF (1 << 1) /* defer indication */
#define TX_BD_LC (1 << 2) /* late collision */
#define TX_BD_RL (1 << 3) /* retransmission limit */
#define TX_BD_RETRY_MASK (0x00f0)
#define TX_BD_RETRY(x) (((x) & 0x00f0) >> 4)
#define TX_BD_UR (1 << 8) /* transmitter underrun */
#define TX_BD_CRC (1 << 11) /* TX CRC enable */
#define TX_BD_PAD (1 << 12) /* pad enable */
#define TX_BD_WRAP (1 << 13)
#define TX_BD_IRQ (1 << 14) /* interrupt request enable */
#define TX_BD_READY (1 << 15) /* TX buffer ready */
#define TX_BD_LEN(x) (((x) & 0xffff) << 16)
#define TX_BD_LEN_MASK (0xffff << 16)
#define TX_BD_STATS (TX_BD_CS | TX_BD_DF | TX_BD_LC | \
TX_BD_RL | TX_BD_RETRY_MASK | TX_BD_UR)
/* RX buffer descriptor */
#define RX_BD_LC (1 << 0) /* late collision */
#define RX_BD_CRC (1 << 1) /* RX CRC error */
#define RX_BD_SF (1 << 2) /* short frame */
#define RX_BD_TL (1 << 3) /* too long */
#define RX_BD_DN (1 << 4) /* dribble nibble */
#define RX_BD_IS (1 << 5) /* invalid symbol */
#define RX_BD_OR (1 << 6) /* receiver overrun */
#define RX_BD_MISS (1 << 7)
#define RX_BD_CF (1 << 8) /* control frame */
#define RX_BD_WRAP (1 << 13)
#define RX_BD_IRQ (1 << 14) /* interrupt request enable */
#define RX_BD_EMPTY (1 << 15)
#define RX_BD_LEN(x) (((x) & 0xffff) << 16)
#define RX_BD_STATS (RX_BD_LC | RX_BD_CRC | RX_BD_SF | RX_BD_TL | \
RX_BD_DN | RX_BD_IS | RX_BD_OR | RX_BD_MISS)
#define ETHOC_BUFSIZ 1536
#define ETHOC_ZLEN 64
#define ETHOC_BD_BASE 0x400
#define ETHOC_TIMEOUT (HZ / 2)
#define ETHOC_MII_TIMEOUT (1 + (HZ / 5))
/**
* struct ethoc - driver-private device structure
* @num_tx: number of send buffers
* @cur_tx: last send buffer written
* @dty_tx: last buffer actually sent
* @num_rx: number of receive buffers
* @cur_rx: current receive buffer
*/
struct ethoc {
u32 num_tx;
u32 cur_tx;
u32 dty_tx;
u32 num_rx;
u32 cur_rx;
};
/**
* struct ethoc_bd - buffer descriptor
* @stat: buffer statistics
* @addr: physical memory address
*/
struct ethoc_bd {
u32 stat;
u32 addr;
};
static inline u32 ethoc_read(struct eth_device *dev, size_t offset)
{
return readl(dev->iobase + offset);
}
static inline void ethoc_write(struct eth_device *dev, size_t offset, u32 data)
{
writel(data, dev->iobase + offset);
}
static inline void ethoc_read_bd(struct eth_device *dev, int index,
struct ethoc_bd *bd)
{
size_t offset = ETHOC_BD_BASE + (index * sizeof(struct ethoc_bd));
bd->stat = ethoc_read(dev, offset + 0);
bd->addr = ethoc_read(dev, offset + 4);
}
static inline void ethoc_write_bd(struct eth_device *dev, int index,
const struct ethoc_bd *bd)
{
size_t offset = ETHOC_BD_BASE + (index * sizeof(struct ethoc_bd));
ethoc_write(dev, offset + 0, bd->stat);
ethoc_write(dev, offset + 4, bd->addr);
}
static int ethoc_set_mac_address(struct eth_device *dev)
{
u8 *mac = dev->enetaddr;
ethoc_write(dev, MAC_ADDR0, (mac[2] << 24) | (mac[3] << 16) |
(mac[4] << 8) | (mac[5] << 0));
ethoc_write(dev, MAC_ADDR1, (mac[0] << 8) | (mac[1] << 0));
return 0;
}
static inline void ethoc_ack_irq(struct eth_device *dev, u32 mask)
{
ethoc_write(dev, INT_SOURCE, mask);
}
static inline void ethoc_enable_rx_and_tx(struct eth_device *dev)
{
u32 mode = ethoc_read(dev, MODER);
mode |= MODER_RXEN | MODER_TXEN;
ethoc_write(dev, MODER, mode);
}
static inline void ethoc_disable_rx_and_tx(struct eth_device *dev)
{
u32 mode = ethoc_read(dev, MODER);
mode &= ~(MODER_RXEN | MODER_TXEN);
ethoc_write(dev, MODER, mode);
}
static int ethoc_init_ring(struct eth_device *dev)
{
struct ethoc *priv = (struct ethoc *)dev->priv;
struct ethoc_bd bd;
int i;
priv->cur_tx = 0;
priv->dty_tx = 0;
priv->cur_rx = 0;
/* setup transmission buffers */
bd.stat = TX_BD_IRQ | TX_BD_CRC;
for (i = 0; i < priv->num_tx; i++) {
if (i == priv->num_tx - 1)
bd.stat |= TX_BD_WRAP;
ethoc_write_bd(dev, i, &bd);
}
bd.stat = RX_BD_EMPTY | RX_BD_IRQ;
for (i = 0; i < priv->num_rx; i++) {
bd.addr = (u32)net_rx_packets[i];
if (i == priv->num_rx - 1)
bd.stat |= RX_BD_WRAP;
flush_dcache_range(bd.addr, bd.addr + PKTSIZE_ALIGN);
ethoc_write_bd(dev, priv->num_tx + i, &bd);
}
return 0;
}
static int ethoc_reset(struct eth_device *dev)
{
u32 mode;
/* TODO: reset controller? */
ethoc_disable_rx_and_tx(dev);
/* TODO: setup registers */
/* enable FCS generation and automatic padding */
mode = ethoc_read(dev, MODER);
mode |= MODER_CRC | MODER_PAD;
ethoc_write(dev, MODER, mode);
/* set full-duplex mode */
mode = ethoc_read(dev, MODER);
mode |= MODER_FULLD;
ethoc_write(dev, MODER, mode);
ethoc_write(dev, IPGT, 0x15);
ethoc_ack_irq(dev, INT_MASK_ALL);
ethoc_enable_rx_and_tx(dev);
return 0;
}
static int ethoc_init(struct eth_device *dev, bd_t * bd)
{
struct ethoc *priv = (struct ethoc *)dev->priv;
printf("ethoc\n");
priv->num_tx = 1;
priv->num_rx = PKTBUFSRX;
ethoc_write(dev, TX_BD_NUM, priv->num_tx);
ethoc_init_ring(dev);
ethoc_reset(dev);
return 0;
}
static int ethoc_update_rx_stats(struct ethoc_bd *bd)
{
int ret = 0;
if (bd->stat & RX_BD_TL) {
debug("ETHOC: " "RX: frame too long\n");
ret++;
}
if (bd->stat & RX_BD_SF) {
debug("ETHOC: " "RX: frame too short\n");
ret++;
}
if (bd->stat & RX_BD_DN)
debug("ETHOC: " "RX: dribble nibble\n");
if (bd->stat & RX_BD_CRC) {
debug("ETHOC: " "RX: wrong CRC\n");
ret++;
}
if (bd->stat & RX_BD_OR) {
debug("ETHOC: " "RX: overrun\n");
ret++;
}
if (bd->stat & RX_BD_LC) {
debug("ETHOC: " "RX: late collision\n");
ret++;
}
return ret;
}
static int ethoc_rx(struct eth_device *dev, int limit)
{
struct ethoc *priv = (struct ethoc *)dev->priv;
int count;
for (count = 0; count < limit; ++count) {
u32 entry;
struct ethoc_bd bd;
entry = priv->num_tx + (priv->cur_rx % priv->num_rx);
ethoc_read_bd(dev, entry, &bd);
if (bd.stat & RX_BD_EMPTY)
break;
debug("%s(): RX buffer %d, %x received\n",
__func__, priv->cur_rx, bd.stat);
if (ethoc_update_rx_stats(&bd) == 0) {
int size = bd.stat >> 16;
size -= 4; /* strip the CRC */
net_process_received_packet((void *)bd.addr, size);
}
/* clear the buffer descriptor so it can be reused */
flush_dcache_range(bd.addr, bd.addr + PKTSIZE_ALIGN);
bd.stat &= ~RX_BD_STATS;
bd.stat |= RX_BD_EMPTY;
ethoc_write_bd(dev, entry, &bd);
priv->cur_rx++;
}
return count;
}
static int ethoc_update_tx_stats(struct ethoc_bd *bd)
{
if (bd->stat & TX_BD_LC)
debug("ETHOC: " "TX: late collision\n");
if (bd->stat & TX_BD_RL)
debug("ETHOC: " "TX: retransmit limit\n");
if (bd->stat & TX_BD_UR)
debug("ETHOC: " "TX: underrun\n");
if (bd->stat & TX_BD_CS)
debug("ETHOC: " "TX: carrier sense lost\n");
return 0;
}
static void ethoc_tx(struct eth_device *dev)
{
struct ethoc *priv = (struct ethoc *)dev->priv;
u32 entry = priv->dty_tx % priv->num_tx;
struct ethoc_bd bd;
ethoc_read_bd(dev, entry, &bd);
if ((bd.stat & TX_BD_READY) == 0)
(void)ethoc_update_tx_stats(&bd);
}
static int ethoc_send(struct eth_device *dev, void *packet, int length)
{
struct ethoc *priv = (struct ethoc *)dev->priv;
struct ethoc_bd bd;
u32 entry;
u32 pending;
int tmo;
entry = priv->cur_tx % priv->num_tx;
ethoc_read_bd(dev, entry, &bd);
if (unlikely(length < ETHOC_ZLEN))
bd.stat |= TX_BD_PAD;
else
bd.stat &= ~TX_BD_PAD;
bd.addr = (u32)packet;
flush_dcache_range(bd.addr, bd.addr + length);
bd.stat &= ~(TX_BD_STATS | TX_BD_LEN_MASK);
bd.stat |= TX_BD_LEN(length);
ethoc_write_bd(dev, entry, &bd);
/* start transmit */
bd.stat |= TX_BD_READY;
ethoc_write_bd(dev, entry, &bd);
/* wait for transfer to succeed */
tmo = get_timer(0) + 5 * CONFIG_SYS_HZ;
while (1) {
pending = ethoc_read(dev, INT_SOURCE);
ethoc_ack_irq(dev, pending & ~INT_MASK_RX);
if (pending & INT_MASK_BUSY)
debug("%s(): packet dropped\n", __func__);
if (pending & INT_MASK_TX) {
ethoc_tx(dev);
break;
}
if (get_timer(0) >= tmo) {
debug("%s(): timed out\n", __func__);
return -1;
}
}
debug("%s(): packet sent\n", __func__);
return 0;
}
static void ethoc_halt(struct eth_device *dev)
{
ethoc_disable_rx_and_tx(dev);
}
static int ethoc_recv(struct eth_device *dev)
{
u32 pending;
pending = ethoc_read(dev, INT_SOURCE);
ethoc_ack_irq(dev, pending);
if (pending & INT_MASK_BUSY)
debug("%s(): packet dropped\n", __func__);
if (pending & INT_MASK_RX) {
debug("%s(): rx irq\n", __func__);
ethoc_rx(dev, PKTBUFSRX);
}
return 0;
}
int ethoc_initialize(u8 dev_num, int base_addr)
{
struct ethoc *priv;
struct eth_device *dev;
priv = malloc(sizeof(*priv));
if (!priv)
return 0;
dev = malloc(sizeof(*dev));
if (!dev) {
free(priv);
return 0;
}
memset(dev, 0, sizeof(*dev));
dev->priv = priv;
dev->iobase = base_addr;
dev->init = ethoc_init;
dev->halt = ethoc_halt;
dev->send = ethoc_send;
dev->recv = ethoc_recv;
dev->write_hwaddr = ethoc_set_mac_address;
sprintf(dev->name, "%s-%hu", "ETHOC", dev_num);
eth_register(dev);
return 1;
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,323 @@
/*
* (C) Copyright 2009 Ilya Yanok, Emcraft Systems Ltd <yanok@emcraft.com>
* (C) Copyright 2008 Armadeus Systems, nc
* (C) Copyright 2008 Eric Jarrige <eric.jarrige@armadeus.org>
* (C) Copyright 2007 Pengutronix, Sascha Hauer <s.hauer@pengutronix.de>
* (C) Copyright 2007 Pengutronix, Juergen Beisert <j.beisert@pengutronix.de>
*
* (C) Copyright 2003
* Wolfgang Denk, DENX Software Engineering, wd@denx.de.
*
* This file is based on mpc4200fec.h
* (C) Copyright Motorola, Inc., 2000
*
* SPDX-License-Identifier: GPL-2.0+
*/
#ifndef __FEC_MXC_H
#define __FEC_MXC_H
void imx_get_mac_from_fuse(int dev_id, unsigned char *mac);
/**
* Layout description of the FEC
*/
struct ethernet_regs {
/* [10:2]addr = 00 */
/* Control and status Registers (offset 000-1FF) */
uint32_t res0[1]; /* MBAR_ETH + 0x000 */
uint32_t ievent; /* MBAR_ETH + 0x004 */
uint32_t imask; /* MBAR_ETH + 0x008 */
uint32_t res1[1]; /* MBAR_ETH + 0x00C */
uint32_t r_des_active; /* MBAR_ETH + 0x010 */
uint32_t x_des_active; /* MBAR_ETH + 0x014 */
uint32_t res2[3]; /* MBAR_ETH + 0x018-20 */
uint32_t ecntrl; /* MBAR_ETH + 0x024 */
uint32_t res3[6]; /* MBAR_ETH + 0x028-03C */
uint32_t mii_data; /* MBAR_ETH + 0x040 */
uint32_t mii_speed; /* MBAR_ETH + 0x044 */
uint32_t res4[7]; /* MBAR_ETH + 0x048-60 */
uint32_t mib_control; /* MBAR_ETH + 0x064 */
uint32_t res5[7]; /* MBAR_ETH + 0x068-80 */
uint32_t r_cntrl; /* MBAR_ETH + 0x084 */
uint32_t res6[15]; /* MBAR_ETH + 0x088-C0 */
uint32_t x_cntrl; /* MBAR_ETH + 0x0C4 */
uint32_t res7[7]; /* MBAR_ETH + 0x0C8-E0 */
uint32_t paddr1; /* MBAR_ETH + 0x0E4 */
uint32_t paddr2; /* MBAR_ETH + 0x0E8 */
uint32_t op_pause; /* MBAR_ETH + 0x0EC */
uint32_t res8[10]; /* MBAR_ETH + 0x0F0-114 */
uint32_t iaddr1; /* MBAR_ETH + 0x118 */
uint32_t iaddr2; /* MBAR_ETH + 0x11C */
uint32_t gaddr1; /* MBAR_ETH + 0x120 */
uint32_t gaddr2; /* MBAR_ETH + 0x124 */
uint32_t res9[7]; /* MBAR_ETH + 0x128-140 */
uint32_t x_wmrk; /* MBAR_ETH + 0x144 */
uint32_t res10[1]; /* MBAR_ETH + 0x148 */
uint32_t r_bound; /* MBAR_ETH + 0x14C */
uint32_t r_fstart; /* MBAR_ETH + 0x150 */
uint32_t res11[11]; /* MBAR_ETH + 0x154-17C */
uint32_t erdsr; /* MBAR_ETH + 0x180 */
uint32_t etdsr; /* MBAR_ETH + 0x184 */
uint32_t emrbr; /* MBAR_ETH + 0x188 */
uint32_t res12[29]; /* MBAR_ETH + 0x18C-1FC */
/* MIB COUNTERS (Offset 200-2FF) */
uint32_t rmon_t_drop; /* MBAR_ETH + 0x200 */
uint32_t rmon_t_packets; /* MBAR_ETH + 0x204 */
uint32_t rmon_t_bc_pkt; /* MBAR_ETH + 0x208 */
uint32_t rmon_t_mc_pkt; /* MBAR_ETH + 0x20C */
uint32_t rmon_t_crc_align; /* MBAR_ETH + 0x210 */
uint32_t rmon_t_undersize; /* MBAR_ETH + 0x214 */
uint32_t rmon_t_oversize; /* MBAR_ETH + 0x218 */
uint32_t rmon_t_frag; /* MBAR_ETH + 0x21C */
uint32_t rmon_t_jab; /* MBAR_ETH + 0x220 */
uint32_t rmon_t_col; /* MBAR_ETH + 0x224 */
uint32_t rmon_t_p64; /* MBAR_ETH + 0x228 */
uint32_t rmon_t_p65to127; /* MBAR_ETH + 0x22C */
uint32_t rmon_t_p128to255; /* MBAR_ETH + 0x230 */
uint32_t rmon_t_p256to511; /* MBAR_ETH + 0x234 */
uint32_t rmon_t_p512to1023; /* MBAR_ETH + 0x238 */
uint32_t rmon_t_p1024to2047; /* MBAR_ETH + 0x23C */
uint32_t rmon_t_p_gte2048; /* MBAR_ETH + 0x240 */
uint32_t rmon_t_octets; /* MBAR_ETH + 0x244 */
uint32_t ieee_t_drop; /* MBAR_ETH + 0x248 */
uint32_t ieee_t_frame_ok; /* MBAR_ETH + 0x24C */
uint32_t ieee_t_1col; /* MBAR_ETH + 0x250 */
uint32_t ieee_t_mcol; /* MBAR_ETH + 0x254 */
uint32_t ieee_t_def; /* MBAR_ETH + 0x258 */
uint32_t ieee_t_lcol; /* MBAR_ETH + 0x25C */
uint32_t ieee_t_excol; /* MBAR_ETH + 0x260 */
uint32_t ieee_t_macerr; /* MBAR_ETH + 0x264 */
uint32_t ieee_t_cserr; /* MBAR_ETH + 0x268 */
uint32_t ieee_t_sqe; /* MBAR_ETH + 0x26C */
uint32_t t_fdxfc; /* MBAR_ETH + 0x270 */
uint32_t ieee_t_octets_ok; /* MBAR_ETH + 0x274 */
uint32_t res13[2]; /* MBAR_ETH + 0x278-27C */
uint32_t rmon_r_drop; /* MBAR_ETH + 0x280 */
uint32_t rmon_r_packets; /* MBAR_ETH + 0x284 */
uint32_t rmon_r_bc_pkt; /* MBAR_ETH + 0x288 */
uint32_t rmon_r_mc_pkt; /* MBAR_ETH + 0x28C */
uint32_t rmon_r_crc_align; /* MBAR_ETH + 0x290 */
uint32_t rmon_r_undersize; /* MBAR_ETH + 0x294 */
uint32_t rmon_r_oversize; /* MBAR_ETH + 0x298 */
uint32_t rmon_r_frag; /* MBAR_ETH + 0x29C */
uint32_t rmon_r_jab; /* MBAR_ETH + 0x2A0 */
uint32_t rmon_r_resvd_0; /* MBAR_ETH + 0x2A4 */
uint32_t rmon_r_p64; /* MBAR_ETH + 0x2A8 */
uint32_t rmon_r_p65to127; /* MBAR_ETH + 0x2AC */
uint32_t rmon_r_p128to255; /* MBAR_ETH + 0x2B0 */
uint32_t rmon_r_p256to511; /* MBAR_ETH + 0x2B4 */
uint32_t rmon_r_p512to1023; /* MBAR_ETH + 0x2B8 */
uint32_t rmon_r_p1024to2047; /* MBAR_ETH + 0x2BC */
uint32_t rmon_r_p_gte2048; /* MBAR_ETH + 0x2C0 */
uint32_t rmon_r_octets; /* MBAR_ETH + 0x2C4 */
uint32_t ieee_r_drop; /* MBAR_ETH + 0x2C8 */
uint32_t ieee_r_frame_ok; /* MBAR_ETH + 0x2CC */
uint32_t ieee_r_crc; /* MBAR_ETH + 0x2D0 */
uint32_t ieee_r_align; /* MBAR_ETH + 0x2D4 */
uint32_t r_macerr; /* MBAR_ETH + 0x2D8 */
uint32_t r_fdxfc; /* MBAR_ETH + 0x2DC */
uint32_t ieee_r_octets_ok; /* MBAR_ETH + 0x2E0 */
uint32_t res14[7]; /* MBAR_ETH + 0x2E4-2FC */
#if defined(CONFIG_MX25) || defined(CONFIG_MX53) || defined(CONFIG_MX6SL)
uint16_t miigsk_cfgr; /* MBAR_ETH + 0x300 */
uint16_t res15[3]; /* MBAR_ETH + 0x302-306 */
uint16_t miigsk_enr; /* MBAR_ETH + 0x308 */
uint16_t res16[3]; /* MBAR_ETH + 0x30a-30e */
uint32_t res17[60]; /* MBAR_ETH + 0x300-3FF */
#else
uint32_t res15[64]; /* MBAR_ETH + 0x300-3FF */
#endif
};
#define FEC_IEVENT_HBERR 0x80000000
#define FEC_IEVENT_BABR 0x40000000
#define FEC_IEVENT_BABT 0x20000000
#define FEC_IEVENT_GRA 0x10000000
#define FEC_IEVENT_TXF 0x08000000
#define FEC_IEVENT_TXB 0x04000000
#define FEC_IEVENT_RXF 0x02000000
#define FEC_IEVENT_RXB 0x01000000
#define FEC_IEVENT_MII 0x00800000
#define FEC_IEVENT_EBERR 0x00400000
#define FEC_IEVENT_LC 0x00200000
#define FEC_IEVENT_RL 0x00100000
#define FEC_IEVENT_UN 0x00080000
#define FEC_IMASK_HBERR 0x80000000
#define FEC_IMASK_BABR 0x40000000
#define FEC_IMASKT_BABT 0x20000000
#define FEC_IMASK_GRA 0x10000000
#define FEC_IMASKT_TXF 0x08000000
#define FEC_IMASK_TXB 0x04000000
#define FEC_IMASKT_RXF 0x02000000
#define FEC_IMASK_RXB 0x01000000
#define FEC_IMASK_MII 0x00800000
#define FEC_IMASK_EBERR 0x00400000
#define FEC_IMASK_LC 0x00200000
#define FEC_IMASKT_RL 0x00100000
#define FEC_IMASK_UN 0x00080000
#define FEC_RCNTRL_MAX_FL_SHIFT 16
#define FEC_RCNTRL_LOOP 0x00000001
#define FEC_RCNTRL_DRT 0x00000002
#define FEC_RCNTRL_MII_MODE 0x00000004
#define FEC_RCNTRL_PROM 0x00000008
#define FEC_RCNTRL_BC_REJ 0x00000010
#define FEC_RCNTRL_FCE 0x00000020
#define FEC_RCNTRL_RGMII 0x00000040
#define FEC_RCNTRL_RMII 0x00000100
#define FEC_RCNTRL_RMII_10T 0x00000200
#define FEC_TCNTRL_GTS 0x00000001
#define FEC_TCNTRL_HBC 0x00000002
#define FEC_TCNTRL_FDEN 0x00000004
#define FEC_TCNTRL_TFC_PAUSE 0x00000008
#define FEC_TCNTRL_RFC_PAUSE 0x00000010
#define FEC_ECNTRL_RESET 0x00000001 /* reset the FEC */
#define FEC_ECNTRL_ETHER_EN 0x00000002 /* enable the FEC */
#define FEC_ECNTRL_SPEED 0x00000020
#define FEC_ECNTRL_DBSWAP 0x00000100
#define FEC_X_WMRK_STRFWD 0x00000100
#define FEC_X_DES_ACTIVE_TDAR 0x01000000
#define FEC_R_DES_ACTIVE_RDAR 0x01000000
#if defined(CONFIG_MX25) || defined(CONFIG_MX53) || defined(CONFIG_MX6SL)
/* defines for MIIGSK */
/* RMII frequency control: 0=50MHz, 1=5MHz */
#define MIIGSK_CFGR_FRCONT (1 << 6)
/* loopback mode */
#define MIIGSK_CFGR_LBMODE (1 << 4)
/* echo mode */
#define MIIGSK_CFGR_EMODE (1 << 3)
/* MII gasket mode field */
#define MIIGSK_CFGR_IF_MODE_MASK (3 << 0)
/* MMI/7-Wire mode */
#define MIIGSK_CFGR_IF_MODE_MII (0 << 0)
/* RMII mode */
#define MIIGSK_CFGR_IF_MODE_RMII (1 << 0)
/* reflects MIIGSK Enable bit (RO) */
#define MIIGSK_ENR_READY (1 << 2)
/* enable MIGSK (set by default) */
#define MIIGSK_ENR_EN (1 << 1)
#endif
/**
* @brief Receive & Transmit Buffer Descriptor definitions
*
* Note: The first BD must be aligned (see DB_ALIGNMENT)
*/
struct fec_bd {
uint16_t data_length; /* payload's length in bytes */
uint16_t status; /* BD's staus (see datasheet) */
uint32_t data_pointer; /* payload's buffer address */
};
/**
* Supported phy types on this platform
*/
enum xceiver_type {
SEVENWIRE, /* 7-wire */
MII10, /* MII 10Mbps */
MII100, /* MII 100Mbps */
RMII, /* RMII */
RGMII, /* RGMII */
};
/**
* @brief i.MX27-FEC private structure
*/
struct fec_priv {
struct ethernet_regs *eth; /* pointer to register'S base */
enum xceiver_type xcv_type; /* transceiver type */
struct fec_bd *rbd_base; /* RBD ring */
int rbd_index; /* next receive BD to read */
struct fec_bd *tbd_base; /* TBD ring */
int tbd_index; /* next transmit BD to write */
bd_t *bd;
uint8_t *tdb_ptr;
int dev_id;
struct mii_dev *bus;
#ifdef CONFIG_PHYLIB
struct phy_device *phydev;
#else
int phy_id;
int (*mii_postcall)(int);
#endif
};
/**
* @brief Numbers of buffer descriptors for receiving
*
* The number defines the stocked memory buffers for the receiving task.
* Larger values makes no sense in this limited environment.
*/
#define FEC_RBD_NUM 64
/**
* @brief Define the ethernet packet size limit in memory
*
* Note: Do not shrink this number. This will force the FEC to spread larger
* frames in more than one BD. This is nothing to worry about, but the current
* driver can't handle it.
*/
#define FEC_MAX_PKT_SIZE 1536
/* Receive BD status bits */
#define FEC_RBD_EMPTY 0x8000 /* Receive BD status: Buffer is empty */
#define FEC_RBD_WRAP 0x2000 /* Receive BD status: Last BD in ring */
/* Receive BD status: Buffer is last in frame (useless here!) */
#define FEC_RBD_LAST 0x0800
#define FEC_RBD_MISS 0x0100 /* Receive BD status: Miss bit for prom mode */
/* Receive BD status: The received frame is broadcast frame */
#define FEC_RBD_BC 0x0080
/* Receive BD status: The received frame is multicast frame */
#define FEC_RBD_MC 0x0040
#define FEC_RBD_LG 0x0020 /* Receive BD status: Frame length violation */
#define FEC_RBD_NO 0x0010 /* Receive BD status: Nonoctet align frame */
#define FEC_RBD_CR 0x0004 /* Receive BD status: CRC error */
#define FEC_RBD_OV 0x0002 /* Receive BD status: Receive FIFO overrun */
#define FEC_RBD_TR 0x0001 /* Receive BD status: Frame is truncated */
#define FEC_RBD_ERR (FEC_RBD_LG | FEC_RBD_NO | FEC_RBD_CR | \
FEC_RBD_OV | FEC_RBD_TR)
/* Transmit BD status bits */
#define FEC_TBD_READY 0x8000 /* Tansmit BD status: Buffer is ready */
#define FEC_TBD_WRAP 0x2000 /* Tansmit BD status: Mark as last BD in ring */
#define FEC_TBD_LAST 0x0800 /* Tansmit BD status: Buffer is last in frame */
#define FEC_TBD_TC 0x0400 /* Tansmit BD status: Transmit the CRC */
#define FEC_TBD_ABC 0x0200 /* Tansmit BD status: Append bad CRC */
/* MII-related definitios */
#define FEC_MII_DATA_ST 0x40000000 /* Start of frame delimiter */
#define FEC_MII_DATA_OP_RD 0x20000000 /* Perform a read operation */
#define FEC_MII_DATA_OP_WR 0x10000000 /* Perform a write operation */
#define FEC_MII_DATA_PA_MSK 0x0f800000 /* PHY Address field mask */
#define FEC_MII_DATA_RA_MSK 0x007c0000 /* PHY Register field mask */
#define FEC_MII_DATA_TA 0x00020000 /* Turnaround */
#define FEC_MII_DATA_DATAMSK 0x0000ffff /* PHY data field */
#define FEC_MII_DATA_RA_SHIFT 18 /* MII Register address bits */
#define FEC_MII_DATA_PA_SHIFT 23 /* MII PHY address bits */
#endif /* __FEC_MXC_H */

View file

@ -0,0 +1,40 @@
#
# Copyright 2009-2011 Freescale Semiconductor, Inc.
#
# SPDX-License-Identifier: GPL-2.0+
#
obj-y += dtsec.o
obj-y += eth.o
obj-y += fm.o
obj-y += init.o
obj-y += tgec.o
obj-y += tgec_phy.o
# Soc have FMAN v3 with mEMAC
obj-$(CONFIG_SYS_FMAN_V3) += memac_phy.o
obj-$(CONFIG_SYS_FMAN_V3) += memac.o
# SoC specific SERDES support
obj-$(CONFIG_P1017) += p1023.o
obj-$(CONFIG_P1023) += p1023.o
# The P204x, P304x, and P5020 are the same
obj-$(CONFIG_PPC_P2041) += p5020.o
obj-$(CONFIG_PPC_P3041) += p5020.o
obj-$(CONFIG_PPC_P4080) += p4080.o
obj-$(CONFIG_PPC_P5020) += p5020.o
obj-$(CONFIG_PPC_P5040) += p5040.o
obj-$(CONFIG_PPC_T1040) += t1040.o
obj-$(CONFIG_PPC_T1042) += t1040.o
obj-$(CONFIG_PPC_T1020) += t1040.o
obj-$(CONFIG_PPC_T1022) += t1040.o
obj-$(CONFIG_PPC_T1023) += t1024.o
obj-$(CONFIG_PPC_T1024) += t1024.o
obj-$(CONFIG_PPC_T2080) += t2080.o
obj-$(CONFIG_PPC_T2081) += t2080.o
obj-$(CONFIG_PPC_T4240) += t4240.o
obj-$(CONFIG_PPC_T4160) += t4240.o
obj-$(CONFIG_PPC_T4080) += t4240.o
obj-$(CONFIG_PPC_B4420) += b4860.o
obj-$(CONFIG_PPC_B4860) += b4860.o
obj-$(CONFIG_LS1043A) += ls1043.o

View file

@ -0,0 +1,137 @@
/*
* Copyright 2012 Freescale Semiconductor, Inc.
* Roy Zang <tie-fei.zang@freescale.com>
*
* SPDX-License-Identifier: GPL-2.0+
*/
#include <common.h>
#include <phy.h>
#include <fm_eth.h>
#include <asm/io.h>
#include <asm/immap_85xx.h>
#include <asm/fsl_serdes.h>
#include <hwconfig.h>
u32 port_to_devdisr[] = {
[FM1_DTSEC1] = FSL_CORENET_DEVDISR2_DTSEC1_1,
[FM1_DTSEC2] = FSL_CORENET_DEVDISR2_DTSEC1_2,
[FM1_DTSEC3] = FSL_CORENET_DEVDISR2_DTSEC1_3,
[FM1_DTSEC4] = FSL_CORENET_DEVDISR2_DTSEC1_4,
[FM1_DTSEC5] = FSL_CORENET_DEVDISR2_DTSEC1_5,
[FM1_DTSEC6] = FSL_CORENET_DEVDISR2_DTSEC1_6,
[FM1_10GEC1] = FSL_CORENET_DEVDISR2_10GEC1_1,
[FM1_10GEC2] = FSL_CORENET_DEVDISR2_10GEC1_2,
};
static int is_device_disabled(enum fm_port port)
{
ccsr_gur_t *gur = (void *)(CONFIG_SYS_MPC85xx_GUTS_ADDR);
u32 devdisr2 = in_be32(&gur->devdisr2);
return port_to_devdisr[port] & devdisr2;
}
void fman_disable_port(enum fm_port port)
{
ccsr_gur_t *gur = (void *)(CONFIG_SYS_MPC85xx_GUTS_ADDR);
setbits_be32(&gur->devdisr2, port_to_devdisr[port]);
}
void fman_enable_port(enum fm_port port)
{
ccsr_gur_t *gur = (void *)(CONFIG_SYS_MPC85xx_GUTS_ADDR);
clrbits_be32(&gur->devdisr2, port_to_devdisr[port]);
}
phy_interface_t fman_port_enet_if(enum fm_port port)
{
#if defined(CONFIG_B4860QDS)
u32 serdes2_prtcl;
char buffer[HWCONFIG_BUFFER_SIZE];
char *buf = NULL;
ccsr_gur_t *gur = (void *)(CONFIG_SYS_MPC85xx_GUTS_ADDR);
#endif
if (is_device_disabled(port))
return PHY_INTERFACE_MODE_NONE;
/*B4860 has two 10Gig Mac*/
if ((port == FM1_10GEC1 || port == FM1_10GEC2) &&
((is_serdes_configured(XAUI_FM1_MAC9)) ||
#if !defined(CONFIG_B4860QDS)
(is_serdes_configured(XFI_FM1_MAC9)) ||
(is_serdes_configured(XFI_FM1_MAC10)) ||
#endif
(is_serdes_configured(XAUI_FM1_MAC10))
))
return PHY_INTERFACE_MODE_XGMII;
#if defined(CONFIG_B4860QDS)
serdes2_prtcl = in_be32(&gur->rcwsr[4]) &
FSL_CORENET2_RCWSR4_SRDS2_PRTCL;
if (serdes2_prtcl) {
serdes2_prtcl >>= FSL_CORENET2_RCWSR4_SRDS2_PRTCL_SHIFT;
switch (serdes2_prtcl) {
case 0x80:
case 0x81:
case 0x82:
case 0x83:
case 0x84:
case 0x85:
case 0x86:
case 0x87:
case 0x88:
case 0x89:
case 0x8a:
case 0x8b:
case 0x8c:
case 0x8d:
case 0x8e:
case 0xb1:
case 0xb2:
/*
* Extract hwconfig from environment since environment
* is not setup yet
*/
getenv_f("hwconfig", buffer, sizeof(buffer));
buf = buffer;
/* check if XFI interface enable in hwconfig for 10g */
if (hwconfig_subarg_cmp_f("fsl_b4860_serdes2",
"sfp_amc", "sfp", buf)) {
if ((port == FM1_10GEC1 ||
port == FM1_10GEC2) &&
((is_serdes_configured(XFI_FM1_MAC9)) ||
(is_serdes_configured(XFI_FM1_MAC10))))
return PHY_INTERFACE_MODE_XGMII;
else if ((port == FM1_DTSEC1) ||
(port == FM1_DTSEC2) ||
(port == FM1_DTSEC3) ||
(port == FM1_DTSEC4))
return PHY_INTERFACE_MODE_NONE;
}
}
}
#endif
/* Fix me need to handle RGMII here first */
switch (port) {
case FM1_DTSEC1:
case FM1_DTSEC2:
case FM1_DTSEC3:
case FM1_DTSEC4:
case FM1_DTSEC5:
case FM1_DTSEC6:
if (is_serdes_configured(SGMII_FM1_DTSEC1 + port - FM1_DTSEC1))
return PHY_INTERFACE_MODE_SGMII;
break;
default:
return PHY_INTERFACE_MODE_NONE;
}
return PHY_INTERFACE_MODE_NONE;
}

View file

@ -0,0 +1,167 @@
/*
* Copyright 2009-2011 Freescale Semiconductor, Inc.
*
* SPDX-License-Identifier: GPL-2.0+
*/
#include <common.h>
#include <asm/types.h>
#include <asm/io.h>
#include <fsl_dtsec.h>
#include <fsl_mdio.h>
#include <phy.h>
#include "fm.h"
#define RCTRL_INIT (RCTRL_GRS | RCTRL_UPROM)
#define TCTRL_INIT TCTRL_GTS
#define MACCFG1_INIT MACCFG1_SOFT_RST
#define MACCFG2_INIT (MACCFG2_PRE_LEN(0x7) | MACCFG2_LEN_CHECK | \
MACCFG2_PAD_CRC | MACCFG2_FULL_DUPLEX | \
MACCFG2_IF_MODE_NIBBLE)
/* MAXFRM - maximum frame length register */
#define MAXFRM_MASK 0x00003fff
static void dtsec_init_mac(struct fsl_enet_mac *mac)
{
struct dtsec *regs = mac->base;
/* soft reset */
out_be32(&regs->maccfg1, MACCFG1_SOFT_RST);
udelay(1000);
/* clear soft reset, Rx/Tx MAC disable */
out_be32(&regs->maccfg1, 0);
/* graceful stop rx */
out_be32(&regs->rctrl, RCTRL_INIT);
udelay(1000);
/* graceful stop tx */
out_be32(&regs->tctrl, TCTRL_INIT);
udelay(1000);
/* disable all interrupts */
out_be32(&regs->imask, IMASK_MASK_ALL);
/* clear all events */
out_be32(&regs->ievent, IEVENT_CLEAR_ALL);
/* set the max Rx length */
out_be32(&regs->maxfrm, mac->max_rx_len & MAXFRM_MASK);
/* set the ecntrl to reset value */
out_be32(&regs->ecntrl, ECNTRL_DEFAULT);
/*
* Rx length check, no strip CRC for Rx, pad and append CRC for Tx,
* full duplex
*/
out_be32(&regs->maccfg2, MACCFG2_INIT);
}
static void dtsec_enable_mac(struct fsl_enet_mac *mac)
{
struct dtsec *regs = mac->base;
/* enable Rx/Tx MAC */
setbits_be32(&regs->maccfg1, MACCFG1_RXTX_EN);
/* clear the graceful Rx stop */
clrbits_be32(&regs->rctrl, RCTRL_GRS);
/* clear the graceful Tx stop */
clrbits_be32(&regs->tctrl, TCTRL_GTS);
}
static void dtsec_disable_mac(struct fsl_enet_mac *mac)
{
struct dtsec *regs = mac->base;
/* graceful Rx stop */
setbits_be32(&regs->rctrl, RCTRL_GRS);
/* graceful Tx stop */
setbits_be32(&regs->tctrl, TCTRL_GTS);
/* disable Rx/Tx MAC */
clrbits_be32(&regs->maccfg1, MACCFG1_RXTX_EN);
}
static void dtsec_set_mac_addr(struct fsl_enet_mac *mac, u8 *mac_addr)
{
struct dtsec *regs = mac->base;
u32 mac_addr1, mac_addr2;
/*
* if a station address of 0x12345678ABCD, perform a write to
* MACSTNADDR1 of 0xCDAB7856, MACSTNADDR2 of 0x34120000
*/
mac_addr1 = (mac_addr[5] << 24) | (mac_addr[4] << 16) | \
(mac_addr[3] << 8) | (mac_addr[2]);
out_be32(&regs->macstnaddr1, mac_addr1);
mac_addr2 = ((mac_addr[1] << 24) | (mac_addr[0] << 16)) & 0xffff0000;
out_be32(&regs->macstnaddr2, mac_addr2);
}
static void dtsec_set_interface_mode(struct fsl_enet_mac *mac,
phy_interface_t type, int speed)
{
struct dtsec *regs = mac->base;
u32 ecntrl, maccfg2;
/* clear all bits relative with interface mode */
ecntrl = in_be32(&regs->ecntrl);
ecntrl &= ~(ECNTRL_TBIM | ECNTRL_GMIIM | ECNTRL_RPM |
ECNTRL_R100M | ECNTRL_SGMIIM);
maccfg2 = in_be32(&regs->maccfg2);
maccfg2 &= ~MACCFG2_IF_MODE_MASK;
if (speed == SPEED_1000)
maccfg2 |= MACCFG2_IF_MODE_BYTE;
else
maccfg2 |= MACCFG2_IF_MODE_NIBBLE;
/* set interface mode */
switch (type) {
case PHY_INTERFACE_MODE_GMII:
ecntrl |= ECNTRL_GMIIM;
break;
case PHY_INTERFACE_MODE_RGMII:
ecntrl |= (ECNTRL_GMIIM | ECNTRL_RPM);
if (speed == SPEED_100)
ecntrl |= ECNTRL_R100M;
break;
case PHY_INTERFACE_MODE_RMII:
if (speed == SPEED_100)
ecntrl |= ECNTRL_R100M;
break;
case PHY_INTERFACE_MODE_SGMII:
ecntrl |= (ECNTRL_SGMIIM | ECNTRL_TBIM);
if (speed == SPEED_100)
ecntrl |= ECNTRL_R100M;
break;
default:
break;
}
out_be32(&regs->ecntrl, ecntrl);
out_be32(&regs->maccfg2, maccfg2);
}
void init_dtsec(struct fsl_enet_mac *mac, void *base,
void *phyregs, int max_rx_len)
{
mac->base = base;
mac->phyregs = phyregs;
mac->max_rx_len = max_rx_len;
mac->init_mac = dtsec_init_mac;
mac->enable_mac = dtsec_enable_mac;
mac->disable_mac = dtsec_disable_mac;
mac->set_mac_addr = dtsec_set_mac_addr;
mac->set_if_mode = dtsec_set_interface_mode;
}

View file

@ -0,0 +1,783 @@
/*
* Copyright 2009-2012 Freescale Semiconductor, Inc.
* Dave Liu <daveliu@freescale.com>
*
* SPDX-License-Identifier: GPL-2.0+
*/
#include <common.h>
#include <asm/io.h>
#include <malloc.h>
#include <net.h>
#include <hwconfig.h>
#include <fm_eth.h>
#include <fsl_mdio.h>
#include <miiphy.h>
#include <phy.h>
#include <fsl_dtsec.h>
#include <fsl_tgec.h>
#include <fsl_memac.h>
#include "fm.h"
static struct eth_device *devlist[NUM_FM_PORTS];
static int num_controllers;
#if defined(CONFIG_MII) || defined(CONFIG_CMD_MII) && !defined(BITBANGMII)
#define TBIANA_SETTINGS (TBIANA_ASYMMETRIC_PAUSE | TBIANA_SYMMETRIC_PAUSE | \
TBIANA_FULL_DUPLEX)
#define TBIANA_SGMII_ACK 0x4001
#define TBICR_SETTINGS (TBICR_ANEG_ENABLE | TBICR_RESTART_ANEG | \
TBICR_FULL_DUPLEX | TBICR_SPEED1_SET)
/* Configure the TBI for SGMII operation */
static void dtsec_configure_serdes(struct fm_eth *priv)
{
#ifdef CONFIG_SYS_FMAN_V3
u32 value;
struct mii_dev bus;
bus.priv = priv->mac->phyregs;
bool sgmii_2500 = (priv->enet_if ==
PHY_INTERFACE_MODE_SGMII_2500) ? true : false;
int i = 0;
qsgmii_loop:
/* SGMII IF mode + AN enable only for 1G SGMII, not for 2.5G */
value = PHY_SGMII_IF_MODE_SGMII;
if (!sgmii_2500)
value |= PHY_SGMII_IF_MODE_AN;
memac_mdio_write(&bus, i, MDIO_DEVAD_NONE, 0x14, value);
/* Dev ability according to SGMII specification */
value = PHY_SGMII_DEV_ABILITY_SGMII;
memac_mdio_write(&bus, i, MDIO_DEVAD_NONE, 0x4, value);
/* Adjust link timer for SGMII -
1.6 ms in units of 8 ns = 2 * 10^5 = 0x30d40 */
memac_mdio_write(&bus, i, MDIO_DEVAD_NONE, 0x13, 0x3);
memac_mdio_write(&bus, i, MDIO_DEVAD_NONE, 0x12, 0xd40);
/* Restart AN */
value = PHY_SGMII_CR_DEF_VAL;
if (!sgmii_2500)
value |= PHY_SGMII_CR_RESET_AN;
memac_mdio_write(&bus, i, MDIO_DEVAD_NONE, 0, value);
if ((priv->enet_if == PHY_INTERFACE_MODE_QSGMII) && (i < 3)) {
i++;
goto qsgmii_loop;
}
#else
struct dtsec *regs = priv->mac->base;
struct tsec_mii_mng *phyregs = priv->mac->phyregs;
/*
* Access TBI PHY registers at given TSEC register offset as
* opposed to the register offset used for external PHY accesses
*/
tsec_local_mdio_write(phyregs, in_be32(&regs->tbipa), 0, TBI_TBICON,
TBICON_CLK_SELECT);
tsec_local_mdio_write(phyregs, in_be32(&regs->tbipa), 0, TBI_ANA,
TBIANA_SGMII_ACK);
tsec_local_mdio_write(phyregs, in_be32(&regs->tbipa), 0,
TBI_CR, TBICR_SETTINGS);
#endif
}
static void dtsec_init_phy(struct eth_device *dev)
{
struct fm_eth *fm_eth = dev->priv;
#ifndef CONFIG_SYS_FMAN_V3
struct dtsec *regs = (struct dtsec *)CONFIG_SYS_FSL_FM1_DTSEC1_ADDR;
/* Assign a Physical address to the TBI */
out_be32(&regs->tbipa, CONFIG_SYS_TBIPA_VALUE);
#endif
if (fm_eth->enet_if == PHY_INTERFACE_MODE_SGMII ||
fm_eth->enet_if == PHY_INTERFACE_MODE_QSGMII ||
fm_eth->enet_if == PHY_INTERFACE_MODE_SGMII_2500)
dtsec_configure_serdes(fm_eth);
}
#ifdef CONFIG_PHYLIB
static int tgec_is_fibre(struct eth_device *dev)
{
struct fm_eth *fm = dev->priv;
char phyopt[20];
sprintf(phyopt, "fsl_fm%d_xaui_phy", fm->fm_index + 1);
return hwconfig_arg_cmp(phyopt, "xfi");
}
#endif
#endif
static u16 muram_readw(u16 *addr)
{
ulong base = (ulong)addr & ~0x3UL;
u32 val32 = in_be32((void *)base);
int byte_pos;
u16 ret;
byte_pos = (ulong)addr & 0x3UL;
if (byte_pos)
ret = (u16)(val32 & 0x0000ffff);
else
ret = (u16)((val32 & 0xffff0000) >> 16);
return ret;
}
static void muram_writew(u16 *addr, u16 val)
{
ulong base = (ulong)addr & ~0x3UL;
u32 org32 = in_be32((void *)base);
u32 val32;
int byte_pos;
byte_pos = (ulong)addr & 0x3UL;
if (byte_pos)
val32 = (org32 & 0xffff0000) | val;
else
val32 = (org32 & 0x0000ffff) | ((u32)val << 16);
out_be32((void *)base, val32);
}
static void bmi_rx_port_disable(struct fm_bmi_rx_port *rx_port)
{
int timeout = 1000000;
clrbits_be32(&rx_port->fmbm_rcfg, FMBM_RCFG_EN);
/* wait until the rx port is not busy */
while ((in_be32(&rx_port->fmbm_rst) & FMBM_RST_BSY) && timeout--)
;
}
static void bmi_rx_port_init(struct fm_bmi_rx_port *rx_port)
{
/* set BMI to independent mode, Rx port disable */
out_be32(&rx_port->fmbm_rcfg, FMBM_RCFG_IM);
/* clear FOF in IM case */
out_be32(&rx_port->fmbm_rim, 0);
/* Rx frame next engine -RISC */
out_be32(&rx_port->fmbm_rfne, NIA_ENG_RISC | NIA_RISC_AC_IM_RX);
/* Rx command attribute - no order, MR[3] = 1 */
clrbits_be32(&rx_port->fmbm_rfca, FMBM_RFCA_ORDER | FMBM_RFCA_MR_MASK);
setbits_be32(&rx_port->fmbm_rfca, FMBM_RFCA_MR(4));
/* enable Rx statistic counters */
out_be32(&rx_port->fmbm_rstc, FMBM_RSTC_EN);
/* disable Rx performance counters */
out_be32(&rx_port->fmbm_rpc, 0);
}
static void bmi_tx_port_disable(struct fm_bmi_tx_port *tx_port)
{
int timeout = 1000000;
clrbits_be32(&tx_port->fmbm_tcfg, FMBM_TCFG_EN);
/* wait until the tx port is not busy */
while ((in_be32(&tx_port->fmbm_tst) & FMBM_TST_BSY) && timeout--)
;
}
static void bmi_tx_port_init(struct fm_bmi_tx_port *tx_port)
{
/* set BMI to independent mode, Tx port disable */
out_be32(&tx_port->fmbm_tcfg, FMBM_TCFG_IM);
/* Tx frame next engine -RISC */
out_be32(&tx_port->fmbm_tfne, NIA_ENG_RISC | NIA_RISC_AC_IM_TX);
out_be32(&tx_port->fmbm_tfene, NIA_ENG_RISC | NIA_RISC_AC_IM_TX);
/* Tx command attribute - no order, MR[3] = 1 */
clrbits_be32(&tx_port->fmbm_tfca, FMBM_TFCA_ORDER | FMBM_TFCA_MR_MASK);
setbits_be32(&tx_port->fmbm_tfca, FMBM_TFCA_MR(4));
/* enable Tx statistic counters */
out_be32(&tx_port->fmbm_tstc, FMBM_TSTC_EN);
/* disable Tx performance counters */
out_be32(&tx_port->fmbm_tpc, 0);
}
static int fm_eth_rx_port_parameter_init(struct fm_eth *fm_eth)
{
struct fm_port_global_pram *pram;
u32 pram_page_offset;
void *rx_bd_ring_base;
void *rx_buf_pool;
u32 bd_ring_base_lo, bd_ring_base_hi;
u32 buf_lo, buf_hi;
struct fm_port_bd *rxbd;
struct fm_port_qd *rxqd;
struct fm_bmi_rx_port *bmi_rx_port = fm_eth->rx_port;
int i;
/* alloc global parameter ram at MURAM */
pram = (struct fm_port_global_pram *)fm_muram_alloc(fm_eth->fm_index,
FM_PRAM_SIZE, FM_PRAM_ALIGN);
if (!pram) {
printf("%s: No muram for Rx global parameter\n", __func__);
return -ENOMEM;
}
fm_eth->rx_pram = pram;
/* parameter page offset to MURAM */
pram_page_offset = (void *)pram - fm_muram_base(fm_eth->fm_index);
/* enable global mode- snooping data buffers and BDs */
out_be32(&pram->mode, PRAM_MODE_GLOBAL);
/* init the Rx queue descriptor pionter */
out_be32(&pram->rxqd_ptr, pram_page_offset + 0x20);
/* set the max receive buffer length, power of 2 */
muram_writew(&pram->mrblr, MAX_RXBUF_LOG2);
/* alloc Rx buffer descriptors from main memory */
rx_bd_ring_base = malloc(sizeof(struct fm_port_bd)
* RX_BD_RING_SIZE);
if (!rx_bd_ring_base)
return -ENOMEM;
memset(rx_bd_ring_base, 0, sizeof(struct fm_port_bd)
* RX_BD_RING_SIZE);
/* alloc Rx buffer from main memory */
rx_buf_pool = malloc(MAX_RXBUF_LEN * RX_BD_RING_SIZE);
if (!rx_buf_pool)
return -ENOMEM;
memset(rx_buf_pool, 0, MAX_RXBUF_LEN * RX_BD_RING_SIZE);
debug("%s: rx_buf_pool = %p\n", __func__, rx_buf_pool);
/* save them to fm_eth */
fm_eth->rx_bd_ring = rx_bd_ring_base;
fm_eth->cur_rxbd = rx_bd_ring_base;
fm_eth->rx_buf = rx_buf_pool;
/* init Rx BDs ring */
rxbd = (struct fm_port_bd *)rx_bd_ring_base;
for (i = 0; i < RX_BD_RING_SIZE; i++) {
muram_writew(&rxbd->status, RxBD_EMPTY);
muram_writew(&rxbd->len, 0);
buf_hi = upper_32_bits(virt_to_phys(rx_buf_pool +
i * MAX_RXBUF_LEN));
buf_lo = lower_32_bits(virt_to_phys(rx_buf_pool +
i * MAX_RXBUF_LEN));
muram_writew(&rxbd->buf_ptr_hi, (u16)buf_hi);
out_be32(&rxbd->buf_ptr_lo, buf_lo);
rxbd++;
}
/* set the Rx queue descriptor */
rxqd = &pram->rxqd;
muram_writew(&rxqd->gen, 0);
bd_ring_base_hi = upper_32_bits(virt_to_phys(rx_bd_ring_base));
bd_ring_base_lo = lower_32_bits(virt_to_phys(rx_bd_ring_base));
muram_writew(&rxqd->bd_ring_base_hi, (u16)bd_ring_base_hi);
out_be32(&rxqd->bd_ring_base_lo, bd_ring_base_lo);
muram_writew(&rxqd->bd_ring_size, sizeof(struct fm_port_bd)
* RX_BD_RING_SIZE);
muram_writew(&rxqd->offset_in, 0);
muram_writew(&rxqd->offset_out, 0);
/* set IM parameter ram pointer to Rx Frame Queue ID */
out_be32(&bmi_rx_port->fmbm_rfqid, pram_page_offset);
return 0;
}
static int fm_eth_tx_port_parameter_init(struct fm_eth *fm_eth)
{
struct fm_port_global_pram *pram;
u32 pram_page_offset;
void *tx_bd_ring_base;
u32 bd_ring_base_lo, bd_ring_base_hi;
struct fm_port_bd *txbd;
struct fm_port_qd *txqd;
struct fm_bmi_tx_port *bmi_tx_port = fm_eth->tx_port;
int i;
/* alloc global parameter ram at MURAM */
pram = (struct fm_port_global_pram *)fm_muram_alloc(fm_eth->fm_index,
FM_PRAM_SIZE, FM_PRAM_ALIGN);
if (!pram) {
printf("%s: No muram for Tx global parameter\n", __func__);
return -ENOMEM;
}
fm_eth->tx_pram = pram;
/* parameter page offset to MURAM */
pram_page_offset = (void *)pram - fm_muram_base(fm_eth->fm_index);
/* enable global mode- snooping data buffers and BDs */
out_be32(&pram->mode, PRAM_MODE_GLOBAL);
/* init the Tx queue descriptor pionter */
out_be32(&pram->txqd_ptr, pram_page_offset + 0x40);
/* alloc Tx buffer descriptors from main memory */
tx_bd_ring_base = malloc(sizeof(struct fm_port_bd)
* TX_BD_RING_SIZE);
if (!tx_bd_ring_base)
return -ENOMEM;
memset(tx_bd_ring_base, 0, sizeof(struct fm_port_bd)
* TX_BD_RING_SIZE);
/* save it to fm_eth */
fm_eth->tx_bd_ring = tx_bd_ring_base;
fm_eth->cur_txbd = tx_bd_ring_base;
/* init Tx BDs ring */
txbd = (struct fm_port_bd *)tx_bd_ring_base;
for (i = 0; i < TX_BD_RING_SIZE; i++) {
muram_writew(&txbd->status, TxBD_LAST);
muram_writew(&txbd->len, 0);
muram_writew(&txbd->buf_ptr_hi, 0);
out_be32(&txbd->buf_ptr_lo, 0);
txbd++;
}
/* set the Tx queue decriptor */
txqd = &pram->txqd;
bd_ring_base_hi = upper_32_bits(virt_to_phys(tx_bd_ring_base));
bd_ring_base_lo = lower_32_bits(virt_to_phys(tx_bd_ring_base));
muram_writew(&txqd->bd_ring_base_hi, (u16)bd_ring_base_hi);
out_be32(&txqd->bd_ring_base_lo, bd_ring_base_lo);
muram_writew(&txqd->bd_ring_size, sizeof(struct fm_port_bd)
* TX_BD_RING_SIZE);
muram_writew(&txqd->offset_in, 0);
muram_writew(&txqd->offset_out, 0);
/* set IM parameter ram pointer to Tx Confirmation Frame Queue ID */
out_be32(&bmi_tx_port->fmbm_tcfqid, pram_page_offset);
return 0;
}
static int fm_eth_init(struct fm_eth *fm_eth)
{
int ret;
ret = fm_eth_rx_port_parameter_init(fm_eth);
if (ret)
return ret;
ret = fm_eth_tx_port_parameter_init(fm_eth);
if (ret)
return ret;
return 0;
}
static int fm_eth_startup(struct fm_eth *fm_eth)
{
struct fsl_enet_mac *mac;
int ret;
mac = fm_eth->mac;
/* Rx/TxBDs, Rx/TxQDs, Rx buff and parameter ram init */
ret = fm_eth_init(fm_eth);
if (ret)
return ret;
/* setup the MAC controller */
mac->init_mac(mac);
/* For some reason we need to set SPEED_100 */
if (((fm_eth->enet_if == PHY_INTERFACE_MODE_SGMII) ||
(fm_eth->enet_if == PHY_INTERFACE_MODE_QSGMII)) &&
mac->set_if_mode)
mac->set_if_mode(mac, fm_eth->enet_if, SPEED_100);
/* init bmi rx port, IM mode and disable */
bmi_rx_port_init(fm_eth->rx_port);
/* init bmi tx port, IM mode and disable */
bmi_tx_port_init(fm_eth->tx_port);
return 0;
}
static void fmc_tx_port_graceful_stop_enable(struct fm_eth *fm_eth)
{
struct fm_port_global_pram *pram;
pram = fm_eth->tx_pram;
/* graceful stop transmission of frames */
setbits_be32(&pram->mode, PRAM_MODE_GRACEFUL_STOP);
sync();
}
static void fmc_tx_port_graceful_stop_disable(struct fm_eth *fm_eth)
{
struct fm_port_global_pram *pram;
pram = fm_eth->tx_pram;
/* re-enable transmission of frames */
clrbits_be32(&pram->mode, PRAM_MODE_GRACEFUL_STOP);
sync();
}
static int fm_eth_open(struct eth_device *dev, bd_t *bd)
{
struct fm_eth *fm_eth;
struct fsl_enet_mac *mac;
#ifdef CONFIG_PHYLIB
int ret;
#endif
fm_eth = (struct fm_eth *)dev->priv;
mac = fm_eth->mac;
/* setup the MAC address */
if (dev->enetaddr[0] & 0x01) {
printf("%s: MacAddress is multcast address\n", __func__);
return 1;
}
mac->set_mac_addr(mac, dev->enetaddr);
/* enable bmi Rx port */
setbits_be32(&fm_eth->rx_port->fmbm_rcfg, FMBM_RCFG_EN);
/* enable MAC rx/tx port */
mac->enable_mac(mac);
/* enable bmi Tx port */
setbits_be32(&fm_eth->tx_port->fmbm_tcfg, FMBM_TCFG_EN);
/* re-enable transmission of frame */
fmc_tx_port_graceful_stop_disable(fm_eth);
#ifdef CONFIG_PHYLIB
if (fm_eth->phydev) {
ret = phy_startup(fm_eth->phydev);
if (ret) {
printf("%s: Could not initialize\n",
fm_eth->phydev->dev->name);
return ret;
}
} else {
return 0;
}
#else
fm_eth->phydev->speed = SPEED_1000;
fm_eth->phydev->link = 1;
fm_eth->phydev->duplex = DUPLEX_FULL;
#endif
/* set the MAC-PHY mode */
mac->set_if_mode(mac, fm_eth->enet_if, fm_eth->phydev->speed);
if (!fm_eth->phydev->link)
printf("%s: No link.\n", fm_eth->phydev->dev->name);
return fm_eth->phydev->link ? 0 : -1;
}
static void fm_eth_halt(struct eth_device *dev)
{
struct fm_eth *fm_eth;
struct fsl_enet_mac *mac;
fm_eth = (struct fm_eth *)dev->priv;
mac = fm_eth->mac;
/* graceful stop the transmission of frames */
fmc_tx_port_graceful_stop_enable(fm_eth);
/* disable bmi Tx port */
bmi_tx_port_disable(fm_eth->tx_port);
/* disable MAC rx/tx port */
mac->disable_mac(mac);
/* disable bmi Rx port */
bmi_rx_port_disable(fm_eth->rx_port);
#ifdef CONFIG_PHYLIB
if (fm_eth->phydev)
phy_shutdown(fm_eth->phydev);
#endif
}
static int fm_eth_send(struct eth_device *dev, void *buf, int len)
{
struct fm_eth *fm_eth;
struct fm_port_global_pram *pram;
struct fm_port_bd *txbd, *txbd_base;
u16 offset_in;
int i;
fm_eth = (struct fm_eth *)dev->priv;
pram = fm_eth->tx_pram;
txbd = fm_eth->cur_txbd;
/* find one empty TxBD */
for (i = 0; muram_readw(&txbd->status) & TxBD_READY; i++) {
udelay(100);
if (i > 0x1000) {
printf("%s: Tx buffer not ready, txbd->status = 0x%x\n",
dev->name, muram_readw(&txbd->status));
return 0;
}
}
/* setup TxBD */
muram_writew(&txbd->buf_ptr_hi, (u16)upper_32_bits(virt_to_phys(buf)));
out_be32(&txbd->buf_ptr_lo, lower_32_bits(virt_to_phys(buf)));
muram_writew(&txbd->len, len);
sync();
muram_writew(&txbd->status, TxBD_READY | TxBD_LAST);
sync();
/* update TxQD, let RISC to send the packet */
offset_in = muram_readw(&pram->txqd.offset_in);
offset_in += sizeof(struct fm_port_bd);
if (offset_in >= muram_readw(&pram->txqd.bd_ring_size))
offset_in = 0;
muram_writew(&pram->txqd.offset_in, offset_in);
sync();
/* wait for buffer to be transmitted */
for (i = 0; muram_readw(&txbd->status) & TxBD_READY; i++) {
udelay(100);
if (i > 0x10000) {
printf("%s: Tx error, txbd->status = 0x%x\n",
dev->name, muram_readw(&txbd->status));
return 0;
}
}
/* advance the TxBD */
txbd++;
txbd_base = (struct fm_port_bd *)fm_eth->tx_bd_ring;
if (txbd >= (txbd_base + TX_BD_RING_SIZE))
txbd = txbd_base;
/* update current txbd */
fm_eth->cur_txbd = (void *)txbd;
return 1;
}
static int fm_eth_recv(struct eth_device *dev)
{
struct fm_eth *fm_eth;
struct fm_port_global_pram *pram;
struct fm_port_bd *rxbd, *rxbd_base;
u16 status, len;
u32 buf_lo, buf_hi;
u8 *data;
u16 offset_out;
int ret = 1;
fm_eth = (struct fm_eth *)dev->priv;
pram = fm_eth->rx_pram;
rxbd = fm_eth->cur_rxbd;
status = muram_readw(&rxbd->status);
while (!(status & RxBD_EMPTY)) {
if (!(status & RxBD_ERROR)) {
buf_hi = muram_readw(&rxbd->buf_ptr_hi);
buf_lo = in_be32(&rxbd->buf_ptr_lo);
data = (u8 *)((ulong)(buf_hi << 16) << 16 | buf_lo);
len = muram_readw(&rxbd->len);
net_process_received_packet(data, len);
} else {
printf("%s: Rx error\n", dev->name);
ret = 0;
}
/* clear the RxBDs */
muram_writew(&rxbd->status, RxBD_EMPTY);
muram_writew(&rxbd->len, 0);
sync();
/* advance RxBD */
rxbd++;
rxbd_base = (struct fm_port_bd *)fm_eth->rx_bd_ring;
if (rxbd >= (rxbd_base + RX_BD_RING_SIZE))
rxbd = rxbd_base;
/* read next status */
status = muram_readw(&rxbd->status);
/* update RxQD */
offset_out = muram_readw(&pram->rxqd.offset_out);
offset_out += sizeof(struct fm_port_bd);
if (offset_out >= muram_readw(&pram->rxqd.bd_ring_size))
offset_out = 0;
muram_writew(&pram->rxqd.offset_out, offset_out);
sync();
}
fm_eth->cur_rxbd = (void *)rxbd;
return ret;
}
static int fm_eth_init_mac(struct fm_eth *fm_eth, struct ccsr_fman *reg)
{
struct fsl_enet_mac *mac;
int num;
void *base, *phyregs = NULL;
num = fm_eth->num;
#ifdef CONFIG_SYS_FMAN_V3
#ifndef CONFIG_FSL_FM_10GEC_REGULAR_NOTATION
if (fm_eth->type == FM_ETH_10G_E) {
/* 10GEC1/10GEC2 use mEMAC9/mEMAC10 on T2080/T4240.
* 10GEC3/10GEC4 use mEMAC1/mEMAC2 on T2080.
* 10GEC1 uses mEMAC1 on T1024.
* so it needs to change the num.
*/
if (fm_eth->num >= 2)
num -= 2;
else
num += 8;
}
#endif
base = &reg->memac[num].fm_memac;
phyregs = &reg->memac[num].fm_memac_mdio;
#else
/* Get the mac registers base address */
if (fm_eth->type == FM_ETH_1G_E) {
base = &reg->mac_1g[num].fm_dtesc;
phyregs = &reg->mac_1g[num].fm_mdio.miimcfg;
} else {
base = &reg->mac_10g[num].fm_10gec;
phyregs = &reg->mac_10g[num].fm_10gec_mdio;
}
#endif
/* alloc mac controller */
mac = malloc(sizeof(struct fsl_enet_mac));
if (!mac)
return -ENOMEM;
memset(mac, 0, sizeof(struct fsl_enet_mac));
/* save the mac to fm_eth struct */
fm_eth->mac = mac;
#ifdef CONFIG_SYS_FMAN_V3
init_memac(mac, base, phyregs, MAX_RXBUF_LEN);
#else
if (fm_eth->type == FM_ETH_1G_E)
init_dtsec(mac, base, phyregs, MAX_RXBUF_LEN);
else
init_tgec(mac, base, phyregs, MAX_RXBUF_LEN);
#endif
return 0;
}
static int init_phy(struct eth_device *dev)
{
struct fm_eth *fm_eth = dev->priv;
#ifdef CONFIG_PHYLIB
struct phy_device *phydev = NULL;
u32 supported;
#endif
if (fm_eth->type == FM_ETH_1G_E)
dtsec_init_phy(dev);
#ifdef CONFIG_PHYLIB
if (fm_eth->bus) {
phydev = phy_connect(fm_eth->bus, fm_eth->phyaddr, dev,
fm_eth->enet_if);
if (!phydev) {
printf("Failed to connect\n");
return -1;
}
} else {
return 0;
}
if (fm_eth->type == FM_ETH_1G_E) {
supported = (SUPPORTED_10baseT_Half |
SUPPORTED_10baseT_Full |
SUPPORTED_100baseT_Half |
SUPPORTED_100baseT_Full |
SUPPORTED_1000baseT_Full);
} else {
supported = SUPPORTED_10000baseT_Full;
if (tgec_is_fibre(dev))
phydev->port = PORT_FIBRE;
}
phydev->supported &= supported;
phydev->advertising = phydev->supported;
fm_eth->phydev = phydev;
phy_config(phydev);
#endif
return 0;
}
int fm_eth_initialize(struct ccsr_fman *reg, struct fm_eth_info *info)
{
struct eth_device *dev;
struct fm_eth *fm_eth;
int i, num = info->num;
int ret;
/* alloc eth device */
dev = (struct eth_device *)malloc(sizeof(struct eth_device));
if (!dev)
return -ENOMEM;
memset(dev, 0, sizeof(struct eth_device));
/* alloc the FMan ethernet private struct */
fm_eth = (struct fm_eth *)malloc(sizeof(struct fm_eth));
if (!fm_eth)
return -ENOMEM;
memset(fm_eth, 0, sizeof(struct fm_eth));
/* save off some things we need from the info struct */
fm_eth->fm_index = info->index - 1; /* keep as 0 based for muram */
fm_eth->num = num;
fm_eth->type = info->type;
fm_eth->rx_port = (void *)&reg->port[info->rx_port_id - 1].fm_bmi;
fm_eth->tx_port = (void *)&reg->port[info->tx_port_id - 1].fm_bmi;
/* set the ethernet max receive length */
fm_eth->max_rx_len = MAX_RXBUF_LEN;
/* init global mac structure */
ret = fm_eth_init_mac(fm_eth, reg);
if (ret)
return ret;
/* keep same as the manual, we call FMAN1, FMAN2, DTSEC1, DTSEC2, etc */
if (fm_eth->type == FM_ETH_1G_E)
sprintf(dev->name, "FM%d@DTSEC%d", info->index, num + 1);
else
sprintf(dev->name, "FM%d@TGEC%d", info->index, num + 1);
devlist[num_controllers++] = dev;
dev->iobase = 0;
dev->priv = (void *)fm_eth;
dev->init = fm_eth_open;
dev->halt = fm_eth_halt;
dev->send = fm_eth_send;
dev->recv = fm_eth_recv;
fm_eth->dev = dev;
fm_eth->bus = info->bus;
fm_eth->phyaddr = info->phy_addr;
fm_eth->enet_if = info->enet_if;
/* startup the FM im */
ret = fm_eth_startup(fm_eth);
if (ret)
return ret;
init_phy(dev);
/* clear the ethernet address */
for (i = 0; i < 6; i++)
dev->enetaddr[i] = 0;
eth_register(dev);
return 0;
}

View file

@ -0,0 +1,425 @@
/*
* Copyright 2009-2011 Freescale Semiconductor, Inc.
* Dave Liu <daveliu@freescale.com>
*
* SPDX-License-Identifier: GPL-2.0+
*/
#include <common.h>
#include <malloc.h>
#include <asm/io.h>
#include <asm/errno.h>
#include "fm.h"
#include "../../qe/qe.h" /* For struct qe_firmware */
#ifdef CONFIG_SYS_QE_FMAN_FW_IN_NAND
#include <nand.h>
#elif defined(CONFIG_SYS_QE_FW_IN_SPIFLASH)
#include <spi_flash.h>
#elif defined(CONFIG_SYS_QE_FMAN_FW_IN_MMC)
#include <mmc.h>
#endif
struct fm_muram muram[CONFIG_SYS_NUM_FMAN];
void *fm_muram_base(int fm_idx)
{
return muram[fm_idx].base;
}
void *fm_muram_alloc(int fm_idx, size_t size, ulong align)
{
void *ret;
ulong align_mask;
size_t off;
void *save;
align_mask = align - 1;
save = muram[fm_idx].alloc;
off = (ulong)save & align_mask;
if (off != 0)
muram[fm_idx].alloc += (align - off);
off = size & align_mask;
if (off != 0)
size += (align - off);
if ((muram[fm_idx].alloc + size) >= muram[fm_idx].top) {
muram[fm_idx].alloc = save;
printf("%s: run out of ram.\n", __func__);
return NULL;
}
ret = muram[fm_idx].alloc;
muram[fm_idx].alloc += size;
memset((void *)ret, 0, size);
return ret;
}
static void fm_init_muram(int fm_idx, void *reg)
{
void *base = reg;
muram[fm_idx].base = base;
muram[fm_idx].size = CONFIG_SYS_FM_MURAM_SIZE;
muram[fm_idx].alloc = base + FM_MURAM_RES_SIZE;
muram[fm_idx].top = base + CONFIG_SYS_FM_MURAM_SIZE;
}
/*
* fm_upload_ucode - Fman microcode upload worker function
*
* This function does the actual uploading of an Fman microcode
* to an Fman.
*/
static void fm_upload_ucode(int fm_idx, struct fm_imem *imem,
u32 *ucode, unsigned int size)
{
unsigned int i;
unsigned int timeout = 1000000;
/* enable address auto increase */
out_be32(&imem->iadd, IRAM_IADD_AIE);
/* write microcode to IRAM */
for (i = 0; i < size / 4; i++)
out_be32(&imem->idata, (be32_to_cpu(ucode[i])));
/* verify if the writing is over */
out_be32(&imem->iadd, 0);
while ((in_be32(&imem->idata) != be32_to_cpu(ucode[0])) && --timeout)
;
if (!timeout)
printf("Fman%u: microcode upload timeout\n", fm_idx + 1);
/* enable microcode from IRAM */
out_be32(&imem->iready, IRAM_READY);
}
/*
* Upload an Fman firmware
*
* This function is similar to qe_upload_firmware(), exception that it uploads
* a microcode to the Fman instead of the QE.
*
* Because the process for uploading a microcode to the Fman is similar for
* that of the QE, the QE firmware binary format is used for Fman microcode.
* It should be possible to unify these two functions, but for now we keep them
* separate.
*/
static int fman_upload_firmware(int fm_idx,
struct fm_imem *fm_imem,
const struct qe_firmware *firmware)
{
unsigned int i;
u32 crc;
size_t calc_size = sizeof(struct qe_firmware);
size_t length;
const struct qe_header *hdr;
if (!firmware) {
printf("Fman%u: Invalid address for firmware\n", fm_idx + 1);
return -EINVAL;
}
hdr = &firmware->header;
length = be32_to_cpu(hdr->length);
/* Check the magic */
if ((hdr->magic[0] != 'Q') || (hdr->magic[1] != 'E') ||
(hdr->magic[2] != 'F')) {
printf("Fman%u: Data at %p is not a firmware\n", fm_idx + 1,
firmware);
return -EPERM;
}
/* Check the version */
if (hdr->version != 1) {
printf("Fman%u: Unsupported firmware version %u\n", fm_idx + 1,
hdr->version);
return -EPERM;
}
/* Validate some of the fields */
if ((firmware->count != 1)) {
printf("Fman%u: Invalid data in firmware header\n", fm_idx + 1);
return -EINVAL;
}
/* Validate the length and check if there's a CRC */
calc_size += (firmware->count - 1) * sizeof(struct qe_microcode);
for (i = 0; i < firmware->count; i++)
/*
* For situations where the second RISC uses the same microcode
* as the first, the 'code_offset' and 'count' fields will be
* zero, so it's okay to add those.
*/
calc_size += sizeof(u32) *
be32_to_cpu(firmware->microcode[i].count);
/* Validate the length */
if (length != calc_size + sizeof(u32)) {
printf("Fman%u: Invalid length in firmware header\n",
fm_idx + 1);
return -EPERM;
}
/*
* Validate the CRC. We would normally call crc32_no_comp(), but that
* function isn't available unless you turn on JFFS support.
*/
crc = be32_to_cpu(*(u32 *)((void *)firmware + calc_size));
if (crc != (crc32(-1, (const void *)firmware, calc_size) ^ -1)) {
printf("Fman%u: Firmware CRC is invalid\n", fm_idx + 1);
return -EIO;
}
/* Loop through each microcode. */
for (i = 0; i < firmware->count; i++) {
const struct qe_microcode *ucode = &firmware->microcode[i];
/* Upload a microcode if it's present */
if (be32_to_cpu(ucode->code_offset)) {
u32 ucode_size;
u32 *code;
printf("Fman%u: Uploading microcode version %u.%u.%u\n",
fm_idx + 1, ucode->major, ucode->minor,
ucode->revision);
code = (void *)firmware +
be32_to_cpu(ucode->code_offset);
ucode_size = sizeof(u32) * be32_to_cpu(ucode->count);
fm_upload_ucode(fm_idx, fm_imem, code, ucode_size);
}
}
return 0;
}
static u32 fm_assign_risc(int port_id)
{
u32 risc_sel, val;
risc_sel = (port_id & 0x1) ? FMFPPRC_RISC2 : FMFPPRC_RISC1;
val = (port_id << FMFPPRC_PORTID_SHIFT) & FMFPPRC_PORTID_MASK;
val |= ((risc_sel << FMFPPRC_ORA_SHIFT) | risc_sel);
return val;
}
static void fm_init_fpm(struct fm_fpm *fpm)
{
int i, port_id;
u32 val;
setbits_be32(&fpm->fmfpee, FMFPEE_EHM | FMFPEE_UEC |
FMFPEE_CER | FMFPEE_DER);
/* IM mode, each even port ID to RISC#1, each odd port ID to RISC#2 */
/* offline/parser port */
for (i = 0; i < MAX_NUM_OH_PORT; i++) {
port_id = OH_PORT_ID_BASE + i;
val = fm_assign_risc(port_id);
out_be32(&fpm->fpmprc, val);
}
/* Rx 1G port */
for (i = 0; i < MAX_NUM_RX_PORT_1G; i++) {
port_id = RX_PORT_1G_BASE + i;
val = fm_assign_risc(port_id);
out_be32(&fpm->fpmprc, val);
}
/* Tx 1G port */
for (i = 0; i < MAX_NUM_TX_PORT_1G; i++) {
port_id = TX_PORT_1G_BASE + i;
val = fm_assign_risc(port_id);
out_be32(&fpm->fpmprc, val);
}
/* Rx 10G port */
port_id = RX_PORT_10G_BASE;
val = fm_assign_risc(port_id);
out_be32(&fpm->fpmprc, val);
/* Tx 10G port */
port_id = TX_PORT_10G_BASE;
val = fm_assign_risc(port_id);
out_be32(&fpm->fpmprc, val);
/* disable the dispatch limit in IM case */
out_be32(&fpm->fpmflc, FMFP_FLC_DISP_LIM_NONE);
/* clear events */
out_be32(&fpm->fmfpee, FMFPEE_CLEAR_EVENT);
/* clear risc events */
for (i = 0; i < 4; i++)
out_be32(&fpm->fpmcev[i], 0xffffffff);
/* clear error */
out_be32(&fpm->fpmrcr, FMFP_RCR_MDEC | FMFP_RCR_IDEC);
}
static int fm_init_bmi(int fm_idx, struct fm_bmi_common *bmi)
{
int blk, i, port_id;
u32 val;
size_t offset;
void *base;
/* alloc free buffer pool in MURAM */
base = fm_muram_alloc(fm_idx, FM_FREE_POOL_SIZE, FM_FREE_POOL_ALIGN);
if (!base) {
printf("%s: no muram for free buffer pool\n", __func__);
return -ENOMEM;
}
offset = base - fm_muram_base(fm_idx);
/* Need 128KB total free buffer pool size */
val = offset / 256;
blk = FM_FREE_POOL_SIZE / 256;
/* in IM, we must not begin from offset 0 in MURAM */
val |= ((blk - 1) << FMBM_CFG1_FBPS_SHIFT);
out_be32(&bmi->fmbm_cfg1, val);
/* disable all BMI interrupt */
out_be32(&bmi->fmbm_ier, FMBM_IER_DISABLE_ALL);
/* clear all events */
out_be32(&bmi->fmbm_ievr, FMBM_IEVR_CLEAR_ALL);
/*
* set port parameters - FMBM_PP_x
* max tasks 10G Rx/Tx=12, 1G Rx/Tx 4, others is 1
* max dma 10G Rx/Tx=3, others is 1
* set port FIFO size - FMBM_PFS_x
* 4KB for all Rx and Tx ports
*/
/* offline/parser port */
for (i = 0; i < MAX_NUM_OH_PORT; i++) {
port_id = OH_PORT_ID_BASE + i - 1;
/* max tasks=1, max dma=1, no extra */
out_be32(&bmi->fmbm_pp[port_id], 0);
/* port FIFO size - 256 bytes, no extra */
out_be32(&bmi->fmbm_pfs[port_id], 0);
}
/* Rx 1G port */
for (i = 0; i < MAX_NUM_RX_PORT_1G; i++) {
port_id = RX_PORT_1G_BASE + i - 1;
/* max tasks=4, max dma=1, no extra */
out_be32(&bmi->fmbm_pp[port_id], FMBM_PP_MXT(4));
/* FIFO size - 4KB, no extra */
out_be32(&bmi->fmbm_pfs[port_id], FMBM_PFS_IFSZ(0xf));
}
/* Tx 1G port FIFO size - 4KB, no extra */
for (i = 0; i < MAX_NUM_TX_PORT_1G; i++) {
port_id = TX_PORT_1G_BASE + i - 1;
/* max tasks=4, max dma=1, no extra */
out_be32(&bmi->fmbm_pp[port_id], FMBM_PP_MXT(4));
/* FIFO size - 4KB, no extra */
out_be32(&bmi->fmbm_pfs[port_id], FMBM_PFS_IFSZ(0xf));
}
/* Rx 10G port */
port_id = RX_PORT_10G_BASE - 1;
/* max tasks=12, max dma=3, no extra */
out_be32(&bmi->fmbm_pp[port_id], FMBM_PP_MXT(12) | FMBM_PP_MXD(3));
/* FIFO size - 4KB, no extra */
out_be32(&bmi->fmbm_pfs[port_id], FMBM_PFS_IFSZ(0xf));
/* Tx 10G port */
port_id = TX_PORT_10G_BASE - 1;
/* max tasks=12, max dma=3, no extra */
out_be32(&bmi->fmbm_pp[port_id], FMBM_PP_MXT(12) | FMBM_PP_MXD(3));
/* FIFO size - 4KB, no extra */
out_be32(&bmi->fmbm_pfs[port_id], FMBM_PFS_IFSZ(0xf));
/* initialize internal buffers data base (linked list) */
out_be32(&bmi->fmbm_init, FMBM_INIT_START);
return 0;
}
static void fm_init_qmi(struct fm_qmi_common *qmi)
{
/* disable enqueue and dequeue of QMI */
clrbits_be32(&qmi->fmqm_gc, FMQM_GC_ENQ_EN | FMQM_GC_DEQ_EN);
/* disable all error interrupts */
out_be32(&qmi->fmqm_eien, FMQM_EIEN_DISABLE_ALL);
/* clear all error events */
out_be32(&qmi->fmqm_eie, FMQM_EIE_CLEAR_ALL);
/* disable all interrupts */
out_be32(&qmi->fmqm_ien, FMQM_IEN_DISABLE_ALL);
/* clear all interrupts */
out_be32(&qmi->fmqm_ie, FMQM_IE_CLEAR_ALL);
}
/* Init common part of FM, index is fm num# like fm as above */
int fm_init_common(int index, struct ccsr_fman *reg)
{
int rc;
#if defined(CONFIG_SYS_QE_FMAN_FW_IN_NOR)
void *addr = (void *)CONFIG_SYS_FMAN_FW_ADDR;
#elif defined(CONFIG_SYS_QE_FMAN_FW_IN_NAND)
size_t fw_length = CONFIG_SYS_QE_FMAN_FW_LENGTH;
void *addr = malloc(CONFIG_SYS_QE_FMAN_FW_LENGTH);
rc = nand_read(&nand_info[0], (loff_t)CONFIG_SYS_FMAN_FW_ADDR,
&fw_length, (u_char *)addr);
if (rc == -EUCLEAN) {
printf("NAND read of FMAN firmware at offset 0x%x failed %d\n",
CONFIG_SYS_FMAN_FW_ADDR, rc);
}
#elif defined(CONFIG_SYS_QE_FW_IN_SPIFLASH)
struct spi_flash *ucode_flash;
void *addr = malloc(CONFIG_SYS_QE_FMAN_FW_LENGTH);
int ret = 0;
ucode_flash = spi_flash_probe(CONFIG_ENV_SPI_BUS, CONFIG_ENV_SPI_CS,
CONFIG_ENV_SPI_MAX_HZ, CONFIG_ENV_SPI_MODE);
if (!ucode_flash)
printf("SF: probe for ucode failed\n");
else {
ret = spi_flash_read(ucode_flash, CONFIG_SYS_FMAN_FW_ADDR,
CONFIG_SYS_QE_FMAN_FW_LENGTH, addr);
if (ret)
printf("SF: read for ucode failed\n");
spi_flash_free(ucode_flash);
}
#elif defined(CONFIG_SYS_QE_FMAN_FW_IN_MMC)
int dev = CONFIG_SYS_MMC_ENV_DEV;
void *addr = malloc(CONFIG_SYS_QE_FMAN_FW_LENGTH);
u32 cnt = CONFIG_SYS_QE_FMAN_FW_LENGTH / 512;
u32 blk = CONFIG_SYS_FMAN_FW_ADDR / 512;
struct mmc *mmc = find_mmc_device(CONFIG_SYS_MMC_ENV_DEV);
if (!mmc)
printf("\nMMC cannot find device for ucode\n");
else {
printf("\nMMC read: dev # %u, block # %u, count %u ...\n",
dev, blk, cnt);
mmc_init(mmc);
(void)mmc->block_dev.block_read(dev, blk, cnt, addr);
/* flush cache after read */
flush_cache((ulong)addr, cnt * 512);
}
#elif defined(CONFIG_SYS_QE_FMAN_FW_IN_REMOTE)
void *addr = (void *)CONFIG_SYS_FMAN_FW_ADDR;
#else
void *addr = NULL;
#endif
/* Upload the Fman microcode if it's present */
rc = fman_upload_firmware(index, &reg->fm_imem, addr);
if (rc)
return rc;
setenv_addr("fman_ucode", addr);
fm_init_muram(index, &reg->muram);
fm_init_qmi(&reg->fm_qmi_common);
fm_init_fpm(&reg->fm_fpm);
/* clear DMA status */
setbits_be32(&reg->fm_dma.fmdmsr, FMDMSR_CLEAR_ALL);
/* set DMA mode */
setbits_be32(&reg->fm_dma.fmdmmr, FMDMMR_SBER);
return fm_init_bmi(index, &reg->fm_bmi_common);
}

View file

@ -0,0 +1,149 @@
/*
* Copyright 2009-2011 Freescale Semiconductor, Inc.
*
* SPDX-License-Identifier: GPL-2.0+
*/
#ifndef __FM_H__
#define __FM_H__
#include <common.h>
#include <phy.h>
#include <fm_eth.h>
#include <fsl_fman.h>
/* Port ID */
#define OH_PORT_ID_BASE 0x01
#define MAX_NUM_OH_PORT 7
#define RX_PORT_1G_BASE 0x08
#define MAX_NUM_RX_PORT_1G CONFIG_SYS_NUM_FM1_DTSEC
#define RX_PORT_10G_BASE 0x10
#define RX_PORT_10G_BASE2 0x08
#define TX_PORT_1G_BASE 0x28
#define MAX_NUM_TX_PORT_1G CONFIG_SYS_NUM_FM1_DTSEC
#define TX_PORT_10G_BASE 0x30
#define TX_PORT_10G_BASE2 0x28
#define MIIM_TIMEOUT 0xFFFF
struct fm_muram {
void *base;
void *top;
size_t size;
void *alloc;
};
#define FM_MURAM_RES_SIZE 0x01000
/* Rx/Tx buffer descriptor */
struct fm_port_bd {
u16 status;
u16 len;
u32 res0;
u16 res1;
u16 buf_ptr_hi;
u32 buf_ptr_lo;
};
/* Common BD flags */
#define BD_LAST 0x0800
/* Rx BD status flags */
#define RxBD_EMPTY 0x8000
#define RxBD_LAST BD_LAST
#define RxBD_FIRST 0x0400
#define RxBD_PHYS_ERR 0x0008
#define RxBD_SIZE_ERR 0x0004
#define RxBD_ERROR (RxBD_PHYS_ERR | RxBD_SIZE_ERR)
/* Tx BD status flags */
#define TxBD_READY 0x8000
#define TxBD_LAST BD_LAST
/* Rx/Tx queue descriptor */
struct fm_port_qd {
u16 gen;
u16 bd_ring_base_hi;
u32 bd_ring_base_lo;
u16 bd_ring_size;
u16 offset_in;
u16 offset_out;
u16 res0;
u32 res1[0x4];
};
/* IM global parameter RAM */
struct fm_port_global_pram {
u32 mode; /* independent mode register */
u32 rxqd_ptr; /* Rx queue descriptor pointer */
u32 txqd_ptr; /* Tx queue descriptor pointer */
u16 mrblr; /* max Rx buffer length */
u16 rxqd_bsy_cnt; /* RxQD busy counter, should be cleared */
u32 res0[0x4];
struct fm_port_qd rxqd; /* Rx queue descriptor */
struct fm_port_qd txqd; /* Tx queue descriptor */
u32 res1[0x28];
};
#define FM_PRAM_SIZE sizeof(struct fm_port_global_pram)
#define FM_PRAM_ALIGN 256
#define PRAM_MODE_GLOBAL 0x20000000
#define PRAM_MODE_GRACEFUL_STOP 0x00800000
#if defined(CONFIG_P1017) || defined(CONFIG_P1023)
#define FM_FREE_POOL_SIZE 0x2000 /* 8K bytes */
#else
#define FM_FREE_POOL_SIZE 0x20000 /* 128K bytes */
#endif
#define FM_FREE_POOL_ALIGN 256
void *fm_muram_alloc(int fm_idx, size_t size, ulong align);
void *fm_muram_base(int fm_idx);
int fm_init_common(int index, struct ccsr_fman *reg);
int fm_eth_initialize(struct ccsr_fman *reg, struct fm_eth_info *info);
phy_interface_t fman_port_enet_if(enum fm_port port);
void fman_disable_port(enum fm_port port);
void fman_enable_port(enum fm_port port);
struct fsl_enet_mac {
void *base; /* MAC controller registers base address */
void *phyregs;
int max_rx_len;
void (*init_mac)(struct fsl_enet_mac *mac);
void (*enable_mac)(struct fsl_enet_mac *mac);
void (*disable_mac)(struct fsl_enet_mac *mac);
void (*set_mac_addr)(struct fsl_enet_mac *mac, u8 *mac_addr);
void (*set_if_mode)(struct fsl_enet_mac *mac, phy_interface_t type,
int speed);
};
/* Fman ethernet private struct */
struct fm_eth {
int fm_index; /* Fman index */
u32 num; /* 0..n-1 for give type */
struct fm_bmi_tx_port *tx_port;
struct fm_bmi_rx_port *rx_port;
enum fm_eth_type type; /* 1G or 10G ethernet */
phy_interface_t enet_if;
struct fsl_enet_mac *mac; /* MAC controller */
struct mii_dev *bus;
struct phy_device *phydev;
int phyaddr;
struct eth_device *dev;
int max_rx_len;
struct fm_port_global_pram *rx_pram; /* Rx parameter table */
struct fm_port_global_pram *tx_pram; /* Tx parameter table */
void *rx_bd_ring; /* Rx BD ring base */
void *cur_rxbd; /* current Rx BD */
void *rx_buf; /* Rx buffer base */
void *tx_bd_ring; /* Tx BD ring base */
void *cur_txbd; /* current Tx BD */
};
#define RX_BD_RING_SIZE 8
#define TX_BD_RING_SIZE 8
#define MAX_RXBUF_LOG2 11
#define MAX_RXBUF_LEN (1 << MAX_RXBUF_LOG2)
#define PORT_IS_ENABLED(port) (fm_port_to_index(port) == -1 ? \
0 : fm_info[fm_port_to_index(port)].enabled)
#endif /* __FM_H__ */

View file

@ -0,0 +1,381 @@
/*
* Copyright 2011-2015 Freescale Semiconductor, Inc.
*
* SPDX-License-Identifier: GPL-2.0+
*/
#include <errno.h>
#include <common.h>
#include <asm/io.h>
#include <fsl_mdio.h>
#ifdef CONFIG_FSL_LAYERSCAPE
#include <asm/arch/fsl_serdes.h>
#else
#include <asm/fsl_serdes.h>
#endif
#include "fm.h"
struct fm_eth_info fm_info[] = {
#if (CONFIG_SYS_NUM_FM1_DTSEC >= 1)
FM_DTSEC_INFO_INITIALIZER(1, 1),
#endif
#if (CONFIG_SYS_NUM_FM1_DTSEC >= 2)
FM_DTSEC_INFO_INITIALIZER(1, 2),
#endif
#if (CONFIG_SYS_NUM_FM1_DTSEC >= 3)
FM_DTSEC_INFO_INITIALIZER(1, 3),
#endif
#if (CONFIG_SYS_NUM_FM1_DTSEC >= 4)
FM_DTSEC_INFO_INITIALIZER(1, 4),
#endif
#if (CONFIG_SYS_NUM_FM1_DTSEC >= 5)
FM_DTSEC_INFO_INITIALIZER(1, 5),
#endif
#if (CONFIG_SYS_NUM_FM1_DTSEC >= 6)
FM_DTSEC_INFO_INITIALIZER(1, 6),
#endif
#if (CONFIG_SYS_NUM_FM1_DTSEC >= 7)
FM_DTSEC_INFO_INITIALIZER(1, 9),
#endif
#if (CONFIG_SYS_NUM_FM1_DTSEC >= 8)
FM_DTSEC_INFO_INITIALIZER(1, 10),
#endif
#if (CONFIG_SYS_NUM_FM2_DTSEC >= 1)
FM_DTSEC_INFO_INITIALIZER(2, 1),
#endif
#if (CONFIG_SYS_NUM_FM2_DTSEC >= 2)
FM_DTSEC_INFO_INITIALIZER(2, 2),
#endif
#if (CONFIG_SYS_NUM_FM2_DTSEC >= 3)
FM_DTSEC_INFO_INITIALIZER(2, 3),
#endif
#if (CONFIG_SYS_NUM_FM2_DTSEC >= 4)
FM_DTSEC_INFO_INITIALIZER(2, 4),
#endif
#if (CONFIG_SYS_NUM_FM2_DTSEC >= 5)
FM_DTSEC_INFO_INITIALIZER(2, 5),
#endif
#if (CONFIG_SYS_NUM_FM2_DTSEC >= 6)
FM_DTSEC_INFO_INITIALIZER(2, 6),
#endif
#if (CONFIG_SYS_NUM_FM2_DTSEC >= 7)
FM_DTSEC_INFO_INITIALIZER(2, 9),
#endif
#if (CONFIG_SYS_NUM_FM2_DTSEC >= 8)
FM_DTSEC_INFO_INITIALIZER(2, 10),
#endif
#if (CONFIG_SYS_NUM_FM1_10GEC >= 1)
FM_TGEC_INFO_INITIALIZER(1, 1),
#endif
#if (CONFIG_SYS_NUM_FM1_10GEC >= 2)
FM_TGEC_INFO_INITIALIZER(1, 2),
#endif
#if (CONFIG_SYS_NUM_FM1_10GEC >= 3)
FM_TGEC_INFO_INITIALIZER2(1, 3),
#endif
#if (CONFIG_SYS_NUM_FM1_10GEC >= 4)
FM_TGEC_INFO_INITIALIZER2(1, 4),
#endif
#if (CONFIG_SYS_NUM_FM2_10GEC >= 1)
FM_TGEC_INFO_INITIALIZER(2, 1),
#endif
#if (CONFIG_SYS_NUM_FM2_10GEC >= 2)
FM_TGEC_INFO_INITIALIZER(2, 2),
#endif
};
int fm_standard_init(bd_t *bis)
{
int i;
struct ccsr_fman *reg;
reg = (void *)CONFIG_SYS_FSL_FM1_ADDR;
if (fm_init_common(0, reg))
return 0;
for (i = 0; i < ARRAY_SIZE(fm_info); i++) {
if ((fm_info[i].enabled) && (fm_info[i].index == 1))
fm_eth_initialize(reg, &fm_info[i]);
}
#if (CONFIG_SYS_NUM_FMAN == 2)
reg = (void *)CONFIG_SYS_FSL_FM2_ADDR;
if (fm_init_common(1, reg))
return 0;
for (i = 0; i < ARRAY_SIZE(fm_info); i++) {
if ((fm_info[i].enabled) && (fm_info[i].index == 2))
fm_eth_initialize(reg, &fm_info[i]);
}
#endif
return 1;
}
/* simple linear search to map from port to array index */
static int fm_port_to_index(enum fm_port port)
{
int i;
for (i = 0; i < ARRAY_SIZE(fm_info); i++) {
if (fm_info[i].port == port)
return i;
}
return -1;
}
/*
* Determine if an interface is actually active based on HW config
* we expect fman_port_enet_if() to report PHY_INTERFACE_MODE_NONE if
* the interface is not active based on HW cfg of the SoC
*/
void fman_enet_init(void)
{
int i;
for (i = 0; i < ARRAY_SIZE(fm_info); i++) {
phy_interface_t enet_if;
enet_if = fman_port_enet_if(fm_info[i].port);
if (enet_if != PHY_INTERFACE_MODE_NONE) {
fm_info[i].enabled = 1;
fm_info[i].enet_if = enet_if;
} else {
fm_info[i].enabled = 0;
}
}
return ;
}
void fm_disable_port(enum fm_port port)
{
int i = fm_port_to_index(port);
if (i == -1)
return;
fm_info[i].enabled = 0;
#ifndef CONFIG_SYS_FMAN_V3
fman_disable_port(port);
#endif
}
void fm_enable_port(enum fm_port port)
{
int i = fm_port_to_index(port);
if (i == -1)
return;
fm_info[i].enabled = 1;
fman_enable_port(port);
}
void fm_info_set_mdio(enum fm_port port, struct mii_dev *bus)
{
int i = fm_port_to_index(port);
if (i == -1)
return;
fm_info[i].bus = bus;
}
void fm_info_set_phy_address(enum fm_port port, int address)
{
int i = fm_port_to_index(port);
if (i == -1)
return;
fm_info[i].phy_addr = address;
}
/*
* Returns the PHY address for a given Fman port
*
* The port must be set via a prior call to fm_info_set_phy_address().
* A negative error code is returned if the port is invalid.
*/
int fm_info_get_phy_address(enum fm_port port)
{
int i = fm_port_to_index(port);
if (i == -1)
return -1;
return fm_info[i].phy_addr;
}
/*
* Returns the type of the data interface between the given MAC and its PHY.
* This is typically determined by the RCW.
*/
phy_interface_t fm_info_get_enet_if(enum fm_port port)
{
int i = fm_port_to_index(port);
if (i == -1)
return PHY_INTERFACE_MODE_NONE;
if (fm_info[i].enabled)
return fm_info[i].enet_if;
return PHY_INTERFACE_MODE_NONE;
}
static void
__def_board_ft_fman_fixup_port(void *blob, char * prop, phys_addr_t pa,
enum fm_port port, int offset)
{
return ;
}
void board_ft_fman_fixup_port(void *blob, char * prop, phys_addr_t pa,
enum fm_port port, int offset)
__attribute__((weak, alias("__def_board_ft_fman_fixup_port")));
int ft_fixup_port(void *blob, struct fm_eth_info *info, char *prop)
{
int off;
uint32_t ph;
phys_addr_t paddr = CONFIG_SYS_CCSRBAR_PHYS + info->compat_offset;
#ifndef CONFIG_SYS_FMAN_V3
u64 dtsec1_addr = (u64)CONFIG_SYS_CCSRBAR_PHYS +
CONFIG_SYS_FSL_FM1_DTSEC1_OFFSET;
#endif
off = fdt_node_offset_by_compat_reg(blob, prop, paddr);
if (off == -FDT_ERR_NOTFOUND)
return -EINVAL;
if (info->enabled) {
fdt_fixup_phy_connection(blob, off, info->enet_if);
board_ft_fman_fixup_port(blob, prop, paddr, info->port, off);
return 0;
}
#ifdef CONFIG_SYS_FMAN_V3
#ifndef CONFIG_FSL_FM_10GEC_REGULAR_NOTATION
/*
* On T2/T4 SoCs, physically FM1_DTSEC9 and FM1_10GEC1 use the same
* dual-role MAC, when FM1_10GEC1 is enabled and FM1_DTSEC9
* is disabled, ensure that the dual-role MAC is not disabled,
* ditto for other dual-role MACs.
*/
if (((info->port == FM1_DTSEC9) && (PORT_IS_ENABLED(FM1_10GEC1))) ||
((info->port == FM1_DTSEC10) && (PORT_IS_ENABLED(FM1_10GEC2))) ||
((info->port == FM1_DTSEC1) && (PORT_IS_ENABLED(FM1_10GEC3))) ||
((info->port == FM1_DTSEC2) && (PORT_IS_ENABLED(FM1_10GEC4))) ||
((info->port == FM1_10GEC1) && (PORT_IS_ENABLED(FM1_DTSEC9))) ||
((info->port == FM1_10GEC2) && (PORT_IS_ENABLED(FM1_DTSEC10))) ||
((info->port == FM1_10GEC3) && (PORT_IS_ENABLED(FM1_DTSEC1))) ||
((info->port == FM1_10GEC4) && (PORT_IS_ENABLED(FM1_DTSEC2)))
#if (CONFIG_SYS_NUM_FMAN == 2)
||
((info->port == FM2_DTSEC9) && (PORT_IS_ENABLED(FM2_10GEC1))) ||
((info->port == FM2_DTSEC10) && (PORT_IS_ENABLED(FM2_10GEC2))) ||
((info->port == FM2_10GEC1) && (PORT_IS_ENABLED(FM2_DTSEC9))) ||
((info->port == FM2_10GEC2) && (PORT_IS_ENABLED(FM2_DTSEC10)))
#endif
#else
/* FM1_DTSECx and FM1_10GECx use the same dual-role MAC */
if (((info->port == FM1_DTSEC1) && (PORT_IS_ENABLED(FM1_10GEC1))) ||
((info->port == FM1_DTSEC2) && (PORT_IS_ENABLED(FM1_10GEC2))) ||
((info->port == FM1_DTSEC3) && (PORT_IS_ENABLED(FM1_10GEC3))) ||
((info->port == FM1_DTSEC4) && (PORT_IS_ENABLED(FM1_10GEC4))) ||
((info->port == FM1_10GEC1) && (PORT_IS_ENABLED(FM1_DTSEC1))) ||
((info->port == FM1_10GEC2) && (PORT_IS_ENABLED(FM1_DTSEC2))) ||
((info->port == FM1_10GEC3) && (PORT_IS_ENABLED(FM1_DTSEC3))) ||
((info->port == FM1_10GEC4) && (PORT_IS_ENABLED(FM1_DTSEC4)))
#endif
)
return 0;
#endif
/* board code might have caused offset to change */
off = fdt_node_offset_by_compat_reg(blob, prop, paddr);
#ifndef CONFIG_SYS_FMAN_V3
/* Don't disable FM1-DTSEC1 MAC as its used for MDIO */
if (paddr != dtsec1_addr)
#endif
fdt_status_disabled(blob, off); /* disable the MAC node */
/* disable the fsl,dpa-ethernet node that points to the MAC */
ph = fdt_get_phandle(blob, off);
do_fixup_by_prop(blob, "fsl,fman-mac", &ph, sizeof(ph),
"status", "disabled", strlen("disabled") + 1, 1);
return 0;
}
void fdt_fixup_fman_ethernet(void *blob)
{
int i;
#ifdef CONFIG_SYS_FMAN_V3
for (i = 0; i < ARRAY_SIZE(fm_info); i++)
ft_fixup_port(blob, &fm_info[i], "fsl,fman-memac");
#else
for (i = 0; i < ARRAY_SIZE(fm_info); i++) {
/* Try the new compatible first.
* If the node is missing, try the old.
*/
if (fm_info[i].type == FM_ETH_1G_E) {
if (ft_fixup_port(blob, &fm_info[i], "fsl,fman-dtsec"))
ft_fixup_port(blob, &fm_info[i],
"fsl,fman-1g-mac");
} else {
if (ft_fixup_port(blob, &fm_info[i], "fsl,fman-tgec"))
ft_fixup_port(blob, &fm_info[i],
"fsl,fman-10g-mac");
}
}
#endif
}
/*QSGMII Riser Card can work in SGMII mode, but the PHY address is different.
*This function scans which Riser Card being used(QSGMII or SGMII Riser Card),
*then set the correct PHY address
*/
void set_sgmii_phy(struct mii_dev *bus, enum fm_port base_port,
unsigned int port_num, int phy_base_addr)
{
unsigned int regnum = 0;
int qsgmii;
int i;
int phy_real_addr;
qsgmii = is_qsgmii_riser_card(bus, phy_base_addr, port_num, regnum);
if (!qsgmii)
return;
for (i = base_port; i < base_port + port_num; i++) {
if (fm_info_get_enet_if(i) == PHY_INTERFACE_MODE_SGMII) {
phy_real_addr = phy_base_addr + i - base_port;
fm_info_set_phy_address(i, phy_real_addr);
}
}
}
/*to check whether qsgmii riser card is used*/
int is_qsgmii_riser_card(struct mii_dev *bus, int phy_base_addr,
unsigned int port_num, unsigned regnum)
{
int i;
int val;
if (!bus)
return 0;
for (i = phy_base_addr; i < phy_base_addr + port_num; i++) {
val = bus->read(bus, i, MDIO_DEVAD_NONE, regnum);
if (val != MIIM_TIMEOUT)
return 1;
}
return 0;
}

View file

@ -0,0 +1,112 @@
/*
* Copyright 2015 Freescale Semiconductor, Inc.
*
* SPDX-License-Identifier: GPL-2.0+
*/
#include <common.h>
#include <phy.h>
#include <fm_eth.h>
#include <asm/io.h>
#include <asm/arch/fsl_serdes.h>
#define FSL_CHASSIS2_RCWSR13_EC1 0xe0000000 /* bits 416..418 */
#define FSL_CHASSIS2_RCWSR13_EC1_DTSEC3_RGMII 0x00000000
#define FSL_CHASSIS2_RCWSR13_EC1_GPIO 0x20000000
#define FSL_CHASSIS2_RCWSR13_EC1_FTM 0xa0000000
#define FSL_CHASSIS2_RCWSR13_EC2 0x1c000000 /* bits 419..421 */
#define FSL_CHASSIS2_RCWSR13_EC2_DTSEC4_RGMII 0x00000000
#define FSL_CHASSIS2_RCWSR13_EC2_GPIO 0x04000000
#define FSL_CHASSIS2_RCWSR13_EC2_1588 0x08000000
#define FSL_CHASSIS2_RCWSR13_EC2_FTM 0x14000000
u32 port_to_devdisr[] = {
[FM1_DTSEC1] = FSL_CHASSIS2_DEVDISR2_DTSEC1_1,
[FM1_DTSEC2] = FSL_CHASSIS2_DEVDISR2_DTSEC1_2,
[FM1_DTSEC3] = FSL_CHASSIS2_DEVDISR2_DTSEC1_3,
[FM1_DTSEC4] = FSL_CHASSIS2_DEVDISR2_DTSEC1_4,
[FM1_DTSEC5] = FSL_CHASSIS2_DEVDISR2_DTSEC1_5,
[FM1_DTSEC6] = FSL_CHASSIS2_DEVDISR2_DTSEC1_6,
[FM1_DTSEC9] = FSL_CHASSIS2_DEVDISR2_DTSEC1_9,
[FM1_DTSEC10] = FSL_CHASSIS2_DEVDISR2_DTSEC1_10,
[FM1_10GEC1] = FSL_CHASSIS2_DEVDISR2_10GEC1_1,
[FM1_10GEC2] = FSL_CHASSIS2_DEVDISR2_10GEC1_2,
[FM1_10GEC3] = FSL_CHASSIS2_DEVDISR2_10GEC1_3,
[FM1_10GEC4] = FSL_CHASSIS2_DEVDISR2_10GEC1_4,
};
static int is_device_disabled(enum fm_port port)
{
struct ccsr_gur *gur = (void *)(CONFIG_SYS_FSL_GUTS_ADDR);
u32 devdisr2 = in_be32(&gur->devdisr2);
return port_to_devdisr[port] & devdisr2;
}
void fman_disable_port(enum fm_port port)
{
struct ccsr_gur *gur = (void *)(CONFIG_SYS_FSL_GUTS_ADDR);
setbits_be32(&gur->devdisr2, port_to_devdisr[port]);
}
phy_interface_t fman_port_enet_if(enum fm_port port)
{
struct ccsr_gur *gur = (void *)(CONFIG_SYS_FSL_GUTS_ADDR);
u32 rcwsr13 = in_be32(&gur->rcwsr[13]);
if (is_device_disabled(port))
return PHY_INTERFACE_MODE_NONE;
if ((port == FM1_10GEC1) && (is_serdes_configured(XFI_FM1_MAC9)))
return PHY_INTERFACE_MODE_XGMII;
if ((port == FM1_DTSEC9) && (is_serdes_configured(XFI_FM1_MAC9)))
return PHY_INTERFACE_MODE_NONE;
if (port == FM1_DTSEC3)
if ((rcwsr13 & FSL_CHASSIS2_RCWSR13_EC1) ==
FSL_CHASSIS2_RCWSR13_EC1_DTSEC3_RGMII) {
return PHY_INTERFACE_MODE_RGMII;
}
if (port == FM1_DTSEC4)
if ((rcwsr13 & FSL_CHASSIS2_RCWSR13_EC2) ==
FSL_CHASSIS2_RCWSR13_EC2_DTSEC4_RGMII) {
return PHY_INTERFACE_MODE_RGMII;
}
/* handle SGMII */
switch (port) {
case FM1_DTSEC1:
case FM1_DTSEC2:
if ((port == FM1_DTSEC2) &&
is_serdes_configured(SGMII_2500_FM1_DTSEC2))
return PHY_INTERFACE_MODE_SGMII_2500;
case FM1_DTSEC5:
case FM1_DTSEC6:
case FM1_DTSEC9:
if (is_serdes_configured(SGMII_FM1_DTSEC1 + port - FM1_DTSEC1))
return PHY_INTERFACE_MODE_SGMII;
else if ((port == FM1_DTSEC9) &&
is_serdes_configured(SGMII_2500_FM1_DTSEC9))
return PHY_INTERFACE_MODE_SGMII_2500;
break;
default:
break;
}
/* handle QSGMII */
switch (port) {
case FM1_DTSEC1:
case FM1_DTSEC2:
case FM1_DTSEC5:
case FM1_DTSEC6:
/* only MAC 1,2,5,6 available for QSGMII */
if (is_serdes_configured(QSGMII_FM1_A))
return PHY_INTERFACE_MODE_QSGMII;
break;
default:
break;
}
return PHY_INTERFACE_MODE_NONE;
}

View file

@ -0,0 +1,142 @@
/*
* Copyright 2012 Freescale Semiconductor, Inc.
* Roy Zang <tie-fei.zang@freescale.com>
*
* SPDX-License-Identifier: GPL-2.0+
*/
/* MAXFRM - maximum frame length */
#define MAXFRM_MASK 0x0000ffff
#include <common.h>
#include <phy.h>
#include <asm/types.h>
#include <asm/io.h>
#include <fsl_memac.h>
#include "fm.h"
static void memac_init_mac(struct fsl_enet_mac *mac)
{
struct memac *regs = mac->base;
/* mask all interrupt */
out_be32(&regs->imask, IMASK_MASK_ALL);
/* clear all events */
out_be32(&regs->ievent, IEVENT_CLEAR_ALL);
/* set the max receive length */
out_be32(&regs->maxfrm, mac->max_rx_len & MAXFRM_MASK);
/* multicast frame reception for the hash entry disable */
out_be32(&regs->hashtable_ctrl, 0);
}
static void memac_enable_mac(struct fsl_enet_mac *mac)
{
struct memac *regs = mac->base;
setbits_be32(&regs->command_config,
MEMAC_CMD_CFG_RXTX_EN | MEMAC_CMD_CFG_NO_LEN_CHK);
}
static void memac_disable_mac(struct fsl_enet_mac *mac)
{
struct memac *regs = mac->base;
clrbits_be32(&regs->command_config, MEMAC_CMD_CFG_RXTX_EN);
}
static void memac_set_mac_addr(struct fsl_enet_mac *mac, u8 *mac_addr)
{
struct memac *regs = mac->base;
u32 mac_addr0, mac_addr1;
/*
* if a station address of 0x12345678ABCD, perform a write to
* MAC_ADDR0 of 0x78563412, MAC_ADDR1 of 0x0000CDAB
*/
mac_addr0 = (mac_addr[3] << 24) | (mac_addr[2] << 16) | \
(mac_addr[1] << 8) | (mac_addr[0]);
out_be32(&regs->mac_addr_0, mac_addr0);
mac_addr1 = ((mac_addr[5] << 8) | mac_addr[4]) & 0x0000ffff;
out_be32(&regs->mac_addr_1, mac_addr1);
}
static void memac_set_interface_mode(struct fsl_enet_mac *mac,
phy_interface_t type, int speed)
{
/* Roy need more work here */
struct memac *regs = mac->base;
u32 if_mode, if_status;
/* clear all bits relative with interface mode */
if_mode = in_be32(&regs->if_mode);
if_status = in_be32(&regs->if_status);
/* set interface mode */
switch (type) {
case PHY_INTERFACE_MODE_GMII:
if_mode &= ~IF_MODE_MASK;
if_mode |= IF_MODE_GMII;
break;
case PHY_INTERFACE_MODE_RGMII:
if_mode |= (IF_MODE_GMII | IF_MODE_RG);
break;
case PHY_INTERFACE_MODE_RMII:
if_mode |= (IF_MODE_GMII | IF_MODE_RM);
break;
case PHY_INTERFACE_MODE_SGMII:
case PHY_INTERFACE_MODE_QSGMII:
if_mode &= ~IF_MODE_MASK;
if_mode |= (IF_MODE_GMII);
break;
case PHY_INTERFACE_MODE_XGMII:
if_mode &= ~IF_MODE_MASK;
if_mode |= IF_MODE_XGMII;
break;
default:
break;
}
/* Enable automatic speed selection for Non-XGMII */
if (type != PHY_INTERFACE_MODE_XGMII)
if_mode |= IF_MODE_EN_AUTO;
if (type == PHY_INTERFACE_MODE_RGMII) {
if_mode &= ~IF_MODE_EN_AUTO;
if_mode &= ~IF_MODE_SETSP_MASK;
switch (speed) {
case SPEED_1000:
if_mode |= IF_MODE_SETSP_1000M;
break;
case SPEED_100:
if_mode |= IF_MODE_SETSP_100M;
break;
case SPEED_10:
if_mode |= IF_MODE_SETSP_10M;
default:
break;
}
}
debug(" %s, if_mode = %x\n", __func__, if_mode);
debug(" %s, if_status = %x\n", __func__, if_status);
out_be32(&regs->if_mode, if_mode);
return;
}
void init_memac(struct fsl_enet_mac *mac, void *base,
void *phyregs, int max_rx_len)
{
mac->base = base;
mac->phyregs = phyregs;
mac->max_rx_len = max_rx_len;
mac->init_mac = memac_init_mac;
mac->enable_mac = memac_enable_mac;
mac->disable_mac = memac_disable_mac;
mac->set_mac_addr = memac_set_mac_addr;
mac->set_if_mode = memac_set_interface_mode;
}

View file

@ -0,0 +1,170 @@
/*
* Copyright 2012 Freescale Semiconductor, Inc.
* Andy Fleming <afleming@gmail.com>
* Roy Zang <tie-fei.zang@freescale.com>
*
* SPDX-License-Identifier: GPL-2.0+
* Some part is taken from tsec.c
*/
#include <common.h>
#include <miiphy.h>
#include <phy.h>
#include <asm/io.h>
#include <fsl_memac.h>
#include <fm_eth.h>
#ifdef CONFIG_SYS_MEMAC_LITTLE_ENDIAN
#define memac_out_32(a, v) out_le32(a, v)
#define memac_clrbits_32(a, v) clrbits_le32(a, v)
#define memac_setbits_32(a, v) setbits_le32(a, v)
#else
#define memac_out_32(a, v) out_be32(a, v)
#define memac_clrbits_32(a, v) clrbits_be32(a, v)
#define memac_setbits_32(a, v) setbits_be32(a, v)
#endif
static u32 memac_in_32(u32 *reg)
{
#ifdef CONFIG_SYS_MEMAC_LITTLE_ENDIAN
return in_le32(reg);
#else
return in_be32(reg);
#endif
}
/*
* Write value to the PHY for this device to the register at regnum, waiting
* until the write is done before it returns. All PHY configuration has to be
* done through the TSEC1 MIIM regs
*/
int memac_mdio_write(struct mii_dev *bus, int port_addr, int dev_addr,
int regnum, u16 value)
{
u32 mdio_ctl;
struct memac_mdio_controller *regs = bus->priv;
u32 c45 = 1; /* Default to 10G interface */
if (dev_addr == MDIO_DEVAD_NONE) {
c45 = 0; /* clause 22 */
dev_addr = regnum & 0x1f;
memac_clrbits_32(&regs->mdio_stat, MDIO_STAT_ENC);
} else
memac_setbits_32(&regs->mdio_stat, MDIO_STAT_ENC);
/* Wait till the bus is free */
while ((memac_in_32(&regs->mdio_stat)) & MDIO_STAT_BSY)
;
/* Set the port and dev addr */
mdio_ctl = MDIO_CTL_PORT_ADDR(port_addr) | MDIO_CTL_DEV_ADDR(dev_addr);
memac_out_32(&regs->mdio_ctl, mdio_ctl);
/* Set the register address */
if (c45)
memac_out_32(&regs->mdio_addr, regnum & 0xffff);
/* Wait till the bus is free */
while ((memac_in_32(&regs->mdio_stat)) & MDIO_STAT_BSY)
;
/* Write the value to the register */
memac_out_32(&regs->mdio_data, MDIO_DATA(value));
/* Wait till the MDIO write is complete */
while ((memac_in_32(&regs->mdio_data)) & MDIO_DATA_BSY)
;
return 0;
}
/*
* Reads from register regnum in the PHY for device dev, returning the value.
* Clears miimcom first. All PHY configuration has to be done through the
* TSEC1 MIIM regs
*/
int memac_mdio_read(struct mii_dev *bus, int port_addr, int dev_addr,
int regnum)
{
u32 mdio_ctl;
struct memac_mdio_controller *regs = bus->priv;
u32 c45 = 1;
if (dev_addr == MDIO_DEVAD_NONE) {
if (!strcmp(bus->name, DEFAULT_FM_TGEC_MDIO_NAME))
return 0xffff;
c45 = 0; /* clause 22 */
dev_addr = regnum & 0x1f;
memac_clrbits_32(&regs->mdio_stat, MDIO_STAT_ENC);
} else
memac_setbits_32(&regs->mdio_stat, MDIO_STAT_ENC);
/* Wait till the bus is free */
while ((memac_in_32(&regs->mdio_stat)) & MDIO_STAT_BSY)
;
/* Set the Port and Device Addrs */
mdio_ctl = MDIO_CTL_PORT_ADDR(port_addr) | MDIO_CTL_DEV_ADDR(dev_addr);
memac_out_32(&regs->mdio_ctl, mdio_ctl);
/* Set the register address */
if (c45)
memac_out_32(&regs->mdio_addr, regnum & 0xffff);
/* Wait till the bus is free */
while ((memac_in_32(&regs->mdio_stat)) & MDIO_STAT_BSY)
;
/* Initiate the read */
mdio_ctl |= MDIO_CTL_READ;
memac_out_32(&regs->mdio_ctl, mdio_ctl);
/* Wait till the MDIO write is complete */
while ((memac_in_32(&regs->mdio_data)) & MDIO_DATA_BSY)
;
/* Return all Fs if nothing was there */
if (memac_in_32(&regs->mdio_stat) & MDIO_STAT_RD_ER)
return 0xffff;
return memac_in_32(&regs->mdio_data) & 0xffff;
}
int memac_mdio_reset(struct mii_dev *bus)
{
return 0;
}
int fm_memac_mdio_init(bd_t *bis, struct memac_mdio_info *info)
{
struct mii_dev *bus = mdio_alloc();
if (!bus) {
printf("Failed to allocate FM TGEC MDIO bus\n");
return -1;
}
bus->read = memac_mdio_read;
bus->write = memac_mdio_write;
bus->reset = memac_mdio_reset;
sprintf(bus->name, info->name);
bus->priv = info->regs;
/*
* On some platforms like B4860, default value of MDIO_CLK_DIV bits
* in mdio_stat(mdio_cfg) register generates MDIO clock too high
* (much higher than 2.5MHz), violating the IEEE specs.
* On other platforms like T1040, default value of MDIO_CLK_DIV bits
* is zero, so MDIO clock is disabled.
* So, for proper functioning of MDIO, MDIO_CLK_DIV bits needs to
* be properly initialized.
* NEG bit default should be '1' as per FMAN-v3 RM, but on platform
* like T2080QDS, this bit default is '0', which leads to MDIO failure
* on XAUI PHY, so set this bit definitely.
*/
memac_setbits_32(
&((struct memac_mdio_controller *)info->regs)->mdio_stat,
MDIO_STAT_CLKDIV(258) | MDIO_STAT_NEG);
return mdio_register(bus);
}

View file

@ -0,0 +1,73 @@
/*
* Copyright 2011 Freescale Semiconductor, Inc.
*
* SPDX-License-Identifier: GPL-2.0+
*/
#include <common.h>
#include <phy.h>
#include <fm_eth.h>
#include <asm/io.h>
#include <asm/immap_85xx.h>
#include <asm/fsl_serdes.h>
static u32 port_to_devdisr[] = {
[FM1_DTSEC1] = MPC85xx_DEVDISR_TSEC1,
[FM1_DTSEC2] = MPC85xx_DEVDISR_TSEC2,
};
static int is_device_disabled(enum fm_port port)
{
ccsr_gur_t *gur = (void *)(CONFIG_SYS_MPC85xx_GUTS_ADDR);
u32 devdisr = in_be32(&gur->devdisr);
return port_to_devdisr[port] & devdisr;
}
void fman_disable_port(enum fm_port port)
{
ccsr_gur_t *gur = (void *)(CONFIG_SYS_MPC85xx_GUTS_ADDR);
/* don't allow disabling of DTSEC1 as its needed for MDIO */
if (port == FM1_DTSEC1)
return;
setbits_be32(&gur->devdisr, port_to_devdisr[port]);
}
void fman_enable_port(enum fm_port port)
{
ccsr_gur_t *gur = (void *)(CONFIG_SYS_MPC85xx_GUTS_ADDR);
clrbits_be32(&gur->devdisr, port_to_devdisr[port]);
}
phy_interface_t fman_port_enet_if(enum fm_port port)
{
ccsr_gur_t *gur = (void *)(CONFIG_SYS_MPC85xx_GUTS_ADDR);
u32 pordevsr = in_be32(&gur->pordevsr);
if (is_device_disabled(port))
return PHY_INTERFACE_MODE_NONE;
/* DTSEC1 can be SGMII, RGMII or RMII */
if (port == FM1_DTSEC1) {
if (is_serdes_configured(SGMII_FM1_DTSEC1))
return PHY_INTERFACE_MODE_SGMII;
if (pordevsr & MPC85xx_PORDEVSR_SGMII1_DIS) {
if (pordevsr & MPC85xx_PORDEVSR_TSEC1_PRTC)
return PHY_INTERFACE_MODE_RGMII;
else
return PHY_INTERFACE_MODE_RMII;
}
}
/* DTSEC2 only supports SGMII or RGMII */
if (port == FM1_DTSEC2) {
if (is_serdes_configured(SGMII_FM1_DTSEC2))
return PHY_INTERFACE_MODE_SGMII;
if (pordevsr & MPC85xx_PORDEVSR_SGMII2_DIS)
return PHY_INTERFACE_MODE_RGMII;
}
return PHY_INTERFACE_MODE_NONE;
}

View file

@ -0,0 +1,99 @@
/*
* Copyright 2011 Freescale Semiconductor, Inc.
*
* SPDX-License-Identifier: GPL-2.0+
*/
#include <common.h>
#include <phy.h>
#include <fm_eth.h>
#include <asm/io.h>
#include <asm/immap_85xx.h>
#include <asm/fsl_serdes.h>
static u32 port_to_devdisr[] = {
[FM1_DTSEC1] = FSL_CORENET_DEVDISR2_DTSEC1_1,
[FM1_DTSEC2] = FSL_CORENET_DEVDISR2_DTSEC1_2,
[FM1_DTSEC3] = FSL_CORENET_DEVDISR2_DTSEC1_3,
[FM1_DTSEC4] = FSL_CORENET_DEVDISR2_DTSEC1_4,
[FM1_10GEC1] = FSL_CORENET_DEVDISR2_10GEC1,
[FM2_DTSEC1] = FSL_CORENET_DEVDISR2_DTSEC2_1,
[FM2_DTSEC2] = FSL_CORENET_DEVDISR2_DTSEC2_2,
[FM2_DTSEC3] = FSL_CORENET_DEVDISR2_DTSEC2_3,
[FM2_DTSEC4] = FSL_CORENET_DEVDISR2_DTSEC2_4,
[FM2_10GEC1] = FSL_CORENET_DEVDISR2_10GEC2,
};
static int is_device_disabled(enum fm_port port)
{
ccsr_gur_t *gur = (void *)(CONFIG_SYS_MPC85xx_GUTS_ADDR);
u32 devdisr2 = in_be32(&gur->devdisr2);
return port_to_devdisr[port] & devdisr2;
}
void fman_disable_port(enum fm_port port)
{
ccsr_gur_t *gur = (void *)(CONFIG_SYS_MPC85xx_GUTS_ADDR);
/* don't allow disabling of DTSEC1 as its needed for MDIO */
if (port == FM1_DTSEC1)
return;
setbits_be32(&gur->devdisr2, port_to_devdisr[port]);
}
void fman_enable_port(enum fm_port port)
{
ccsr_gur_t *gur = (void *)(CONFIG_SYS_MPC85xx_GUTS_ADDR);
clrbits_be32(&gur->devdisr2, port_to_devdisr[port]);
}
phy_interface_t fman_port_enet_if(enum fm_port port)
{
ccsr_gur_t *gur = (void *)(CONFIG_SYS_MPC85xx_GUTS_ADDR);
u32 rcwsr11 = in_be32(&gur->rcwsr[11]);
if (is_device_disabled(port))
return PHY_INTERFACE_MODE_NONE;
if ((port == FM1_10GEC1) && (is_serdes_configured(XAUI_FM1)))
return PHY_INTERFACE_MODE_XGMII;
if ((port == FM2_10GEC1) && (is_serdes_configured(XAUI_FM2)))
return PHY_INTERFACE_MODE_XGMII;
/* handle RGMII first */
if ((port == FM1_DTSEC1) && ((rcwsr11 & FSL_CORENET_RCWSR11_EC1) ==
FSL_CORENET_RCWSR11_EC1_FM1_DTSEC1))
return PHY_INTERFACE_MODE_RGMII;
if ((port == FM1_DTSEC2) && ((rcwsr11 & FSL_CORENET_RCWSR11_EC2) ==
FSL_CORENET_RCWSR11_EC2_FM1_DTSEC2))
return PHY_INTERFACE_MODE_RGMII;
if ((port == FM2_DTSEC1) && ((rcwsr11 & FSL_CORENET_RCWSR11_EC2) ==
FSL_CORENET_RCWSR11_EC2_FM2_DTSEC1))
return PHY_INTERFACE_MODE_RGMII;
switch (port) {
case FM1_DTSEC1:
case FM1_DTSEC2:
case FM1_DTSEC3:
case FM1_DTSEC4:
if (is_serdes_configured(SGMII_FM1_DTSEC1 + port - FM1_DTSEC1))
return PHY_INTERFACE_MODE_SGMII;
break;
case FM2_DTSEC1:
case FM2_DTSEC2:
case FM2_DTSEC3:
case FM2_DTSEC4:
if (is_serdes_configured(SGMII_FM2_DTSEC1 + port - FM2_DTSEC1))
return PHY_INTERFACE_MODE_SGMII;
break;
default:
return PHY_INTERFACE_MODE_NONE;
}
return PHY_INTERFACE_MODE_NONE;
}

View file

@ -0,0 +1,90 @@
/*
* Copyright 2011 Freescale Semiconductor, Inc.
*
* SPDX-License-Identifier: GPL-2.0+
*/
#include <common.h>
#include <phy.h>
#include <fm_eth.h>
#include <asm/io.h>
#include <asm/immap_85xx.h>
#include <asm/fsl_serdes.h>
static u32 port_to_devdisr[] = {
[FM1_DTSEC1] = FSL_CORENET_DEVDISR2_DTSEC1_1,
[FM1_DTSEC2] = FSL_CORENET_DEVDISR2_DTSEC1_2,
[FM1_DTSEC3] = FSL_CORENET_DEVDISR2_DTSEC1_3,
[FM1_DTSEC4] = FSL_CORENET_DEVDISR2_DTSEC1_4,
[FM1_DTSEC5] = FSL_CORENET_DEVDISR2_DTSEC1_5,
[FM1_10GEC1] = FSL_CORENET_DEVDISR2_10GEC1,
};
static int is_device_disabled(enum fm_port port)
{
ccsr_gur_t *gur = (void *)(CONFIG_SYS_MPC85xx_GUTS_ADDR);
u32 devdisr2 = in_be32(&gur->devdisr2);
return port_to_devdisr[port] & devdisr2;
}
void fman_disable_port(enum fm_port port)
{
ccsr_gur_t *gur = (void *)(CONFIG_SYS_MPC85xx_GUTS_ADDR);
/* don't allow disabling of DTSEC1 as its needed for MDIO */
if (port == FM1_DTSEC1)
return;
setbits_be32(&gur->devdisr2, port_to_devdisr[port]);
}
void fman_enable_port(enum fm_port port)
{
ccsr_gur_t *gur = (void *)(CONFIG_SYS_MPC85xx_GUTS_ADDR);
clrbits_be32(&gur->devdisr2, port_to_devdisr[port]);
}
phy_interface_t fman_port_enet_if(enum fm_port port)
{
ccsr_gur_t *gur = (void *)(CONFIG_SYS_MPC85xx_GUTS_ADDR);
u32 rcwsr11 = in_be32(&gur->rcwsr[11]);
if (is_device_disabled(port))
return PHY_INTERFACE_MODE_NONE;
if ((port == FM1_10GEC1) && (is_serdes_configured(XAUI_FM1)))
return PHY_INTERFACE_MODE_XGMII;
/* handle RGMII first */
if ((port == FM1_DTSEC4) && ((rcwsr11 & FSL_CORENET_RCWSR11_EC1) ==
FSL_CORENET_RCWSR11_EC1_FM1_DTSEC4_RGMII))
return PHY_INTERFACE_MODE_RGMII;
if ((port == FM1_DTSEC4) && ((rcwsr11 & FSL_CORENET_RCWSR11_EC1) ==
FSL_CORENET_RCWSR11_EC1_FM1_DTSEC4_MII))
return PHY_INTERFACE_MODE_MII;
if ((port == FM1_DTSEC5) && ((rcwsr11 & FSL_CORENET_RCWSR11_EC2) ==
FSL_CORENET_RCWSR11_EC2_FM1_DTSEC5_RGMII))
return PHY_INTERFACE_MODE_RGMII;
if ((port == FM1_DTSEC5) && ((rcwsr11 & FSL_CORENET_RCWSR11_EC2) ==
FSL_CORENET_RCWSR11_EC2_FM1_DTSEC5_MII))
return PHY_INTERFACE_MODE_MII;
switch (port) {
case FM1_DTSEC1:
case FM1_DTSEC2:
case FM1_DTSEC3:
case FM1_DTSEC4:
case FM1_DTSEC5:
if (is_serdes_configured(SGMII_FM1_DTSEC1 + port - FM1_DTSEC1))
return PHY_INTERFACE_MODE_SGMII;
break;
default:
return PHY_INTERFACE_MODE_NONE;
}
return PHY_INTERFACE_MODE_NONE;
}

View file

@ -0,0 +1,107 @@
/*
* Copyright 2011 Freescale Semiconductor, Inc.
*
* SPDX-License-Identifier: GPL-2.0+
*/
#include <common.h>
#include <phy.h>
#include <fm_eth.h>
#include <asm/io.h>
#include <asm/immap_85xx.h>
#include <asm/fsl_serdes.h>
u32 port_to_devdisr[] = {
[FM1_DTSEC1] = FSL_CORENET_DEVDISR2_DTSEC1_1,
[FM1_DTSEC2] = FSL_CORENET_DEVDISR2_DTSEC1_2,
[FM1_DTSEC3] = FSL_CORENET_DEVDISR2_DTSEC1_3,
[FM1_DTSEC4] = FSL_CORENET_DEVDISR2_DTSEC1_4,
[FM1_DTSEC5] = FSL_CORENET_DEVDISR2_DTSEC1_5,
[FM1_10GEC1] = FSL_CORENET_DEVDISR2_10GEC1,
[FM2_DTSEC1] = FSL_CORENET_DEVDISR2_DTSEC2_1,
[FM2_DTSEC2] = FSL_CORENET_DEVDISR2_DTSEC2_2,
[FM2_DTSEC3] = FSL_CORENET_DEVDISR2_DTSEC2_3,
[FM2_DTSEC4] = FSL_CORENET_DEVDISR2_DTSEC2_4,
[FM2_DTSEC5] = FSL_CORENET_DEVDISR2_DTSEC2_5,
[FM2_10GEC1] = FSL_CORENET_DEVDISR2_10GEC2,
};
static int is_device_disabled(enum fm_port port)
{
ccsr_gur_t *gur = (void *)(CONFIG_SYS_MPC85xx_GUTS_ADDR);
u32 devdisr2 = in_be32(&gur->devdisr2);
return port_to_devdisr[port] & devdisr2;
}
void fman_disable_port(enum fm_port port)
{
ccsr_gur_t *gur = (void *)(CONFIG_SYS_MPC85xx_GUTS_ADDR);
/* don't allow disabling of DTSEC1 as its needed for MDIO */
if (port == FM1_DTSEC1)
return;
setbits_be32(&gur->devdisr2, port_to_devdisr[port]);
}
void fman_enable_port(enum fm_port port)
{
ccsr_gur_t *gur = (void *)(CONFIG_SYS_MPC85xx_GUTS_ADDR);
clrbits_be32(&gur->devdisr2, port_to_devdisr[port]);
}
phy_interface_t fman_port_enet_if(enum fm_port port)
{
ccsr_gur_t *gur = (void *)(CONFIG_SYS_MPC85xx_GUTS_ADDR);
u32 rcwsr11 = in_be32(&gur->rcwsr[11]);
if (is_device_disabled(port))
return PHY_INTERFACE_MODE_NONE;
if ((port == FM1_10GEC1) && (is_serdes_configured(XAUI_FM1)))
return PHY_INTERFACE_MODE_XGMII;
if ((port == FM2_10GEC1) && (is_serdes_configured(XAUI_FM2)))
return PHY_INTERFACE_MODE_XGMII;
/* handle RGMII first */
if ((port == FM1_DTSEC5) && ((rcwsr11 & FSL_CORENET_RCWSR11_EC1) ==
FSL_CORENET_RCWSR11_EC1_FM1_DTSEC5_RGMII))
return PHY_INTERFACE_MODE_RGMII;
if ((port == FM1_DTSEC5) && ((rcwsr11 & FSL_CORENET_RCWSR11_EC1) ==
FSL_CORENET_RCWSR11_EC1_FM1_DTSEC5_MII))
return PHY_INTERFACE_MODE_MII;
if ((port == FM2_DTSEC5) && ((rcwsr11 & FSL_CORENET_RCWSR11_EC2) ==
FSL_CORENET_RCWSR11_EC2_FM2_DTSEC5_RGMII))
return PHY_INTERFACE_MODE_RGMII;
if ((port == FM2_DTSEC5) && ((rcwsr11 & FSL_CORENET_RCWSR11_EC2) ==
FSL_CORENET_RCWSR11_EC2_FM2_DTSEC5_MII))
return PHY_INTERFACE_MODE_MII;
switch (port) {
case FM1_DTSEC1:
case FM1_DTSEC2:
case FM1_DTSEC3:
case FM1_DTSEC4:
case FM1_DTSEC5:
if (is_serdes_configured(SGMII_FM1_DTSEC1 + port - FM1_DTSEC1))
return PHY_INTERFACE_MODE_SGMII;
break;
case FM2_DTSEC1:
case FM2_DTSEC2:
case FM2_DTSEC3:
case FM2_DTSEC4:
case FM2_DTSEC5:
if (is_serdes_configured(SGMII_FM2_DTSEC1 + port - FM2_DTSEC1))
return PHY_INTERFACE_MODE_SGMII;
break;
default:
return PHY_INTERFACE_MODE_NONE;
}
return PHY_INTERFACE_MODE_NONE;
}

View file

@ -0,0 +1,88 @@
/* Copyright 2014 Freescale Semiconductor, Inc.
*
* Shengzhou Liu <Shengzhou.Liu@freescale.com>
*
* SPDX-License-Identifier: GPL-2.0+
*/
#include <common.h>
#include <phy.h>
#include <fm_eth.h>
#include <asm/immap_85xx.h>
#include <asm/fsl_serdes.h>
u32 port_to_devdisr[] = {
[FM1_DTSEC1] = FSL_CORENET_DEVDISR2_DTSEC1_1,
[FM1_DTSEC2] = FSL_CORENET_DEVDISR2_DTSEC1_2,
[FM1_DTSEC3] = FSL_CORENET_DEVDISR2_DTSEC1_3,
[FM1_DTSEC4] = FSL_CORENET_DEVDISR2_DTSEC1_4,
[FM1_10GEC1] = FSL_CORENET_DEVDISR2_10GEC1_1, /* MAC1 */
};
static int is_device_disabled(enum fm_port port)
{
ccsr_gur_t *gur = (void __iomem *)(CONFIG_SYS_MPC85xx_GUTS_ADDR);
u32 devdisr2 = in_be32(&gur->devdisr2);
return port_to_devdisr[port] & devdisr2;
}
void fman_disable_port(enum fm_port port)
{
ccsr_gur_t *gur = (void __iomem *)(CONFIG_SYS_MPC85xx_GUTS_ADDR);
setbits_be32(&gur->devdisr2, port_to_devdisr[port]);
}
phy_interface_t fman_port_enet_if(enum fm_port port)
{
ccsr_gur_t *gur = (void __iomem *)(CONFIG_SYS_MPC85xx_GUTS_ADDR);
u32 rcwsr13 = in_be32(&gur->rcwsr[13]);
if (is_device_disabled(port))
return PHY_INTERFACE_MODE_NONE;
if ((port == FM1_10GEC1) && (is_serdes_configured(XFI_FM1_MAC1)))
return PHY_INTERFACE_MODE_XGMII;
if ((port == FM1_DTSEC3) && ((rcwsr13 & FSL_CORENET_RCWSR13_EC2) ==
FSL_CORENET_RCWSR13_EC2_RGMII) &&
(!is_serdes_configured(QSGMII_FM1_A)))
return PHY_INTERFACE_MODE_RGMII;
if ((port == FM1_DTSEC4) && ((rcwsr13 & FSL_CORENET_RCWSR13_EC1) ==
FSL_CORENET_RCWSR13_EC1_RGMII) &&
(!is_serdes_configured(QSGMII_FM1_A)))
return PHY_INTERFACE_MODE_RGMII;
/* handle SGMII */
switch (port) {
case FM1_DTSEC1:
case FM1_DTSEC2:
case FM1_DTSEC3:
if (is_serdes_configured(SGMII_FM1_DTSEC1 + port - FM1_DTSEC1))
return PHY_INTERFACE_MODE_SGMII;
else if (is_serdes_configured(SGMII_2500_FM1_DTSEC1
+ port - FM1_DTSEC1))
return PHY_INTERFACE_MODE_SGMII_2500;
break;
default:
break;
}
/* handle QSGMII */
switch (port) {
case FM1_DTSEC1:
case FM1_DTSEC2:
case FM1_DTSEC3:
case FM1_DTSEC4:
/* check lane A on SerDes1 */
if (is_serdes_configured(QSGMII_FM1_A))
return PHY_INTERFACE_MODE_QSGMII;
break;
default:
break;
}
return PHY_INTERFACE_MODE_NONE;
}

View file

@ -0,0 +1,67 @@
/*
* Copyright 2013 Freescale Semiconductor, Inc.
*
* SPDX-License-Identifier: GPL-2.0+
*/
#include <common.h>
#include <phy.h>
#include <fm_eth.h>
#include <asm/io.h>
#include <asm/immap_85xx.h>
#include <asm/fsl_serdes.h>
phy_interface_t fman_port_enet_if(enum fm_port port)
{
ccsr_gur_t *gur = (void *)(CONFIG_SYS_MPC85xx_GUTS_ADDR);
u32 rcwsr13 = in_be32(&gur->rcwsr[13]);
/* handle RGMII first */
if ((port == FM1_DTSEC2) &&
((rcwsr13 & FSL_CORENET_RCWSR13_MAC2_GMII_SEL) ==
FSL_CORENET_RCWSR13_MAC2_GMII_SEL_ENET_PORT)) {
if ((rcwsr13 & FSL_CORENET_RCWSR13_EC1) ==
FSL_CORENET_RCWSR13_EC1_FM1_DTSEC4_RGMII)
return PHY_INTERFACE_MODE_RGMII;
else if ((rcwsr13 & FSL_CORENET_RCWSR13_EC1) ==
FSL_CORENET_RCWSR13_EC1_FM1_DTSEC4_MII)
return PHY_INTERFACE_MODE_MII;
}
if ((port == FM1_DTSEC4) &&
((rcwsr13 & FSL_CORENET_RCWSR13_MAC2_GMII_SEL) ==
FSL_CORENET_RCWSR13_MAC2_GMII_SEL_L2_SWITCH)) {
if ((rcwsr13 & FSL_CORENET_RCWSR13_EC1) ==
FSL_CORENET_RCWSR13_EC1_FM1_DTSEC4_RGMII)
return PHY_INTERFACE_MODE_RGMII;
else if ((rcwsr13 & FSL_CORENET_RCWSR13_EC1) ==
FSL_CORENET_RCWSR13_EC1_FM1_DTSEC4_MII)
return PHY_INTERFACE_MODE_MII;
}
if (port == FM1_DTSEC5) {
if ((rcwsr13 & FSL_CORENET_RCWSR13_EC2) ==
FSL_CORENET_RCWSR13_EC2_FM1_DTSEC5_RGMII)
return PHY_INTERFACE_MODE_RGMII;
else if ((rcwsr13 & FSL_CORENET_RCWSR13_EC2) ==
FSL_CORENET_RCWSR13_EC2_FM1_DTSEC5_MII)
return PHY_INTERFACE_MODE_MII;
}
switch (port) {
case FM1_DTSEC1:
case FM1_DTSEC2:
if (is_serdes_configured(QSGMII_SW1_A + port - FM1_DTSEC1) ||
is_serdes_configured(SGMII_SW1_MAC1 + port - FM1_DTSEC1))
return PHY_INTERFACE_MODE_QSGMII;
case FM1_DTSEC3:
case FM1_DTSEC4:
case FM1_DTSEC5:
if (is_serdes_configured(SGMII_FM1_DTSEC1 + port - FM1_DTSEC1))
return PHY_INTERFACE_MODE_SGMII;
break;
default:
return PHY_INTERFACE_MODE_NONE;
}
return PHY_INTERFACE_MODE_NONE;
}

View file

@ -0,0 +1,93 @@
/*
* Copyright 2012 Freescale Semiconductor, Inc.
*
* Shengzhou Liu <Shengzhou.Liu@freescale.com>
*
* SPDX-License-Identifier: GPL-2.0+
*/
#include <common.h>
#include <phy.h>
#include <fm_eth.h>
#include <asm/immap_85xx.h>
#include <asm/fsl_serdes.h>
u32 port_to_devdisr[] = {
[FM1_DTSEC1] = FSL_CORENET_DEVDISR2_DTSEC1_1,
[FM1_DTSEC2] = FSL_CORENET_DEVDISR2_DTSEC1_2,
[FM1_DTSEC3] = FSL_CORENET_DEVDISR2_DTSEC1_3,
[FM1_DTSEC4] = FSL_CORENET_DEVDISR2_DTSEC1_4,
[FM1_DTSEC5] = FSL_CORENET_DEVDISR2_DTSEC1_5,
[FM1_DTSEC6] = FSL_CORENET_DEVDISR2_DTSEC1_6,
[FM1_DTSEC9] = FSL_CORENET_DEVDISR2_DTSEC1_9,
[FM1_DTSEC10] = FSL_CORENET_DEVDISR2_DTSEC1_10,
[FM1_10GEC1] = FSL_CORENET_DEVDISR2_10GEC1_1,
[FM1_10GEC2] = FSL_CORENET_DEVDISR2_10GEC1_2,
[FM1_10GEC3] = FSL_CORENET_DEVDISR2_10GEC1_3,
[FM1_10GEC4] = FSL_CORENET_DEVDISR2_10GEC1_4,
};
static int is_device_disabled(enum fm_port port)
{
ccsr_gur_t *gur = (void __iomem *)(CONFIG_SYS_MPC85xx_GUTS_ADDR);
u32 devdisr2 = in_be32(&gur->devdisr2);
return port_to_devdisr[port] & devdisr2;
}
void fman_disable_port(enum fm_port port)
{
ccsr_gur_t *gur = (void __iomem *)(CONFIG_SYS_MPC85xx_GUTS_ADDR);
setbits_be32(&gur->devdisr2, port_to_devdisr[port]);
}
phy_interface_t fman_port_enet_if(enum fm_port port)
{
ccsr_gur_t *gur = (void __iomem *)(CONFIG_SYS_MPC85xx_GUTS_ADDR);
u32 rcwsr13 = in_be32(&gur->rcwsr[13]);
if (is_device_disabled(port))
return PHY_INTERFACE_MODE_NONE;
if ((port == FM1_10GEC1 || port == FM1_10GEC2) &&
((is_serdes_configured(XAUI_FM1_MAC9)) ||
(is_serdes_configured(XFI_FM1_MAC9)) ||
(is_serdes_configured(XFI_FM1_MAC10))))
return PHY_INTERFACE_MODE_XGMII;
if ((port == FM1_10GEC3 || port == FM1_10GEC4) &&
((is_serdes_configured(XFI_FM1_MAC1)) ||
(is_serdes_configured(XFI_FM1_MAC2))))
return PHY_INTERFACE_MODE_XGMII;
if ((port == FM1_DTSEC3) && ((rcwsr13 & FSL_CORENET_RCWSR13_EC1) ==
FSL_CORENET_RCWSR13_EC1_DTSEC3_RGMII))
return PHY_INTERFACE_MODE_RGMII;
if ((port == FM1_DTSEC4) && ((rcwsr13 & FSL_CORENET_RCWSR13_EC2) ==
FSL_CORENET_RCWSR13_EC2_DTSEC4_RGMII))
return PHY_INTERFACE_MODE_RGMII;
if ((port == FM1_DTSEC10) && ((rcwsr13 & FSL_CORENET_RCWSR13_EC2) ==
FSL_CORENET_RCWSR13_EC2_DTSEC10_RGMII))
return PHY_INTERFACE_MODE_RGMII;
switch (port) {
case FM1_DTSEC1:
case FM1_DTSEC2:
case FM1_DTSEC3:
case FM1_DTSEC4:
case FM1_DTSEC5:
case FM1_DTSEC6:
case FM1_DTSEC9:
case FM1_DTSEC10:
if (is_serdes_configured(SGMII_FM1_DTSEC1 + port - FM1_DTSEC1))
return PHY_INTERFACE_MODE_SGMII;
break;
default:
return PHY_INTERFACE_MODE_NONE;
}
return PHY_INTERFACE_MODE_NONE;
}

View file

@ -0,0 +1,171 @@
/*
* Copyright 2012 Freescale Semiconductor, Inc.
* Roy Zang <tie-fei.zang@freescale.com>
*
* SPDX-License-Identifier: GPL-2.0+
*/
#include <common.h>
#include <phy.h>
#include <fm_eth.h>
#include <asm/io.h>
#include <asm/immap_85xx.h>
#include <asm/fsl_serdes.h>
u32 port_to_devdisr[] = {
[FM1_DTSEC1] = FSL_CORENET_DEVDISR2_DTSEC1_1,
[FM1_DTSEC2] = FSL_CORENET_DEVDISR2_DTSEC1_2,
[FM1_DTSEC3] = FSL_CORENET_DEVDISR2_DTSEC1_3,
[FM1_DTSEC4] = FSL_CORENET_DEVDISR2_DTSEC1_4,
[FM1_DTSEC5] = FSL_CORENET_DEVDISR2_DTSEC1_5,
[FM1_DTSEC6] = FSL_CORENET_DEVDISR2_DTSEC1_6,
[FM1_DTSEC9] = FSL_CORENET_DEVDISR2_DTSEC1_9,
[FM1_DTSEC10] = FSL_CORENET_DEVDISR2_DTSEC1_10,
[FM1_10GEC1] = FSL_CORENET_DEVDISR2_10GEC1_1,
[FM1_10GEC2] = FSL_CORENET_DEVDISR2_10GEC1_2,
[FM2_DTSEC1] = FSL_CORENET_DEVDISR2_DTSEC2_1,
[FM2_DTSEC2] = FSL_CORENET_DEVDISR2_DTSEC2_2,
[FM2_DTSEC3] = FSL_CORENET_DEVDISR2_DTSEC2_3,
[FM2_DTSEC4] = FSL_CORENET_DEVDISR2_DTSEC2_4,
[FM2_DTSEC5] = FSL_CORENET_DEVDISR2_DTSEC2_5,
[FM2_DTSEC6] = FSL_CORENET_DEVDISR2_DTSEC2_6,
[FM2_DTSEC9] = FSL_CORENET_DEVDISR2_DTSEC2_9,
[FM2_DTSEC10] = FSL_CORENET_DEVDISR2_DTSEC2_10,
[FM2_10GEC1] = FSL_CORENET_DEVDISR2_10GEC2_1,
[FM2_10GEC2] = FSL_CORENET_DEVDISR2_10GEC2_2,
};
static int is_device_disabled(enum fm_port port)
{
ccsr_gur_t *gur = (void __iomem *)(CONFIG_SYS_MPC85xx_GUTS_ADDR);
u32 devdisr2 = in_be32(&gur->devdisr2);
return port_to_devdisr[port] & devdisr2;
}
void fman_disable_port(enum fm_port port)
{
ccsr_gur_t *gur = (void __iomem *)(CONFIG_SYS_MPC85xx_GUTS_ADDR);
setbits_be32(&gur->devdisr2, port_to_devdisr[port]);
}
void fman_enable_port(enum fm_port port)
{
ccsr_gur_t *gur = (void __iomem *)(CONFIG_SYS_MPC85xx_GUTS_ADDR);
clrbits_be32(&gur->devdisr2, port_to_devdisr[port]);
}
phy_interface_t fman_port_enet_if(enum fm_port port)
{
ccsr_gur_t *gur = (void __iomem *)(CONFIG_SYS_MPC85xx_GUTS_ADDR);
u32 rcwsr13 = in_be32(&gur->rcwsr[13]);
if (is_device_disabled(port))
return PHY_INTERFACE_MODE_NONE;
if ((port == FM1_10GEC1 || port == FM1_10GEC2) &&
((is_serdes_configured(XAUI_FM1_MAC9)) ||
(is_serdes_configured(XAUI_FM1_MAC10)) ||
(is_serdes_configured(XFI_FM1_MAC9)) ||
(is_serdes_configured(XFI_FM1_MAC10))))
return PHY_INTERFACE_MODE_XGMII;
if ((port == FM1_DTSEC9 || port == FM1_DTSEC10) &&
((is_serdes_configured(XFI_FM1_MAC9)) ||
(is_serdes_configured(XFI_FM1_MAC10))))
return PHY_INTERFACE_MODE_XGMII;
if ((port == FM2_10GEC1 || port == FM2_10GEC2) &&
((is_serdes_configured(XAUI_FM2_MAC9)) ||
(is_serdes_configured(XAUI_FM2_MAC10)) ||
(is_serdes_configured(XFI_FM2_MAC9)) ||
(is_serdes_configured(XFI_FM2_MAC10))))
return PHY_INTERFACE_MODE_XGMII;
#define FSL_CORENET_RCWSR13_EC1 0x60000000 /* bits 417..418 */
#define FSL_CORENET_RCWSR13_EC1_FM2_DTSEC5_RGMII 0x00000000
#define FSL_CORENET_RCWSR13_EC1_FM2_GPIO 0x40000000
#define FSL_CORENET_RCWSR13_EC2 0x18000000 /* bits 419..420 */
#define FSL_CORENET_RCWSR13_EC2_FM1_DTSEC5_RGMII 0x00000000
#define FSL_CORENET_RCWSR13_EC2_FM2_DTSEC6_RGMII 0x08000000
#define FSL_CORENET_RCWSR13_EC2_FM1_GPIO 0x10000000
/* handle RGMII first */
if ((port == FM2_DTSEC5) && ((rcwsr13 & FSL_CORENET_RCWSR13_EC1) ==
FSL_CORENET_RCWSR13_EC1_FM2_DTSEC5_RGMII))
return PHY_INTERFACE_MODE_RGMII;
if ((port == FM1_DTSEC5) && ((rcwsr13 & FSL_CORENET_RCWSR13_EC2) ==
FSL_CORENET_RCWSR13_EC2_FM1_DTSEC5_RGMII))
return PHY_INTERFACE_MODE_RGMII;
if ((port == FM2_DTSEC6) && ((rcwsr13 & FSL_CORENET_RCWSR13_EC2) ==
FSL_CORENET_RCWSR13_EC2_FM2_DTSEC6_RGMII))
return PHY_INTERFACE_MODE_RGMII;
switch (port) {
case FM1_DTSEC1:
case FM1_DTSEC2:
case FM1_DTSEC3:
case FM1_DTSEC4:
case FM1_DTSEC5:
case FM1_DTSEC6:
case FM1_DTSEC9:
case FM1_DTSEC10:
if (is_serdes_configured(SGMII_FM1_DTSEC1 + port - FM1_DTSEC1))
return PHY_INTERFACE_MODE_SGMII;
break;
case FM2_DTSEC1:
case FM2_DTSEC2:
case FM2_DTSEC3:
case FM2_DTSEC4:
case FM2_DTSEC5:
case FM2_DTSEC6:
case FM2_DTSEC9:
case FM2_DTSEC10:
if (is_serdes_configured(SGMII_FM2_DTSEC1 + port - FM2_DTSEC1))
return PHY_INTERFACE_MODE_SGMII;
break;
default:
break;
}
/* handle QSGMII */
switch (port) {
case FM1_DTSEC1:
case FM1_DTSEC2:
case FM1_DTSEC3:
case FM1_DTSEC4:
/* check lane G on SerDes1 */
if (is_serdes_configured(QSGMII_FM1_A))
return PHY_INTERFACE_MODE_QSGMII;
break;
case FM1_DTSEC5:
case FM1_DTSEC6:
case FM1_DTSEC9:
case FM1_DTSEC10:
/* check lane C on SerDes1 */
if (is_serdes_configured(QSGMII_FM1_B))
return PHY_INTERFACE_MODE_QSGMII;
break;
case FM2_DTSEC1:
case FM2_DTSEC2:
case FM2_DTSEC3:
case FM2_DTSEC4:
/* check lane G on SerDes2 */
if (is_serdes_configured(QSGMII_FM2_A))
return PHY_INTERFACE_MODE_QSGMII;
break;
case FM2_DTSEC5:
case FM2_DTSEC6:
case FM2_DTSEC9:
case FM2_DTSEC10:
/* check lane C on SerDes2 */
if (is_serdes_configured(QSGMII_FM2_B))
return PHY_INTERFACE_MODE_QSGMII;
break;
default:
break;
}
return PHY_INTERFACE_MODE_NONE;
}

View file

@ -0,0 +1,105 @@
/*
* Copyright 2009-2011 Freescale Semiconductor, Inc.
* Dave Liu <daveliu@freescale.com>
*
* SPDX-License-Identifier: GPL-2.0+
*/
/* MAXFRM - maximum frame length */
#define MAXFRM_MASK 0x0000ffff
#include <common.h>
#include <phy.h>
#include <asm/types.h>
#include <asm/io.h>
#include <fsl_tgec.h>
#include "fm.h"
#define TGEC_CMD_CFG_INIT (TGEC_CMD_CFG_NO_LEN_CHK | \
TGEC_CMD_CFG_RX_ER_DISC | \
TGEC_CMD_CFG_STAT_CLR | \
TGEC_CMD_CFG_PAUSE_IGNORE | \
TGEC_CMD_CFG_CRC_FWD)
#define TGEC_CMD_CFG_FINAL (TGEC_CMD_CFG_NO_LEN_CHK | \
TGEC_CMD_CFG_RX_ER_DISC | \
TGEC_CMD_CFG_PAUSE_IGNORE | \
TGEC_CMD_CFG_CRC_FWD)
static void tgec_init_mac(struct fsl_enet_mac *mac)
{
struct tgec *regs = mac->base;
/* mask all interrupt */
out_be32(&regs->imask, IMASK_MASK_ALL);
/* clear all events */
out_be32(&regs->ievent, IEVENT_CLEAR_ALL);
/* set the max receive length */
out_be32(&regs->maxfrm, mac->max_rx_len & MAXFRM_MASK);
/*
* 1588 disable, insert second mac disable payload length check
* disable, normal operation, any rx error frame is discarded, clear
* counters, pause frame ignore, no promiscuous, LAN mode Rx CRC no
* strip, Tx CRC append, Rx disable and Tx disable
*/
out_be32(&regs->command_config, TGEC_CMD_CFG_INIT);
udelay(1000);
out_be32(&regs->command_config, TGEC_CMD_CFG_FINAL);
/* multicast frame reception for the hash entry disable */
out_be32(&regs->hashtable_ctrl, 0);
}
static void tgec_enable_mac(struct fsl_enet_mac *mac)
{
struct tgec *regs = mac->base;
setbits_be32(&regs->command_config, TGEC_CMD_CFG_RXTX_EN);
}
static void tgec_disable_mac(struct fsl_enet_mac *mac)
{
struct tgec *regs = mac->base;
clrbits_be32(&regs->command_config, TGEC_CMD_CFG_RXTX_EN);
}
static void tgec_set_mac_addr(struct fsl_enet_mac *mac, u8 *mac_addr)
{
struct tgec *regs = mac->base;
u32 mac_addr0, mac_addr1;
/*
* if a station address of 0x12345678ABCD, perform a write to
* MAC_ADDR0 of 0x78563412, MAC_ADDR1 of 0x0000CDAB
*/
mac_addr0 = (mac_addr[3] << 24) | (mac_addr[2] << 16) | \
(mac_addr[1] << 8) | (mac_addr[0]);
out_be32(&regs->mac_addr_0, mac_addr0);
mac_addr1 = ((mac_addr[5] << 8) | mac_addr[4]) & 0x0000ffff;
out_be32(&regs->mac_addr_1, mac_addr1);
}
static void tgec_set_interface_mode(struct fsl_enet_mac *mac,
phy_interface_t type, int speed)
{
/* nothing right now */
return;
}
void init_tgec(struct fsl_enet_mac *mac, void *base,
void *phyregs, int max_rx_len)
{
mac->base = base;
mac->phyregs = phyregs;
mac->max_rx_len = max_rx_len;
mac->init_mac = tgec_init_mac;
mac->enable_mac = tgec_enable_mac;
mac->disable_mac = tgec_disable_mac;
mac->set_mac_addr = tgec_set_mac_addr;
mac->set_if_mode = tgec_set_interface_mode;
}

View file

@ -0,0 +1,126 @@
/*
* Copyright 2009-2011 Freescale Semiconductor, Inc.
* Andy Fleming <afleming@gmail.com>
*
* SPDX-License-Identifier: GPL-2.0+
* Some part is taken from tsec.c
*/
#include <common.h>
#include <miiphy.h>
#include <phy.h>
#include <asm/io.h>
#include <fsl_tgec.h>
#include <fm_eth.h>
/*
* Write value to the PHY for this device to the register at regnum, waiting
* until the write is done before it returns. All PHY configuration has to be
* done through the TSEC1 MIIM regs
*/
static int tgec_mdio_write(struct mii_dev *bus, int port_addr, int dev_addr,
int regnum, u16 value)
{
u32 mdio_ctl;
u32 stat_val;
struct tgec_mdio_controller *regs = bus->priv;
if (dev_addr == MDIO_DEVAD_NONE)
return 0;
/* Wait till the bus is free */
stat_val = MDIO_STAT_CLKDIV(100);
out_be32(&regs->mdio_stat, stat_val);
while ((in_be32(&regs->mdio_stat)) & MDIO_STAT_BSY)
;
/* Set the port and dev addr */
mdio_ctl = MDIO_CTL_PORT_ADDR(port_addr) | MDIO_CTL_DEV_ADDR(dev_addr);
out_be32(&regs->mdio_ctl, mdio_ctl);
/* Set the register address */
out_be32(&regs->mdio_addr, regnum & 0xffff);
/* Wait till the bus is free */
while ((in_be32(&regs->mdio_stat)) & MDIO_STAT_BSY)
;
/* Write the value to the register */
out_be32(&regs->mdio_data, MDIO_DATA(value));
/* Wait till the MDIO write is complete */
while ((in_be32(&regs->mdio_data)) & MDIO_DATA_BSY)
;
return 0;
}
/*
* Reads from register regnum in the PHY for device dev, returning the value.
* Clears miimcom first. All PHY configuration has to be done through the
* TSEC1 MIIM regs
*/
static int tgec_mdio_read(struct mii_dev *bus, int port_addr, int dev_addr,
int regnum)
{
u32 mdio_ctl;
u32 stat_val;
struct tgec_mdio_controller *regs = bus->priv;
if (dev_addr == MDIO_DEVAD_NONE)
return 0xffff;
stat_val = MDIO_STAT_CLKDIV(100);
out_be32(&regs->mdio_stat, stat_val);
/* Wait till the bus is free */
while ((in_be32(&regs->mdio_stat)) & MDIO_STAT_BSY)
;
/* Set the Port and Device Addrs */
mdio_ctl = MDIO_CTL_PORT_ADDR(port_addr) | MDIO_CTL_DEV_ADDR(dev_addr);
out_be32(&regs->mdio_ctl, mdio_ctl);
/* Set the register address */
out_be32(&regs->mdio_addr, regnum & 0xffff);
/* Wait till the bus is free */
while ((in_be32(&regs->mdio_stat)) & MDIO_STAT_BSY)
;
/* Initiate the read */
mdio_ctl |= MDIO_CTL_READ;
out_be32(&regs->mdio_ctl, mdio_ctl);
/* Wait till the MDIO write is complete */
while ((in_be32(&regs->mdio_data)) & MDIO_DATA_BSY)
;
/* Return all Fs if nothing was there */
if (in_be32(&regs->mdio_stat) & MDIO_STAT_RD_ER)
return 0xffff;
return in_be32(&regs->mdio_data) & 0xffff;
}
static int tgec_mdio_reset(struct mii_dev *bus)
{
return 0;
}
int fm_tgec_mdio_init(bd_t *bis, struct tgec_mdio_info *info)
{
struct mii_dev *bus = mdio_alloc();
if (!bus) {
printf("Failed to allocate FM TGEC MDIO bus\n");
return -1;
}
bus->read = tgec_mdio_read;
bus->write = tgec_mdio_write;
bus->reset = tgec_mdio_reset;
sprintf(bus->name, info->name);
bus->priv = info->regs;
return mdio_register(bus);
}

View file

@ -0,0 +1,15 @@
#
# Copyright 2014 Freescale Semiconductor, Inc.
#
# SPDX-License-Identifier: GPL-2.0+
#
# Layerscape MC driver
obj-y += mc.o \
mc_sys.o \
dpmng.o \
dprc.o \
dpbp.o \
dpni.o \
dpmac.o
obj-y += dpio/

View file

@ -0,0 +1,159 @@
/*
* Freescale Layerscape MC I/O wrapper
*
* Copyright (C) 2013-2015 Freescale Semiconductor, Inc.
* Author: German Rivera <German.Rivera@freescale.com>
*
* SPDX-License-Identifier: GPL-2.0+
*/
#include <fsl-mc/fsl_mc_sys.h>
#include <fsl-mc/fsl_mc_cmd.h>
#include <fsl-mc/fsl_dpbp.h>
int dpbp_open(struct fsl_mc_io *mc_io,
uint32_t cmd_flags,
int dpbp_id,
uint16_t *token)
{
struct mc_command cmd = { 0 };
int err;
/* prepare command */
cmd.header = mc_encode_cmd_header(DPBP_CMDID_OPEN,
cmd_flags,
0);
DPBP_CMD_OPEN(cmd, dpbp_id);
/* send command to mc*/
err = mc_send_command(mc_io, &cmd);
if (err)
return err;
/* retrieve response parameters */
*token = MC_CMD_HDR_READ_TOKEN(cmd.header);
return err;
}
int dpbp_close(struct fsl_mc_io *mc_io,
uint32_t cmd_flags,
uint16_t token)
{
struct mc_command cmd = { 0 };
/* prepare command */
cmd.header = mc_encode_cmd_header(DPBP_CMDID_CLOSE, cmd_flags,
token);
/* send command to mc*/
return mc_send_command(mc_io, &cmd);
}
int dpbp_create(struct fsl_mc_io *mc_io,
uint32_t cmd_flags,
const struct dpbp_cfg *cfg,
uint16_t *token)
{
struct mc_command cmd = { 0 };
int err;
(void)(cfg); /* unused */
/* prepare command */
cmd.header = mc_encode_cmd_header(DPBP_CMDID_CREATE,
cmd_flags,
0);
/* send command to mc*/
err = mc_send_command(mc_io, &cmd);
if (err)
return err;
/* retrieve response parameters */
*token = MC_CMD_HDR_READ_TOKEN(cmd.header);
return 0;
}
int dpbp_destroy(struct fsl_mc_io *mc_io,
uint32_t cmd_flags,
uint16_t token)
{
struct mc_command cmd = { 0 };
/* prepare command */
cmd.header = mc_encode_cmd_header(DPBP_CMDID_DESTROY,
cmd_flags,
token);
/* send command to mc*/
return mc_send_command(mc_io, &cmd);
}
int dpbp_enable(struct fsl_mc_io *mc_io,
uint32_t cmd_flags,
uint16_t token)
{
struct mc_command cmd = { 0 };
/* prepare command */
cmd.header = mc_encode_cmd_header(DPBP_CMDID_ENABLE, cmd_flags,
token);
/* send command to mc*/
return mc_send_command(mc_io, &cmd);
}
int dpbp_disable(struct fsl_mc_io *mc_io,
uint32_t cmd_flags,
uint16_t token)
{
struct mc_command cmd = { 0 };
/* prepare command */
cmd.header = mc_encode_cmd_header(DPBP_CMDID_DISABLE,
cmd_flags,
token);
/* send command to mc*/
return mc_send_command(mc_io, &cmd);
}
int dpbp_reset(struct fsl_mc_io *mc_io,
uint32_t cmd_flags,
uint16_t token)
{
struct mc_command cmd = { 0 };
/* prepare command */
cmd.header = mc_encode_cmd_header(DPBP_CMDID_RESET,
cmd_flags,
token);
/* send command to mc*/
return mc_send_command(mc_io, &cmd);
}
int dpbp_get_attributes(struct fsl_mc_io *mc_io,
uint32_t cmd_flags,
uint16_t token,
struct dpbp_attr *attr)
{
struct mc_command cmd = { 0 };
int err;
/* prepare command */
cmd.header = mc_encode_cmd_header(DPBP_CMDID_GET_ATTR,
cmd_flags,
token);
/* send command to mc*/
err = mc_send_command(mc_io, &cmd);
if (err)
return err;
/* retrieve response parameters */
DPBP_RSP_GET_ATTRIBUTES(cmd, attr);
return 0;
}

View file

@ -0,0 +1,9 @@
#
# Copyright 2014 Freescale Semiconductor, Inc.
#
# SPDX-License-Identifier: GPL-2.0+
#
# Layerscape MC DPIO driver
obj-y += dpio.o \
qbman_portal.o

View file

@ -0,0 +1,158 @@
/*
* Copyright (C) 2013-2015 Freescale Semiconductor
*
* SPDX-License-Identifier: GPL-2.0+
*/
#include <fsl-mc/fsl_mc_sys.h>
#include <fsl-mc/fsl_mc_cmd.h>
#include <fsl-mc/fsl_dpio.h>
int dpio_open(struct fsl_mc_io *mc_io,
uint32_t cmd_flags,
int dpio_id,
uint16_t *token)
{
struct mc_command cmd = { 0 };
int err;
/* prepare command */
cmd.header = mc_encode_cmd_header(DPIO_CMDID_OPEN,
cmd_flags,
0);
DPIO_CMD_OPEN(cmd, dpio_id);
/* send command to mc*/
err = mc_send_command(mc_io, &cmd);
if (err)
return err;
/* retrieve response parameters */
*token = MC_CMD_HDR_READ_TOKEN(cmd.header);
return 0;
}
int dpio_close(struct fsl_mc_io *mc_io,
uint32_t cmd_flags,
uint16_t token)
{
struct mc_command cmd = { 0 };
/* prepare command */
cmd.header = mc_encode_cmd_header(DPIO_CMDID_CLOSE,
cmd_flags,
token);
/* send command to mc*/
return mc_send_command(mc_io, &cmd);
}
int dpio_create(struct fsl_mc_io *mc_io,
uint32_t cmd_flags,
const struct dpio_cfg *cfg,
uint16_t *token)
{
struct mc_command cmd = { 0 };
int err;
/* prepare command */
cmd.header = mc_encode_cmd_header(DPIO_CMDID_CREATE,
cmd_flags,
0);
DPIO_CMD_CREATE(cmd, cfg);
/* send command to mc*/
err = mc_send_command(mc_io, &cmd);
if (err)
return err;
/* retrieve response parameters */
*token = MC_CMD_HDR_READ_TOKEN(cmd.header);
return 0;
}
int dpio_destroy(struct fsl_mc_io *mc_io,
uint32_t cmd_flags,
uint16_t token)
{
struct mc_command cmd = { 0 };
/* prepare command */
cmd.header = mc_encode_cmd_header(DPIO_CMDID_DESTROY,
cmd_flags,
token);
/* send command to mc*/
return mc_send_command(mc_io, &cmd);
}
int dpio_enable(struct fsl_mc_io *mc_io,
uint32_t cmd_flags,
uint16_t token)
{
struct mc_command cmd = { 0 };
/* prepare command */
cmd.header = mc_encode_cmd_header(DPIO_CMDID_ENABLE,
cmd_flags,
token);
/* send command to mc*/
return mc_send_command(mc_io, &cmd);
}
int dpio_disable(struct fsl_mc_io *mc_io,
uint32_t cmd_flags,
uint16_t token)
{
struct mc_command cmd = { 0 };
/* prepare command */
cmd.header = mc_encode_cmd_header(DPIO_CMDID_DISABLE,
cmd_flags,
token);
/* send command to mc*/
return mc_send_command(mc_io, &cmd);
}
int dpio_reset(struct fsl_mc_io *mc_io,
uint32_t cmd_flags,
uint16_t token)
{
struct mc_command cmd = { 0 };
/* prepare command */
cmd.header = mc_encode_cmd_header(DPIO_CMDID_RESET,
cmd_flags,
token);
/* send command to mc*/
return mc_send_command(mc_io, &cmd);
}
int dpio_get_attributes(struct fsl_mc_io *mc_io,
uint32_t cmd_flags,
uint16_t token,
struct dpio_attr *attr)
{
struct mc_command cmd = { 0 };
int err;
/* prepare command */
cmd.header = mc_encode_cmd_header(DPIO_CMDID_GET_ATTR,
cmd_flags,
token);
/* send command to mc*/
err = mc_send_command(mc_io, &cmd);
if (err)
return err;
/* retrieve response parameters */
DPIO_RSP_GET_ATTR(cmd, attr);
return 0;
}

View file

@ -0,0 +1,599 @@
/*
* Copyright (C) 2014 Freescale Semiconductor
*
* SPDX-License-Identifier: GPL-2.0+
*/
#include "qbman_portal.h"
/* QBMan portal management command codes */
#define QBMAN_MC_ACQUIRE 0x30
#define QBMAN_WQCHAN_CONFIGURE 0x46
/* CINH register offsets */
#define QBMAN_CINH_SWP_EQAR 0x8c0
#define QBMAN_CINH_SWP_DCAP 0xac0
#define QBMAN_CINH_SWP_SDQCR 0xb00
#define QBMAN_CINH_SWP_RAR 0xcc0
/* CENA register offsets */
#define QBMAN_CENA_SWP_EQCR(n) (0x000 + ((uint32_t)(n) << 6))
#define QBMAN_CENA_SWP_DQRR(n) (0x200 + ((uint32_t)(n) << 6))
#define QBMAN_CENA_SWP_RCR(n) (0x400 + ((uint32_t)(n) << 6))
#define QBMAN_CENA_SWP_CR 0x600
#define QBMAN_CENA_SWP_RR(vb) (0x700 + ((uint32_t)(vb) >> 1))
#define QBMAN_CENA_SWP_VDQCR 0x780
/* Reverse mapping of QBMAN_CENA_SWP_DQRR() */
#define QBMAN_IDX_FROM_DQRR(p) (((unsigned long)p & 0xff) >> 6)
/*******************************/
/* Pre-defined attribute codes */
/*******************************/
struct qb_attr_code code_generic_verb = QB_CODE(0, 0, 7);
struct qb_attr_code code_generic_rslt = QB_CODE(0, 8, 8);
/*************************/
/* SDQCR attribute codes */
/*************************/
/* we put these here because at least some of them are required by
* qbman_swp_init() */
struct qb_attr_code code_sdqcr_dct = QB_CODE(0, 24, 2);
struct qb_attr_code code_sdqcr_fc = QB_CODE(0, 29, 1);
struct qb_attr_code code_sdqcr_tok = QB_CODE(0, 16, 8);
#define CODE_SDQCR_DQSRC(n) QB_CODE(0, n, 1)
enum qbman_sdqcr_dct {
qbman_sdqcr_dct_null = 0,
qbman_sdqcr_dct_prio_ics,
qbman_sdqcr_dct_active_ics,
qbman_sdqcr_dct_active
};
enum qbman_sdqcr_fc {
qbman_sdqcr_fc_one = 0,
qbman_sdqcr_fc_up_to_3 = 1
};
/*********************************/
/* Portal constructor/destructor */
/*********************************/
/* Software portals should always be in the power-on state when we initialise,
* due to the CCSR-based portal reset functionality that MC has. */
struct qbman_swp *qbman_swp_init(const struct qbman_swp_desc *d)
{
int ret;
struct qbman_swp *p = malloc(sizeof(struct qbman_swp));
if (!p)
return NULL;
p->desc = d;
#ifdef QBMAN_CHECKING
p->mc.check = swp_mc_can_start;
#endif
p->mc.valid_bit = QB_VALID_BIT;
p->sdq = 0;
qb_attr_code_encode(&code_sdqcr_dct, &p->sdq, qbman_sdqcr_dct_prio_ics);
qb_attr_code_encode(&code_sdqcr_fc, &p->sdq, qbman_sdqcr_fc_up_to_3);
qb_attr_code_encode(&code_sdqcr_tok, &p->sdq, 0xbb);
atomic_set(&p->vdq.busy, 1);
p->vdq.valid_bit = QB_VALID_BIT;
p->dqrr.next_idx = 0;
p->dqrr.valid_bit = QB_VALID_BIT;
ret = qbman_swp_sys_init(&p->sys, d);
if (ret) {
free(p);
printf("qbman_swp_sys_init() failed %d\n", ret);
return NULL;
}
qbman_cinh_write(&p->sys, QBMAN_CINH_SWP_SDQCR, p->sdq);
return p;
}
/***********************/
/* Management commands */
/***********************/
/*
* Internal code common to all types of management commands.
*/
void *qbman_swp_mc_start(struct qbman_swp *p)
{
void *ret;
#ifdef QBMAN_CHECKING
BUG_ON(p->mc.check != swp_mc_can_start);
#endif
ret = qbman_cena_write_start(&p->sys, QBMAN_CENA_SWP_CR);
#ifdef QBMAN_CHECKING
if (!ret)
p->mc.check = swp_mc_can_submit;
#endif
return ret;
}
void qbman_swp_mc_submit(struct qbman_swp *p, void *cmd, uint32_t cmd_verb)
{
uint32_t *v = cmd;
#ifdef QBMAN_CHECKING
BUG_ON(p->mc.check != swp_mc_can_submit);
#endif
lwsync();
/* TBD: "|=" is going to hurt performance. Need to move as many fields
* out of word zero, and for those that remain, the "OR" needs to occur
* at the caller side. This debug check helps to catch cases where the
* caller wants to OR but has forgotten to do so. */
BUG_ON((*v & cmd_verb) != *v);
*v = cmd_verb | p->mc.valid_bit;
qbman_cena_write_complete(&p->sys, QBMAN_CENA_SWP_CR, cmd);
/* TODO: add prefetch support for GPP */
#ifdef QBMAN_CHECKING
p->mc.check = swp_mc_can_poll;
#endif
}
void *qbman_swp_mc_result(struct qbman_swp *p)
{
uint32_t *ret, verb;
#ifdef QBMAN_CHECKING
BUG_ON(p->mc.check != swp_mc_can_poll);
#endif
ret = qbman_cena_read(&p->sys, QBMAN_CENA_SWP_RR(p->mc.valid_bit));
/* Remove the valid-bit - command completed iff the rest is non-zero */
verb = ret[0] & ~QB_VALID_BIT;
if (!verb)
return NULL;
#ifdef QBMAN_CHECKING
p->mc.check = swp_mc_can_start;
#endif
p->mc.valid_bit ^= QB_VALID_BIT;
return ret;
}
/***********/
/* Enqueue */
/***********/
/* These should be const, eventually */
static struct qb_attr_code code_eq_cmd = QB_CODE(0, 0, 2);
static struct qb_attr_code code_eq_orp_en = QB_CODE(0, 2, 1);
static struct qb_attr_code code_eq_tgt_id = QB_CODE(2, 0, 24);
/* static struct qb_attr_code code_eq_tag = QB_CODE(3, 0, 32); */
static struct qb_attr_code code_eq_qd_en = QB_CODE(0, 4, 1);
static struct qb_attr_code code_eq_qd_bin = QB_CODE(4, 0, 16);
static struct qb_attr_code code_eq_qd_pri = QB_CODE(4, 16, 4);
static struct qb_attr_code code_eq_rsp_stash = QB_CODE(5, 16, 1);
static struct qb_attr_code code_eq_rsp_lo = QB_CODE(6, 0, 32);
enum qbman_eq_cmd_e {
/* No enqueue, primarily for plugging ORP gaps for dropped frames */
qbman_eq_cmd_empty,
/* DMA an enqueue response once complete */
qbman_eq_cmd_respond,
/* DMA an enqueue response only if the enqueue fails */
qbman_eq_cmd_respond_reject
};
void qbman_eq_desc_clear(struct qbman_eq_desc *d)
{
memset(d, 0, sizeof(*d));
}
void qbman_eq_desc_set_no_orp(struct qbman_eq_desc *d, int respond_success)
{
uint32_t *cl = qb_cl(d);
qb_attr_code_encode(&code_eq_orp_en, cl, 0);
qb_attr_code_encode(&code_eq_cmd, cl,
respond_success ? qbman_eq_cmd_respond :
qbman_eq_cmd_respond_reject);
}
void qbman_eq_desc_set_response(struct qbman_eq_desc *d,
dma_addr_t storage_phys,
int stash)
{
uint32_t *cl = qb_cl(d);
qb_attr_code_encode_64(&code_eq_rsp_lo, (uint64_t *)cl, storage_phys);
qb_attr_code_encode(&code_eq_rsp_stash, cl, !!stash);
}
void qbman_eq_desc_set_qd(struct qbman_eq_desc *d, uint32_t qdid,
uint32_t qd_bin, uint32_t qd_prio)
{
uint32_t *cl = qb_cl(d);
qb_attr_code_encode(&code_eq_qd_en, cl, 1);
qb_attr_code_encode(&code_eq_tgt_id, cl, qdid);
qb_attr_code_encode(&code_eq_qd_bin, cl, qd_bin);
qb_attr_code_encode(&code_eq_qd_pri, cl, qd_prio);
}
#define EQAR_IDX(eqar) ((eqar) & 0x7)
#define EQAR_VB(eqar) ((eqar) & 0x80)
#define EQAR_SUCCESS(eqar) ((eqar) & 0x100)
int qbman_swp_enqueue(struct qbman_swp *s, const struct qbman_eq_desc *d,
const struct qbman_fd *fd)
{
uint32_t *p;
const uint32_t *cl = qb_cl(d);
uint32_t eqar = qbman_cinh_read(&s->sys, QBMAN_CINH_SWP_EQAR);
debug("EQAR=%08x\n", eqar);
if (!EQAR_SUCCESS(eqar))
return -EBUSY;
p = qbman_cena_write_start(&s->sys,
QBMAN_CENA_SWP_EQCR(EQAR_IDX(eqar)));
word_copy(&p[1], &cl[1], 7);
word_copy(&p[8], fd, sizeof(*fd) >> 2);
lwsync();
/* Set the verb byte, have to substitute in the valid-bit */
p[0] = cl[0] | EQAR_VB(eqar);
qbman_cena_write_complete(&s->sys,
QBMAN_CENA_SWP_EQCR(EQAR_IDX(eqar)),
p);
return 0;
}
/***************************/
/* Volatile (pull) dequeue */
/***************************/
/* These should be const, eventually */
static struct qb_attr_code code_pull_dct = QB_CODE(0, 0, 2);
static struct qb_attr_code code_pull_dt = QB_CODE(0, 2, 2);
static struct qb_attr_code code_pull_rls = QB_CODE(0, 4, 1);
static struct qb_attr_code code_pull_stash = QB_CODE(0, 5, 1);
static struct qb_attr_code code_pull_numframes = QB_CODE(0, 8, 4);
static struct qb_attr_code code_pull_token = QB_CODE(0, 16, 8);
static struct qb_attr_code code_pull_dqsource = QB_CODE(1, 0, 24);
static struct qb_attr_code code_pull_rsp_lo = QB_CODE(2, 0, 32);
enum qb_pull_dt_e {
qb_pull_dt_channel,
qb_pull_dt_workqueue,
qb_pull_dt_framequeue
};
void qbman_pull_desc_clear(struct qbman_pull_desc *d)
{
memset(d, 0, sizeof(*d));
}
void qbman_pull_desc_set_storage(struct qbman_pull_desc *d,
struct ldpaa_dq *storage,
dma_addr_t storage_phys,
int stash)
{
uint32_t *cl = qb_cl(d);
/* Squiggle the pointer 'storage' into the extra 2 words of the
* descriptor (which aren't copied to the hw command) */
*(void **)&cl[4] = storage;
if (!storage) {
qb_attr_code_encode(&code_pull_rls, cl, 0);
return;
}
qb_attr_code_encode(&code_pull_rls, cl, 1);
qb_attr_code_encode(&code_pull_stash, cl, !!stash);
qb_attr_code_encode_64(&code_pull_rsp_lo, (uint64_t *)cl, storage_phys);
}
void qbman_pull_desc_set_numframes(struct qbman_pull_desc *d, uint8_t numframes)
{
uint32_t *cl = qb_cl(d);
BUG_ON(!numframes || (numframes > 16));
qb_attr_code_encode(&code_pull_numframes, cl,
(uint32_t)(numframes - 1));
}
void qbman_pull_desc_set_token(struct qbman_pull_desc *d, uint8_t token)
{
uint32_t *cl = qb_cl(d);
qb_attr_code_encode(&code_pull_token, cl, token);
}
void qbman_pull_desc_set_fq(struct qbman_pull_desc *d, uint32_t fqid)
{
uint32_t *cl = qb_cl(d);
qb_attr_code_encode(&code_pull_dct, cl, 1);
qb_attr_code_encode(&code_pull_dt, cl, qb_pull_dt_framequeue);
qb_attr_code_encode(&code_pull_dqsource, cl, fqid);
}
int qbman_swp_pull(struct qbman_swp *s, struct qbman_pull_desc *d)
{
uint32_t *p;
uint32_t *cl = qb_cl(d);
if (!atomic_dec_and_test(&s->vdq.busy)) {
atomic_inc(&s->vdq.busy);
return -EBUSY;
}
s->vdq.storage = *(void **)&cl[4];
s->vdq.token = qb_attr_code_decode(&code_pull_token, cl);
p = qbman_cena_write_start(&s->sys, QBMAN_CENA_SWP_VDQCR);
word_copy(&p[1], &cl[1], 3);
lwsync();
/* Set the verb byte, have to substitute in the valid-bit */
p[0] = cl[0] | s->vdq.valid_bit;
s->vdq.valid_bit ^= QB_VALID_BIT;
qbman_cena_write_complete(&s->sys, QBMAN_CENA_SWP_VDQCR, p);
return 0;
}
/****************/
/* Polling DQRR */
/****************/
static struct qb_attr_code code_dqrr_verb = QB_CODE(0, 0, 8);
static struct qb_attr_code code_dqrr_response = QB_CODE(0, 0, 7);
static struct qb_attr_code code_dqrr_stat = QB_CODE(0, 8, 8);
#define QBMAN_DQRR_RESPONSE_DQ 0x60
#define QBMAN_DQRR_RESPONSE_FQRN 0x21
#define QBMAN_DQRR_RESPONSE_FQRNI 0x22
#define QBMAN_DQRR_RESPONSE_FQPN 0x24
#define QBMAN_DQRR_RESPONSE_FQDAN 0x25
#define QBMAN_DQRR_RESPONSE_CDAN 0x26
#define QBMAN_DQRR_RESPONSE_CSCN_MEM 0x27
#define QBMAN_DQRR_RESPONSE_CGCU 0x28
#define QBMAN_DQRR_RESPONSE_BPSCN 0x29
#define QBMAN_DQRR_RESPONSE_CSCN_WQ 0x2a
/* NULL return if there are no unconsumed DQRR entries. Returns a DQRR entry
* only once, so repeated calls can return a sequence of DQRR entries, without
* requiring they be consumed immediately or in any particular order. */
const struct ldpaa_dq *qbman_swp_dqrr_next(struct qbman_swp *s)
{
uint32_t verb;
uint32_t response_verb;
uint32_t flags;
const struct ldpaa_dq *dq;
const uint32_t *p;
dq = qbman_cena_read(&s->sys, QBMAN_CENA_SWP_DQRR(s->dqrr.next_idx));
p = qb_cl(dq);
verb = qb_attr_code_decode(&code_dqrr_verb, p);
/* If the valid-bit isn't of the expected polarity, nothing there. Note,
* in the DQRR reset bug workaround, we shouldn't need to skip these
* check, because we've already determined that a new entry is available
* and we've invalidated the cacheline before reading it, so the
* valid-bit behaviour is repaired and should tell us what we already
* knew from reading PI.
*/
if ((verb & QB_VALID_BIT) != s->dqrr.valid_bit) {
qbman_cena_invalidate_prefetch(&s->sys,
QBMAN_CENA_SWP_DQRR(s->dqrr.next_idx));
return NULL;
}
/* There's something there. Move "next_idx" attention to the next ring
* entry (and prefetch it) before returning what we found. */
s->dqrr.next_idx++;
s->dqrr.next_idx &= QBMAN_DQRR_SIZE - 1; /* Wrap around at 4 */
/* TODO: it's possible to do all this without conditionals, optimise it
* later. */
if (!s->dqrr.next_idx)
s->dqrr.valid_bit ^= QB_VALID_BIT;
/* If this is the final response to a volatile dequeue command
indicate that the vdq is no longer busy */
flags = ldpaa_dq_flags(dq);
response_verb = qb_attr_code_decode(&code_dqrr_response, &verb);
if ((response_verb == QBMAN_DQRR_RESPONSE_DQ) &&
(flags & LDPAA_DQ_STAT_VOLATILE) &&
(flags & LDPAA_DQ_STAT_EXPIRED))
atomic_inc(&s->vdq.busy);
qbman_cena_invalidate_prefetch(&s->sys,
QBMAN_CENA_SWP_DQRR(s->dqrr.next_idx));
return dq;
}
/* Consume DQRR entries previously returned from qbman_swp_dqrr_next(). */
void qbman_swp_dqrr_consume(struct qbman_swp *s, const struct ldpaa_dq *dq)
{
qbman_cinh_write(&s->sys, QBMAN_CINH_SWP_DCAP, QBMAN_IDX_FROM_DQRR(dq));
}
/*********************************/
/* Polling user-provided storage */
/*********************************/
void qbman_dq_entry_set_oldtoken(struct ldpaa_dq *dq,
unsigned int num_entries,
uint8_t oldtoken)
{
memset(dq, oldtoken, num_entries * sizeof(*dq));
}
int qbman_dq_entry_has_newtoken(struct qbman_swp *s,
const struct ldpaa_dq *dq,
uint8_t newtoken)
{
/* To avoid converting the little-endian DQ entry to host-endian prior
* to us knowing whether there is a valid entry or not (and run the
* risk of corrupting the incoming hardware LE write), we detect in
* hardware endianness rather than host. This means we need a different
* "code" depending on whether we are BE or LE in software, which is
* where DQRR_TOK_OFFSET comes in... */
static struct qb_attr_code code_dqrr_tok_detect =
QB_CODE(0, DQRR_TOK_OFFSET, 8);
/* The user trying to poll for a result treats "dq" as const. It is
* however the same address that was provided to us non-const in the
* first place, for directing hardware DMA to. So we can cast away the
* const because it is mutable from our perspective. */
uint32_t *p = qb_cl((struct ldpaa_dq *)dq);
uint32_t token;
token = qb_attr_code_decode(&code_dqrr_tok_detect, &p[1]);
if (token != newtoken)
return 0;
/* Only now do we convert from hardware to host endianness. Also, as we
* are returning success, the user has promised not to call us again, so
* there's no risk of us converting the endianness twice... */
make_le32_n(p, 16);
/* VDQCR "no longer busy" hook - not quite the same as DQRR, because the
* fact "VDQCR" shows busy doesn't mean that the result we're looking at
* is from the same command. Eg. we may be looking at our 10th dequeue
* result from our first VDQCR command, yet the second dequeue command
* could have been kicked off already, after seeing the 1st result. Ie.
* the result we're looking at is not necessarily proof that we can
* reset "busy". We instead base the decision on whether the current
* result is sitting at the first 'storage' location of the busy
* command. */
if (s->vdq.storage == dq) {
s->vdq.storage = NULL;
atomic_inc(&s->vdq.busy);
}
return 1;
}
/********************************/
/* Categorising dequeue entries */
/********************************/
static inline int __qbman_dq_entry_is_x(const struct ldpaa_dq *dq, uint32_t x)
{
const uint32_t *p = qb_cl(dq);
uint32_t response_verb = qb_attr_code_decode(&code_dqrr_response, p);
return response_verb == x;
}
int qbman_dq_entry_is_DQ(const struct ldpaa_dq *dq)
{
return __qbman_dq_entry_is_x(dq, QBMAN_DQRR_RESPONSE_DQ);
}
/*********************************/
/* Parsing frame dequeue results */
/*********************************/
/* These APIs assume qbman_dq_entry_is_DQ() is TRUE */
uint32_t ldpaa_dq_flags(const struct ldpaa_dq *dq)
{
const uint32_t *p = qb_cl(dq);
return qb_attr_code_decode(&code_dqrr_stat, p);
}
const struct dpaa_fd *ldpaa_dq_fd(const struct ldpaa_dq *dq)
{
const uint32_t *p = qb_cl(dq);
return (const struct dpaa_fd *)&p[8];
}
/******************/
/* Buffer release */
/******************/
/* These should be const, eventually */
/* static struct qb_attr_code code_release_num = QB_CODE(0, 0, 3); */
static struct qb_attr_code code_release_set_me = QB_CODE(0, 5, 1);
static struct qb_attr_code code_release_bpid = QB_CODE(0, 16, 16);
void qbman_release_desc_clear(struct qbman_release_desc *d)
{
uint32_t *cl;
memset(d, 0, sizeof(*d));
cl = qb_cl(d);
qb_attr_code_encode(&code_release_set_me, cl, 1);
}
void qbman_release_desc_set_bpid(struct qbman_release_desc *d, uint32_t bpid)
{
uint32_t *cl = qb_cl(d);
qb_attr_code_encode(&code_release_bpid, cl, bpid);
}
#define RAR_IDX(rar) ((rar) & 0x7)
#define RAR_VB(rar) ((rar) & 0x80)
#define RAR_SUCCESS(rar) ((rar) & 0x100)
int qbman_swp_release(struct qbman_swp *s, const struct qbman_release_desc *d,
const uint64_t *buffers, unsigned int num_buffers)
{
uint32_t *p;
const uint32_t *cl = qb_cl(d);
uint32_t rar = qbman_cinh_read(&s->sys, QBMAN_CINH_SWP_RAR);
debug("RAR=%08x\n", rar);
if (!RAR_SUCCESS(rar))
return -EBUSY;
BUG_ON(!num_buffers || (num_buffers > 7));
/* Start the release command */
p = qbman_cena_write_start(&s->sys,
QBMAN_CENA_SWP_RCR(RAR_IDX(rar)));
/* Copy the caller's buffer pointers to the command */
u64_to_le32_copy(&p[2], buffers, num_buffers);
lwsync();
/* Set the verb byte, have to substitute in the valid-bit and the number
* of buffers. */
p[0] = cl[0] | RAR_VB(rar) | num_buffers;
qbman_cena_write_complete(&s->sys,
QBMAN_CENA_SWP_RCR(RAR_IDX(rar)),
p);
return 0;
}
/*******************/
/* Buffer acquires */
/*******************/
/* These should be const, eventually */
static struct qb_attr_code code_acquire_bpid = QB_CODE(0, 16, 16);
static struct qb_attr_code code_acquire_num = QB_CODE(1, 0, 3);
static struct qb_attr_code code_acquire_r_num = QB_CODE(1, 0, 3);
int qbman_swp_acquire(struct qbman_swp *s, uint32_t bpid, uint64_t *buffers,
unsigned int num_buffers)
{
uint32_t *p;
uint32_t verb, rslt, num;
BUG_ON(!num_buffers || (num_buffers > 7));
/* Start the management command */
p = qbman_swp_mc_start(s);
if (!p)
return -EBUSY;
/* Encode the caller-provided attributes */
qb_attr_code_encode(&code_acquire_bpid, p, bpid);
qb_attr_code_encode(&code_acquire_num, p, num_buffers);
/* Complete the management command */
p = qbman_swp_mc_complete(s, p, p[0] | QBMAN_MC_ACQUIRE);
/* Decode the outcome */
verb = qb_attr_code_decode(&code_generic_verb, p);
rslt = qb_attr_code_decode(&code_generic_rslt, p);
num = qb_attr_code_decode(&code_acquire_r_num, p);
BUG_ON(verb != QBMAN_MC_ACQUIRE);
/* Determine success or failure */
if (unlikely(rslt != QBMAN_MC_RSLT_OK)) {
printf("Acquire buffers from BPID 0x%x failed, code=0x%02x\n",
bpid, rslt);
return -EIO;
}
BUG_ON(num > num_buffers);
/* Copy the acquired buffers to the caller's array */
u64_from_le32_copy(buffers, &p[2], num);
return (int)num;
}

View file

@ -0,0 +1,167 @@
/*
* Copyright (C) 2014 Freescale Semiconductor
*
* SPDX-License-Identifier: GPL-2.0+
*/
#include "qbman_private.h"
#include <fsl-mc/fsl_qbman_portal.h>
#include <fsl-mc/fsl_dpaa_fd.h>
/* All QBMan command and result structures use this "valid bit" encoding */
#define QB_VALID_BIT ((uint32_t)0x80)
/* Management command result codes */
#define QBMAN_MC_RSLT_OK 0xf0
/* TBD: as of QBMan 4.1, DQRR will be 8 rather than 4! */
#define QBMAN_DQRR_SIZE 4
/* --------------------- */
/* portal data structure */
/* --------------------- */
struct qbman_swp {
const struct qbman_swp_desc *desc;
/* The qbman_sys (ie. arch/OS-specific) support code can put anything it
* needs in here. */
struct qbman_swp_sys sys;
/* Management commands */
struct {
#ifdef QBMAN_CHECKING
enum swp_mc_check {
swp_mc_can_start, /* call __qbman_swp_mc_start() */
swp_mc_can_submit, /* call __qbman_swp_mc_submit() */
swp_mc_can_poll, /* call __qbman_swp_mc_result() */
} check;
#endif
uint32_t valid_bit; /* 0x00 or 0x80 */
} mc;
/* Push dequeues */
uint32_t sdq;
/* Volatile dequeues */
struct {
/* VDQCR supports a "1 deep pipeline", meaning that if you know
* the last-submitted command is already executing in the
* hardware (as evidenced by at least 1 valid dequeue result),
* you can write another dequeue command to the register, the
* hardware will start executing it as soon as the
* already-executing command terminates. (This minimises latency
* and stalls.) With that in mind, this "busy" variable refers
* to whether or not a command can be submitted, not whether or
* not a previously-submitted command is still executing. In
* other words, once proof is seen that the previously-submitted
* command is executing, "vdq" is no longer "busy".
*/
atomic_t busy;
uint32_t valid_bit; /* 0x00 or 0x80 */
/* We need to determine when vdq is no longer busy. This depends
* on whether the "busy" (last-submitted) dequeue command is
* targeting DQRR or main-memory, and detected is based on the
* presence of the dequeue command's "token" showing up in
* dequeue entries in DQRR or main-memory (respectively). Debug
* builds will, when submitting vdq commands, verify that the
* dequeue result location is not already equal to the command's
* token value. */
struct ldpaa_dq *storage; /* NULL if DQRR */
uint32_t token;
} vdq;
/* DQRR */
struct {
uint32_t next_idx;
uint32_t valid_bit;
} dqrr;
};
/* -------------------------- */
/* portal management commands */
/* -------------------------- */
/* Different management commands all use this common base layer of code to issue
* commands and poll for results. The first function returns a pointer to where
* the caller should fill in their MC command (though they should ignore the
* verb byte), the second function commits merges in the caller-supplied command
* verb (which should not include the valid-bit) and submits the command to
* hardware, and the third function checks for a completed response (returns
* non-NULL if only if the response is complete). */
void *qbman_swp_mc_start(struct qbman_swp *p);
void qbman_swp_mc_submit(struct qbman_swp *p, void *cmd, uint32_t cmd_verb);
void *qbman_swp_mc_result(struct qbman_swp *p);
/* Wraps up submit + poll-for-result */
static inline void *qbman_swp_mc_complete(struct qbman_swp *swp, void *cmd,
uint32_t cmd_verb)
{
int loopvar;
qbman_swp_mc_submit(swp, cmd, cmd_verb);
DBG_POLL_START(loopvar);
do {
DBG_POLL_CHECK(loopvar);
cmd = qbman_swp_mc_result(swp);
} while (!cmd);
return cmd;
}
/* ------------ */
/* qb_attr_code */
/* ------------ */
/* This struct locates a sub-field within a QBMan portal (CENA) cacheline which
* is either serving as a configuration command or a query result. The
* representation is inherently little-endian, as the indexing of the words is
* itself little-endian in nature and layerscape is little endian for anything
* that crosses a word boundary too (64-bit fields are the obvious examples).
*/
struct qb_attr_code {
unsigned int word; /* which uint32_t[] array member encodes the field */
unsigned int lsoffset; /* encoding offset from ls-bit */
unsigned int width; /* encoding width. (bool must be 1.) */
};
/* Macros to define codes */
#define QB_CODE(a, b, c) { a, b, c}
/* decode a field from a cacheline */
static inline uint32_t qb_attr_code_decode(const struct qb_attr_code *code,
const uint32_t *cacheline)
{
return d32_uint32_t(code->lsoffset, code->width, cacheline[code->word]);
}
/* encode a field to a cacheline */
static inline void qb_attr_code_encode(const struct qb_attr_code *code,
uint32_t *cacheline, uint32_t val)
{
cacheline[code->word] =
r32_uint32_t(code->lsoffset, code->width, cacheline[code->word])
| e32_uint32_t(code->lsoffset, code->width, val);
}
static inline void qb_attr_code_encode_64(const struct qb_attr_code *code,
uint64_t *cacheline, uint64_t val)
{
cacheline[code->word / 2] = val;
}
/* ---------------------- */
/* Descriptors/cachelines */
/* ---------------------- */
/* To avoid needless dynamic allocation, the driver API often gives the caller
* a "descriptor" type that the caller can instantiate however they like.
* Ultimately though, it is just a cacheline of binary storage (or something
* smaller when it is known that the descriptor doesn't need all 64 bytes) for
* holding pre-formatted pieces of hardware commands. The performance-critical
* code can then copy these descriptors directly into hardware command
* registers more efficiently than trying to construct/format commands
* on-the-fly. The API user sees the descriptor as an array of 32-bit words in
* order for the compiler to know its size, but the internal details are not
* exposed. The following macro is used within the driver for converting *any*
* descriptor pointer to a usable array pointer. The use of a macro (instead of
* an inline) is necessary to work with different descriptor types and to work
* correctly with const and non-const inputs (and similarly-qualified outputs).
*/
#define qb_cl(d) (&(d)->dont_manipulate_directly[0])

View file

@ -0,0 +1,169 @@
/*
* Copyright (C) 2014 Freescale Semiconductor
*
* SPDX-License-Identifier: GPL-2.0+
*/
/* Perform extra checking */
#include <common.h>
#include <errno.h>
#include <asm/io.h>
#include <linux/types.h>
#include <asm/atomic.h>
#include <malloc.h>
#include <fsl-mc/fsl_qbman_base.h>
#define QBMAN_CHECKING
/* Any time there is a register interface which we poll on, this provides a
* "break after x iterations" scheme for it. It's handy for debugging, eg.
* where you don't want millions of lines of log output from a polling loop
* that won't, because such things tend to drown out the earlier log output
* that might explain what caused the problem. (NB: put ";" after each macro!)
* TODO: we should probably remove this once we're done sanitising the
* simulator...
*/
#define DBG_POLL_START(loopvar) (loopvar = 10)
#define DBG_POLL_CHECK(loopvar) \
do {if (!(loopvar--)) BUG_ON(NULL == "DBG_POLL_CHECK"); } while (0)
/* For CCSR or portal-CINH registers that contain fields at arbitrary offsets
* and widths, these macro-generated encode/decode/isolate/remove inlines can
* be used.
*
* Eg. to "d"ecode a 14-bit field out of a register (into a "uint16_t" type),
* where the field is located 3 bits "up" from the least-significant bit of the
* register (ie. the field location within the 32-bit register corresponds to a
* mask of 0x0001fff8), you would do;
* uint16_t field = d32_uint16_t(3, 14, reg_value);
*
* Or to "e"ncode a 1-bit boolean value (input type is "int", zero is FALSE,
* non-zero is TRUE, so must convert all non-zero inputs to 1, hence the "!!"
* operator) into a register at bit location 0x00080000 (19 bits "in" from the
* LS bit), do;
* reg_value |= e32_int(19, 1, !!field);
*
* If you wish to read-modify-write a register, such that you leave the 14-bit
* field as-is but have all other fields set to zero, then "i"solate the 14-bit
* value using;
* reg_value = i32_uint16_t(3, 14, reg_value);
*
* Alternatively, you could "r"emove the 1-bit boolean field (setting it to
* zero) but leaving all other fields as-is;
* reg_val = r32_int(19, 1, reg_value);
*
*/
#define MAKE_MASK32(width) (width == 32 ? 0xffffffff : \
(uint32_t)((1 << width) - 1))
#define DECLARE_CODEC32(t) \
static inline uint32_t e32_##t(uint32_t lsoffset, uint32_t width, t val) \
{ \
BUG_ON(width > (sizeof(t) * 8)); \
return ((uint32_t)val & MAKE_MASK32(width)) << lsoffset; \
} \
static inline t d32_##t(uint32_t lsoffset, uint32_t width, uint32_t val) \
{ \
BUG_ON(width > (sizeof(t) * 8)); \
return (t)((val >> lsoffset) & MAKE_MASK32(width)); \
} \
static inline uint32_t i32_##t(uint32_t lsoffset, uint32_t width, \
uint32_t val) \
{ \
BUG_ON(width > (sizeof(t) * 8)); \
return e32_##t(lsoffset, width, d32_##t(lsoffset, width, val)); \
} \
static inline uint32_t r32_##t(uint32_t lsoffset, uint32_t width, \
uint32_t val) \
{ \
BUG_ON(width > (sizeof(t) * 8)); \
return ~(MAKE_MASK32(width) << lsoffset) & val; \
}
DECLARE_CODEC32(uint32_t)
DECLARE_CODEC32(uint16_t)
DECLARE_CODEC32(uint8_t)
DECLARE_CODEC32(int)
/*********************/
/* Debugging assists */
/*********************/
static inline void __hexdump(unsigned long start, unsigned long end,
unsigned long p, size_t sz, const unsigned char *c)
{
while (start < end) {
unsigned int pos = 0;
char buf[64];
int nl = 0;
pos += sprintf(buf + pos, "%08lx: ", start);
do {
if ((start < p) || (start >= (p + sz)))
pos += sprintf(buf + pos, "..");
else
pos += sprintf(buf + pos, "%02x", *(c++));
if (!(++start & 15)) {
buf[pos++] = '\n';
nl = 1;
} else {
nl = 0;
if (!(start & 1))
buf[pos++] = ' ';
if (!(start & 3))
buf[pos++] = ' ';
}
} while (start & 15);
if (!nl)
buf[pos++] = '\n';
buf[pos] = '\0';
debug("%s", buf);
}
}
static inline void hexdump(const void *ptr, size_t sz)
{
unsigned long p = (unsigned long)ptr;
unsigned long start = p & ~(unsigned long)15;
unsigned long end = (p + sz + 15) & ~(unsigned long)15;
const unsigned char *c = ptr;
__hexdump(start, end, p, sz, c);
}
#if defined(__BIG_ENDIAN)
#define DQRR_TOK_OFFSET 0
#else
#define DQRR_TOK_OFFSET 24
#endif
/* Similarly-named functions */
#define upper32(a) upper_32_bits(a)
#define lower32(a) lower_32_bits(a)
/****************/
/* arch assists */
/****************/
static inline void dcbz(void *ptr)
{
uint32_t *p = ptr;
BUG_ON((unsigned long)ptr & 63);
p[0] = 0;
p[1] = 0;
p[2] = 0;
p[3] = 0;
p[4] = 0;
p[5] = 0;
p[6] = 0;
p[7] = 0;
p[8] = 0;
p[9] = 0;
p[10] = 0;
p[11] = 0;
p[12] = 0;
p[13] = 0;
p[14] = 0;
p[15] = 0;
}
#define lwsync()
#include "qbman_sys.h"

View file

@ -0,0 +1,290 @@
/*
* Copyright (C) 2014 Freescale Semiconductor
*
* SPDX-License-Identifier: GPL-2.0+
*/
/* qbman_sys_decl.h and qbman_sys.h are the two platform-specific files in the
* driver. They are only included via qbman_private.h, which is itself a
* platform-independent file and is included by all the other driver source.
*
* qbman_sys_decl.h is included prior to all other declarations and logic, and
* it exists to provide compatibility with any linux interfaces our
* single-source driver code is dependent on (eg. kmalloc). Ie. this file
* provides linux compatibility.
*
* This qbman_sys.h header, on the other hand, is included *after* any common
* and platform-neutral declarations and logic in qbman_private.h, and exists to
* implement any platform-specific logic of the qbman driver itself. Ie. it is
* *not* to provide linux compatibility.
*/
/* Trace the 3 different classes of read/write access to QBMan. #undef as
* required. */
#undef QBMAN_CCSR_TRACE
#undef QBMAN_CINH_TRACE
#undef QBMAN_CENA_TRACE
/* Temporarily define this to get around the fact that cache enabled mapping is
* not working right now. Will remove this after uboot could map the cache
* enabled portal memory.
*/
#define QBMAN_CINH_ONLY
static inline void word_copy(void *d, const void *s, unsigned int cnt)
{
uint32_t *dd = d;
const uint32_t *ss = s;
while (cnt--)
*(dd++) = *(ss++);
}
/* Currently, the CENA support code expects each 32-bit word to be written in
* host order, and these are converted to hardware (little-endian) order on
* command submission. However, 64-bit quantities are must be written (and read)
* as two 32-bit words with the least-significant word first, irrespective of
* host endianness. */
static inline void u64_to_le32_copy(void *d, const uint64_t *s,
unsigned int cnt)
{
uint32_t *dd = d;
const uint32_t *ss = (const uint32_t *)s;
while (cnt--) {
/* TBD: the toolchain was choking on the use of 64-bit types up
* until recently so this works entirely with 32-bit variables.
* When 64-bit types become usable again, investigate better
* ways of doing this. */
#if defined(__BIG_ENDIAN)
*(dd++) = ss[1];
*(dd++) = ss[0];
ss += 2;
#else
*(dd++) = *(ss++);
*(dd++) = *(ss++);
#endif
}
}
static inline void u64_from_le32_copy(uint64_t *d, const void *s,
unsigned int cnt)
{
const uint32_t *ss = s;
uint32_t *dd = (uint32_t *)d;
while (cnt--) {
#if defined(__BIG_ENDIAN)
dd[1] = *(ss++);
dd[0] = *(ss++);
dd += 2;
#else
*(dd++) = *(ss++);
*(dd++) = *(ss++);
#endif
}
}
/* Convert a host-native 32bit value into little endian */
#if defined(__BIG_ENDIAN)
static inline uint32_t make_le32(uint32_t val)
{
return ((val & 0xff) << 24) | ((val & 0xff00) << 8) |
((val & 0xff0000) >> 8) | ((val & 0xff000000) >> 24);
}
#else
#define make_le32(val) (val)
#endif
static inline void make_le32_n(uint32_t *val, unsigned int num)
{
while (num--) {
*val = make_le32(*val);
val++;
}
}
/******************/
/* Portal access */
/******************/
struct qbman_swp_sys {
/* On GPP, the sys support for qbman_swp is here. The CENA region isi
* not an mmap() of the real portal registers, but an allocated
* place-holder, because the actual writes/reads to/from the portal are
* marshalled from these allocated areas using QBMan's "MC access
* registers". CINH accesses are atomic so there's no need for a
* place-holder. */
void *cena;
void __iomem *addr_cena;
void __iomem *addr_cinh;
};
/* P_OFFSET is (ACCESS_CMD,0,12) - offset within the portal
* C is (ACCESS_CMD,12,1) - is inhibited? (0==CENA, 1==CINH)
* SWP_IDX is (ACCESS_CMD,16,10) - Software portal index
* P is (ACCESS_CMD,28,1) - (0==special portal, 1==any portal)
* T is (ACCESS_CMD,29,1) - Command type (0==READ, 1==WRITE)
* E is (ACCESS_CMD,31,1) - Command execute (1 to issue, poll for 0==complete)
*/
static inline void qbman_cinh_write(struct qbman_swp_sys *s, uint32_t offset,
uint32_t val)
{
__raw_writel(val, s->addr_cinh + offset);
#ifdef QBMAN_CINH_TRACE
pr_info("qbman_cinh_write(%p:0x%03x) 0x%08x\n",
s->addr_cinh, offset, val);
#endif
}
static inline uint32_t qbman_cinh_read(struct qbman_swp_sys *s, uint32_t offset)
{
uint32_t reg = __raw_readl(s->addr_cinh + offset);
#ifdef QBMAN_CINH_TRACE
pr_info("qbman_cinh_read(%p:0x%03x) 0x%08x\n",
s->addr_cinh, offset, reg);
#endif
return reg;
}
static inline void *qbman_cena_write_start(struct qbman_swp_sys *s,
uint32_t offset)
{
void *shadow = s->cena + offset;
#ifdef QBMAN_CENA_TRACE
pr_info("qbman_cena_write_start(%p:0x%03x) %p\n",
s->addr_cena, offset, shadow);
#endif
BUG_ON(offset & 63);
dcbz(shadow);
return shadow;
}
static inline void qbman_cena_write_complete(struct qbman_swp_sys *s,
uint32_t offset, void *cmd)
{
const uint32_t *shadow = cmd;
int loop;
#ifdef QBMAN_CENA_TRACE
pr_info("qbman_cena_write_complete(%p:0x%03x) %p\n",
s->addr_cena, offset, shadow);
hexdump(cmd, 64);
#endif
for (loop = 15; loop >= 0; loop--)
#ifdef QBMAN_CINH_ONLY
__raw_writel(shadow[loop], s->addr_cinh +
offset + loop * 4);
#else
__raw_writel(shadow[loop], s->addr_cena +
offset + loop * 4);
#endif
}
static inline void *qbman_cena_read(struct qbman_swp_sys *s, uint32_t offset)
{
uint32_t *shadow = s->cena + offset;
unsigned int loop;
#ifdef QBMAN_CENA_TRACE
pr_info("qbman_cena_read(%p:0x%03x) %p\n",
s->addr_cena, offset, shadow);
#endif
for (loop = 0; loop < 16; loop++)
#ifdef QBMAN_CINH_ONLY
shadow[loop] = __raw_readl(s->addr_cinh + offset
+ loop * 4);
#else
shadow[loop] = __raw_readl(s->addr_cena + offset
+ loop * 4);
#endif
#ifdef QBMAN_CENA_TRACE
hexdump(shadow, 64);
#endif
return shadow;
}
static inline void qbman_cena_invalidate_prefetch(struct qbman_swp_sys *s,
uint32_t offset)
{
}
/******************/
/* Portal support */
/******************/
/* The SWP_CFG portal register is special, in that it is used by the
* platform-specific code rather than the platform-independent code in
* qbman_portal.c. So use of it is declared locally here. */
#define QBMAN_CINH_SWP_CFG 0xd00
/* For MC portal use, we always configure with
* DQRR_MF is (SWP_CFG,20,3) - DQRR max fill (<- 0x4)
* EST is (SWP_CFG,16,3) - EQCR_CI stashing threshold (<- 0x0)
* RPM is (SWP_CFG,12,2) - RCR production notification mode (<- 0x3)
* DCM is (SWP_CFG,10,2) - DQRR consumption notification mode (<- 0x2)
* EPM is (SWP_CFG,8,2) - EQCR production notification mode (<- 0x3)
* SD is (SWP_CFG,5,1) - memory stashing drop enable (<- FALSE)
* SP is (SWP_CFG,4,1) - memory stashing priority (<- TRUE)
* SE is (SWP_CFG,3,1) - memory stashing enable (<- 0x0)
* DP is (SWP_CFG,2,1) - dequeue stashing priority (<- TRUE)
* DE is (SWP_CFG,1,1) - dequeue stashing enable (<- 0x0)
* EP is (SWP_CFG,0,1) - EQCR_CI stashing priority (<- FALSE)
*/
static inline uint32_t qbman_set_swp_cfg(uint8_t max_fill, uint8_t wn,
uint8_t est, uint8_t rpm, uint8_t dcm,
uint8_t epm, int sd, int sp, int se,
int dp, int de, int ep)
{
uint32_t reg;
reg = e32_uint8_t(20, 3, max_fill) | e32_uint8_t(16, 3, est) |
e32_uint8_t(12, 2, rpm) | e32_uint8_t(10, 2, dcm) |
e32_uint8_t(8, 2, epm) | e32_int(5, 1, sd) |
e32_int(4, 1, sp) | e32_int(3, 1, se) | e32_int(2, 1, dp) |
e32_int(1, 1, de) | e32_int(0, 1, ep) | e32_uint8_t(14, 1, wn);
return reg;
}
static inline int qbman_swp_sys_init(struct qbman_swp_sys *s,
const struct qbman_swp_desc *d)
{
uint32_t reg;
s->addr_cena = d->cena_bar;
s->addr_cinh = d->cinh_bar;
s->cena = (void *)valloc(CONFIG_SYS_PAGE_SIZE);
memset((void *)s->cena, 0x00, CONFIG_SYS_PAGE_SIZE);
if (!s->cena) {
printf("Could not allocate page for cena shadow\n");
return -1;
}
#ifdef QBMAN_CHECKING
/* We should never be asked to initialise for a portal that isn't in
* the power-on state. (Ie. don't forget to reset portals when they are
* decommissioned!)
*/
reg = qbman_cinh_read(s, QBMAN_CINH_SWP_CFG);
BUG_ON(reg);
#endif
#ifdef QBMAN_CINH_ONLY
reg = qbman_set_swp_cfg(4, 1, 0, 3, 2, 3, 0, 1, 0, 1, 0, 0);
#else
reg = qbman_set_swp_cfg(4, 0, 0, 3, 2, 3, 0, 1, 0, 1, 0, 0);
#endif
qbman_cinh_write(s, QBMAN_CINH_SWP_CFG, reg);
reg = qbman_cinh_read(s, QBMAN_CINH_SWP_CFG);
if (!reg) {
printf("The portal is not enabled!\n");
free(s->cena);
return -1;
}
return 0;
}
static inline void qbman_swp_sys_finish(struct qbman_swp_sys *s)
{
free((void *)s->cena);
}

View file

@ -0,0 +1,222 @@
/*
* Freescale Layerscape MC I/O wrapper
*
* Copyright (C) 2015 Freescale Semiconductor, Inc.
* Author: Prabhakar Kushwaha <prabhakar@freescale.com>
*
* SPDX-License-Identifier: GPL-2.0+
*/
#include <fsl-mc/fsl_mc_sys.h>
#include <fsl-mc/fsl_mc_cmd.h>
#include <fsl-mc/fsl_dpmac.h>
int dpmac_open(struct fsl_mc_io *mc_io,
uint32_t cmd_flags,
int dpmac_id,
uint16_t *token)
{
struct mc_command cmd = { 0 };
int err;
/* prepare command */
cmd.header = mc_encode_cmd_header(DPMAC_CMDID_OPEN,
cmd_flags,
0);
DPMAC_CMD_OPEN(cmd, dpmac_id);
/* send command to mc*/
err = mc_send_command(mc_io, &cmd);
if (err)
return err;
/* retrieve response parameters */
*token = MC_CMD_HDR_READ_TOKEN(cmd.header);
return err;
}
int dpmac_close(struct fsl_mc_io *mc_io,
uint32_t cmd_flags,
uint16_t token)
{
struct mc_command cmd = { 0 };
/* prepare command */
cmd.header = mc_encode_cmd_header(DPMAC_CMDID_CLOSE, cmd_flags,
token);
/* send command to mc*/
return mc_send_command(mc_io, &cmd);
}
int dpmac_create(struct fsl_mc_io *mc_io,
uint32_t cmd_flags,
const struct dpmac_cfg *cfg,
uint16_t *token)
{
struct mc_command cmd = { 0 };
int err;
/* prepare command */
cmd.header = mc_encode_cmd_header(DPMAC_CMDID_CREATE,
cmd_flags,
0);
DPMAC_CMD_CREATE(cmd, cfg);
/* send command to mc*/
err = mc_send_command(mc_io, &cmd);
if (err)
return err;
/* retrieve response parameters */
*token = MC_CMD_HDR_READ_TOKEN(cmd.header);
return 0;
}
int dpmac_destroy(struct fsl_mc_io *mc_io,
uint32_t cmd_flags,
uint16_t token)
{
struct mc_command cmd = { 0 };
/* prepare command */
cmd.header = mc_encode_cmd_header(DPMAC_CMDID_DESTROY,
cmd_flags,
token);
/* send command to mc*/
return mc_send_command(mc_io, &cmd);
}
int dpmac_get_attributes(struct fsl_mc_io *mc_io,
uint32_t cmd_flags,
uint16_t token,
struct dpmac_attr *attr)
{
struct mc_command cmd = { 0 };
int err;
/* prepare command */
cmd.header = mc_encode_cmd_header(DPMAC_CMDID_GET_ATTR,
cmd_flags,
token);
/* send command to mc*/
err = mc_send_command(mc_io, &cmd);
if (err)
return err;
/* retrieve response parameters */
DPMAC_RSP_GET_ATTRIBUTES(cmd, attr);
return 0;
}
int dpmac_mdio_read(struct fsl_mc_io *mc_io,
uint32_t cmd_flags,
uint16_t token,
struct dpmac_mdio_cfg *cfg)
{
struct mc_command cmd = { 0 };
int err;
/* prepare command */
cmd.header = mc_encode_cmd_header(DPMAC_CMDID_MDIO_READ,
cmd_flags,
token);
DPMAC_CMD_MDIO_READ(cmd, cfg);
/* send command to mc*/
err = mc_send_command(mc_io, &cmd);
if (err)
return err;
/* retrieve response parameters */
DPMAC_RSP_MDIO_READ(cmd, cfg->data);
return 0;
}
int dpmac_mdio_write(struct fsl_mc_io *mc_io,
uint32_t cmd_flags,
uint16_t token,
struct dpmac_mdio_cfg *cfg)
{
struct mc_command cmd = { 0 };
/* prepare command */
cmd.header = mc_encode_cmd_header(DPMAC_CMDID_MDIO_WRITE,
cmd_flags,
token);
DPMAC_CMD_MDIO_WRITE(cmd, cfg);
/* send command to mc*/
return mc_send_command(mc_io, &cmd);
}
int dpmac_get_link_cfg(struct fsl_mc_io *mc_io,
uint32_t cmd_flags,
uint16_t token,
struct dpmac_link_cfg *cfg)
{
struct mc_command cmd = { 0 };
int err = 0;
/* prepare command */
cmd.header = mc_encode_cmd_header(DPMAC_CMDID_GET_LINK_CFG,
cmd_flags,
token);
/* send command to mc*/
err = mc_send_command(mc_io, &cmd);
if (err)
return err;
DPMAC_RSP_GET_LINK_CFG(cmd, cfg);
return 0;
}
int dpmac_set_link_state(struct fsl_mc_io *mc_io,
uint32_t cmd_flags,
uint16_t token,
struct dpmac_link_state *link_state)
{
struct mc_command cmd = { 0 };
/* prepare command */
cmd.header = mc_encode_cmd_header(DPMAC_CMDID_SET_LINK_STATE,
cmd_flags,
token);
DPMAC_CMD_SET_LINK_STATE(cmd, link_state);
/* send command to mc*/
return mc_send_command(mc_io, &cmd);
}
int dpmac_get_counter(struct fsl_mc_io *mc_io,
uint32_t cmd_flags,
uint16_t token,
enum dpmac_counter type,
uint64_t *counter)
{
struct mc_command cmd = { 0 };
int err = 0;
/* prepare command */
cmd.header = mc_encode_cmd_header(DPMAC_CMDID_GET_COUNTER,
cmd_flags,
token);
DPMAC_CMD_GET_COUNTER(cmd, type);
/* send command to mc*/
err = mc_send_command(mc_io, &cmd);
if (err)
return err;
DPMAC_RSP_GET_COUNTER(cmd, *counter);
return 0;
}

View file

@ -0,0 +1,31 @@
/* Copyright 2013-2015 Freescale Semiconductor Inc.
*
* SPDX-License-Identifier: GPL-2.0+
*/
#include <fsl-mc/fsl_mc_sys.h>
#include <fsl-mc/fsl_mc_cmd.h>
#include <fsl-mc/fsl_dpmng.h>
#include "fsl_dpmng_cmd.h"
int mc_get_version(struct fsl_mc_io *mc_io,
uint32_t cmd_flags,
struct mc_version *mc_ver_info)
{
struct mc_command cmd = { 0 };
int err;
/* prepare command */
cmd.header = mc_encode_cmd_header(DPMNG_CMDID_GET_VERSION,
cmd_flags,
0);
/* send command to mc*/
err = mc_send_command(mc_io, &cmd);
if (err)
return err;
/* retrieve response parameters */
DPMNG_RSP_GET_VERSION(cmd, mc_ver_info);
return 0;
}

View file

@ -0,0 +1,604 @@
/*
* Copyright (C) 2013-2015 Freescale Semiconductor
*
* SPDX-License-Identifier: GPL-2.0+
*/
#include <fsl-mc/fsl_mc_sys.h>
#include <fsl-mc/fsl_mc_cmd.h>
#include <fsl-mc/fsl_dpni.h>
int dpni_open(struct fsl_mc_io *mc_io,
uint32_t cmd_flags,
int dpni_id,
uint16_t *token)
{
struct mc_command cmd = { 0 };
int err;
/* prepare command */
cmd.header = mc_encode_cmd_header(DPNI_CMDID_OPEN,
cmd_flags,
0);
DPNI_CMD_OPEN(cmd, dpni_id);
/* send command to mc*/
err = mc_send_command(mc_io, &cmd);
if (err)
return err;
/* retrieve response parameters */
*token = MC_CMD_HDR_READ_TOKEN(cmd.header);
return 0;
}
int dpni_close(struct fsl_mc_io *mc_io,
uint32_t cmd_flags,
uint16_t token)
{
struct mc_command cmd = { 0 };
/* prepare command */
cmd.header = mc_encode_cmd_header(DPNI_CMDID_CLOSE,
cmd_flags,
token);
/* send command to mc*/
return mc_send_command(mc_io, &cmd);
}
int dpni_create(struct fsl_mc_io *mc_io,
uint32_t cmd_flags,
const struct dpni_cfg *cfg,
uint16_t *token)
{
struct mc_command cmd = { 0 };
int err;
/* prepare command */
cmd.header = mc_encode_cmd_header(DPNI_CMDID_CREATE,
cmd_flags,
0);
DPNI_CMD_CREATE(cmd, cfg);
/* send command to mc*/
err = mc_send_command(mc_io, &cmd);
if (err)
return err;
/* retrieve response parameters */
*token = MC_CMD_HDR_READ_TOKEN(cmd.header);
return 0;
}
int dpni_destroy(struct fsl_mc_io *mc_io,
uint32_t cmd_flags,
uint16_t token)
{
struct mc_command cmd = { 0 };
/* prepare command */
cmd.header = mc_encode_cmd_header(DPNI_CMDID_DESTROY,
cmd_flags,
token);
/* send command to mc*/
return mc_send_command(mc_io, &cmd);
}
int dpni_set_pools(struct fsl_mc_io *mc_io,
uint32_t cmd_flags,
uint16_t token,
const struct dpni_pools_cfg *cfg)
{
struct mc_command cmd = { 0 };
/* prepare command */
cmd.header = mc_encode_cmd_header(DPNI_CMDID_SET_POOLS,
cmd_flags,
token);
DPNI_CMD_SET_POOLS(cmd, cfg);
/* send command to mc*/
return mc_send_command(mc_io, &cmd);
}
int dpni_enable(struct fsl_mc_io *mc_io,
uint32_t cmd_flags,
uint16_t token)
{
struct mc_command cmd = { 0 };
/* prepare command */
cmd.header = mc_encode_cmd_header(DPNI_CMDID_ENABLE,
cmd_flags,
token);
/* send command to mc*/
return mc_send_command(mc_io, &cmd);
}
int dpni_disable(struct fsl_mc_io *mc_io,
uint32_t cmd_flags,
uint16_t token)
{
struct mc_command cmd = { 0 };
/* prepare command */
cmd.header = mc_encode_cmd_header(DPNI_CMDID_DISABLE,
cmd_flags,
token);
/* send command to mc*/
return mc_send_command(mc_io, &cmd);
}
int dpni_reset(struct fsl_mc_io *mc_io,
uint32_t cmd_flags,
uint16_t token)
{
struct mc_command cmd = { 0 };
/* prepare command */
cmd.header = mc_encode_cmd_header(DPNI_CMDID_RESET,
cmd_flags,
token);
/* send command to mc*/
return mc_send_command(mc_io, &cmd);
}
int dpni_get_attributes(struct fsl_mc_io *mc_io,
uint32_t cmd_flags,
uint16_t token,
struct dpni_attr *attr)
{
struct mc_command cmd = { 0 };
int err;
/* prepare command */
cmd.header = mc_encode_cmd_header(DPNI_CMDID_GET_ATTR,
cmd_flags,
token);
/* send command to mc*/
err = mc_send_command(mc_io, &cmd);
if (err)
return err;
/* retrieve response parameters */
DPNI_RSP_GET_ATTR(cmd, attr);
return 0;
}
int dpni_get_rx_buffer_layout(struct fsl_mc_io *mc_io,
uint32_t cmd_flags,
uint16_t token,
struct dpni_buffer_layout *layout)
{
struct mc_command cmd = { 0 };
int err;
/* prepare command */
cmd.header = mc_encode_cmd_header(DPNI_CMDID_GET_RX_BUFFER_LAYOUT,
cmd_flags,
token);
/* send command to mc*/
err = mc_send_command(mc_io, &cmd);
if (err)
return err;
/* retrieve response parameters */
DPNI_RSP_GET_RX_BUFFER_LAYOUT(cmd, layout);
return 0;
}
int dpni_set_rx_buffer_layout(struct fsl_mc_io *mc_io,
uint32_t cmd_flags,
uint16_t token,
const struct dpni_buffer_layout *layout)
{
struct mc_command cmd = { 0 };
/* prepare command */
cmd.header = mc_encode_cmd_header(DPNI_CMDID_SET_RX_BUFFER_LAYOUT,
cmd_flags,
token);
DPNI_CMD_SET_RX_BUFFER_LAYOUT(cmd, layout);
/* send command to mc*/
return mc_send_command(mc_io, &cmd);
}
int dpni_get_tx_buffer_layout(struct fsl_mc_io *mc_io,
uint32_t cmd_flags,
uint16_t token,
struct dpni_buffer_layout *layout)
{
struct mc_command cmd = { 0 };
int err;
/* prepare command */
cmd.header = mc_encode_cmd_header(DPNI_CMDID_GET_TX_BUFFER_LAYOUT,
cmd_flags,
token);
/* send command to mc*/
err = mc_send_command(mc_io, &cmd);
if (err)
return err;
/* retrieve response parameters */
DPNI_RSP_GET_TX_BUFFER_LAYOUT(cmd, layout);
return 0;
}
int dpni_set_tx_buffer_layout(struct fsl_mc_io *mc_io,
uint32_t cmd_flags,
uint16_t token,
const struct dpni_buffer_layout *layout)
{
struct mc_command cmd = { 0 };
/* prepare command */
cmd.header = mc_encode_cmd_header(DPNI_CMDID_SET_TX_BUFFER_LAYOUT,
cmd_flags,
token);
DPNI_CMD_SET_TX_BUFFER_LAYOUT(cmd, layout);
/* send command to mc*/
return mc_send_command(mc_io, &cmd);
}
int dpni_get_tx_conf_buffer_layout(struct fsl_mc_io *mc_io,
uint32_t cmd_flags,
uint16_t token,
struct dpni_buffer_layout *layout)
{
struct mc_command cmd = { 0 };
int err;
/* prepare command */
cmd.header = mc_encode_cmd_header(DPNI_CMDID_GET_TX_CONF_BUFFER_LAYOUT,
cmd_flags,
token);
/* send command to mc*/
err = mc_send_command(mc_io, &cmd);
if (err)
return err;
/* retrieve response parameters */
DPNI_RSP_GET_TX_CONF_BUFFER_LAYOUT(cmd, layout);
return 0;
}
int dpni_set_tx_conf_buffer_layout(struct fsl_mc_io *mc_io,
uint32_t cmd_flags,
uint16_t token,
const struct dpni_buffer_layout *layout)
{
struct mc_command cmd = { 0 };
/* prepare command */
cmd.header = mc_encode_cmd_header(DPNI_CMDID_SET_TX_CONF_BUFFER_LAYOUT,
cmd_flags,
token);
DPNI_CMD_SET_TX_CONF_BUFFER_LAYOUT(cmd, layout);
/* send command to mc*/
return mc_send_command(mc_io, &cmd);
}
int dpni_get_qdid(struct fsl_mc_io *mc_io,
uint32_t cmd_flags,
uint16_t token,
uint16_t *qdid)
{
struct mc_command cmd = { 0 };
int err;
/* prepare command */
cmd.header = mc_encode_cmd_header(DPNI_CMDID_GET_QDID,
cmd_flags,
token);
/* send command to mc*/
err = mc_send_command(mc_io, &cmd);
if (err)
return err;
/* retrieve response parameters */
DPNI_RSP_GET_QDID(cmd, *qdid);
return 0;
}
int dpni_get_tx_data_offset(struct fsl_mc_io *mc_io,
uint32_t cmd_flags,
uint16_t token,
uint16_t *data_offset)
{
struct mc_command cmd = { 0 };
int err;
/* prepare command */
cmd.header = mc_encode_cmd_header(DPNI_CMDID_GET_TX_DATA_OFFSET,
cmd_flags,
token);
/* send command to mc*/
err = mc_send_command(mc_io, &cmd);
if (err)
return err;
/* retrieve response parameters */
DPNI_RSP_GET_TX_DATA_OFFSET(cmd, *data_offset);
return 0;
}
int dpni_get_counter(struct fsl_mc_io *mc_io,
uint32_t cmd_flags,
uint16_t token,
enum dpni_counter counter,
uint64_t *value)
{
struct mc_command cmd = { 0 };
int err;
/* prepare command */
cmd.header = mc_encode_cmd_header(DPNI_CMDID_GET_COUNTER,
cmd_flags,
token);
DPNI_CMD_GET_COUNTER(cmd, counter);
/* send command to mc*/
err = mc_send_command(mc_io, &cmd);
if (err)
return err;
/* retrieve response parameters */
DPNI_RSP_GET_COUNTER(cmd, *value);
return 0;
}
int dpni_set_counter(struct fsl_mc_io *mc_io,
uint32_t cmd_flags,
uint16_t token,
enum dpni_counter counter,
uint64_t value)
{
struct mc_command cmd = { 0 };
/* prepare command */
cmd.header = mc_encode_cmd_header(DPNI_CMDID_SET_COUNTER,
cmd_flags,
token);
DPNI_CMD_SET_COUNTER(cmd, counter, value);
/* send command to mc*/
return mc_send_command(mc_io, &cmd);
}
int dpni_set_link_cfg(struct fsl_mc_io *mc_io,
uint32_t cmd_flags,
uint16_t token,
const struct dpni_link_cfg *cfg)
{
struct mc_command cmd = { 0 };
/* prepare command */
cmd.header = mc_encode_cmd_header(DPNI_CMDID_SET_LINK_CFG,
cmd_flags,
token);
DPNI_CMD_SET_LINK_CFG(cmd, cfg);
/* send command to mc*/
return mc_send_command(mc_io, &cmd);
}
int dpni_get_link_state(struct fsl_mc_io *mc_io,
uint32_t cmd_flags,
uint16_t token,
struct dpni_link_state *state)
{
struct mc_command cmd = { 0 };
int err;
/* prepare command */
cmd.header = mc_encode_cmd_header(DPNI_CMDID_GET_LINK_STATE,
cmd_flags,
token);
/* send command to mc*/
err = mc_send_command(mc_io, &cmd);
if (err)
return err;
/* retrieve response parameters */
DPNI_RSP_GET_LINK_STATE(cmd, state);
return 0;
}
int dpni_set_primary_mac_addr(struct fsl_mc_io *mc_io,
uint32_t cmd_flags,
uint16_t token,
const uint8_t mac_addr[6])
{
struct mc_command cmd = { 0 };
/* prepare command */
cmd.header = mc_encode_cmd_header(DPNI_CMDID_SET_PRIM_MAC,
cmd_flags,
token);
DPNI_CMD_SET_PRIMARY_MAC_ADDR(cmd, mac_addr);
/* send command to mc*/
return mc_send_command(mc_io, &cmd);
}
int dpni_get_primary_mac_addr(struct fsl_mc_io *mc_io,
uint32_t cmd_flags,
uint16_t token,
uint8_t mac_addr[6])
{
struct mc_command cmd = { 0 };
int err;
/* prepare command */
cmd.header = mc_encode_cmd_header(DPNI_CMDID_GET_PRIM_MAC,
cmd_flags,
token);
/* send command to mc*/
err = mc_send_command(mc_io, &cmd);
if (err)
return err;
/* retrieve response parameters */
DPNI_RSP_GET_PRIMARY_MAC_ADDR(cmd, mac_addr);
return 0;
}
int dpni_add_mac_addr(struct fsl_mc_io *mc_io,
uint32_t cmd_flags,
uint16_t token,
const uint8_t mac_addr[6])
{
struct mc_command cmd = { 0 };
/* prepare command */
cmd.header = mc_encode_cmd_header(DPNI_CMDID_ADD_MAC_ADDR,
cmd_flags,
token);
DPNI_CMD_ADD_MAC_ADDR(cmd, mac_addr);
/* send command to mc*/
return mc_send_command(mc_io, &cmd);
}
int dpni_remove_mac_addr(struct fsl_mc_io *mc_io,
uint32_t cmd_flags,
uint16_t token,
const uint8_t mac_addr[6])
{
struct mc_command cmd = { 0 };
/* prepare command */
cmd.header = mc_encode_cmd_header(DPNI_CMDID_REMOVE_MAC_ADDR,
cmd_flags,
token);
DPNI_CMD_REMOVE_MAC_ADDR(cmd, mac_addr);
/* send command to mc*/
return mc_send_command(mc_io, &cmd);
}
int dpni_set_tx_flow(struct fsl_mc_io *mc_io,
uint32_t cmd_flags,
uint16_t token,
uint16_t *flow_id,
const struct dpni_tx_flow_cfg *cfg)
{
struct mc_command cmd = { 0 };
int err;
/* prepare command */
cmd.header = mc_encode_cmd_header(DPNI_CMDID_SET_TX_FLOW,
cmd_flags,
token);
DPNI_CMD_SET_TX_FLOW(cmd, *flow_id, cfg);
/* send command to mc*/
err = mc_send_command(mc_io, &cmd);
if (err)
return err;
/* retrieve response parameters */
DPNI_RSP_SET_TX_FLOW(cmd, *flow_id);
return 0;
}
int dpni_get_tx_flow(struct fsl_mc_io *mc_io,
uint32_t cmd_flags,
uint16_t token,
uint16_t flow_id,
struct dpni_tx_flow_attr *attr)
{
struct mc_command cmd = { 0 };
int err;
/* prepare command */
cmd.header = mc_encode_cmd_header(DPNI_CMDID_GET_TX_FLOW,
cmd_flags,
token);
DPNI_CMD_GET_TX_FLOW(cmd, flow_id);
/* send command to mc*/
err = mc_send_command(mc_io, &cmd);
if (err)
return err;
/* retrieve response parameters */
DPNI_RSP_GET_TX_FLOW(cmd, attr);
return 0;
}
int dpni_set_rx_flow(struct fsl_mc_io *mc_io,
uint32_t cmd_flags,
uint16_t token,
uint8_t tc_id,
uint16_t flow_id,
const struct dpni_queue_cfg *cfg)
{
struct mc_command cmd = { 0 };
/* prepare command */
cmd.header = mc_encode_cmd_header(DPNI_CMDID_SET_RX_FLOW,
cmd_flags,
token);
DPNI_CMD_SET_RX_FLOW(cmd, tc_id, flow_id, cfg);
/* send command to mc*/
return mc_send_command(mc_io, &cmd);
}
int dpni_get_rx_flow(struct fsl_mc_io *mc_io,
uint32_t cmd_flags,
uint16_t token,
uint8_t tc_id,
uint16_t flow_id,
struct dpni_queue_attr *attr)
{
struct mc_command cmd = { 0 };
int err;
/* prepare command */
cmd.header = mc_encode_cmd_header(DPNI_CMDID_GET_RX_FLOW,
cmd_flags,
token);
DPNI_CMD_GET_RX_FLOW(cmd, tc_id, flow_id);
/* send command to mc*/
err = mc_send_command(mc_io, &cmd);
if (err)
return err;
/* retrieve response parameters */
DPNI_RSP_GET_RX_FLOW(cmd, attr);
return 0;
}

Some files were not shown because too many files have changed in this diff Show more