mirror of
https://github.com/logicog/RTLPlayground.git
synced 2026-08-30 14:52:51 +08:00
652 lines
20 KiB
C
652 lines
20 KiB
C
/*
|
|
* This is a driver implementation for the Internal PHYs and RTL8221/RTL8224 PHYs
|
|
* for the RTL827x platform
|
|
* This code is in the Public Domain
|
|
*/
|
|
|
|
#define REGDBG
|
|
|
|
// Phy ID of the external RTL8224 PHY.
|
|
#define RTL8224_PHY_ID 0x00
|
|
|
|
#include <stdint.h>
|
|
#include "rtl837x_common.h"
|
|
#include "rtl837x_sfr.h"
|
|
#include "rtl837x_regs.h"
|
|
#include "rtl837x_phy.h"
|
|
#include "phy.h"
|
|
#include "machine.h"
|
|
|
|
#pragma codeseg BANK2
|
|
#pragma constseg BANK2
|
|
|
|
extern __code uint16_t bit_mask[16];
|
|
extern __code const struct machine machine;
|
|
extern __xdata struct machine_runtime machine_detected;
|
|
|
|
__xdata struct phy_settings phy_settings;
|
|
|
|
// SDS-settings for RTL8224 first SerDes which is connected to the RTL837x-SOC.
|
|
// Array contrains register-value, and SDS-CMD, which already encodes (sds_index, page, reg).
|
|
// This array is used in phy_config_8224().
|
|
//
|
|
// Note: Adding `Swapping the RX for N-devices`-setting on the end of the array, didn't work.
|
|
// Setting will apply but still no packets flow.
|
|
// Settings are `0x2000, 0xc10c`,
|
|
__code uint16_t rtl8224_sds0_setttings[42] = {
|
|
// SDS_DATA, SDS_CMD
|
|
0x4480, 0xc842,
|
|
0x0400, 0xc9c2,
|
|
0x6d02, 0xcc42,
|
|
0x424e, 0xcdc2,
|
|
0x0002, 0xcec2,
|
|
0x1390, 0xce6c,
|
|
0x003f, 0xca6c,
|
|
0x0200, 0xc86c,
|
|
0x0080, 0xc25c,
|
|
0x0408, 0xc35c,
|
|
0x020d, 0xc3dc,
|
|
0x0601, 0xc4dc,
|
|
0x222c, 0xc5dc,
|
|
0xa217, 0xc65c,
|
|
0xfe40, 0xc6dc,
|
|
0xf5c1, 0xcadc,
|
|
0x0443, 0xcb5c,
|
|
0xabb0, 0xcedc,
|
|
0x5078, 0xc90c,
|
|
0xc45c, 0xc18c,
|
|
0, 0
|
|
};
|
|
|
|
void rtl8224_phy_enable(void) __banked
|
|
{
|
|
uint16_t pval;
|
|
|
|
// p001e.0a90:00f3 R02f8-000000f3 R02f4-000000fc P000001.1e000a90:00fc
|
|
print_string("\r\nrtl8224_phy_enable called\r\n");
|
|
phy_read(RTL8224_PHY_ID, PHY_MMD30, RTL837X_CFG_PHY_MDI_REVERSE);
|
|
pval = SFR_DATA_U16;
|
|
|
|
// PHY Initialization:
|
|
REG_WRITE(0x2f8, 0, 0, pval >> 8, pval);
|
|
pval &= 0xfff0;
|
|
pval |= 0x0c;
|
|
REG_WRITE(0x2f4, 0, 0, pval >> 8, pval);
|
|
|
|
phy_write(RTL8224_PHY_ID, PHY_MMD30, RTL837X_CFG_PHY_MDI_REVERSE, pval);
|
|
delay(50);
|
|
|
|
if (machine_detected.isN) {
|
|
print_string(" N-settings");
|
|
// TX_POLARITY_SWAP
|
|
rtl8224_write_reg_u16(RTL837X_CFG_PHY_TX_POLARITY_SWAP, 0x596A);
|
|
}
|
|
|
|
print_string("\r\nrtl8224_phy_enable done\r\n");
|
|
}
|
|
|
|
|
|
void phy_config_8261(uint8_t phy, uint8_t sds) __banked
|
|
{
|
|
print_string("phy_config_8261: phy "); print_byte(phy);
|
|
print_string(" sds "); print_byte(sds); write_char('\n');
|
|
phy_write(phy, PHY_MMD30, 0x141, 0x80aa); // P000008.1e000141:80aa P000008.1e000143:8c07 p031e.0143:0c07
|
|
phy_write(phy, PHY_MMD30, 0x143, 0x8c07);
|
|
phy_read(phy, PHY_MMD30, 0x143);
|
|
print_phy_data();
|
|
phy_write(phy, PHY_MMD30, 0x141, 0x5078); // P000008.1e000141:5078 P000008.1e000143:8c86 p031e.0143:0c86
|
|
phy_write(phy, PHY_MMD30, 0x143, 0x8c86);
|
|
phy_read(phy, PHY_MMD30, 0x143);
|
|
print_phy_data();
|
|
phy_read(phy, PHY_MMD30, 0x105);
|
|
print_phy_data(); // p031e.0105:0000
|
|
|
|
phy_write(phy, PHY_MMD30, 0xe1, 0x00); // P000008.1e0000e1:0000
|
|
phy_write(phy, PHY_MMD30, 0xe3, 0x00); // P000008.1e0000e3:0000
|
|
phy_write(phy, PHY_MMD30, 0xe4, 0x01); // P000008.1e0000e4:0001
|
|
phy_write(phy, PHY_MMD30, 0xe0, 0x2f); // P000008.1e0000e0:002f
|
|
|
|
// The following are actually bit-ops:
|
|
phy_write(phy, PHY_MMD31, 0xa442, 0x8418); // p031f.a442:0418 P000008.1f00a442:8418
|
|
phy_write(phy, PHY_MMD31, 0xa448, 0x07a0); // p031f.a448:07a0 P000008.1f00a448:07a0
|
|
phy_write(phy, PHY_MMD31, 0xa43a, 0x003f); // p031f.a43a:0030 P000008.1e0000e2:003f
|
|
|
|
phy_write(phy, PHY_MMD31, 0xc800, 0x5a02); // P000008.1f00c800:5a02
|
|
|
|
phy_write(phy, PHY_MMD30, 0x01ee, 0x5a02); // p031e.01ee:5a00 P000008.1e0001ee:5a02
|
|
phy_write(phy, PHY_MMD30, 0x0230, 0x0002); // p031e.0230:0000 P000008.1e000230:0002
|
|
phy_write(phy, PHY_MMD31, 0xc802, 0x0073); // p031f.c802:0000 P000008.1f00c802:0073
|
|
phy_write(phy, PHY_MMD30, 0x01ef, 0xe004); // p031e.01ef:0004 P000008.1e0001ef:e004
|
|
delay(20);
|
|
phy_write(phy, PHY_MMD30, 0x01ef, 0x0004); // p031e.01ef:e004 P000008.1e0001ef:0004
|
|
delay(20);
|
|
phy_write(phy, PHY_MMD30, 0x0230, 0x01c2); // p031e.0230:01c2 P000008.1e000230:0002
|
|
|
|
phy_read(phy, PHY_MMD30, 0x103);
|
|
print_phy_data(); // p031e.0103:8261
|
|
|
|
phy_write(phy, PHY_MMD30, 0x01c8, 0x0104); // p031e.01c8:0104 P000008.1e0001c8:0104
|
|
phy_write(phy, PHY_MMD30, 0x01c9, 0x8080); // p031e.01c9:8080 P000008.1e0001c9:8080
|
|
phy_write(phy, PHY_MMD30, 0x01ca, 0x2020); // p031e.01ca:2020 P000008.1e0001ca:2020
|
|
phy_write(phy, PHY_MMD30, 0x0105, 0x0000); // p031e.0105:0000 P000008.1e000105:0000
|
|
phy_write(phy, PHY_MMD30, 0x00c2, 0x880d); // p031e.00c2:880d P000008.1e0000c2:880d
|
|
phy_write(phy, PHY_MMD30, 0x03f1, 0x0072); // p031e.03f1:0072 P000008.1e0003f1:0072
|
|
phy_write(phy, PHY_MMD30, 0x02a2, 0x0010); // p031e.02a2:0010 P000008.1e0002a2:0010
|
|
phy_write(phy, PHY_MMD30, 0x00c1, 0x0127); // p031e.00c1:0127 P000008.1e0000c1:0127
|
|
phy_write(phy, PHY_MMD30, 0x00c1, 0x0167); // p031e.00c1:0127 P000008.1e0000c1:0167
|
|
|
|
sds_write_v(sds, 0x21, 0x00, 0x4096); // Q002100:4906
|
|
sds_write_v(sds, 0x36, 0x05, 0x4000); // Q003605:4000
|
|
sds_write_v(sds, 0x1f, 0x02, 0x001f); // Q001f02:001f
|
|
|
|
phy_read(phy, 0x01, 0x0000);
|
|
print_phy_data(); // p0301.0000:2040
|
|
|
|
phy_write(phy, 0x01, 0x0000, 0x2040); // P000008.01000000:2040
|
|
delay(20);
|
|
|
|
sds_write_v(sds, 0, 0, 0x1603); // Q000000:1603
|
|
delay(20);
|
|
sds_write_v(sds, 0, 0, 0x1601); // Q000000:1601
|
|
delay(20);
|
|
sds_write_v(sds, 0, 0, 0x1603); //Q000000:1603
|
|
delay(20);
|
|
// r6330:00005555 R6330-00005555 r7b20:000003ed R7b20-000003ed
|
|
print_string("\r\nphy_config_8261 done\n");
|
|
}
|
|
|
|
|
|
void phy_config(uint8_t phy) __banked
|
|
{
|
|
print_string("\r\nphy_config: ");
|
|
write_char('0' + phy);
|
|
|
|
delay(20);
|
|
// PHY configuration: External 8221B?
|
|
// p081e.75f3:ffff P000100.1e0075f3:fffe
|
|
phy_modify(phy, PHY_MMD30, 0x75f3, 0x0001, 0x0000);
|
|
delay(20);
|
|
|
|
// p081e.697a:ffff P000100.1e00697a:ffc1 / p031e.697a:0003 P000008.1e00697a:0001
|
|
// SERDES OPTION 1 Register (MMD 30.0x6) bits 0-5: 0x01: Set HiSGMII+SGMII
|
|
phy_modify(phy, PHY_MMD30, 0x697a, 0x003f, 0x0001);
|
|
delay(20);
|
|
|
|
// p031f.a432:0811 P000008.1f00a432:0831
|
|
// PHYCR2 PHY Specific Control Register 2, MMD 31. 0xA432), set bit 5: enable EEE
|
|
phy_modify(phy, PHY_MMD31, PHY_MMD31_PHYCR2, 0x0000, 0x0020);
|
|
|
|
// p0307.003e:0000 P000008.0700003e:0001
|
|
// EEE avertisment 2 register MMMD 7.0x003e, set bit 0: 2.5G has EEE capability
|
|
phy_modify(phy, PHY_MMD_AN, PHY_EEE_ADV2, 0x0000, 0x0001);
|
|
delay(20);
|
|
|
|
// p031f.a442:043c P000008.1f00a442:0430
|
|
// Unknown, but clear bits 2/3
|
|
phy_modify(phy, PHY_MMD31, 0xa442, 0x000c, 0x0000);
|
|
delay(20);
|
|
|
|
// P000100.1e0075b5:e084
|
|
phy_write(phy, PHY_MMD30, 0x75b5, 0xe084);
|
|
delay(20);
|
|
|
|
// p031e.75b2:0000 P000008.1e0075b2:0060
|
|
// set bits 5/6
|
|
phy_modify(phy, PHY_MMD30, 0x75b2, 0x0000, 0x0060);
|
|
delay(20);
|
|
|
|
// p081f.d040:ffff P000100.1f00d040:feff
|
|
// LCR6 (LED Control Register 6, MMD 31.D040), set bits 8/9 to 0b10
|
|
phy_modify(phy, PHY_MMD30, 0xd040, 0x0300, 0x0200);
|
|
delay(20);
|
|
|
|
// p081f.a400:ffff P000100.1f00a400:ffff, then: p081f.a400:ffff P000100.1f00a400:bfff
|
|
// p031f.a400:1040 P000008.1f00a400:5040, then: p031f.a400:5040 P000008.1f00a400:1040
|
|
// FEDCR (Fast Ethernet Duplex Control Register, MMD 31.0xA400)
|
|
// Set bit 14, sleep, then clear again, according to the datasheet these bits are reserved
|
|
phy_modify(phy, PHY_MMD31, PHY_MMD31_FEDCR, 0x0000, 0x4000);
|
|
delay(20);
|
|
|
|
phy_modify(phy, PHY_MMD31, PHY_MMD31_FEDCR, 0x4000, 0x0000);
|
|
delay(20);
|
|
|
|
print_string("\r\n phy config done\r\n");
|
|
}
|
|
|
|
|
|
void phy_config_8224(void) __banked
|
|
{
|
|
uint16_t pval;
|
|
print_string("\r\nphy_config_8224 called\r\nRTL8224 ID: ");
|
|
|
|
// Print RTL8224 chip id
|
|
rtl8224_read_reg_u16(RTL837X_REG_CHIP_ID + 1);
|
|
print_short(SFR_DATA_U16);
|
|
rtl8224_read_reg_u16(RTL837X_REG_CHIP_ID);
|
|
print_byte(SFR_DATA_U16 >> 8);
|
|
print_byte(SFR_DATA_U16);
|
|
write_char('\n');
|
|
|
|
// p001e.7b20:0bff R02f8-00000bff R02f4-00000bed P000001.1e007b20:0bed
|
|
phy_read(RTL8224_PHY_ID, PHY_MMD30, 0x7b20);
|
|
pval = SFR_DATA_U16;
|
|
|
|
REG_WRITE(0x2f8, 0, 0, pval >> 8, pval);
|
|
pval &= 0x0fe0;
|
|
pval |= 0x000d;
|
|
REG_WRITE(0x2f4, 0, 0, pval >> 8, pval);
|
|
|
|
phy_write(RTL8224_PHY_ID, PHY_MMD30, 0x7b20, pval);
|
|
|
|
uint8_t i = 0;
|
|
while (rtl8224_sds0_setttings[i]) {
|
|
rtl8224_write_reg_u16(RTL837X_SDS_INDACS_WRITE_DATA, rtl8224_sds0_setttings[i]);
|
|
i++;
|
|
rtl8224_write_reg_u16(RTL837X_SDS_INDACS_CMD, rtl8224_sds0_setttings[i]);
|
|
i++;
|
|
do {
|
|
rtl8224_read_reg_u16(0x3f8);
|
|
} while (SFR_DATA_8 & 0x80);
|
|
}
|
|
|
|
print_string("\r\nphy_config_8224 done\r\n");
|
|
}
|
|
|
|
|
|
/*
|
|
* Set Speed of a PHY
|
|
* See e.g. RTL8221B datasheet
|
|
* duplex: 0: half, 1: full, 2: both
|
|
*/
|
|
void phy_set_speed(void) __banked
|
|
{
|
|
uint16_t v;
|
|
|
|
print_string("Setting port "); print_phys_port(phy_settings.port);
|
|
if (machine.n_10g && phy_settings.port == 3)
|
|
phy_settings.is10g_port = 1;
|
|
if (machine.n_10g == 2 && phy_settings.port == 8)
|
|
phy_settings.is10g_port = 1;
|
|
if (phy_settings.speed == PHY_OFF) {
|
|
print_string(" to disabled");
|
|
} else {
|
|
print_string(" to speed ");
|
|
switch(phy_settings.speed) {
|
|
case PHY_SPEED_AUTO:
|
|
print_string("auto");
|
|
if (phy_settings.is10g_port)
|
|
print_string (" (10g)");
|
|
break;
|
|
case PHY_SPEED_10M:
|
|
print_string("10M");
|
|
if (phy_settings.duplex)
|
|
print_string(" full duplex");
|
|
else
|
|
print_string(" half duplex");
|
|
break;
|
|
case PHY_SPEED_100M:
|
|
print_string("100M");
|
|
if (phy_settings.duplex)
|
|
print_string(" full duplex");
|
|
else
|
|
print_string(" half duplex");
|
|
break;
|
|
case PHY_SPEED_1G:
|
|
print_string("1G");
|
|
break;
|
|
case PHY_SPEED_2G5:
|
|
print_string("2G5");
|
|
break;
|
|
default:
|
|
print_string("UNKNOWN");
|
|
break;
|
|
}
|
|
}
|
|
write_char('\n');
|
|
|
|
phy_read(phy_settings.port, PHY_MMD31, 0xa610);
|
|
v = SFR_DATA_U16;
|
|
if (phy_settings.speed == PHY_OFF) {
|
|
phy_write(phy_settings.port, PHY_MMD31, 0xa610, v | 0x0800);
|
|
return;
|
|
}
|
|
// Port is on, make sure of it:
|
|
if (v & 0x0800)
|
|
phy_write(phy_settings.port, PHY_MMD31, 0xa610, v & 0xf7ff);
|
|
|
|
if (phy_settings.speed == PHY_SPEED_AUTO) {
|
|
// AN Advertisement Register (MMD 7.0x0010)
|
|
// bits 0-4: 0x1 (802.3 supported), Extended Next Page format used
|
|
phy_write(phy_settings.port, PHY_MMD_AN, PHY_ANEG_ADV, 0x15e1);
|
|
// Multi-GBASE-TBASE-T AN Control 1 Register (MMD 7.0x0020)
|
|
// bit 14: SLAVE, bit 13: Multi-Port device, bit 8: 2.5GBit available, 1: LD
|
|
phy_write(phy_settings.port, PHY_MMD_AN, PHY_ANEG_MGBASE_CTRL, 0x6081);
|
|
// GBCR (1000Base-T Control Register, MMD 31.0xA412)
|
|
if (phy_settings.is10g_port)
|
|
phy_modify(phy_settings.port, PHY_MMD31, PHY_MMD31_GBCR, 0x0000, 0x0e00);
|
|
else
|
|
phy_modify(phy_settings.port, PHY_MMD31, PHY_MMD31_GBCR, 0x0000, 0x0200);
|
|
phy_write(phy_settings.port, PHY_MMD_AN, PHY_ANEG_CTRL, 0x3200); // Restart AN
|
|
} else {
|
|
// AN Control Register (MMD 7.0x0000)
|
|
phy_write(phy_settings.port, PHY_MMD_AN, PHY_ANEG_CTRL, 0x2000); // Clear bit 12: No Autoneg, Set Extended Pages (bit 13)
|
|
if (phy_settings.speed == PHY_SPEED_10M) {
|
|
phy_write(phy_settings.port, PHY_MMD_AN, PHY_ANEG_MGBASE_CTRL, 0x6001);
|
|
if (!phy_settings.duplex)
|
|
phy_write(phy_settings.port, PHY_MMD_AN, PHY_ANEG_ADV, 0x1421);
|
|
else if (phy_settings.duplex == 1)
|
|
phy_write(phy_settings.port, PHY_MMD_AN, PHY_ANEG_ADV, 0x1441);
|
|
else
|
|
phy_write(phy_settings.port, PHY_MMD_AN, PHY_ANEG_ADV, 0x1461);
|
|
phy_modify(phy_settings.port, PHY_MMD31, PHY_MMD31_GBCR, 0x0200, 0x0000);
|
|
} else if (phy_settings.speed == PHY_SPEED_100M) {
|
|
phy_write(phy_settings.port, PHY_MMD_AN, PHY_ANEG_MGBASE_CTRL, 0x6001);
|
|
if (!phy_settings.duplex)
|
|
phy_write(phy_settings.port, PHY_MMD_AN, PHY_ANEG_ADV, 0x1481);
|
|
if (phy_settings.duplex == 1)
|
|
phy_write(phy_settings.port, PHY_MMD_AN, PHY_ANEG_ADV, 0x1501);
|
|
else
|
|
phy_write(phy_settings.port, PHY_MMD_AN, PHY_ANEG_ADV, 0x1581);
|
|
phy_modify(phy_settings.port, PHY_MMD31, PHY_MMD31_GBCR, 0x0200, 0x0000);
|
|
} else {
|
|
// AN Advertisement Register (MMD 7.0x0010)
|
|
// bits 0-4: 0x1 (802.3 supported), Extended Next Page format used
|
|
phy_write(phy_settings.port, PHY_MMD_AN, PHY_ANEG_ADV, 0x1001);
|
|
if (phy_settings.speed == PHY_SPEED_1G) {
|
|
// Multi-GBASE-TBASE-T AN Control 1 Register (MMD 7.0x0020)
|
|
// bit 14: SLAVE, bit 13: Multi-Port device, 1: LD Loop timin enableed
|
|
phy_write(phy_settings.port, PHY_MMD_AN, PHY_ANEG_MGBASE_CTRL, 0x6001);
|
|
// GBCR (1000Base-T Control Register, MMD 31.0xA412)
|
|
phy_modify(phy_settings.port, PHY_MMD31, PHY_MMD31_GBCR, 0x0000, 0x0200);
|
|
} else if (phy_settings.speed == PHY_SPEED_2G5) {
|
|
// Multi-GBASE-TBASE-T AN Control 1 Register (MMD 7.0x0020)
|
|
// bit 14: SLAVE, bit 13: Multi-Port device, bit 8: 2.5GBit available, 1: LD Loop timin enableed
|
|
phy_write(phy_settings.port, PHY_MMD_AN, PHY_ANEG_MGBASE_CTRL, 0x6081);
|
|
// GBCR (1000Base-T Control Register, MMD 31.0xA412)
|
|
phy_modify(phy_settings.port, PHY_MMD31, PHY_MMD31_GBCR, 0x0200, 0x0000);
|
|
} else if (phy_settings.speed == PHY_SPEED_5G) {
|
|
phy_write(phy_settings.port, PHY_MMD_AN, PHY_ANEG_MGBASE_CTRL, 0x6081);
|
|
phy_modify(phy_settings.port, PHY_MMD31, PHY_MMD31_GBCR, 0x0400, 0x0000);
|
|
} else if (phy_settings.speed == PHY_SPEED_10G) {
|
|
phy_write(phy_settings.port, PHY_MMD_AN, PHY_ANEG_MGBASE_CTRL, 0x6081);
|
|
phy_modify(phy_settings.port, PHY_MMD31, PHY_MMD31_GBCR, 0x0800, 0x0000);
|
|
}
|
|
}
|
|
phy_write(phy_settings.port, PHY_MMD_AN, PHY_ANEG_CTRL, 0x3000); // Enable AN
|
|
}
|
|
}
|
|
|
|
|
|
void phy_set_duplex(void) __banked
|
|
{
|
|
uint16_t v;
|
|
|
|
print_string("Setting port "); print_phys_port(phy_settings.port);
|
|
if (phy_settings.duplex)
|
|
print_string(" to full duplex");
|
|
else
|
|
print_string(" to half duplex");
|
|
write_char('\n');
|
|
|
|
phy_read(phy_settings.port, PHY_MMD_AN, PHY_ANEG_CTRL);
|
|
v = SFR_DATA_U16;
|
|
if (!(v & 0x1000)) { // AN disabled, we are in forced mode
|
|
phy_read(phy_settings.port, PHY_MMD31, PHY_MMD31_FEDCR);
|
|
v = SFR_DATA_U16;
|
|
if (phy_settings.duplex)
|
|
v |= 0x0100;
|
|
else
|
|
v &= 0xfeff;
|
|
phy_write(phy_settings.port, PHY_MMD31, PHY_MMD31_FEDCR, v);
|
|
return;
|
|
}
|
|
// Disable AN
|
|
phy_write(phy_settings.port, PHY_MMD_AN, PHY_ANEG_CTRL, 0x2000);
|
|
phy_read(phy_settings.port, PHY_MMD_AN, PHY_ANEG_ADV);
|
|
v = SFR_DATA_U16;
|
|
if (v & 0x0060) {
|
|
if (phy_settings.duplex)
|
|
phy_modify(phy_settings.port, PHY_MMD_AN, PHY_ANEG_ADV, 0xffbf, 0x0040);
|
|
else
|
|
phy_modify(phy_settings.port, PHY_MMD_AN, PHY_ANEG_ADV, 0xffdf, 0x0020);
|
|
}
|
|
if (v & 0x0180) {
|
|
if (phy_settings.duplex)
|
|
phy_modify(phy_settings.port, PHY_MMD_AN, PHY_ANEG_ADV, 0xfeff, 0x0100);
|
|
else
|
|
phy_modify(phy_settings.port, PHY_MMD_AN, PHY_ANEG_ADV, 0xff7f, 0x0080);
|
|
}
|
|
// Restart AN
|
|
phy_write(phy_settings.port, PHY_MMD_AN, PHY_ANEG_CTRL, 0x3000);
|
|
}
|
|
|
|
|
|
void phy_show(uint8_t port) __banked
|
|
{
|
|
uint16_t v;
|
|
|
|
// The actual PHY speed is in a Realtek propriatary register
|
|
print_string("\nLink speed: ");
|
|
phy_read(port, PHY_MMD31, PHY_MMD31_PHYSR);
|
|
v = SFR_DATA_U16;
|
|
switch(((v & 0x0600) >> 7) | ((v & 0x0030) >> 4)) {
|
|
case 0:
|
|
print_string("10M");
|
|
break;
|
|
case 1:
|
|
print_string("100M");
|
|
break;
|
|
case 2:
|
|
print_string("1000M");
|
|
break;
|
|
case 3:
|
|
print_string("500M");
|
|
break;
|
|
case 4:
|
|
print_string("10G");
|
|
break;
|
|
case 5:
|
|
print_string("2500M");
|
|
break;
|
|
case 6:
|
|
print_string("5G");
|
|
break;
|
|
default:
|
|
print_string("Down");
|
|
}
|
|
|
|
if ( (((v & 0x0600) >> 7) | ((v & 0x0030) >> 4)) <= 6) { // Link is up
|
|
if (v & 0x8)
|
|
print_string(" full duplex");
|
|
else
|
|
print_string(" half duplex");
|
|
}
|
|
|
|
phy_read(port, PHY_MMD_AN, PHY_ANEG_CTRL);
|
|
v = SFR_DATA_U16;
|
|
if (!(v & 0x1000)) { // AN disabled, we are in forced mode
|
|
phy_read(port, PHY_MMD_PMAPMD, 0);
|
|
v = SFR_DATA_U16;
|
|
print_string("\nForced speed: "); print_short(v); write_char('\n');
|
|
uint8_t s1 = ((v & 0x40) ? 0x2 : 0x0) | ((v & 0x2000) ? 0x1 : 0x0);
|
|
uint8_t s2 = (v >> 2) & 0xf;
|
|
switch(s1) {
|
|
case 0:
|
|
print_string("10M\n");
|
|
break;
|
|
case 1:
|
|
print_string("100M\n");
|
|
break;
|
|
case 2:
|
|
print_string("1000M\n");
|
|
break;
|
|
case 3:
|
|
switch (s2) {
|
|
case 0:
|
|
print_string("10G\n");
|
|
break;
|
|
case 6:
|
|
print_string("2500M\n");
|
|
break;
|
|
case 7:
|
|
print_string("5G\n");
|
|
break;
|
|
default:
|
|
print_string("Unknown\n");
|
|
}
|
|
break;
|
|
default:
|
|
print_string("Unknown\n");
|
|
}
|
|
phy_read(port, PHY_MMD31, PHY_MMD31_FEDCR);
|
|
v = SFR_DATA_U16;
|
|
print_string("Duplex: "); print_short(v); print_string(" enabled: ");
|
|
if (v & 0x100)
|
|
print_string("yes");
|
|
else
|
|
print_string("no");
|
|
write_char('\n');
|
|
|
|
} else {
|
|
print_string("\nAN enabled, advertising:");
|
|
phy_read(port, PHY_MMD_AN, PHY_ANEG_ADV);
|
|
v = SFR_DATA_U16;
|
|
if (v & 0x0020)
|
|
print_string(" 10Base-Half");
|
|
if (v & 0x0040)
|
|
print_string(" 10Base-Full");
|
|
if (v & 0x0080)
|
|
print_string(" 100Base-Half");
|
|
if (v & 0x0100)
|
|
print_string(" 100Base-Full");
|
|
phy_read(port, PHY_MMD31, PHY_MMD31_GBCR);
|
|
v = SFR_DATA_U16;
|
|
if (v & 0x0200)
|
|
print_string(" 1000Base-Full");
|
|
phy_read(port, PHY_MMD_AN, PHY_ANEG_MGBASE_CTRL);
|
|
v = SFR_DATA_U16;
|
|
if (v & 0x0080)
|
|
print_string(" 2500BaseN-Full");
|
|
if (v & 0x0100)
|
|
print_string(" 5000BaseN-Full");
|
|
if (v & 0x1000)
|
|
print_string(" 10GBaseN-Full");
|
|
}
|
|
phy_read(port, PHY_MMD_AN, PHY_ANEG_LP_ABILITY);
|
|
v = SFR_DATA_U16;
|
|
print_string("\nLink Partner advertises:");
|
|
if (v & 0x0020)
|
|
print_string(" 10Base-Half");
|
|
if (v & 0x0040)
|
|
print_string(" 10Base-Full");
|
|
if (v & 0x0080)
|
|
print_string(" 100Base-Half");
|
|
if (v & 0x0100)
|
|
print_string(" 100Base-Full");
|
|
phy_read(port, PHY_MMD31, PHY_MMD31_GANLPAR);
|
|
v = SFR_DATA_U16;
|
|
if (v & 0x0400)
|
|
print_string(" 1000Base-Half");
|
|
if (v & 0x0800)
|
|
print_string(" 1000Base-Full");
|
|
phy_read(port, PHY_MMD_AN, PHY_ANEG_MGBASE_ADV);
|
|
v = SFR_DATA_U16;
|
|
if (v & 0x0020)
|
|
print_string(" 2500Base-Full");
|
|
if (v & 0x0040)
|
|
print_string(" 5000Base-Full");
|
|
if (v & 0x0800)
|
|
print_string(" 10GBase-Full");
|
|
write_char('\n');
|
|
}
|
|
|
|
|
|
void phy_reset(uint8_t port) __banked
|
|
{
|
|
uint16_t v;
|
|
phy_read(port, PHY_MMD31, 0xa610);
|
|
v = SFR_DATA_U16;
|
|
// If PHY off, do nothing
|
|
if (v & 0x0800)
|
|
return;
|
|
|
|
// Disable PHY
|
|
phy_write(port, PHY_MMD31, 0xa610, v | 0x0800);
|
|
delay(2);
|
|
// Re-enable PHY
|
|
phy_write(port, PHY_MMD31, 0xa610, v & 0xf7ff);
|
|
}
|
|
|
|
// Read RTL8224 register.
|
|
// Registers names are the same as on the RTL837x.
|
|
// Reading only reads the lower 16-bit part of the 32-bit register.
|
|
// When also needing read the upper 16-bits, use register address + 1.
|
|
// Readed values it return via sfr-data.
|
|
void inline rtl8224_read_reg_u16(uint16_t reg) __banked
|
|
{
|
|
// void phy_read(uint8_t phy_id, uint8_t dev_id, uint16_t reg)
|
|
// phy_read(RTL8224_PHY_ID, PHY_MMD30, reg);
|
|
|
|
SFR_SMI_REG_U16 = reg; // c2, c2
|
|
|
|
SFR_SMI_PHY = RTL8224_PHY_ID; // a5
|
|
SFR_SMI_DEV = PHY_MMD30 << 3 | 2; // c4
|
|
|
|
SFR_EXEC_GO = SFR_EXEC_READ_SMI;
|
|
do {
|
|
} while (SFR_EXEC_STATUS != 0);
|
|
}
|
|
|
|
// Write RTL8224 register.
|
|
// Registers names are the same as on the RTL837x.
|
|
// Writing only the lower 16-bit part of the 32-bit register.
|
|
// When also needing to write the upper 16-bits, use register address + 1.
|
|
void inline rtl8224_write_reg_u16(uint16_t reg, uint16_t val) __banked
|
|
{
|
|
SFR_DATA_U16 = val; // SFR_A6, SFR_A7
|
|
SFR_SMI_REG_U16 = reg; // SFR_C2, SFR_C3
|
|
|
|
//void phy_write(uint8_t phy_id, uint8_t dev_id, uint16_t reg, uint16_t v)
|
|
// phy_write(RTL8224_PHY_ID, PHY_MMD30, reg, val);
|
|
|
|
uint16_t phy_mask = bit_mask[RTL8224_PHY_ID];
|
|
|
|
SFR_SMI_PHYMASK = phy_mask; // SFR_C5
|
|
SFR_SMI_DEV = (phy_mask >> 8) | PHY_MMD30 << 3 | 2; // SFR_C4: bit 2 can also be set for some option
|
|
SFR_EXEC_GO = SFR_EXEC_WRITE_SMI;
|
|
do {
|
|
} while (SFR_EXEC_STATUS != 0);
|
|
}
|
|
|
|
// // Modify RTL8224 register.
|
|
// // Registers names are the same as on the RTL837x.
|
|
// // Modifies only the lower 16-bit part of the 32-bit register.
|
|
// // When also needing to modifie the upper 16-bits, use register address + 1.
|
|
// void rtl8224_modify_reg_u16(uint16_t reg, uint16_t clear, uint16_t set) __banked
|
|
// {
|
|
// phy_read(RTL8224_PHY_ID, PHY_MMD30, reg);
|
|
// uint16_t pval = SFR_DATA_U16;
|
|
// pval &= ~(clear);
|
|
// pval |= set;
|
|
// phy_write(RTL8224_PHY_ID, PHY_MMD30, reg, pval);
|
|
// }
|
|
|
|
|
|
// Write to the RTL8224 SDS registers.
|
|
void rtl8224_sds_write(uint16_t sds_cmd, uint16_t value) __banked
|
|
{
|
|
// Wait for command bit is cleared
|
|
do {
|
|
rtl8224_read_reg_u16(RTL837X_SDS_INDACS_CMD);
|
|
} while (SFR_DATA_8 & 0x80);
|
|
|
|
rtl8224_write_reg_u16(RTL837X_SDS_INDACS_WRITE_DATA, value);
|
|
|
|
rtl8224_write_reg_u16(RTL837X_SDS_INDACS_CMD, sds_cmd);
|
|
|
|
// Wait for command bit is cleared
|
|
do {
|
|
rtl8224_read_reg_u16(RTL837X_SDS_INDACS_CMD);
|
|
} while (SFR_DATA_8 & 0x80);
|
|
}
|