mirror of
https://github.com/logicog/RTLPlayground.git
synced 2026-10-29 17:54:14 +08:00
The 100M branch of phy_set_speed() is missing an else. A port set to 100M half writes 0x1481, falls into the next if, finds duplex is not 1 and takes its else, and immediately overwrites the register with 0x1581. The port ends up advertising 100BASE-TX half and full, which is what "both" is supposed to mean. The 10M branch directly above has the chain the 100M one meant to have, and the values line up with it: bit 7 is 100BASE-TX half, bit 8 is full, so 0x1481, 0x1501 and 0x1581 are half, full and both, exactly as 0x1421, 0x1441 and 0x1461 are for 10M. Forcing half duplex is something people do to match a link partner that cannot negotiate, so quietly advertising full as well is the wrong way for this to fail. Not tried on hardware: every port on the switch here has a link up, and setting the speed restarts negotiation.
654 lines
20 KiB
C
654 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);
|
|
else 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 = 0x00;
|
|
if ((uint8_t)v & 0x40)
|
|
s1 = 0x2;
|
|
if (v & 0x2000)
|
|
s1 |= 0x1;
|
|
uint8_t s2 = ((uint8_t)v >> 2) & 0xf;
|
|
switch(s1 & 0x3) {
|
|
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;
|
|
}
|
|
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 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 = (uint8_t)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 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, __xdata 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);
|
|
}
|