Skip to content

Commit

Permalink
expand
Browse files Browse the repository at this point in the history
  • Loading branch information
scaprile committed Mar 14, 2024
1 parent 46806c9 commit 7db88a3
Show file tree
Hide file tree
Showing 8 changed files with 135 additions and 159 deletions.
57 changes: 10 additions & 47 deletions src/drivers/imxrt.c
Original file line number Diff line number Diff line change
Expand Up @@ -48,13 +48,6 @@ static uint8_t s_rxbuf[ETH_DESC_CNT][ETH_PKT_SIZE] MG_64BYTE_ALIGNED;
static uint8_t s_txbuf[ETH_DESC_CNT][ETH_PKT_SIZE] MG_64BYTE_ALIGNED;
static struct mg_tcpip_if *s_ifp; // MIP interface

enum {
MG_PHYREG_BCR = 0,
MG_PHYREG_BSR = 1,
MG_PHYREG_ID1 = 2,
MG_PHYREG_ID2 = 3
};

static uint16_t enet_phy_read(uint8_t addr, uint8_t reg) {
ENET->EIR |= MG_BIT(23); // MII interrupt clear
ENET->MMFR = (1 << 30) | (2 << 28) | (addr << 23) | (reg << 18) | (2 << 16);
Expand Down Expand Up @@ -104,28 +97,8 @@ static bool mg_tcpip_driver_imxrt_init(struct mg_tcpip_if *ifp) {
// TODO(): Otherwise, guess (currently assuming max freq)
int cr = (d == NULL || d->mdc_cr < 0) ? 24 : d->mdc_cr;
ENET->MSCR = (1 << 8) | ((cr & 0x3f) << 1); // HOLDTIME 2 clks

enet_phy_write(d->phy_addr, MG_PHYREG_BCR, MG_BIT(15)); // Reset PHY
enet_phy_write(d->phy_addr, MG_PHYREG_BCR,
MG_BIT(12)); // Set autonegotiation

// PHY: Enable 50 MHz external ref clock at XI (preserve defaults)
uint32_t id = enet_phy_id(d->phy_addr);
MG_INFO(("PHY ID: %#04x %#04x", (uint16_t) (id >> 16), (uint16_t) id));
// 2000 a140 - TI DP83825I
// 0007 c0fx - LAN8720
// 0022 1561 - KSZ8081RNB

if ((id & 0xffff0000) == 0x220000) { // KSZ8081RNB, like EVK-RTxxxx boards
enet_phy_write(d->phy_addr, 31,
MG_BIT(15) | MG_BIT(8) | MG_BIT(7)); // PC2R
} else if ((id & 0xffff0000) == 0x20000000) { // DP83825I, like Teensy4.1
enet_phy_write(d->phy_addr, 23, 0x81); // 50MHz clock input
enet_phy_write(d->phy_addr, 24, 0x280); // LED status, active high
} else { // Default to LAN8720
MG_INFO(("Defaulting to LAN8720 PHY...")); // TODO()
}

struct mg_phy phy = {eth_read_phy, eth_write_phy};
mg_phy_init(&phy, d->phy_addr);
// Select RMII mode, 100M, keep CRC, set max rx length, disable loop
ENET->RCR = (1518 << 16) | MG_BIT(8) | MG_BIT(2);
// ENET->RCR |= MG_BIT(3); // Receive all
Expand Down Expand Up @@ -171,30 +144,20 @@ static size_t mg_tcpip_driver_imxrt_tx(const void *buf, size_t len,
}

static bool mg_tcpip_driver_imxrt_up(struct mg_tcpip_if *ifp) {
struct mg_tcpip_driver_imxrt_data *d =
(struct mg_tcpip_driver_imxrt_data *) ifp->driver_data;
uint32_t bsr = enet_phy_read(d->phy_addr, MG_PHYREG_BSR);
bool up = bsr & MG_BIT(2) ? 1 : 0;
struct mg_tcpip_driver_stm32f_data *d =
(struct mg_tcpip_driver_stm32f_data *) ifp->driver_data;
uint8_t speed = MG_PHY_SPEED_10M;
bool up = false, full_duplex = false;
struct mg_phy phy = {eth_read_phy, eth_write_phy};
up = mg_phy_up(&phy, d->phy_addr, &full_duplex, &speed);
if ((ifp->state == MG_TCPIP_STATE_DOWN) && up) { // link state just went up
// tmp = reg with flags set to the most likely situation: 100M full-duplex
// if(link is slow or half) set flags otherwise
// reg = tmp
uint32_t tcr = ENET->TCR | MG_BIT(2); // Full-duplex
uint32_t rcr = ENET->RCR & ~MG_BIT(9); // 100M
uint32_t phy_id = enet_phy_id(d->phy_addr);
if ((phy_id & 0xffff0000) == 0x220000) { // KSZ8081RNB
uint16_t pc1r = enet_phy_read(d->phy_addr, 30); // Read PC1R
if ((pc1r & 3) == 1) rcr |= MG_BIT(9); // 10M
if ((pc1r & MG_BIT(2)) == 0) tcr &= ~MG_BIT(2); // Half-duplex
} else if ((phy_id & 0xffff0000) == 0x20000000) { // DP83825I
uint16_t physts = enet_phy_read(d->phy_addr, 16); // Read PHYSTS
if (physts & MG_BIT(1)) rcr |= MG_BIT(9); // 10M
if ((physts & MG_BIT(2)) == 0) tcr &= ~MG_BIT(2); // Half-duplex
} else { // Default to LAN8720
uint16_t scsr = enet_phy_read(d->phy_addr, 31); // Read CSCR
if ((scsr & MG_BIT(3)) == 0) rcr |= MG_BIT(9); // 10M
if ((scsr & MG_BIT(4)) == 0) tcr &= ~MG_BIT(2); // Half-duplex
}
if (speed == MG_PHY_SPEED_10M) rcr |= MG_BIT(9); // 10M
if (full_duplex == false) tcr &= ~MG_BIT(2); // Half-duplex
ENET->TCR = tcr; // IRQ handler does not fiddle with these registers
ENET->RCR = rcr;
MG_DEBUG(("Link is %uM %s-duplex", rcr & MG_BIT(9) ? 10 : 100,
Expand Down
110 changes: 81 additions & 29 deletions src/drivers/phy.c
Original file line number Diff line number Diff line change
@@ -1,52 +1,104 @@
#include "phy.h"

enum {
MG_PHY_KSZ8x = 0x22,
MG_PHY_DP83x = 0x2000,
MG_PHY_LAN87x = 0x7,
enum { // ID1 ID2
MG_PHY_KSZ8x = 0x22, // 0022 1561 - KSZ8081RNB
MG_PHY_DP83x = 0x2000, // 2000 a140 - TI DP83825I
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_REG_CSCR = 31,
MG_PHY_DP83x_REG_PHYSTS = 16,
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 id) {
switch (id) {
case MG_PHY_KSZ8x: return "KSZ8x";
case MG_PHY_DP83x: return "DP83x";
case MG_PHY_LAN87x: return "LAN87x";
default: return "unknown";
static const char *mg_phy_id_to_str(uint16_t id1, uint16_t id2) {
switch (id1) {
case MG_PHY_DP83x:
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) {
uint16_t id1 = phy->read_reg(phy->addr, MG_PHY_REG_ID1);
uint16_t 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)));
void mg_phy_init(struct mg_phy *phy, uint8_t phy_addr, uint8_t config) {
phy->write_reg(phy_addr, MG_PHY_REG_BCR, MG_BIT(15)); // Reset PHY
phy->write_reg(phy_addr, MG_PHY_REG_BCR, MG_BIT(12)); // Autonegotiation

phy->write_reg(phy->addr, MG_PHY_REG_BCR, MG_BIT(15)); // Reset PHY
phy->write_reg(phy->addr, MG_PHY_REG_BCR, MG_BIT(12)); // Autonegotiation
uint16_t id1 = phy->read_reg(phy_addr, MG_PHY_REG_ID1);
uint16_t 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_KSZ8x) {
phy->write_reg(phy->addr, MG_PHY_REG_CSCR,
MG_BIT(15) | MG_BIT(8) | MG_BIT(7));
} else if (id1 == MG_PHY_DP83x) {
phy->write_reg(phy->addr, 23, 0x81); // 50MHz clock input
phy->write_reg(phy->addr, 24, 0x280); // LED status, active high
bool clkconfig = config & 1;
if (clkconfig == MG_PHY_CONF_CLOCKING) {
// Use PHY crystal oscillator (preserve defaults)
// nothing to do
} else if (clkconfig == MG_PHY_CONF_CLOCKED) {
// Enable 50 MHz external ref clock at XI (preserve defaults)
if (id1 == MG_PHY_DP83x) {
phy->write_reg(phy_addr, MG_PHY_DP83x_REG_RCSR, MG_BIT(7) | MG_BIT(0));
} else if (id1 == MG_PHY_KSZ8x) {
phy->write_reg(phy_addr, MG_PHY_KSZ8x_REG_PC2R,
MG_BIT(15) | MG_BIT(8) | MG_BIT(7));
} else if (id1 == MG_PHY_LAN87x) {
// nothing to do
} else if (id1 == MG_PHY_RTL8201) {
phy->write_reg(phy_addr, MG_PHY_RTL8201_REG_PAGESEL, 7); // Select page 7
phy->write_reg(phy_addr, MG_PHY_RTL8201_REG_RMSR, 0x7ffb);
phy->write_reg(phy_addr, MG_PHY_RTL8201_REG_PAGESEL, 0); // Select page 0
}
}

bool leddrive = config & 2;
if (leddrive && 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, bool *full_duplex, uint8_t *speed) {
uint16_t bsr = phy->read_reg(phy->addr, MG_PHY_REG_BSR);
bool mg_phy_up(struct mg_phy *phy, uint8_t phy_addr, bool *full_duplex,
uint8_t *speed) {
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
bool up = bsr & MG_BIT(2);
if (up) {
uint16_t scsr = phy->read_reg(phy->addr, MG_PHY_REG_CSCR);
*full_duplex = scsr & MG_BIT(4);
*speed = scsr & MG_BIT(3) ? MG_PHY_SPEED_100M : MG_PHY_SPEED_10M;
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 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);
if (bcr & MG_BIT(15)) return 0; // still resetting
*full_duplex = bcr & MG_BIT(8);
*speed = (bcr & MG_BIT(13)) ? MG_PHY_SPEED_100M : MG_PHY_SPEED_10M;
}
}
return up;
}
7 changes: 4 additions & 3 deletions src/drivers/phy.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,13 @@
#include "net_builtin.h"

struct mg_phy {
uint8_t addr;
uint16_t (*read_reg)(uint8_t addr, uint8_t reg);
void (*write_reg)(uint8_t addr, uint8_t reg, uint16_t value);
};

enum { MG_PHY_CONF_CLOCKING = 0, MG_PHY_CONF_CLOCKED = 1, MG_PHY_CONF_LEDS_AH = 2};

enum { MG_PHY_SPEED_10M, MG_PHY_SPEED_100M, MG_PHY_SPEED_1000M };

void mg_phy_init(struct mg_phy *);
bool mg_phy_up(struct mg_phy *, bool *full_duplex, uint8_t *speed);
void mg_phy_init(struct mg_phy *, uint8_t addr, uint8_t configtype);
bool mg_phy_up(struct mg_phy *, uint8_t addr, bool *full_duplex, uint8_t *speed);
58 changes: 11 additions & 47 deletions src/drivers/ra.c
Original file line number Diff line number Diff line change
Expand Up @@ -41,13 +41,6 @@ static uint8_t s_rxbuf[ETH_DESC_CNT][ETH_PKT_SIZE] MG_32BYTE_ALIGNED;
static uint8_t s_txbuf[ETH_DESC_CNT][ETH_PKT_SIZE] MG_32BYTE_ALIGNED;
static struct mg_tcpip_if *s_ifp; // MIP interface

enum {
MG_PHYREG_BCR = 0,
MG_PHYREG_BSR = 1,
MG_PHYREG_ID1 = 2,
MG_PHYREG_ID2 = 3
};

// fastest is 3 cycles (SUB + BNE) on a 3-stage pipeline or equivalent
static inline void raspin(volatile uint32_t count) {
while (count--) (void) 0;
Expand Down Expand Up @@ -164,28 +157,9 @@ static bool mg_tcpip_driver_ra_init(struct mg_tcpip_if *ifp) {
EDMAC->EDMR = MG_BIT(6); // Initialize, little-endian (27.2.1)

MG_DEBUG(("PHY addr: %d, smispin: %d", d->phy_addr, s_smispin));
raeth_phy_write(d->phy_addr, MG_PHYREG_BCR, MG_BIT(15)); // Reset PHY
raeth_phy_write(d->phy_addr, MG_PHYREG_BCR,
MG_BIT(12)); // Set autonegotiation

// PHY: Enable ref clock (preserve defaults)
uint32_t id = raeth_phy_id(d->phy_addr);
MG_INFO(("PHY ID: %#04x %#04x", (uint16_t) (id >> 16), (uint16_t) id));
// 2000 a140 - TI DP83825I
// 0007 c0fx - LAN8720
// 0022 156x - KSZ8081RNB/KSZ8091RNB

if ((id & 0xffff0000) == 0x220000) { // KSZ8091RNB, like EK-RA6Mx boards
// 25 MHz xtal at XI/XO (default)
raeth_phy_write(d->phy_addr, 31, MG_BIT(15) | MG_BIT(8)); // PC2R
} else if ((id & 0xffff0000) == 0x20000000) { // DP83825I, like ???
// 50 MHz external at XI ???
raeth_phy_write(d->phy_addr, 23, 0x81); // 50MHz clock input
raeth_phy_write(d->phy_addr, 24, 0x280); // LED status, active high
} else { // Default to LAN8720
MG_INFO(("Defaulting to LAN8720 PHY...")); // TODO()
}

struct mg_phy phy = {eth_read_phy, eth_write_phy};
mg_phy_init(&phy, d->phy_addr);

// Select RMII mode,
ETHERC->ECMR = MG_BIT(2) | MG_BIT(1); // 100M, Full-duplex, CRC
// ETHERC->ECMR |= MG_BIT(0); // Receive all
Expand Down Expand Up @@ -231,29 +205,19 @@ static size_t mg_tcpip_driver_ra_tx(const void *buf, size_t len,
}

static bool mg_tcpip_driver_ra_up(struct mg_tcpip_if *ifp) {
struct mg_tcpip_driver_ra_data *d =
(struct mg_tcpip_driver_ra_data *) ifp->driver_data;
uint32_t bsr = raeth_phy_read(d->phy_addr, MG_PHYREG_BSR);
bool up = bsr & MG_BIT(2) ? 1 : 0;
struct mg_tcpip_driver_stm32f_data *d =
(struct mg_tcpip_driver_stm32f_data *) ifp->driver_data;
uint8_t speed = MG_PHY_SPEED_10M;
bool up = false, full_duplex = false;
struct mg_phy phy = {eth_read_phy, eth_write_phy};
up = mg_phy_up(&phy, d->phy_addr, &full_duplex, &speed);
if ((ifp->state == MG_TCPIP_STATE_DOWN) && up) { // link state just went up
// tmp = reg with flags set to the most likely situation: 100M full-duplex
// if(link is slow or half) set flags otherwise
// reg = tmp
uint32_t ecmr = ETHERC->ECMR | MG_BIT(2) | MG_BIT(1); // 100M Full-duplex
uint32_t phy_id = raeth_phy_id(d->phy_addr);
if ((phy_id & 0xffff0000) == 0x220000) { // KSZ8091RNB
uint16_t pc1r = raeth_phy_read(d->phy_addr, 30); // Read PC1R
if ((pc1r & 3) == 1) ecmr &= ~MG_BIT(2); // 10M
if ((pc1r & MG_BIT(2)) == 0) ecmr &= ~MG_BIT(1); // Half-duplex
} else if ((phy_id & 0xffff0000) == 0x20000000) { // DP83825I
uint16_t physts = raeth_phy_read(d->phy_addr, 16); // Read PHYSTS
if (physts & MG_BIT(1)) ecmr &= ~MG_BIT(2); // 10M
if ((physts & MG_BIT(2)) == 0) ecmr &= ~MG_BIT(1); // Half-duplex
} else { // Default to LAN8720
uint16_t scsr = raeth_phy_read(d->phy_addr, 31); // Read CSCR
if ((scsr & MG_BIT(3)) == 0) ecmr &= ~MG_BIT(2); // 10M
if ((scsr & MG_BIT(4)) == 0) ecmr &= ~MG_BIT(1); // Half-duplex
}
if (speed == MG_PHY_SPEED_10M) ecmr &= ~MG_BIT(2); // 10M
if (full_duplex == false) ecmr &= ~MG_BIT(1); // Half-duplex
ETHERC->ECMR = ecmr; // IRQ handler does not fiddle with these registers
MG_DEBUG(("Link is %uM %s-duplex", ecmr & MG_BIT(2) ? 100 : 10,
ecmr & MG_BIT(1) ? "full" : "half"));
Expand Down
21 changes: 7 additions & 14 deletions src/drivers/stm32f.c
Original file line number Diff line number Diff line change
Expand Up @@ -30,13 +30,6 @@ static uint8_t s_txno; // Current TX descriptor
static uint8_t s_rxno; // Current RX descriptor

static struct mg_tcpip_if *s_ifp; // MIP interface
enum {
MG_PHYREG_BCR = 0,
MG_PHYREG_BSR = 1,
MG_PHYREG_ID1 = 2,
MG_PHYREG_ID2 = 3,
MG_PHYREG_CSCR = 31
};

static uint16_t eth_read_phy(uint8_t addr, uint8_t reg) {
ETH->MACMIIAR &= (7 << 2);
Expand Down Expand Up @@ -144,8 +137,8 @@ static bool mg_tcpip_driver_stm32f_init(struct mg_tcpip_if *ifp) {
ETH->MACIMR = MG_BIT(3) | MG_BIT(9); // Mask timestamp & PMT IT
ETH->MACFCR = MG_BIT(7); // Disable zero quarta pause
// ETH->MACFFR = MG_BIT(31); // Receive all
struct mg_phy phy = {d->phy_addr, eth_read_phy, eth_write_phy};
mg_phy_init(&phy);
struct mg_phy phy = {eth_read_phy, eth_write_phy};
mg_phy_init(&phy, d->phy_addr);
ETH->DMARDLAR = (uint32_t) (uintptr_t) s_rxdesc; // RX descriptors
ETH->DMATDLAR = (uint32_t) (uintptr_t) s_txdesc; // RX descriptors
ETH->DMAIER = MG_BIT(6) | MG_BIT(16); // RIE, NISE
Expand All @@ -154,9 +147,6 @@ static bool mg_tcpip_driver_stm32f_init(struct mg_tcpip_if *ifp) {
ETH->DMAOMR =
MG_BIT(1) | MG_BIT(13) | MG_BIT(21) | MG_BIT(25); // SR, ST, TSF, RSF

MG_DEBUG(("PHY ID: %#04hx %#04hx", eth_read_phy(phy_addr, MG_PHYREG_ID1),
eth_read_phy(phy_addr, MG_PHYREG_ID2)));

// MAC address filtering
ETH->MACA0HR = ((uint32_t) ifp->mac[5] << 8U) | ifp->mac[4];
ETH->MACA0LR = (uint32_t) (ifp->mac[3] << 24) |
Expand Down Expand Up @@ -193,9 +183,12 @@ static bool mg_tcpip_driver_stm32f_up(struct mg_tcpip_if *ifp) {
(struct mg_tcpip_driver_stm32f_data *) ifp->driver_data;
uint8_t speed = MG_PHY_SPEED_10M;
bool up = false, full_duplex = false;
struct mg_phy phy = {d->phy_addr, eth_read_phy, eth_write_phy};
up = mg_phy_up(&phy, &full_duplex, &speed);
struct mg_phy phy = {eth_read_phy, eth_write_phy};
up = mg_phy_up(&phy, d->phy_addr, &full_duplex, &speed);
if ((ifp->state == MG_TCPIP_STATE_DOWN) && up) { // link state just went up
// tmp = reg with flags set to the most likely situation: 100M full-duplex
// if(link is slow or half) set flags otherwise
// reg = tmp
uint32_t maccr = ETH->MACCR | MG_BIT(14) | MG_BIT(11); // 100M, Full-duplex
if (speed == MG_PHY_SPEED_10M) maccr &= ~MG_BIT(14); // 10M
if (full_duplex == false) maccr &= ~MG_BIT(11); // Half-duplex
Expand Down
2 changes: 1 addition & 1 deletion src/drivers/stm32f.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ struct mg_tcpip_driver_stm32f_data {

#define MG_TCPIP_DRIVER_DATA \
static struct mg_tcpip_driver_stm32f_data driver_data = { \
.mdc_cr = MG_DRIVER_MDC_CR, \
.mdc_cr = MG_DRIVER_MDC_CR, \
.phy_addr = MG_TCPIP_PHY_ADDR, \
};

Expand Down
Loading

0 comments on commit 7db88a3

Please sign in to comment.