moccos mizuki / EthernetXpresso

Dependents:   XNetServicesMin

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers LPC1769Emac.cpp Source File

LPC1769Emac.cpp

00001 #include "LPC1769Emac.h"
00002 #include <LPC1768/ARM/cmsis.h>
00003 #include <mbed.h>
00004 #include "LAN8710AReg.h"
00005 #include "LPC1769Reg.h"
00006 #include "Frame.h"
00007 
00008 #define BUFFER_SECTION __attribute((section("AHBSRAM1"),aligned(8)))
00009 
00010 #define SET_EMAC_BIT(NAME, PATTERN) (LPC_EMAC->NAME = PATTERN}
00011 #define ENABLE_EMAC_BIT(NAME, MASK) (LPC_EMAC->NAME |= MASK}
00012 #define DISABLE_EMAC_BIT(NAME, MASK)(LPC_EMAC->NAME = LPC_EMAC->NAME & ~MASK}
00013 
00014 using namespace LPC1769Reg;
00015 
00016 uint8_t LPC1769Emac::mac_[6];
00017 
00018 Descriptor LPC1769Emac::rx_desc_[LPC1769Emac::N_RX_BUF] BUFFER_SECTION;
00019 Descriptor LPC1769Emac::tx_desc_[LPC1769Emac::N_TX_BUF] BUFFER_SECTION;
00020 StatusRx LPC1769Emac::rx_status_[LPC1769Emac::N_RX_BUF] BUFFER_SECTION;
00021 StatusTx LPC1769Emac::tx_status_[LPC1769Emac::N_TX_BUF] BUFFER_SECTION;
00022 Frame LPC1769Emac::rx_frame_[LPC1769Emac::N_RX_BUF] BUFFER_SECTION;
00023 Frame LPC1769Emac::tx_frame_[LPC1769Emac::N_TX_BUF] BUFFER_SECTION;
00024 
00025 // === inline debug function
00026 #if 0
00027 #include "global.h"
00028 static inline void print_buffer(const char* title, uint8_t *buf, uint16_t size) {
00029     serial.printf("%s (%d bytes) dst:%02x %02x %02x %02x %02x %02x ",
00030     title, size, buf[0], buf[1], buf[2], buf[3], buf[4], buf[5]);
00031     buf += 6;
00032     serial.printf("src:%02x %02x %02x %02x %02x %02x ",
00033     buf[0], buf[1], buf[2], buf[3], buf[4], buf[5]);
00034     buf += 6;
00035     serial.printf("type:%02x %02x\r\n", buf[0], buf[1]);
00036 }
00037 #endif
00038 
00039 // === public functions ===
00040 LPC1769Emac::LPC1769Emac() {
00041     // set address
00042     // enable ethernet block
00043     LPC_SC->PCONP |= 0x40000000;
00044 
00045     // enable alternate functions for ethernet.
00046     // old values are not saved because those pins have only two functions.
00047     LPC_PINCON->PINSEL2 = 0x50150105;
00048     LPC_PINCON->PINSEL3 = (LPC_PINCON->PINSEL3 & ~0x0000000F) | 0x00000005;
00049 }
00050 
00051 LPC1769Emac::~LPC1769Emac() {
00052     // TODO: disable PHY before disable ethernet block?
00053 
00054     // disable alternate functions for ethernet
00055     LPC_PINCON->PINSEL2 = 0x00000000;
00056     LPC_PINCON->PINSEL3 = LPC_PINCON->PINSEL3 & ~0x0000000F;
00057 
00058     // disable ethernet block
00059     LPC_SC->PCONP = LPC_SC->PCONP & ~0x40000000;
00060 }
00061 
00062 bool
00063 LPC1769Emac::PhyWrite(uint8_t reg, uint16_t value) {
00064     LPC_EMAC->MADR = PHY_ADDR | reg;
00065     LPC_EMAC->MWTD = value;
00066 
00067     uint32_t i;
00068     for (i = 0; i < 0x10000; i++) {
00069         if ((LPC_EMAC->MIND & MIND_BUSY) == 0) {
00070             return true;
00071         }
00072     }
00073     return false;
00074 }
00075 
00076 uint16_t
00077 LPC1769Emac::PhyRead(uint8_t reg) {
00078     LPC_EMAC->MADR = PHY_ADDR | reg;
00079     LPC_EMAC->MCMD = MCMD_READ;
00080 
00081     uint32_t i;
00082     for (i = 0; i < 0x10000; i++) {
00083         if ((LPC_EMAC->MIND & MIND_BUSY) == 0) {
00084             break;
00085         }
00086     }
00087     LPC_EMAC->MCMD = 0x0000;
00088 
00089     return LPC_EMAC->MRDD;
00090 }
00091 
00092 void
00093 LPC1769Emac::SetAddress(uint8_t a5, uint8_t a4, uint8_t a3, uint8_t a2, uint8_t a1, uint8_t a0) {
00094     mac_[5] = a0;
00095     mac_[4] = a1;
00096     mac_[3] = a2;
00097     mac_[2] = a3;
00098     mac_[1] = a4;
00099     mac_[0] = a5;
00100 }
00101 
00102 void
00103 LPC1769Emac::UpdateAddress(uint8_t a5, uint8_t a4, uint8_t a3, uint8_t a2, uint8_t a1, uint8_t a0) {
00104     mac_[5] = a0;
00105     mac_[4] = a1;
00106     mac_[3] = a2;
00107     mac_[2] = a3;
00108     mac_[1] = a4;
00109     mac_[0] = a5;
00110     WriteAddress_();
00111 }
00112 
00113 void
00114 LPC1769Emac::StartRx() {
00115     LPC_EMAC->Command |= CMD_RX_EN;
00116     LPC_EMAC->MAC1 |= MAC1_RX_EN;
00117     //LPC_EMAC->IntEnable |= INT_RX_DONE;
00118 }
00119 
00120 void
00121 LPC1769Emac::StartTx() {
00122     LPC_EMAC->Command |= CMD_TX_EN;
00123     //LPC_EMAC->IntEnable |= INT_TX_DONE;
00124 }
00125 
00126 void
00127 LPC1769Emac::StopRx() {
00128     LPC_EMAC->Command = LPC_EMAC->Command & ~CMD_RX_EN;
00129     LPC_EMAC->MAC1 &= ~MAC1_RX_EN;
00130     //LPC_EMAC->IntEnable &= ~INT_RX_DONE;
00131 }
00132 
00133 void
00134 LPC1769Emac::StopTx() {
00135     LPC_EMAC->Command = LPC_EMAC->Command & ~CMD_TX_EN;
00136     //LPC_EMAC->IntEnable &= ~INT_TX_DONE;
00137 }
00138 
00139 bool
00140 LPC1769Emac::Link() {
00141     return ((PhyRead(SMIReg::BasicStatus) & SMIReg::BS_LINK) > 0);
00142 }
00143 
00144 uint16_t
00145 LPC1769Emac::Read(void *buf, uint16_t max_size) {
00146     uint32_t index = LPC_EMAC->RxConsumeIndex;
00147     if (index == LPC_EMAC->RxProduceIndex || max_size == 0) return 0;
00148 
00149     uint16_t packet_size = (rx_status_[index].info & Descriptor::SIZE_MASK) + 1;
00150     uint16_t size = packet_size - read_size_;
00151     if (size > Frame::MAX_FRAME_LEN) size = Frame::MAX_FRAME_LEN;
00152     if (size > max_size) size = max_size;
00153     memcpy(buf, read_next_, size);
00154 
00155     read_size_ += size;
00156     if (read_size_ >= packet_size) {
00157         // goto next buffer
00158         index = (index >= LPC_EMAC->RxDescriptorNumber) ? 0 : ++index;
00159         read_size_ = 0;
00160         read_next_ = rx_frame_[index].buffer;
00161         LPC_EMAC->RxConsumeIndex = index;
00162     } else {
00163         read_next_ += size;
00164     }
00165 
00166     return size;
00167 }
00168 
00169 uint16_t
00170 LPC1769Emac::ReadyToReceive() {
00171     uint32_t index = LPC_EMAC->RxConsumeIndex;
00172     if (index == LPC_EMAC->RxProduceIndex) return 0;
00173     else return (rx_status_[index].info & Descriptor::SIZE_MASK) + 1;
00174 }
00175 
00176 uint16_t
00177 LPC1769Emac::Write(void *buf, uint16_t size) {
00178     if (size + write_size_ > Frame::MAX_FRAME_LEN) size = Frame::MAX_FRAME_LEN - write_size_;
00179     if (size == 0) return 0;
00180     uint32_t index = LPC_EMAC->TxProduceIndex;
00181 
00182     memcpy(write_next_, buf, size);
00183     write_next_ += size;
00184     write_size_ += size;
00185     tx_desc_[index].control = (tx_desc_[index].control & (~Descriptor::SIZE_MASK)) | (size - 1);
00186     
00187     return size;
00188 }
00189 
00190 bool
00191 LPC1769Emac::Send() {
00192     if (write_size_ == 0) return true;
00193     uint32_t index = LPC_EMAC->TxProduceIndex;
00194     uint32_t index_next = (index == LPC_EMAC->TxDescriptorNumber) ? 0 : index + 1;
00195     if (index_next == LPC_EMAC->TxConsumeIndex) return false;
00196 
00197     tx_desc_[index].control = (tx_desc_[index].control & (~Descriptor::SIZE_MASK)) | (write_size_ - 1);
00198     LPC_EMAC->TxProduceIndex = index_next;
00199     write_next_ = tx_frame_[index_next].buffer;
00200     write_size_ = 0;
00201 
00202     return true;
00203 }
00204 
00205 bool
00206 LPC1769Emac::Send(void *buf, uint16_t size) {
00207     write_next_ -= write_size_;
00208     write_size_ = 0;
00209     if (!Write(buf, size)) return false;
00210     return Send();
00211 }
00212 
00213 bool
00214 LPC1769Emac::Reset(LinkMode mode) {
00215     bool link_100m = false;
00216     bool duplex_full = false;
00217     switch (mode) {
00218         case HalfDuplex10:
00219             break;
00220         case FullDuplex10:
00221             duplex_full = true;
00222             break;
00223         case HalfDuplex100:
00224             link_100m = true;
00225             break;
00226         case FullDuplex100:
00227             duplex_full = true;
00228             link_100m = true;
00229             break;
00230         case AutoNegotiate:
00231             duplex_full = true;
00232             link_100m = true;
00233             break;
00234         default:
00235             return false;
00236     }
00237 
00238     // reset EMAC
00239     LPC_EMAC->MAC1 = MAC1_RESET_MASK;
00240     LPC_EMAC->Command = CMD_RESET_MASK;
00241     wait_us(100);
00242 
00243     // emac settings
00244     LPC_EMAC->MCFG = MCFG_RESET_MII | MCFG_CS_DIV52;  // reset MII Management
00245     wait_us(200);
00246     LPC_EMAC->MAC1 = 0x0000;
00247     LPC_EMAC->CLRT = CLRT_DEFAULT;
00248     LPC_EMAC->MAXF = Frame::MAX_FRAME_LEN;
00249     LPC_EMAC->IPGR = IPGR_DEFAULT;
00250     LPC_EMAC->MCFG = MCFG_CS_DIV52;
00251     if (duplex_full) {
00252         LPC_EMAC->MAC2 = MAC2_AUTO_PAD | MAC2_FULL_DUPLEX;
00253         LPC_EMAC->IPGT = IPGT_FULL_DUPLEX;
00254         LPC_EMAC->Command = CMD_RMII | CMD_FULL_DUPLEX;
00255     } else {
00256         LPC_EMAC->MAC2 = MAC2_AUTO_PAD;
00257         LPC_EMAC->IPGT = IPGT_HALF_DUPLEX;
00258         LPC_EMAC->Command = CMD_RMII;
00259     }
00260     LPC_EMAC->SUPP = link_100m ? SUPP_100M_MODE : 0x0000;
00261 
00262     // reset PHY
00263     // "the reset process will be completed within 0.5s from the setting of this bit" (8710a datasheet)
00264     PhyWrite(SMIReg::BasicControl, SMIReg::BC_RESET);
00265     uint32_t i;
00266     bool success_flag = false;
00267     for (i = 0; i < 500; i++) {
00268         if ((PhyRead(SMIReg::BasicControl) & SMIReg::BC_RESET) == 0) {  // reset bit is self-clearing
00269             success_flag = true;
00270             break;
00271         }
00272         wait_ms(1);
00273     }
00274     if (!success_flag) return false;  // failed to reset
00275 
00276     // why 100M settings fail??
00277     switch (mode) {
00278         case HalfDuplex10:
00279             PhyWrite(SMIReg::BasicControl, 0x0000);
00280             break;
00281         case FullDuplex10:
00282             PhyWrite(SMIReg::BasicControl, SMIReg::BC_FULL_DUPLEX);
00283             break;
00284         case HalfDuplex100:
00285             PhyWrite(SMIReg::BasicControl, SMIReg::BC_100M);
00286             break;
00287         case FullDuplex100:
00288             PhyWrite(SMIReg::BasicControl, SMIReg::BC_FULL_DUPLEX | SMIReg::BC_100M);
00289             break;
00290         case AutoNegotiate:
00291             PhyWrite(SMIReg::BasicControl, SMIReg::BC_AUTONEG);
00292             // CheckAutoNeg_();
00293             break;
00294         default:
00295             return false;
00296     }
00297     
00298     InitRxBuffer_();
00299     InitTxBuffer_();
00300     WriteAddress_();
00301 
00302 
00303     LPC_EMAC->IntClear  = INT_ALL_MASK;
00304     LPC_EMAC->RxFilterCtrl = RXFC_BCAST | RXFC_PERFECT;
00305 
00306     return true;
00307 }
00308 
00309 // === private functions ===
00310 void
00311 LPC1769Emac::WriteAddress_() {
00312     LPC_EMAC->SA0 = ((uint16_t)mac_[5] << 8) | mac_[4];
00313     LPC_EMAC->SA1 = ((uint16_t)mac_[3] << 8) | mac_[2];
00314     LPC_EMAC->SA2 = ((uint16_t)mac_[1] << 8) | mac_[0];
00315 }
00316 
00317 // currently not used
00318 bool
00319 LPC1769Emac::CheckAutoNeg_() {
00320     bool success_flag = false;
00321     uint32_t i, r;
00322     for (i = 0; i < 0x10000; i++) {
00323         r = PhyRead(SMIReg::BasicStatus);
00324         if (r & SMIReg::BS_AUTONEG_COMPLETE) {
00325             success_flag = true;
00326             break;
00327         }
00328     }
00329     if (!success_flag) return false;
00330 
00331     r = PhyRead(SMIReg::PHYSpecialControlStatus);
00332 
00333     // update regs
00334     bool link_100m = ((r & SMIReg::SP_100M) > 0);
00335     bool duplex_full = ((r & SMIReg::SP_FULL_DUPLEX) > 0);
00336     
00337     if (duplex_full) {
00338         LPC_EMAC->MAC2 = MAC2_AUTO_PAD | MAC2_FULL_DUPLEX;
00339         LPC_EMAC->IPGT = IPGT_FULL_DUPLEX;
00340         LPC_EMAC->Command = CMD_RMII | CMD_FULL_DUPLEX;
00341     } else {
00342         LPC_EMAC->MAC2 = MAC2_AUTO_PAD;
00343         LPC_EMAC->IPGT = IPGT_HALF_DUPLEX;
00344         LPC_EMAC->Command = CMD_RMII;
00345     }
00346     LPC_EMAC->SUPP = link_100m ? SUPP_100M_MODE : 0x0000;
00347 
00348     return true;
00349 }
00350 
00351 void
00352 LPC1769Emac::InitRxBuffer_() {
00353     uint16_t i;
00354 
00355     // set initial descriptor
00356     for (i = 0; i < N_RX_BUF; i++) {
00357         rx_desc_[i].packet = (uint32_t)&rx_frame_[i];
00358         rx_desc_[i].control = 0x80000000 | (Frame::MAX_FRAME_LEN - 1);
00359     }
00360 
00361     // emac registers
00362     LPC_EMAC->RxDescriptor = (uint32_t)rx_desc_;
00363     LPC_EMAC->RxStatus = (uint32_t)rx_status_;
00364     LPC_EMAC->RxDescriptorNumber = N_RX_BUF - 1;  // this register is minus one encoded
00365     LPC_EMAC->RxConsumeIndex  = 0;
00366     read_next_ = rx_frame_[0].buffer;
00367     read_size_ = 0;
00368 }
00369 
00370 void
00371 LPC1769Emac::InitTxBuffer_() {
00372     uint16_t i;
00373 
00374     // set initial descriptor
00375     for (i = 0; i < N_TX_BUF; i++) {
00376         tx_desc_[i].packet = (uint32_t)&tx_frame_[i];
00377         tx_desc_[i].control = 0xf4000000 | Frame::MAX_FRAME_LEN;
00378         tx_status_[i].info = 0;
00379     }
00380 
00381     // emac registers
00382     LPC_EMAC->TxDescriptor = (uint32_t)tx_desc_;
00383     LPC_EMAC->TxStatus = (uint32_t)tx_status_;
00384     LPC_EMAC->TxDescriptorNumber = N_TX_BUF - 1;  // this register is minus one encoded
00385     LPC_EMAC->TxProduceIndex  = 0;
00386     write_next_ = tx_frame_[0].buffer;
00387     write_size_ = 0;
00388 }