⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 at91_ether.c

📁 ARM板驱动程序源代码
💻 C
📖 第 1 页 / 共 2 页
字号:
/* * Ethernet driver for the Atmel AT91RM9200 (Thunder) * * (c) SAN People (Pty) Ltd * * Based on an earlier Atmel EMAC macrocell driver by Atmel and Lineo Inc. * Initial version by Rick Bronson 01/11/2003 * * Intel LXT971A PHY support by Christopher Bahns & David Knickerbocker *   (Polaroid Corporation) * * 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. */#define CONFIG_AT91_ETHER_RMII /**/1#define END_CACHE_VIRT_TO_PHYS(x) x#define ether_crc(length, data)    bitreverse(crc32_le(~0, data, length))unsigned long bitreverse(unsigned long x){	x = (x >> 16) | (x << 16);	x = (x >> 8 & 0x00ff00ff) | (x << 8 & 0xff00ff00);	x = (x >> 4 & 0x0f0f0f0f) | (x << 4 & 0xf0f0f0f0);	x = (x >> 2 & 0x33333333) | (x << 2 & 0xcccccccc);	x = (x >> 1 & 0x55555555) | (x << 1 & 0xaaaaaaaa);	return x;}#include "crc32table.h"unsigned long  crc32_le(unsigned long crc, unsigned char const *p, int len){	const unsigned long      *b =(unsigned long *)p;	const unsigned long      *tab = crc32table_le;#define DO_CRC(x) crc = tab[ (crc ^ (x)) & 255 ] ^ (crc>>8)	/* Align it */	if(!(((long)b)&3 && len)){		do {			DO_CRC(*((unsigned char *)b)++);		} while ((--len) && ((long)b)&3 );	}	if((len >= 4)){		/* load data 32 bits wide, xor data 32 bits wide. */		size_t save_len = len & 3;	        len = len >> 2;		--b; /* use pre increment below(*++b) for speed */		do {			crc ^= *++b;			DO_CRC(0);			DO_CRC(0);			DO_CRC(0);			DO_CRC(0);		} while (--len);		b++; /* point to next byte(s) */		len = save_len;	}	/* And the last few bytes */	if(len){		do {			DO_CRC(*((unsigned char *)b)++);		} while (--len);	}	return (crc);#undef ENDIAN_SHIFT#undef DO_CRC}#define barrier() static __inline int is_valid_ether_addr( unsigned char *addr ){	const char zaddr[6] = {0,};	return !(addr[0]&1) && memcmp( addr, zaddr, 6);}static	struct at91_private  at91_mac_private;/* ........................... PHY INTERFACE ........................... *//* * Enable the MDIO bit in MAC control register * When not called from an interrupt-handler, access to the PHY must be *  protected by a spinlock. */static void enable_mdi(AT91PS_EMAC regs){	regs->EMAC_CTL |= AT91C_EMAC_MPE;	/* enable management port */}/* * Disable the MDIO bit in the MAC control register */static void disable_mdi(AT91PS_EMAC regs){	regs->EMAC_CTL &= ~AT91C_EMAC_MPE;	/* disable management port */}/* * Write value to the a PHY register * Note: MDI interface is assumed to already have been enabled. */static void write_phy(AT91PS_EMAC regs, unsigned char phy_addr, unsigned char address, unsigned int value){	regs->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 (!(regs->EMAC_SR & AT91C_EMAC_IDLE)) { barrier(); }}/* * Read value stored in a PHY register. * Note: MDI interface is assumed to already have been enabled. */static void read_phy(AT91PS_EMAC regs, unsigned char phy_addr, unsigned char address, unsigned int *value){	regs->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 (!(regs->EMAC_SR & AT91C_EMAC_IDLE)) { barrier(); }	*value = (regs->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, AT91PS_EMAC regs) {	unsigned int bmsr, bmcr, lpa, mac_cfg;	unsigned int speed, duplex;	/* Link status is latched, so read twice to get current value */	read_phy(regs, 0, MII_BMSR, &bmsr);	read_phy(regs, 0, MII_BMSR, &bmsr);	if (!(bmsr & BMSR_LSTATUS)) return -1;			/* no link */	read_phy(regs, 0, MII_BMCR, &bmcr);	if (bmcr & BMCR_ANENABLE) {				/* AutoNegotiation is enabled */		if (!(bmsr & BMSR_ANEGCOMPLETE)) return -2;	/* auto-negotitation in progress */		read_phy(regs, 0, 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 = regs->EMAC_CFG & ~(AT91C_EMAC_SPD | AT91C_EMAC_FD);	if (speed == SPEED_100) {		if (duplex == DUPLEX_FULL)		/* 100 Full Duplex */			regs->EMAC_CFG = mac_cfg | AT91C_EMAC_SPD | AT91C_EMAC_FD;		else					/* 100 Half Duplex */			regs->EMAC_CFG = mac_cfg | AT91C_EMAC_SPD;	} else {		if (duplex == DUPLEX_FULL)		/* 10 Full Duplex */			regs->EMAC_CFG = mac_cfg | AT91C_EMAC_FD;		else					/* 10 Half Duplex */			regs->EMAC_CFG = mac_cfg;	}	return 0;}/* * Handle interrupts from the PHY */static void at91ether_phy_interrupt(void *dev_id){	struct net_device *dev = (struct net_device *) dev_id;	struct at91_private *lp = (struct at91_private *) dev->priv;	AT91PS_EMAC emac = (AT91PS_EMAC) dev->base_addr;	int status;	unsigned int phy;	enable_mdi(emac);	if (lp->phy_type == MII_DM9161_ID)		read_phy(emac, 0, MII_DSINTR_REG, &phy);	/* ack interrupt in Davicom PHY */	else if (lp->phy_type == MII_LXT971A_ID)		read_phy(emac, 0, MII_ISINTS_REG, &phy);	/* ack interrupt in Intel PHY */			status = AT91_SYS->PIOC_ISR;		/* acknowledge interrupt in PIO */	status = update_linkspeed(dev, emac);#if 0	if (status == -1) {			/* link is down */		netif_carrier_off(dev);	} else if (status == -2) {		/* auto-negotiation in progress */		/* Do nothing - another interrupt generated when negotiation complete */	} else {				/* link is operational */		netif_carrier_on(dev);	}#endif	disable_mdi(emac);}/* * Initialize and enable the PHY interrupt when link-state changes */static void enable_phyirq(struct net_device *dev, AT91PS_EMAC regs){	struct at91_private *lp = (struct at91_private *) dev->priv;	unsigned int dsintr, status;	if (lp->phy_type == MII_RTL8201BL_ID) return;	/* TODO: Check error code.  Really need a generic PIO (interrupt)*/		request_irq(AT91C_ID_PIOC, at91ether_phy_interrupt, 0, dev->name, dev);	status = AT91_SYS->PIOC_ISR;		/* clear any pending PIO interrupts */#ifdef CONFIG_MACH_CSB337	AT91_SYS->PIOC_IER = AT91C_PIO_PC2;	/* Enable interrupt */#else	AT91_SYS->PIOC_IER = AT91C_PIO_PC4;	/* Enable interrupt */#endif	spin_lock_irq(&lp->lock);	enable_mdi(regs);			if (lp->phy_type == MII_DM9161_ID) {		/* for Davicom PHY */		read_phy(regs, 0, MII_DSINTR_REG, &dsintr);		dsintr = dsintr & ~0xf00;		/* clear bits 8..11 */		write_phy(regs, 0, MII_DSINTR_REG, dsintr);	}	else if (lp->phy_type == MII_LXT971A_ID) {	/* for Intel PHY */		read_phy(regs, 0, MII_ISINTE_REG, &dsintr);		dsintr = dsintr | 0xf2;			/* set bits 1, 4..7 */		write_phy(regs, 0, MII_ISINTE_REG, dsintr);	}			disable_mdi(regs);	spin_unlock_irq(&lp->lock);}/* * Disable the PHY interrupt */static void disable_phyirq(struct net_device *dev, AT91PS_EMAC regs){	struct at91_private *lp = (struct at91_private *) dev->priv;	unsigned int dsintr;	if (lp->phy_type == MII_RTL8201BL_ID) return;	spin_lock_irq(&lp->lock);	enable_mdi(regs);	if (lp->phy_type == MII_DM9161_ID) {		/* for Davicom PHY */		read_phy(regs, 0, MII_DSINTR_REG, &dsintr);		dsintr = dsintr | 0xf00;			/* set bits 8..11 */		write_phy(regs, 0, MII_DSINTR_REG, dsintr);	}	else if (lp->phy_type == MII_LXT971A_ID) {	/* for Intel PHY */		read_phy(regs, 0, MII_ISINTE_REG, &dsintr);		dsintr = dsintr & ~0xf2;			/* clear bits 1, 4..7 */		write_phy(regs, 0, MII_ISINTE_REG, dsintr);	}	disable_mdi(regs);	spin_unlock_irq(&lp->lock);#ifdef CONFIG_MACH_CSB337	AT91_SYS->PIOC_IDR = AT91C_PIO_PC2;		/* Disable interrupt */#else	AT91_SYS->PIOC_IDR = AT91C_PIO_PC4;		/* Disable interrupt */#endif	free_irq(AT91C_ID_PIOC, dev);			/* Free interrupt handler */}/* * Perform a software reset of the PHY. */static void reset_phy(struct net_device *dev, AT91PS_EMAC regs){	struct at91_private *lp = (struct at91_private *) dev->priv;	unsigned int bmcr;	spin_lock_irq(&lp->lock);	enable_mdi(regs);	/* Perform PHY reset */	write_phy(regs, 0, MII_BMCR, BMCR_RESET);	/* Wait until PHY reset is complete */	do {		read_phy(regs, 0, MII_BMCR, &bmcr);	} while (!(bmcr && BMCR_RESET));	disable_mdi(regs);	spin_unlock_irq(&lp->lock);}/* ......................... ADDRESS MANAGEMENT ........................ *//* * Set the ethernet MAC address in dev->dev_addr */static void get_mac_address(struct net_device *dev) {	AT91PS_EMAC regs = (AT91PS_EMAC) dev->base_addr;	char addr[6];	unsigned int hi, lo;	/* Check if bootloader set address in Specific-Address 1 */	hi = regs->EMAC_SA1H;	lo = regs->EMAC_SA1L;#ifdef CONFIG_MACH_CSB337	addr[5] = (lo & 0xff);			/* The CSB337 bootloader stores the MAC the wrong-way around */	addr[4] = (lo & 0xff00) >> 8;	addr[3] = (lo & 0xff0000) >> 16;	addr[2] = (lo & 0xff000000) >> 24;	addr[1] = (hi & 0xff);	addr[0] = (hi & 0xff00) >> 8;#else	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;#endif	if (is_valid_ether_addr(addr)) {		memcpy(dev->dev_addr, &addr, 6);		return;	}	/* Check if bootloader set address in Specific-Address 2 */	hi = regs->EMAC_SA2H;	lo = regs->EMAC_SA2L;#ifdef CONFIG_MACH_CSB337	addr[5] = (lo & 0xff);			/* The CSB337 bootloader stores the MAC the wrong-way around */	addr[4] = (lo & 0xff00) >> 8;	addr[3] = (lo & 0xff0000) >> 16;	addr[2] = (lo & 0xff000000) >> 24;	addr[1] = (hi & 0xff);	addr[0] = (hi & 0xff00) >> 8;#else	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;#endif	if (is_valid_ether_addr(addr)) {		memcpy(dev->dev_addr, &addr, 6);		return;	}}/* * Program the hardware MAC address from dev->dev_addr. */static int update_mac_address(struct net_device *dev){	AT91PS_EMAC regs = (AT91PS_EMAC) dev->base_addr;	regs->EMAC_SA1L = (dev->dev_addr[3] << 24) | (dev->dev_addr[2] << 16) | (dev->dev_addr[1] << 8) | (dev->dev_addr[0]);	regs->EMAC_SA1H = (dev->dev_addr[5] << 8) | (dev->dev_addr[4]);	regs->EMAC_SA2L = 0;	regs->EMAC_SA2H = 0;	return 0;}/* * Store the new hardware address in dev->dev_addr, and update the MAC. */static int set_mac_address(struct net_device *dev, void* addr){	struct sockaddr *address = addr;	if (!is_valid_ether_addr(address->sa_data))		return -EADDRNOTAVAIL;	memcpy(dev->dev_addr, address->sa_data, ETH_ALEN);	update_mac_address(dev);	printk("%s: Setting MAC address to %02x:%02x:%02x:%02x:%02x:%02x\n", dev->name,		dev->dev_addr[0], dev->dev_addr[1], dev->dev_addr[2],		dev->dev_addr[3], dev->dev_addr[4], dev->dev_addr[5]);	return 0;}/* * Add multicast addresses to the internal multicast-hash table. */static void at91ether_sethashtable(struct net_device *dev, AT91PS_EMAC regs){	struct dev_mc_list *curr;	unsigned char mc_filter[2];	unsigned int i, bitnr;	mc_filter[0] = mc_filter[1] = 0;	curr = dev->mc_list;	for (i = 0; i < dev->mc_count; i++, curr = curr->next) {		if (!curr) break;	/* unexpected end of list */		bitnr = ether_crc(ETH_ALEN, curr->dmi_addr) >> 26;		mc_filter[bitnr >> 5] |= 1 << (bitnr & 31);	}	regs->EMAC_HSH = mc_filter[1];	regs->EMAC_HSL = mc_filter[0];}/* * Enable/Disable promiscuous and multicast modes. */static void at91ether_set_rx_mode(struct net_device *dev){	AT91PS_EMAC regs = (AT91PS_EMAC) dev->base_addr;	if (dev->flags & IFF_PROMISC) {			/* Enable promiscuous mode */		regs->EMAC_CFG |= AT91C_EMAC_CAF;	} else if (dev->flags & (~IFF_PROMISC)) {	/* Disable promiscuous mode */		regs->EMAC_CFG &= ~AT91C_EMAC_CAF;	}	if (dev->flags & IFF_ALLMULTI) {		/* Enable all multicast mode */		regs->EMAC_HSH = -1;		regs->EMAC_HSL = -1;		regs->EMAC_CFG |= AT91C_EMAC_MTI;	} else if (dev->mc_count > 0) {			/* Enable specific multicasts */		at91ether_sethashtable(dev, regs);		regs->EMAC_CFG |= AT91C_EMAC_MTI;	} else if (dev->flags & (~IFF_ALLMULTI)) {	/* Disable all multicast mode */		regs->EMAC_HSH = 0;		regs->EMAC_HSL = 0;		regs->EMAC_CFG &= ~AT91C_EMAC_MTI;	}}/* ............................... IOCTL ............................... */static int mdio_read(struct net_device *dev, int phy_id, int location){	AT91PS_EMAC regs = (AT91PS_EMAC) dev->base_addr;	unsigned int value;	read_phy(regs, phy_id, location, &value);	return value;}static void mdio_write(struct net_device *dev, int phy_id, int location, int value)

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -