mirror of
https://github.com/cesanta/mongoose.git
synced 2026-10-02 22:42:52 +07:00
141 lines
5.3 KiB
C
141 lines
5.3 KiB
C
#include "phy.h"
|
|
|
|
enum { // ID1 ID2
|
|
MG_PHY_KSZ8x = 0x22, // 0022 1561 - KSZ8081RNB
|
|
MG_PHY_DP83x = 0x2000,
|
|
MG_PHY_DP83867 = 0xa231, // 2000 a231 - TI DP83867I
|
|
MG_PHY_DP83825 = 0xa140, // 2000 a140 - TI DP83825I
|
|
MG_PHY_DP83848 = 0x5ca2, // 2000 5ca2 - TI DP83848I
|
|
MG_PHY_LAN87x = 0x7, // 0007 c0fx - LAN8720
|
|
MG_PHY_RTL8201 = 0x1C // 001c c816 - RTL8201
|
|
};
|
|
|
|
enum {
|
|
MG_PHY_REG_BCR = 0,
|
|
MG_PHY_REG_BSR = 1,
|
|
MG_PHY_REG_ID1 = 2,
|
|
MG_PHY_REG_ID2 = 3,
|
|
MG_PHY_DP83x_REG_PHYSTS = 16,
|
|
MG_PHY_DP83867_REG_PHYSTS = 17,
|
|
MG_PHY_DP83x_REG_RCSR = 23,
|
|
MG_PHY_DP83x_REG_LEDCR = 24,
|
|
MG_PHY_KSZ8x_REG_PC1R = 30,
|
|
MG_PHY_KSZ8x_REG_PC2R = 31,
|
|
MG_PHY_LAN87x_REG_SCSR = 31,
|
|
MG_PHY_RTL8201_REG_RMSR = 16, // in page 7
|
|
MG_PHY_RTL8201_REG_PAGESEL = 31
|
|
};
|
|
|
|
static const char *mg_phy_id_to_str(uint16_t id1, uint16_t id2) {
|
|
switch (id1) {
|
|
case MG_PHY_DP83x:
|
|
switch (id2) {
|
|
case MG_PHY_DP83867:
|
|
return "DP83867";
|
|
case MG_PHY_DP83848:
|
|
return "DP83848";
|
|
case MG_PHY_DP83825:
|
|
return "DP83825";
|
|
default:
|
|
return "DP83x";
|
|
}
|
|
case MG_PHY_KSZ8x:
|
|
return "KSZ8x";
|
|
case MG_PHY_LAN87x:
|
|
return "LAN87x";
|
|
case MG_PHY_RTL8201:
|
|
return "RTL8201";
|
|
default:
|
|
return "unknown";
|
|
}
|
|
(void) id2;
|
|
}
|
|
|
|
void mg_phy_init(struct mg_phy *phy, uint8_t phy_addr, uint8_t config) {
|
|
uint16_t id1, id2;
|
|
phy->write_reg(phy_addr, MG_PHY_REG_BCR, MG_BIT(15)); // Reset PHY
|
|
while (phy->read_reg(phy_addr, MG_PHY_REG_BCR) & MG_BIT(15)) (void) 0;
|
|
// MG_PHY_REG_BCR[12]: Autonegotiation is default unless hw says otherwise
|
|
|
|
id1 = phy->read_reg(phy_addr, MG_PHY_REG_ID1);
|
|
id2 = phy->read_reg(phy_addr, MG_PHY_REG_ID2);
|
|
MG_INFO(("PHY ID: %#04x %#04x (%s)", id1, id2, mg_phy_id_to_str(id1, id2)));
|
|
|
|
if (id1 == MG_PHY_DP83x && id2 == MG_PHY_DP83867) {
|
|
phy->write_reg(phy_addr, 0x0d, 0x1f); // write 0x10d to IO_MUX_CFG (0x0170)
|
|
phy->write_reg(phy_addr, 0x0e, 0x170);
|
|
phy->write_reg(phy_addr, 0x0d, 0x401f);
|
|
phy->write_reg(phy_addr, 0x0e, 0x10d);
|
|
}
|
|
|
|
if (config & MG_PHY_CLOCKS_MAC) {
|
|
// Use PHY crystal oscillator (preserve defaults)
|
|
// nothing to do
|
|
} else { // MAC clocks PHY, PHY has no xtal
|
|
// Enable 50 MHz external ref clock at XI (preserve defaults)
|
|
if (id1 == MG_PHY_DP83x && id2 != MG_PHY_DP83867 && id2 != MG_PHY_DP83848) {
|
|
phy->write_reg(phy_addr, MG_PHY_DP83x_REG_RCSR, MG_BIT(7) | MG_BIT(0));
|
|
} else if (id1 == MG_PHY_KSZ8x) {
|
|
// Disable isolation (override hw, it doesn't make sense at this point)
|
|
phy->write_reg( // #2848, some NXP boards set ISO, even though
|
|
phy_addr, MG_PHY_REG_BCR, // docs say they don't
|
|
phy->read_reg(phy_addr, MG_PHY_REG_BCR) & (uint16_t) ~MG_BIT(10));
|
|
phy->write_reg(phy_addr, MG_PHY_KSZ8x_REG_PC2R, // now do clock stuff
|
|
MG_BIT(15) | MG_BIT(8) | MG_BIT(7));
|
|
} else if (id1 == MG_PHY_LAN87x) {
|
|
// nothing to do
|
|
} else if (id1 == MG_PHY_RTL8201) {
|
|
// assume PHY has been hardware strapped properly
|
|
#if 0
|
|
phy->write_reg(phy_addr, MG_PHY_RTL8201_REG_PAGESEL, 7); // Select page 7
|
|
phy->write_reg(phy_addr, MG_PHY_RTL8201_REG_RMSR, 0x1ffa);
|
|
phy->write_reg(phy_addr, MG_PHY_RTL8201_REG_PAGESEL, 0); // Select page 0
|
|
#endif
|
|
}
|
|
}
|
|
|
|
if (config & MG_PHY_LEDS_ACTIVE_HIGH && id1 == MG_PHY_DP83x) {
|
|
phy->write_reg(phy_addr, MG_PHY_DP83x_REG_LEDCR,
|
|
MG_BIT(9) | MG_BIT(7)); // LED status, active high
|
|
} // Other PHYs do not support this feature
|
|
}
|
|
|
|
bool mg_phy_up(struct mg_phy *phy, uint8_t phy_addr, bool *full_duplex,
|
|
uint8_t *speed) {
|
|
bool up = false;
|
|
uint16_t bsr = phy->read_reg(phy_addr, MG_PHY_REG_BSR);
|
|
if ((bsr & MG_BIT(5)) && !(bsr & MG_BIT(2))) // some PHYs latch down events
|
|
bsr = phy->read_reg(phy_addr, MG_PHY_REG_BSR); // read again
|
|
up = bsr & MG_BIT(2);
|
|
if (up && full_duplex != NULL && speed != NULL) {
|
|
uint16_t id1 = phy->read_reg(phy_addr, MG_PHY_REG_ID1);
|
|
if (id1 == MG_PHY_DP83x) {
|
|
uint16_t id2 = phy->read_reg(phy_addr, MG_PHY_REG_ID2);
|
|
if (id2 == MG_PHY_DP83867) {
|
|
uint16_t physts = phy->read_reg(phy_addr, MG_PHY_DP83867_REG_PHYSTS);
|
|
*full_duplex = physts & MG_BIT(13);
|
|
*speed = (physts & MG_BIT(15)) ? MG_PHY_SPEED_1000M
|
|
: (physts & MG_BIT(14)) ? MG_PHY_SPEED_100M
|
|
: MG_PHY_SPEED_10M;
|
|
} else {
|
|
uint16_t physts = phy->read_reg(phy_addr, MG_PHY_DP83x_REG_PHYSTS);
|
|
*full_duplex = physts & MG_BIT(2);
|
|
*speed = (physts & MG_BIT(1)) ? MG_PHY_SPEED_10M : MG_PHY_SPEED_100M;
|
|
}
|
|
} else if (id1 == MG_PHY_KSZ8x) {
|
|
uint16_t pc1r = phy->read_reg(phy_addr, MG_PHY_KSZ8x_REG_PC1R);
|
|
*full_duplex = pc1r & MG_BIT(2);
|
|
*speed = (pc1r & 3) == 1 ? MG_PHY_SPEED_10M : MG_PHY_SPEED_100M;
|
|
} else if (id1 == MG_PHY_LAN87x) {
|
|
uint16_t scsr = phy->read_reg(phy_addr, MG_PHY_LAN87x_REG_SCSR);
|
|
*full_duplex = scsr & MG_BIT(4);
|
|
*speed = (scsr & MG_BIT(3)) ? MG_PHY_SPEED_100M : MG_PHY_SPEED_10M;
|
|
} else if (id1 == MG_PHY_RTL8201) {
|
|
uint16_t bcr = phy->read_reg(phy_addr, MG_PHY_REG_BCR);
|
|
*full_duplex = bcr & MG_BIT(8);
|
|
*speed = (bcr & MG_BIT(13)) ? MG_PHY_SPEED_100M : MG_PHY_SPEED_10M;
|
|
}
|
|
}
|
|
return up;
|
|
}
|