Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
Diff: LPC1769Emac.cpp
- 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;
+}