📄 at91_ether.c
字号:
/* * Ethernet driver for the Atmel AT91RM9200 * Based on NetBSD AT91 EMAC driver */#include <rthw.h>#include <rtthread.h>#include <AT9200.h>#include <mii.h>#include <ethtool.h>#include <AT91RM9200_EMAC.h>#include <pio.h>#include <at91_ether.h>#include <ethernetif.h>#define barrier() __asm__ __volatile__("": : :"memory")#define CONFIG_AT91_ETHER_RMII/* Davicom specific registers */#define MII_DSCSR_REG 17#define MII_DSINTR_REG 21AT91PS_SYS AT91_SYS = (AT91PS_SYS) AT91C_BASE_SYS;AT91PS_EMAC AT91_EMAC = (AT91PS_EMAC) AT91C_BASE_EMAC;#define MAX_ADDR_LEN 6struct net_device_stats{ unsigned long rx_packets; /* total packets received */ unsigned long tx_packets; /* total packets transmitted */ unsigned long rx_errors; /* receive bad packets */ unsigned long tx_errors; /* transmit bad packets */};#define IFF_PROMISC 0x100 /* receive all packets */#define IFF_ALLMULTI 0x200 /* receive all multicast packets*/#define PHY_IRQ 32#define MAX_RBUFF_SZ 0x600 /* 1518 rounded up */#define MAX_RX_DESCR 9 /* max number of receive buffers */struct rbf_t{ rt_uint32_t addr; rt_uint32_t size;};struct net_device{ /* inherit from ethernet device */ struct eth_device parent; int state ; rt_uint32_t current_rb_index; /* current receive buffer index */ /* interface address info. */ rt_uint8_t dev_addr[MAX_ADDR_LEN]; /* hw address */ rt_uint8_t phy_addr; struct rbf_t descriptors[MAX_RX_DESCR]; /* must be on sizeof (rbf_t) boundary */ rt_uint8_t rx_buf[MAX_RX_DESCR][MAX_RBUFF_SZ]; /* must be on long boundary */ rt_uint8_t tx_buf[MAX_RBUFF_SZ]; /* must be on long boundary */ struct net_device_stats stats;};static struct net_device at91_dev_entry;static struct net_device *at91_dev =&at91_dev_entry;/* ........................... PHY INTERFACE ........................... *//* * Write value to the a PHY register * Note: MDI interface is assumed to already have been enabled. */rt_inline void write_phy(rt_uint8_t phy_addr, rt_uint8_t address, rt_uint32_t value){ AT91_EMAC->EMAC_MAN = (AT91C_EMAC_HIGH | AT91C_EMAC_CODE_802_3 | AT91C_EMAC_RW_W |((phy_addr & 0x1f) << 23)| (address << 18)) + (value & 0xffff); /* Wait until IDLE bit in Network Status register is cleared */ // TODO: Enforce some maximum loop-count? while (!(AT91_EMAC->EMAC_SR & AT91C_EMAC_IDLE)) { barrier(); }}/* * Read value stored in a PHY register. * Note: MDI interface is assumed to already have been enabled. */rt_inline void read_phy(rt_uint8_t phy_addr, rt_uint8_t address, rt_uint32_t *value){ AT91_EMAC->EMAC_MAN = AT91C_EMAC_HIGH | AT91C_EMAC_CODE_802_3 | AT91C_EMAC_RW_R |((phy_addr & 0x1f) << 23)| (address << 18); /* Wait until IDLE bit in Network Status register is cleared */ // TODO: Enforce some maximum loop-count? while (!(AT91_EMAC->EMAC_SR & AT91C_EMAC_IDLE)) { barrier(); } *value = (AT91_EMAC->EMAC_MAN & 0x0000ffff);}/* ........................... PHY MANAGEMENT .......................... *//* * Access the PHY to determine the current Link speed and Mode, and update the * MAC accordingly. * If no link or auto-negotiation is busy, then no changes are made. * Returns: 0 : OK * -1 : No link * -2 : AutoNegotiation still in progress */static int update_linkspeed(struct net_device *dev){ rt_uint32_t bmsr, bmcr, lpa, mac_cfg; rt_uint32_t speed, duplex; /* Link up, or auto-negotiation still in progress */ read_phy(dev->phy_addr ,MII_BMSR, &bmsr); read_phy(dev->phy_addr ,MII_BMSR, &bmsr); if (!(bmsr & BMSR_LSTATUS)) return -1; /* no link */ read_phy(dev->phy_addr, MII_BMCR, &bmcr); if (bmcr & BMCR_ANENABLE) { /* AutoNegotiation is enabled */ if (!(bmsr & BMSR_ANEGCOMPLETE)) return -2; /* auto-negotitation in progress */ read_phy(dev->phy_addr, MII_LPA, &lpa); if ((lpa & LPA_100FULL) || (lpa & LPA_100HALF)) speed = SPEED_100; else speed = SPEED_10; if ((lpa & LPA_100FULL) || (lpa & LPA_10FULL)) duplex = DUPLEX_FULL; else duplex = DUPLEX_HALF; } else { speed = (bmcr & BMCR_SPEED100) ? SPEED_100 : SPEED_10; duplex = (bmcr & BMCR_FULLDPLX) ? DUPLEX_FULL : DUPLEX_HALF; } /* Update the MAC */ mac_cfg = AT91_EMAC->EMAC_CFG & ~(AT91C_EMAC_SPD | AT91C_EMAC_FD); if (speed == SPEED_100) { if (duplex == DUPLEX_FULL) /* 100 Full Duplex */ AT91_EMAC->EMAC_CFG = mac_cfg | AT91C_EMAC_SPD | AT91C_EMAC_FD; else /* 100 Half Duplex */ AT91_EMAC->EMAC_CFG = mac_cfg | AT91C_EMAC_SPD; } else { if (duplex == DUPLEX_FULL) /* 10 Full Duplex */ AT91_EMAC->EMAC_CFG = mac_cfg | AT91C_EMAC_FD; else /* 10 Half Duplex */ AT91_EMAC->EMAC_CFG = mac_cfg; } return 0;}void netif_carrier_off(struct net_device *dev ){ dev->state = 0;}void netif_carrier_on(struct net_device *dev ){ dev->state = 1;}/* * Handle interrupts from the PHY */void at91ether_phy_interrupt(int irq){ int status; rt_uint32_t phy; AT91_EMAC->EMAC_CTL |= AT91C_EMAC_MPE; /* enable management port */ read_phy(at91_dev->phy_addr, MII_DSINTR_REG, &phy); /* acknowledge interrupt in PHY */ status = AT91_SYS->PIOC_ISR; /* acknowledge interrupt in PIO */ status = update_linkspeed(at91_dev); if (status == -1) { /* link is down */ netif_carrier_off(at91_dev); } else if (status == -2) { /* auto-negotiation in progress */ /* Do nothing - another interrupt generated when negotiation complete */ } else { /* link is operational */ netif_carrier_on(at91_dev); } AT91_EMAC->EMAC_CTL &= ~AT91C_EMAC_MPE; /* disable management port */}/* * Initialize and enable the PHY interrupt when link-state changes */void enable_phyirq(struct net_device *dev){ static int first_init = 0; if (first_init == 0) { // TODO: Check error code. Really need a generic PIO (interrupt) // layer since we're really only interested in the PC4 line. rt_hw_interrupt_install(PHY_IRQ, at91ether_phy_interrupt, 0); rt_hw_interrupt_umask(PHY_IRQ); AT91_SYS->PIOC_ODR = AT91C_PIO_PC4; /* Configure as input */ first_init = 1; } else { rt_uint32_t dsintr, status; status = AT91_SYS->PIOC_ISR; /* clear any pending PIO interrupts */ AT91_SYS->PIOC_IER = AT91C_PIO_PC4; /* Enable interrupt */ AT91_EMAC->EMAC_CTL |= AT91C_EMAC_MPE; /* enable management port */ read_phy(dev->phy_addr, MII_DSINTR_REG, &dsintr); dsintr = dsintr & ~0xf00; /* clear bits 8..11 */ write_phy(dev->phy_addr,MII_DSINTR_REG, dsintr); AT91_EMAC->EMAC_CTL &= ~AT91C_EMAC_MPE; /* disable management port */ }}/* * Disable the PHY interrupt */void disable_phyirq(struct net_device *dev){ rt_uint32_t dsintr; rt_base_t level; level = rt_hw_interrupt_disable(); AT91_EMAC->EMAC_CTL |= AT91C_EMAC_MPE; /* enable management port */ read_phy(dev->phy_addr, MII_DSINTR_REG, &dsintr); dsintr = dsintr | 0xf00; /* set bits 8..11 */ write_phy(dev->phy_addr, MII_DSINTR_REG, dsintr); AT91_EMAC->EMAC_CTL &= ~AT91C_EMAC_MPE; /* disable management port */ rt_hw_interrupt_enable(level); AT91_SYS->PIOC_IDR = AT91C_PIO_PC4; /* Disable interrupt */}/** * is_valid_ether_addr - Determine if the given Ethernet address is valid * @addr: Pointer to a six-byte array containing the Ethernet address * * Check that the Ethernet address (MAC) is not 00:00:00:00:00:00, is not * a multicast address, and is not FF:FF:FF:FF:FF:FF. The multicast * and FF:FF:... tests are combined into the single test "!(addr[0]&1)". * * Return true if the address is valid. */rt_inline int is_valid_ether_addr(rt_uint8_t *addr){ const char zaddr[6] ={ 0, }; return !(addr[0]&1) && rt_memcmp( addr, zaddr, 6);}/* ......................... ADDRESS MANAGEMENT ........................ *//* * Set the ethernet MAC address in dev->dev_addr */void get_mac_address(struct net_device *dev){ static rt_uint8_t def_mac[] = {0x00,0xaa,0xbb,0xcc,0xdd,0x00}; rt_uint8_t addr[6]; rt_uint32_t hi, lo; /* Check if bootloader set address in Specific-Address 1 */ hi = AT91_EMAC->EMAC_SA1H; lo = AT91_EMAC->EMAC_SA1L; addr[0] = (lo & 0xff); addr[1] = (lo & 0xff00) >> 8; addr[2] = (lo & 0xff0000) >> 16; addr[3] = (lo & 0xff000000) >> 24; addr[4] = (hi & 0xff); addr[5] = (hi & 0xff00) >> 8; if (is_valid_ether_addr(addr)) { rt_memcpy(dev->dev_addr, &addr, 6); return; } /* Check if bootloader set address in Specific-Address 2 */ hi = AT91_EMAC->EMAC_SA2H; lo = AT91_EMAC->EMAC_SA2L; addr[0] = (lo & 0xff); addr[1] = (lo & 0xff00) >> 8; addr[2] = (lo & 0xff0000) >> 16; addr[3] = (lo & 0xff000000) >> 24; addr[4] = (hi & 0xff); addr[5] = (hi & 0xff00) >> 8; if (is_valid_ether_addr(addr)) { rt_memcpy(dev->dev_addr, &addr, 6); return; } rt_memcpy(dev->dev_addr,&def_mac,6); return;}/*
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -