moccos mizuki / EthernetXpresso

Dependents:   XNetServicesMin

Revision:
0:b4bf563e9741
Child:
1:95a4c234aaf6
diff -r 000000000000 -r b4bf563e9741 LPC1769Emac.cpp
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/LPC1769Emac.cpp	Sun May 06 10:11:53 2012 +0000
@@ -0,0 +1,380 @@
+#include "LPC1769Emac.h"
+#include <LPC1768/ARM/cmsis.h>
+#include <mbed.h>
+#include "LAN8710AReg.h"
+#include "LPC1769Reg.h"
+#include "Frame.h"
+
+#define BUFFER_SECTION __attribute((section("AHBSRAM1"),aligned(8)))
+
+#define SET_EMAC_BIT(NAME, PATTERN) (LPC_EMAC->NAME = PATTERN}
+#define ENABLE_EMAC_BIT(NAME, MASK) (LPC_EMAC->NAME |= MASK}
+#define DISABLE_EMAC_BIT(NAME, MASK)(LPC_EMAC->NAME = LPC_EMAC->NAME & ~MASK}
+
+using namespace LPC1769Reg;
+
+uint8_t LPC1769Emac::mac_[6];
+
+Descriptor LPC1769Emac::rx_desc_[LPC1769Emac::N_RX_BUF] BUFFER_SECTION;
+Descriptor LPC1769Emac::tx_desc_[LPC1769Emac::N_TX_BUF] BUFFER_SECTION;
+StatusRx LPC1769Emac::rx_status_[LPC1769Emac::N_RX_BUF] BUFFER_SECTION;
+StatusTx LPC1769Emac::tx_status_[LPC1769Emac::N_TX_BUF] BUFFER_SECTION;
+Frame LPC1769Emac::rx_frame_[LPC1769Emac::N_RX_BUF] BUFFER_SECTION;
+Frame LPC1769Emac::tx_frame_[LPC1769Emac::N_TX_BUF] BUFFER_SECTION;
+
+// === inline debug function
+#ifdef __MY_DEBUG__
+#include "global.h"
+static inline void print_buffer(const char* title, uint8_t *buf, uint16_t size) {
+    serial.printf("%s (%d bytes) dst:%02x %02x %02x %02x %02x %02x ",
+    title, size, buf[0], buf[1], buf[2], buf[3], buf[4], buf[5]);
+    buf += 6;
+    serial.printf("src:%02x %02x %02x %02x %02x %02x ",
+    buf[0], buf[1], buf[2], buf[3], buf[4], buf[5]);
+    buf += 6;
+    serial.printf("type:%02x %02x\r\n", buf[0], buf[1]);
+}
+#else
+static inline void print_buffer(const char* title, uint8_t *buf, uint16_t size){}
+#endif
+
+// === public functions ===
+LPC1769Emac::LPC1769Emac() {
+    // set address
+    // enable ethernet block
+    LPC_SC->PCONP |= 0x40000000;
+
+    // enable alternate functions for ethernet.
+    // old values are not saved because those pins have only two functions.
+    LPC_PINCON->PINSEL2 = 0x50150105;
+    LPC_PINCON->PINSEL3 = (LPC_PINCON->PINSEL3 & ~0x0000000F) | 0x00000005;
+}
+
+LPC1769Emac::~LPC1769Emac() {
+    // TODO: disable PHY before disable ethernet block?
+
+    // disable alternate functions for ethernet
+    LPC_PINCON->PINSEL2 = 0x00000000;
+    LPC_PINCON->PINSEL3 = LPC_PINCON->PINSEL3 & ~0x0000000F;
+
+    // disable ethernet block
+    LPC_SC->PCONP = LPC_SC->PCONP & ~0x40000000;
+}
+
+bool
+LPC1769Emac::PhyWrite(uint8_t reg, uint16_t value) {
+    LPC_EMAC->MADR = PHY_ADDR | reg;
+    LPC_EMAC->MWTD = value;
+
+    uint32_t i;
+    for (i = 0; i < 0x10000; i++) {
+        if ((LPC_EMAC->MIND & MIND_BUSY) == 0) {
+            return true;
+        }
+    }
+    return false;
+}
+
+uint16_t
+LPC1769Emac::PhyRead(uint16_t reg) {
+    LPC_EMAC->MADR = PHY_ADDR | reg;
+    LPC_EMAC->MCMD = MCMD_READ;
+
+    uint32_t i;
+    for (i = 0; i < 0x10000; i++) {
+        if ((LPC_EMAC->MIND & MIND_BUSY) == 0) {
+            break;
+        }
+    }
+    LPC_EMAC->MCMD = 0x0000;
+
+    return LPC_EMAC->MRDD;
+}
+
+void
+LPC1769Emac::SetAddress(uint8_t a5, uint8_t a4, uint8_t a3, uint8_t a2, uint8_t a1, uint8_t a0) {
+    mac_[5] = a0;
+    mac_[4] = a1;
+    mac_[3] = a2;
+    mac_[2] = a3;
+    mac_[1] = a4;
+    mac_[0] = a5;
+    //WriteAddress_();
+}
+
+void
+LPC1769Emac::StartRx() {
+    LPC_EMAC->Command |= CMD_RX_EN;
+    LPC_EMAC->MAC1 |= MAC1_RX_EN;
+    //LPC_EMAC->IntEnable |= INT_RX_DONE;
+}
+
+void
+LPC1769Emac::StartTx() {
+    LPC_EMAC->Command |= CMD_TX_EN;
+    //LPC_EMAC->IntEnable |= INT_TX_DONE;
+}
+
+void
+LPC1769Emac::StopRx() {
+    LPC_EMAC->Command = LPC_EMAC->Command & ~CMD_RX_EN;
+    LPC_EMAC->MAC1 &= ~MAC1_RX_EN;
+    //LPC_EMAC->IntEnable &= ~INT_RX_DONE;
+}
+
+void
+LPC1769Emac::StopTx() {
+    LPC_EMAC->Command = LPC_EMAC->Command & ~CMD_TX_EN;
+    //LPC_EMAC->IntEnable &= ~INT_TX_DONE;
+}
+
+bool
+LPC1769Emac::Link() {
+    return ((PhyRead(SMIReg::BasicStatus) & SMIReg::BS_LINK) > 0);
+}
+
+uint16_t
+LPC1769Emac::Recv(void *buf, uint16_t max_size) {
+    uint32_t index = LPC_EMAC->RxConsumeIndex;
+    if (index == LPC_EMAC->RxProduceIndex || max_size == 0) return 0;
+
+    uint16_t packet_size = (rx_status_[index].info & Descriptor::SIZE_MASK) + 1;
+    uint16_t size = packet_size - read_size_;
+    if (size > Frame::MAX_FRAME_LEN) size = Frame::MAX_FRAME_LEN;
+    if (size > max_size) size = max_size;
+    memcpy(buf, read_next_, size);
+
+    read_size_ += size;
+    if (read_size_ >= packet_size) {
+        // goto next buffer
+        index = (index >= LPC_EMAC->RxDescriptorNumber) ? 0 : ++index;
+        read_size_ = 0;
+        read_next_ = rx_frame_[index].buffer;
+        LPC_EMAC->RxConsumeIndex = index;
+    } else {
+        read_next_ += size;
+    }
+
+    return size;
+}
+
+uint16_t
+LPC1769Emac::ReadyToReceive() {
+    uint32_t index = LPC_EMAC->RxConsumeIndex;
+    if (index == LPC_EMAC->RxProduceIndex) return 0;
+    else return (rx_status_[index].info & Descriptor::SIZE_MASK) + 1;
+}
+
+uint16_t
+LPC1769Emac::Write(void *buf, uint16_t size) {
+    if (size + write_size_ > Frame::MAX_FRAME_LEN) size = Frame::MAX_FRAME_LEN - write_size_;
+    if (size == 0) return 0;
+    uint32_t index = LPC_EMAC->TxProduceIndex;
+
+    memcpy(write_next_, buf, size);
+    write_next_ += size;
+    write_size_ += size;
+    tx_desc_[index].control = (tx_desc_[index].control & (~Descriptor::SIZE_MASK)) | (size - 1);
+    
+    return size;
+}
+
+bool
+LPC1769Emac::Send() {
+    if (write_size_ == 0) return true;
+    uint32_t index = LPC_EMAC->TxProduceIndex;
+    uint32_t index_next = (index == LPC_EMAC->TxDescriptorNumber) ? 0 : index + 1;
+    if (index_next == LPC_EMAC->TxConsumeIndex) return false;
+
+    tx_desc_[index].control = (tx_desc_[index].control & (~Descriptor::SIZE_MASK)) | (write_size_ - 1);
+    LPC_EMAC->TxProduceIndex = index_next;
+    write_next_ = tx_frame_[index_next].buffer;
+    write_size_ = 0;
+
+    return true;
+}
+
+bool
+LPC1769Emac::Send(void *buf, uint16_t size) {
+    write_next_ -= write_size_;
+    write_size_ = 0;
+    if (!Write(buf, size)) return false;
+    return Send();
+}
+
+bool
+LPC1769Emac::Reset(LinkMode mode) {
+    bool link_100m = false;
+    bool duplex_full = false;
+    switch (mode) {
+        case HalfDuplex10:
+            break;
+        case FullDuplex10:
+            duplex_full = true;
+            break;
+        case HalfDuplex100:
+            link_100m = true;
+            break;
+        case FullDuplex100:
+            duplex_full = true;
+            link_100m = true;
+            break;
+        case AutoNegotiate:
+            duplex_full = true;
+            link_100m = true;
+            break;
+        default:
+            return false;
+    }
+
+    // reset EMAC
+    LPC_EMAC->MAC1 = MAC1_RESET_MASK;
+    LPC_EMAC->Command = CMD_RESET_MASK;
+    wait_us(100);
+
+    // emac settings
+    LPC_EMAC->MCFG = MCFG_RESET_MII | MCFG_CS_DIV52;  // reset MII Management
+    wait_us(200);
+    LPC_EMAC->MAC1 = 0x0000;
+    LPC_EMAC->CLRT = CLRT_DEFAULT;
+    LPC_EMAC->MAXF = Frame::MAX_FRAME_LEN;
+    LPC_EMAC->IPGR = IPGR_DEFAULT;
+    LPC_EMAC->MCFG = MCFG_CS_DIV52;
+    if (duplex_full) {
+        LPC_EMAC->MAC2 = MAC2_AUTO_PAD | MAC2_FULL_DUPLEX;
+        LPC_EMAC->IPGT = IPGT_FULL_DUPLEX;
+        LPC_EMAC->Command = CMD_RMII | CMD_FULL_DUPLEX;
+    } else {
+        LPC_EMAC->MAC2 = MAC2_AUTO_PAD;
+        LPC_EMAC->IPGT = IPGT_HALF_DUPLEX;
+        LPC_EMAC->Command = CMD_RMII;
+    }
+    LPC_EMAC->SUPP = link_100m ? SUPP_100M_MODE : 0x0000;
+
+    // reset PHY
+    // "the reset process will be completed within 0.5s from the setting of this bit" (8710a datasheet)
+    PhyWrite(SMIReg::BasicControl, SMIReg::BC_RESET);
+    uint32_t i;
+    bool success_flag = false;
+    for (i = 0; i < 500; i++) {
+        if ((PhyRead(SMIReg::BasicControl) & SMIReg::BC_RESET) == 0) {  // reset bit is self-clearing
+            success_flag = true;
+            break;
+        }
+        wait_ms(1);
+    }
+    if (!success_flag) return false;  // failed to reset
+
+    // why 100M settings fail??
+    switch (mode) {
+        case HalfDuplex10:
+            PhyWrite(SMIReg::BasicControl, 0x0000);
+            break;
+        case FullDuplex10:
+            PhyWrite(SMIReg::BasicControl, SMIReg::BC_FULL_DUPLEX);
+            break;
+        case HalfDuplex100:
+            PhyWrite(SMIReg::BasicControl, SMIReg::BC_100M);
+            break;
+        case FullDuplex100:
+            PhyWrite(SMIReg::BasicControl, SMIReg::BC_FULL_DUPLEX | SMIReg::BC_100M);
+            break;
+        case AutoNegotiate:
+            PhyWrite(SMIReg::BasicControl, SMIReg::BC_AUTONEG);
+            // CheckAutoNeg_();
+            break;
+        default:
+            return false;
+    }
+    
+    InitRxBuffer_();
+    InitTxBuffer_();
+    WriteAddress_();
+
+
+    LPC_EMAC->IntClear  = INT_ALL_MASK;
+    LPC_EMAC->RxFilterCtrl = RXFC_BCAST | RXFC_PERFECT;
+
+    return true;
+}
+
+// === private functions ===
+void
+LPC1769Emac::WriteAddress_() {
+    LPC_EMAC->SA0 = ((uint16_t)mac_[5] << 8) | mac_[4];
+    LPC_EMAC->SA1 = ((uint16_t)mac_[3] << 8) | mac_[2];
+    LPC_EMAC->SA2 = ((uint16_t)mac_[1] << 8) | mac_[0];
+}
+
+// currently not used
+bool
+LPC1769Emac::CheckAutoNeg_() {
+    bool success_flag = false;
+    uint32_t i, r;
+    for (i = 0; i < 0x10000; i++) {
+        r = PhyRead(SMIReg::BasicStatus);
+        if (r & SMIReg::BS_AUTONEG_COMPLETE) {
+            success_flag = true;
+            break;
+        }
+    }
+    if (!success_flag) return false;
+
+    r = PhyRead(SMIReg::PHYSpecialControlStatus);
+
+    // update regs
+    bool link_100m = ((r & SMIReg::SP_100M) > 0);
+    bool duplex_full = ((r & SMIReg::SP_FULL_DUPLEX) > 0);
+    
+    if (duplex_full) {
+        LPC_EMAC->MAC2 = MAC2_AUTO_PAD | MAC2_FULL_DUPLEX;
+        LPC_EMAC->IPGT = IPGT_FULL_DUPLEX;
+        LPC_EMAC->Command = CMD_RMII | CMD_FULL_DUPLEX;
+    } else {
+        LPC_EMAC->MAC2 = MAC2_AUTO_PAD;
+        LPC_EMAC->IPGT = IPGT_HALF_DUPLEX;
+        LPC_EMAC->Command = CMD_RMII;
+    }
+    LPC_EMAC->SUPP = link_100m ? SUPP_100M_MODE : 0x0000;
+
+    return true;
+}
+
+void
+LPC1769Emac::InitRxBuffer_() {
+    uint16_t i;
+
+    // set initial descriptor
+    for (i = 0; i < N_RX_BUF; i++) {
+        rx_desc_[i].packet = (uint32_t)&rx_frame_[i];
+        rx_desc_[i].control = 0x80000000 | (Frame::MAX_FRAME_LEN - 1);
+    }
+
+    // emac registers
+    LPC_EMAC->RxDescriptor = (uint32_t)rx_desc_;
+    LPC_EMAC->RxStatus = (uint32_t)rx_status_;
+    LPC_EMAC->RxDescriptorNumber = N_RX_BUF - 1;  // this register is minus one encoded
+    LPC_EMAC->RxConsumeIndex  = 0;
+    read_next_ = rx_frame_[0].buffer;
+    read_size_ = 0;
+}
+
+void
+LPC1769Emac::InitTxBuffer_() {
+    uint16_t i;
+
+    // set initial descriptor
+    for (i = 0; i < N_TX_BUF; i++) {
+        tx_desc_[i].packet = (uint32_t)&tx_frame_[i];
+        tx_desc_[i].control = 0xf4000000 | Frame::MAX_FRAME_LEN;
+        tx_status_[i].info = 0;
+    }
+
+    // emac registers
+    LPC_EMAC->TxDescriptor = (uint32_t)tx_desc_;
+    LPC_EMAC->TxStatus = (uint32_t)tx_status_;
+    LPC_EMAC->TxDescriptorNumber = N_TX_BUF - 1;  // this register is minus one encoded
+    LPC_EMAC->TxProduceIndex  = 0;
+    write_next_ = tx_frame_[0].buffer;
+    write_size_ = 0;
+}