123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519 |
- /*
- * Copyright (c) 2013 Nuvoton Technology Corp.
- *
- * See file CREDITS for list of people who contributed to this
- * project.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation; either version 2 of
- * the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
- * MA 02111-1307 USA
- *
- * Description: NUC472 MAC driver source file
- */
- #include <rtconfig.h>
- #ifdef RT_USING_LWIP
- #include <rtthread.h>
- #include "nuc472_eth.h"
- #include <netif/ethernetif.h>
- #include <netif/etharp.h>
- #include <lwip/icmp.h>
- #include "lwipopts.h"
- #define ETH_TRIGGER_RX() EMAC->RXST = 0
- #define ETH_TRIGGER_TX() EMAC->TXST = 0
- #define ETH_ENABLE_TX() EMAC->CTL |= EMAC_CTL_TXON
- #define ETH_ENABLE_RX() EMAC->CTL |= EMAC_CTL_RXON
- #define ETH_DISABLE_TX() EMAC->CTL &= ~EMAC_CTL_TXON
- #define ETH_DISABLE_RX() EMAC->CTL &= ~EMAC_CTL_RXON
-
- rt_uint8_t ethaddr[] = {0x00, 0x00, 0x00, 0x59, 0x16, 0x88};
- struct rt_nuc472_emac
- {
- struct eth_device parent;
- EMAC_T *emac_base;
- rt_uint8_t dev_addr[6];
- struct eth_descriptor volatile *cur_tx_desc_ptr, *cur_rx_desc_ptr, *fin_tx_desc_ptr;
- struct eth_descriptor rx_desc[RX_DESCRIPTOR_NUM];
- struct eth_descriptor tx_desc[TX_DESCRIPTOR_NUM];
- rt_uint8_t rx_buf[RX_DESCRIPTOR_NUM][PACKET_BUFFER_SIZE];
- rt_uint8_t tx_buf[TX_DESCRIPTOR_NUM][PACKET_BUFFER_SIZE];
- };
- typedef struct rt_nuc472_emac* rt_nuc472_emac_t;
- struct rt_nuc472_emac nuc472_emac_device;
- static void mdio_write(rt_nuc472_emac_t emac, rt_uint8_t addr, rt_uint8_t reg, rt_uint16_t val)
- {
- EMAC_T *emac_base = emac->emac_base;
- emac_base->MIIMDAT = val;
- emac_base->MIIMCTL = (addr << EMAC_MIIMCTL_PHYADDR_Pos) | reg | EMAC_MIIMCTL_BUSY_Msk | EMAC_MIIMCTL_WRITE_Msk | EMAC_MIIMCTL_MDCON_Msk;
- while (emac_base->MIIMCTL & EMAC_MIIMCTL_BUSY_Msk);
- }
- static rt_uint16_t mdio_read(rt_nuc472_emac_t emac, rt_uint8_t addr, rt_uint8_t reg)
- {
- EMAC_T *emac_base = emac->emac_base;
- emac_base->MIIMCTL = (addr << EMAC_MIIMCTL_PHYADDR_Pos) | reg | EMAC_MIIMCTL_BUSY_Msk | EMAC_MIIMCTL_MDCON_Msk;
- while (emac_base->MIIMCTL & EMAC_MIIMCTL_BUSY_Msk);
- return(emac_base->MIIMDAT);
- }
- static int reset_phy(rt_nuc472_emac_t emac)
- {
- rt_uint16_t reg;
- rt_uint32_t delay;
- EMAC_T *emac_base = emac->emac_base;
- mdio_write(emac, CONFIG_PHY_ADDR, MII_BMCR, BMCR_RESET);
- delay = 2000;
- while(delay-- > 0)
- {
- if((mdio_read(emac, CONFIG_PHY_ADDR, MII_BMCR) & BMCR_RESET) == 0)
- break;
- }
- if(delay == 0)
- {
- rt_kprintf("Reset phy failed\n");
- return(-1);
- }
- mdio_write(emac, CONFIG_PHY_ADDR, MII_ADVERTISE, ADVERTISE_CSMA |
- ADVERTISE_10HALF |
- ADVERTISE_10FULL |
- ADVERTISE_100HALF |
- ADVERTISE_100FULL);
- reg = mdio_read(emac, CONFIG_PHY_ADDR, MII_BMCR);
- mdio_write(emac, CONFIG_PHY_ADDR, MII_BMCR, reg | BMCR_ANRESTART);
- delay = 200000;
- while(delay-- > 0)
- {
- if((mdio_read(emac, CONFIG_PHY_ADDR, MII_BMSR) & (BMSR_ANEGCOMPLETE | BMSR_LSTATUS))
- == (BMSR_ANEGCOMPLETE | BMSR_LSTATUS))
- break;
- }
- if(delay == 0)
- {
- rt_kprintf("AN failed. Set to 100 FULL\n");
- emac_base->CTL |= (EMAC_CTL_OPMODE_Msk | EMAC_CTL_FUDUP_Msk);
- return(-1);
- }
- else
- {
- reg = mdio_read(emac, CONFIG_PHY_ADDR, MII_LPA);
- if(reg & ADVERTISE_100FULL)
- {
- rt_kprintf("100 full\n");
- emac_base->CTL |= (EMAC_CTL_OPMODE_Msk | EMAC_CTL_FUDUP_Msk);
- }
- else if(reg & ADVERTISE_100HALF)
- {
- rt_kprintf("100 half\n");
- emac_base->CTL = (EMAC->CTL & ~EMAC_CTL_FUDUP_Msk) | EMAC_CTL_OPMODE_Msk;
- }
- else if(reg & ADVERTISE_10FULL)
- {
- rt_kprintf("10 full\n");
- emac_base->CTL = (EMAC->CTL & ~EMAC_CTL_OPMODE_Msk) | EMAC_CTL_FUDUP_Msk;
- } else
- {
- rt_kprintf("10 half\n");
- emac_base->CTL &= ~(EMAC_CTL_OPMODE_Msk | EMAC_CTL_FUDUP_Msk);
- }
- }
- return(0);
- }
- static void init_tx_desc(rt_nuc472_emac_t emac)
- {
- EMAC_T *emac_base = emac->emac_base;
- rt_uint32_t i;
- emac->cur_tx_desc_ptr = emac->fin_tx_desc_ptr = &emac->tx_desc[0];
- for(i = 0; i < TX_DESCRIPTOR_NUM; i++)
- {
- emac->tx_desc[i].status1 = TXFD_PADEN | TXFD_CRCAPP | TXFD_INTEN;
- emac->tx_desc[i].buf = &emac->tx_buf[i][0];
- emac->tx_desc[i].status2 = 0;
- emac->tx_desc[i].next = &emac->tx_desc[(i + 1) % TX_DESCRIPTOR_NUM];
- }
- emac_base->TXDSA = (unsigned int)&emac->tx_desc[0];
- return;
- }
- static void init_rx_desc(rt_nuc472_emac_t emac)
- {
- EMAC_T *emac_base = emac->emac_base;
- rt_uint32_t i;
- emac->cur_rx_desc_ptr = &emac->rx_desc[0];
- for(i = 0; i < RX_DESCRIPTOR_NUM; i++)
- {
- emac->rx_desc[i].status1 = OWNERSHIP_EMAC;
- emac->rx_desc[i].buf = &emac->rx_buf[i][0];
- emac->rx_desc[i].status2 = 0;
- emac->rx_desc[i].next = &emac->rx_desc[(i + 1) % RX_DESCRIPTOR_NUM];
- }
- emac_base->RXDSA = (unsigned int)&emac->rx_desc[0];
- return;
- }
- static void set_mac_addr(rt_nuc472_emac_t emac, rt_uint8_t *addr)
- {
- EMAC_T *emac_base = emac->emac_base;
- emac_base->CAM0M = (addr[0] << 24) |
- (addr[1] << 16) |
- (addr[2] << 8) |
- addr[3];
- emac_base->CAM0L = (addr[4] << 24) |
- (addr[5] << 16);
- emac_base->CAMCTL = EMAC_CAMCTL_CMPEN_Msk | EMAC_CAMCTL_AMP_Msk | EMAC_CAMCTL_ABP_Msk;
- emac_base->CAMEN = 1; // Enable CAM entry 0
- }
- void EMAC_init(rt_nuc472_emac_t emac, rt_uint8_t *mac_addr)
- {
- EMAC_T *emac_base = emac->emac_base;
- RT_ASSERT(emac->dev_addr != RT_NULL);
- CLK_EnableModuleClock(EMAC_MODULE);
- // Configure MDC clock rate to HCLK / (127 + 1) = 656 kHz if system is running at 84 MHz
- CLK_SetModuleClock(EMAC_MODULE, 0, CLK_CLKDIV3_EMAC(127));
- // Configure RMII pins
- SYS->GPC_MFPL = SYS_GPC_MFPL_PC0MFP_EMAC_REFCLK |
- SYS_GPC_MFPL_PC1MFP_EMAC_MII_RXERR |
- SYS_GPC_MFPL_PC2MFP_EMAC_MII_RXDV |
- SYS_GPC_MFPL_PC3MFP_EMAC_MII_RXD1 |
- SYS_GPC_MFPL_PC4MFP_EMAC_MII_RXD0 |
- SYS_GPC_MFPL_PC6MFP_EMAC_MII_TXD0 |
- SYS_GPC_MFPL_PC7MFP_EMAC_MII_TXD1;
- SYS->GPC_MFPH = SYS_GPC_MFPH_PC8MFP_EMAC_MII_TXEN;
- // Enable high slew rate on all RMII pins
- PC->SLEWCTL |= 0x1DF;
- // Configure MDC, MDIO at PB14 & PB15
- SYS->GPB_MFPH = SYS_GPB_MFPH_PB14MFP_EMAC_MII_MDC | SYS_GPB_MFPH_PB15MFP_EMAC_MII_MDIO;
-
- // Reset MAC
- emac_base->CTL = EMAC_CTL_RST_Msk;
- init_tx_desc(emac);
- init_rx_desc(emac);
- set_mac_addr(emac, mac_addr); // need to reconfigure hardware address 'cos we just RESET emc...
- reset_phy(emac);
- emac_base->CTL |= EMAC_CTL_STRIPCRC_Msk | EMAC_CTL_RXON_Msk | EMAC_CTL_TXON_Msk | EMAC_CTL_RMIIEN_Msk | EMAC_CTL_RMIIRXCTL_Msk;
- emac_base->INTEN |= EMAC_INTEN_RXIEN_Msk |
- EMAC_INTEN_RXGDIEN_Msk |
- EMAC_INTEN_RDUIEN_Msk |
- EMAC_INTEN_RXBEIEN_Msk |
- EMAC_INTEN_TXIEN_Msk |
- EMAC_INTEN_TXABTIEN_Msk |
- EMAC_INTEN_TXCPIEN_Msk |
- EMAC_INTEN_TXBEIEN_Msk;
- emac_base->RXST = 0; // trigger Rx
-
- //NVIC_SetPriorityGrouping(NVIC_PRIORITYGROUP_4);
- NVIC_SetPriority(EMAC_TX_IRQn, 0);
- NVIC_EnableIRQ(EMAC_TX_IRQn);
- NVIC_SetPriority(EMAC_RX_IRQn, 1);
- NVIC_EnableIRQ(EMAC_RX_IRQn);
- }
- void ETH_halt(rt_nuc472_emac_t emac)
- {
- EMAC_T *emac_base = emac->emac_base;
- emac_base->CTL &= ~(EMAC_CTL_RXON_Msk | EMAC_CTL_TXON_Msk);
- }
- void emac_emac_done(rt_nuc472_emac_t emac)
- {
- EMAC_T *emac_base = emac->emac_base;
- unsigned int status;
- status = emac_base->INTSTS & 0xFFFF0000;
- emac_base->INTSTS = status;
- if(status & EMAC_INTSTS_TSALMIF_Msk)
- {
- // TODO: time stamp alarm. write me!!
- }
- if(status & EMAC_INTSTS_TXBEIF_Msk)
- {
- // Shouldn't goes here, unless descriptor corrupted
- return;
- }
- }
- rt_uint8_t *emac_get_tx_buf(rt_nuc472_emac_t emac)
- {
- if(emac->cur_tx_desc_ptr->status1 & OWNERSHIP_EMAC)
- {
- return(RT_NULL);
- }
- else
- {
- return(emac->cur_tx_desc_ptr->buf);
- }
- }
- void ETH_trigger_tx(rt_nuc472_emac_t emac, rt_uint16_t length)
- {
- struct eth_descriptor volatile *desc;
-
- emac->cur_tx_desc_ptr->status2 = (unsigned int)length;
- desc = emac->cur_tx_desc_ptr->next; // in case TX is transmitting and overwrite next pointer before we can update cur_tx_desc_ptr
- emac->cur_tx_desc_ptr->status1 |= OWNERSHIP_EMAC;
- emac->cur_tx_desc_ptr = desc;
-
- ETH_TRIGGER_TX();
- }
- /*
- * NUC472 EMAC Driver for RT-Thread
- * Change Logs:
- * Date Author Notes
- * 2017-12-31 Bluebear233 first implementation
- */
- void rt_nuc472_emac_tx_isr(rt_nuc472_emac_t emac)
- {
- emac_emac_done(emac);
- }
- void rt_nuc472_emac_rx_isr(rt_nuc472_emac_t emac)
- {
- EMAC_T *emac_base = emac->emac_base;
-
- unsigned int status = emac_base->INTSTS & 0xFFFF;
- emac_base->INTSTS = status;
-
- eth_device_ready(&emac->parent);
- }
- void EMAC_TX_IRQHandler(void)
- {
- /* enter interrupt */
- rt_interrupt_enter();
-
- rt_nuc472_emac_tx_isr(&nuc472_emac_device);
- /* leave interrupt */
- rt_interrupt_leave();
- }
- void EMAC_RX_IRQHandler(void)
- {
- /* enter interrupt */
- rt_interrupt_enter();
-
- rt_nuc472_emac_rx_isr(&nuc472_emac_device);
- /* leave interrupt */
- rt_interrupt_leave();
- }
- static rt_err_t rt_nuc472_emac_init(rt_device_t dev)
- {
- rt_nuc472_emac_t emac = (rt_nuc472_emac_t)dev;
- EMAC_init(emac, emac->dev_addr);
- #if LWIP_IPV4 && LWIP_IGMP
- netif_set_igmp_mac_filter(stm32_eth->parent.netif, igmp_mac_filter);
- #endif /* LWIP_IPV4 && LWIP_IGMP */
-
- return RT_EOK;
- }
- static rt_err_t rt_nuc472_emac_open(rt_device_t dev, rt_uint16_t oflag)
- {
- return RT_EOK;
- }
- static rt_err_t rt_nuc472_emac_close(rt_device_t dev)
- {
- return RT_EOK;
- }
- static rt_size_t rt_nuc472_emac_read(rt_device_t dev, rt_off_t pos, void* buffer, rt_size_t size)
- {
- rt_set_errno(-RT_ENOSYS);
- return 0;
- }
- static rt_size_t rt_nuc472_emac_write (rt_device_t dev, rt_off_t pos, const void* buffer, rt_size_t size)
- {
- rt_set_errno(-RT_ENOSYS);
- return 0;
- }
- static rt_err_t rt_nuc472_emac_control(rt_device_t dev, int cmd, void *args)
- {
- rt_nuc472_emac_t emac = (rt_nuc472_emac_t)dev;
- switch(cmd)
- {
- case NIOCTL_GADDR:
- /* get mac address */
- if(args) rt_memcpy(args, emac->dev_addr, 6);
- else return -RT_ERROR;
- break;
- default :
- break;
- }
- return RT_EOK;
- }
- rt_err_t rt_nuc472_emac_tx(rt_device_t dev, struct pbuf* p)
- {
- rt_nuc472_emac_t emac = (rt_nuc472_emac_t)dev;
- struct pbuf* q;
- rt_uint32_t offset;
- rt_uint8_t *buf = emac_get_tx_buf(emac);
- /* get free tx buffer */
- if(buf == RT_NULL)
- {
- rt_kprintf("none tx buf\n");
- return -RT_ERROR;
- }
- offset = 0;
- for (q = p; q != NULL; q = q->next)
- {
- rt_uint8_t* ptr;
- rt_uint32_t len;
- len = q->len;
- ptr = q->payload;
- /* Copy the frame to be sent into memory pointed by the current ETHERNET DMA Tx descriptor */
- while (len)
- {
- buf[offset] = *ptr;
- offset ++;
- ptr ++;
- len --;
- }
- }
- ETH_trigger_tx(emac, offset);
- /* Return SUCCESS */
- return RT_EOK;
- }
- struct pbuf *rt_nuc472_emac_rx(rt_device_t dev)
- {
- rt_nuc472_emac_t emac = (rt_nuc472_emac_t)dev;
- unsigned int status;
- struct pbuf* p;
- /* init p pointer */
- p = RT_NULL;
- status = emac->cur_rx_desc_ptr->status1;
- if(status & OWNERSHIP_EMAC)
- {
- goto end;
- }
- if (status & RXFD_RXGD)
- {
- //ethernetif_input(status & 0xFFFF, cur_rx_desc_ptr->buf, cur_rx_desc_ptr->status2, (u32_t)cur_rx_desc_ptr->next);
- /* allocate buffer */
- p = pbuf_alloc(PBUF_LINK, status & 0xFFFF, PBUF_RAM);
- if (p != RT_NULL)
- {
- const char * from;
- struct pbuf* q;
- from = (const char *)(emac->cur_rx_desc_ptr->buf);
- for (q = p; q != RT_NULL; q = q->next)
- {
- /* Copy the received frame into buffer from memory pointed by the current ETHERNET DMA Rx descriptor */
- memcpy(q->payload, from, q->len);
- from += q->len;
- }
- }
- }
-
- emac->cur_rx_desc_ptr->status1 = OWNERSHIP_EMAC;
- emac->cur_rx_desc_ptr = emac->cur_rx_desc_ptr->next;
-
- return p;
-
- end:
- ETH_TRIGGER_RX();
-
- return p;
- }
- static void rt_hw_nuc472_emac_register(rt_nuc472_emac_t emac, char *dev_name, EMAC_T *emac_base, rt_uint8_t *mac_addr)
- {
- emac->emac_base = emac_base;
- rt_memcpy(emac->dev_addr, mac_addr, sizeof(emac->dev_addr));
- emac->parent.parent.init = rt_nuc472_emac_init;
- emac->parent.parent.open = rt_nuc472_emac_open;
- emac->parent.parent.close = rt_nuc472_emac_close;
- emac->parent.parent.read = rt_nuc472_emac_read;
- emac->parent.parent.write = rt_nuc472_emac_write;
- emac->parent.parent.control = rt_nuc472_emac_control;
- emac->parent.parent.user_data = RT_NULL;
- emac->parent.eth_rx = rt_nuc472_emac_rx;
- emac->parent.eth_tx = rt_nuc472_emac_tx;
- /* register eth device */
- eth_device_init(&(emac->parent), dev_name);
- }
- void rt_hw_nuc472_emac_init(void)
- {
- rt_hw_nuc472_emac_register(&nuc472_emac_device, "eh0", EMAC, ethaddr);
- }
- #endif
|