fec_mxc: driver for FEC ethernet controller on i.MX27
Signed-off-by: Ilya Yanok <yanok@emcraft.com> Signed-off-by: Ben Warren <biggerbadderben@gmail.com>
This commit is contained in:
		
							parent
							
								
									0544c63681
								
							
						
					
					
						commit
						0b23fb368d
					
				|  | @ -20,6 +20,7 @@ | |||
| 
 | ||||
| #include <common.h> | ||||
| #include <div64.h> | ||||
| #include <netdev.h> | ||||
| #include <asm/io.h> | ||||
| #include <asm/arch/imx-regs.h> | ||||
| 
 | ||||
|  | @ -159,6 +160,15 @@ int print_cpuinfo (void) | |||
| } | ||||
| #endif | ||||
| 
 | ||||
| int cpu_eth_init(bd_t *bis) | ||||
| { | ||||
| #if defined(CONFIG_FEC_MXC) | ||||
| 	return fecmxc_initialize(bis); | ||||
| #else | ||||
| 	return 0; | ||||
| #endif | ||||
| } | ||||
| 
 | ||||
| void imx_gpio_mode(int gpio_mode) | ||||
| { | ||||
| 	struct gpio_regs *regs = (struct gpio_regs *)IMX_GPIO_BASE; | ||||
|  |  | |||
|  | @ -37,6 +37,7 @@ COBJS-$(CONFIG_DNET) += dnet.o | |||
| COBJS-$(CONFIG_E1000) += e1000.o | ||||
| COBJS-$(CONFIG_EEPRO100) += eepro100.o | ||||
| COBJS-$(CONFIG_ENC28J60) += enc28j60.o | ||||
| COBJS-$(CONFIG_FEC_MXC) += fec_mxc.o | ||||
| COBJS-$(CONFIG_FSLDMAFEC) += fsl_mcdmafec.o mcfmii.o | ||||
| COBJS-$(CONFIG_GRETH) += greth.o | ||||
| COBJS-$(CONFIG_INCA_IP_SWITCH) += inca-ip_sw.o | ||||
|  |  | |||
|  | @ -0,0 +1,742 @@ | |||
| /*
 | ||||
|  * (C) Copyright 2009 Ilya Yanok, Emcraft Systems Ltd <yanok@emcraft.com> | ||||
|  * (C) Copyright 2008,2009 Eric Jarrige <eric.jarrige@armadeus.org> | ||||
|  * (C) Copyright 2008 Armadeus Systems nc | ||||
|  * (C) Copyright 2007 Pengutronix, Sascha Hauer <s.hauer@pengutronix.de> | ||||
|  * (C) Copyright 2007 Pengutronix, Juergen Beisert <j.beisert@pengutronix.de> | ||||
|  * | ||||
|  * 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 | ||||
|  */ | ||||
| 
 | ||||
| #include <common.h> | ||||
| #include <malloc.h> | ||||
| #include <net.h> | ||||
| #include <miiphy.h> | ||||
| #include "fec_mxc.h" | ||||
| 
 | ||||
| #include <asm/arch/clock.h> | ||||
| #include <asm/arch/imx-regs.h> | ||||
| #include <asm/io.h> | ||||
| #include <asm/errno.h> | ||||
| 
 | ||||
| DECLARE_GLOBAL_DATA_PTR; | ||||
| 
 | ||||
| #ifndef CONFIG_MII | ||||
| #error "CONFIG_MII has to be defined!" | ||||
| #endif | ||||
| 
 | ||||
| #undef DEBUG | ||||
| 
 | ||||
| struct nbuf { | ||||
| 	uint8_t data[1500];	/**< actual data */ | ||||
| 	int length;		/**< actual length */ | ||||
| 	int used;		/**< buffer in use or not */ | ||||
| 	uint8_t head[16];	/**< MAC header(6 + 6 + 2) + 2(aligned) */ | ||||
| }; | ||||
| 
 | ||||
| struct fec_priv gfec = { | ||||
| 	.eth       = (struct ethernet_regs *)IMX_FEC_BASE, | ||||
| 	.xcv_type  = MII100, | ||||
| 	.rbd_base  = NULL, | ||||
| 	.rbd_index = 0, | ||||
| 	.tbd_base  = NULL, | ||||
| 	.tbd_index = 0, | ||||
| 	.bd        = NULL, | ||||
| }; | ||||
| 
 | ||||
| /*
 | ||||
|  * MII-interface related functions | ||||
|  */ | ||||
| static int fec_miiphy_read(char *dev, uint8_t phyAddr, uint8_t regAddr, | ||||
| 		uint16_t *retVal) | ||||
| { | ||||
| 	struct eth_device *edev = eth_get_dev_by_name(dev); | ||||
| 	struct fec_priv *fec = (struct fec_priv *)edev->priv; | ||||
| 
 | ||||
| 	uint32_t reg;		/* convenient holder for the PHY register */ | ||||
| 	uint32_t phy;		/* convenient holder for the PHY */ | ||||
| 	uint32_t start; | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * reading from any PHY's register is done by properly | ||||
| 	 * programming the FEC's MII data register. | ||||
| 	 */ | ||||
| 	writel(FEC_IEVENT_MII, &fec->eth->ievent); | ||||
| 	reg = regAddr << FEC_MII_DATA_RA_SHIFT; | ||||
| 	phy = phyAddr << FEC_MII_DATA_PA_SHIFT; | ||||
| 
 | ||||
| 	writel(FEC_MII_DATA_ST | FEC_MII_DATA_OP_RD | FEC_MII_DATA_TA | | ||||
| 			phy | reg, &fec->eth->mii_data); | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * wait for the related interrupt | ||||
| 	 */ | ||||
| 	start = get_timer_masked(); | ||||
| 	while (!(readl(&fec->eth->ievent) & FEC_IEVENT_MII)) { | ||||
| 		if (get_timer(start) > (CONFIG_SYS_HZ / 1000)) { | ||||
| 			printf("Read MDIO failed...\n"); | ||||
| 			return -1; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * clear mii interrupt bit | ||||
| 	 */ | ||||
| 	writel(FEC_IEVENT_MII, &fec->eth->ievent); | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * it's now safe to read the PHY's register | ||||
| 	 */ | ||||
| 	*retVal = readl(&fec->eth->mii_data); | ||||
| 	debug("fec_miiphy_read: phy: %02x reg:%02x val:%#x\n", phyAddr, | ||||
| 			regAddr, *retVal); | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static int fec_miiphy_write(char *dev, uint8_t phyAddr, uint8_t regAddr, | ||||
| 		uint16_t data) | ||||
| { | ||||
| 	struct eth_device *edev = eth_get_dev_by_name(dev); | ||||
| 	struct fec_priv *fec = (struct fec_priv *)edev->priv; | ||||
| 
 | ||||
| 	uint32_t reg;		/* convenient holder for the PHY register */ | ||||
| 	uint32_t phy;		/* convenient holder for the PHY */ | ||||
| 	uint32_t start; | ||||
| 
 | ||||
| 	reg = regAddr << FEC_MII_DATA_RA_SHIFT; | ||||
| 	phy = phyAddr << FEC_MII_DATA_PA_SHIFT; | ||||
| 
 | ||||
| 	writel(FEC_MII_DATA_ST | FEC_MII_DATA_OP_WR | | ||||
| 		FEC_MII_DATA_TA | phy | reg | data, &fec->eth->mii_data); | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * wait for the MII interrupt | ||||
| 	 */ | ||||
| 	start = get_timer_masked(); | ||||
| 	while (!(readl(&fec->eth->ievent) & FEC_IEVENT_MII)) { | ||||
| 		if (get_timer(start) > (CONFIG_SYS_HZ / 1000)) { | ||||
| 			printf("Write MDIO failed...\n"); | ||||
| 			return -1; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * clear MII interrupt bit | ||||
| 	 */ | ||||
| 	writel(FEC_IEVENT_MII, &fec->eth->ievent); | ||||
| 	debug("fec_miiphy_write: phy: %02x reg:%02x val:%#x\n", phyAddr, | ||||
| 			regAddr, data); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static int miiphy_restart_aneg(struct eth_device *dev) | ||||
| { | ||||
| 	/*
 | ||||
| 	 * Wake up from sleep if necessary | ||||
| 	 * Reset PHY, then delay 300ns | ||||
| 	 */ | ||||
| 	miiphy_write(dev->name, CONFIG_FEC_MXC_PHYADDR, PHY_MIPGSR, 0x00FF); | ||||
| 	miiphy_write(dev->name, CONFIG_FEC_MXC_PHYADDR, PHY_BMCR, | ||||
| 			PHY_BMCR_RESET); | ||||
| 	udelay(1000); | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * Set the auto-negotiation advertisement register bits | ||||
| 	 */ | ||||
| 	miiphy_write(dev->name, CONFIG_FEC_MXC_PHYADDR, PHY_ANAR, 0x1e0); | ||||
| 	miiphy_write(dev->name, CONFIG_FEC_MXC_PHYADDR, PHY_BMCR, | ||||
| 			PHY_BMCR_AUTON | PHY_BMCR_RST_NEG); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static int miiphy_wait_aneg(struct eth_device *dev) | ||||
| { | ||||
| 	uint32_t start; | ||||
| 	uint16_t status; | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * Wait for AN completion | ||||
| 	 */ | ||||
| 	start = get_timer_masked(); | ||||
| 	do { | ||||
| 		if (get_timer(start) > (CONFIG_SYS_HZ * 5)) { | ||||
| 			printf("%s: Autonegotiation timeout\n", dev->name); | ||||
| 			return -1; | ||||
| 		} | ||||
| 
 | ||||
| 		if (miiphy_read(dev->name, CONFIG_FEC_MXC_PHYADDR, | ||||
| 					PHY_BMSR, &status)) { | ||||
| 			printf("%s: Autonegotiation failed. status: 0x%04x\n", | ||||
| 					dev->name, status); | ||||
| 			return -1; | ||||
| 		} | ||||
| 	} while (!(status & PHY_BMSR_LS)); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| static int fec_rx_task_enable(struct fec_priv *fec) | ||||
| { | ||||
| 	writel(1 << 24, &fec->eth->r_des_active); | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static int fec_rx_task_disable(struct fec_priv *fec) | ||||
| { | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static int fec_tx_task_enable(struct fec_priv *fec) | ||||
| { | ||||
| 	writel(1 << 24, &fec->eth->x_des_active); | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static int fec_tx_task_disable(struct fec_priv *fec) | ||||
| { | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * Initialize receive task's buffer descriptors | ||||
|  * @param[in] fec all we know about the device yet | ||||
|  * @param[in] count receive buffer count to be allocated | ||||
|  * @param[in] size size of each receive buffer | ||||
|  * @return 0 on success | ||||
|  * | ||||
|  * For this task we need additional memory for the data buffers. And each | ||||
|  * data buffer requires some alignment. Thy must be aligned to a specific | ||||
|  * boundary each (DB_DATA_ALIGNMENT). | ||||
|  */ | ||||
| static int fec_rbd_init(struct fec_priv *fec, int count, int size) | ||||
| { | ||||
| 	int ix; | ||||
| 	uint32_t p = 0; | ||||
| 
 | ||||
| 	/* reserve data memory and consider alignment */ | ||||
| 	fec->rdb_ptr = malloc(size * count + DB_DATA_ALIGNMENT); | ||||
| 	p = (uint32_t)fec->rdb_ptr; | ||||
| 	if (!p) { | ||||
| 		puts("fec_imx27: not enough malloc memory!\n"); | ||||
| 		return -ENOMEM; | ||||
| 	} | ||||
| 	memset((void *)p, 0, size * count + DB_DATA_ALIGNMENT); | ||||
| 	p += DB_DATA_ALIGNMENT-1; | ||||
| 	p &= ~(DB_DATA_ALIGNMENT-1); | ||||
| 
 | ||||
| 	for (ix = 0; ix < count; ix++) { | ||||
| 		writel(p, &fec->rbd_base[ix].data_pointer); | ||||
| 		p += size; | ||||
| 		writew(FEC_RBD_EMPTY, &fec->rbd_base[ix].status); | ||||
| 		writew(0, &fec->rbd_base[ix].data_length); | ||||
| 	} | ||||
| 	/*
 | ||||
| 	 * mark the last RBD to close the ring | ||||
| 	 */ | ||||
| 	writew(FEC_RBD_WRAP | FEC_RBD_EMPTY, &fec->rbd_base[ix - 1].status); | ||||
| 	fec->rbd_index = 0; | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * Initialize transmit task's buffer descriptors | ||||
|  * @param[in] fec all we know about the device yet | ||||
|  * | ||||
|  * Transmit buffers are created externally. We only have to init the BDs here.\n | ||||
|  * Note: There is a race condition in the hardware. When only one BD is in | ||||
|  * use it must be marked with the WRAP bit to use it for every transmitt. | ||||
|  * This bit in combination with the READY bit results into double transmit | ||||
|  * of each data buffer. It seems the state machine checks READY earlier then | ||||
|  * resetting it after the first transfer. | ||||
|  * Using two BDs solves this issue. | ||||
|  */ | ||||
| static void fec_tbd_init(struct fec_priv *fec) | ||||
| { | ||||
| 	writew(0x0000, &fec->tbd_base[0].status); | ||||
| 	writew(FEC_TBD_WRAP, &fec->tbd_base[1].status); | ||||
| 	fec->tbd_index = 0; | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * Mark the given read buffer descriptor as free | ||||
|  * @param[in] last 1 if this is the last buffer descriptor in the chain, else 0 | ||||
|  * @param[in] pRbd buffer descriptor to mark free again | ||||
|  */ | ||||
| static void fec_rbd_clean(int last, struct fec_bd *pRbd) | ||||
| { | ||||
| 	/*
 | ||||
| 	 * Reset buffer descriptor as empty | ||||
| 	 */ | ||||
| 	if (last) | ||||
| 		writew(FEC_RBD_WRAP | FEC_RBD_EMPTY, &pRbd->status); | ||||
| 	else | ||||
| 		writew(FEC_RBD_EMPTY, &pRbd->status); | ||||
| 	/*
 | ||||
| 	 * no data in it | ||||
| 	 */ | ||||
| 	writew(0, &pRbd->data_length); | ||||
| } | ||||
| 
 | ||||
| static int fec_get_hwaddr(struct eth_device *dev, unsigned char *mac) | ||||
| { | ||||
| 	struct iim_regs *iim = (struct iim_regs *)IMX_IIM_BASE; | ||||
| 	int i; | ||||
| 
 | ||||
| 	for (i = 0; i < 6; i++) | ||||
| 		mac[6-1-i] = readl(&iim->iim_bank_area0[IIM0_MAC + i]); | ||||
| 
 | ||||
| 	return is_valid_ether_addr(mac); | ||||
| } | ||||
| 
 | ||||
| static int fec_set_hwaddr(struct eth_device *dev, unsigned char *mac) | ||||
| { | ||||
| 	struct fec_priv *fec = (struct fec_priv *)dev->priv; | ||||
| 
 | ||||
| 	writel(0, &fec->eth->iaddr1); | ||||
| 	writel(0, &fec->eth->iaddr2); | ||||
| 	writel(0, &fec->eth->gaddr1); | ||||
| 	writel(0, &fec->eth->gaddr2); | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * Set physical address | ||||
| 	 */ | ||||
| 	writel((mac[0] << 24) + (mac[1] << 16) + (mac[2] << 8) + mac[3], | ||||
| 			&fec->eth->paddr1); | ||||
| 	writel((mac[4] << 24) + (mac[5] << 16) + 0x8808, &fec->eth->paddr2); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * Start the FEC engine | ||||
|  * @param[in] dev Our device to handle | ||||
|  */ | ||||
| static int fec_open(struct eth_device *edev) | ||||
| { | ||||
| 	struct fec_priv *fec = (struct fec_priv *)edev->priv; | ||||
| 
 | ||||
| 	debug("fec_open: fec_open(dev)\n"); | ||||
| 	/* full-duplex, heartbeat disabled */ | ||||
| 	writel(1 << 2, &fec->eth->x_cntrl); | ||||
| 	fec->rbd_index = 0; | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * Enable FEC-Lite controller | ||||
| 	 */ | ||||
| 	writel(FEC_ECNTRL_ETHER_EN, &fec->eth->ecntrl); | ||||
| 
 | ||||
| 	miiphy_wait_aneg(edev); | ||||
| 	miiphy_speed(edev->name, 0); | ||||
| 	miiphy_duplex(edev->name, 0); | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * Enable SmartDMA receive task | ||||
| 	 */ | ||||
| 	fec_rx_task_enable(fec); | ||||
| 
 | ||||
| 	udelay(100000); | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static int fec_init(struct eth_device *dev, bd_t* bd) | ||||
| { | ||||
| 	uint32_t base; | ||||
| 	struct fec_priv *fec = (struct fec_priv *)dev->priv; | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * reserve memory for both buffer descriptor chains at once | ||||
| 	 * Datasheet forces the startaddress of each chain is 16 byte | ||||
| 	 * aligned | ||||
| 	 */ | ||||
| 	fec->base_ptr = malloc((2 + FEC_RBD_NUM) * | ||||
| 			sizeof(struct fec_bd) + DB_ALIGNMENT); | ||||
| 	base = (uint32_t)fec->base_ptr; | ||||
| 	if (!base) { | ||||
| 		puts("fec_imx27: not enough malloc memory!\n"); | ||||
| 		return -ENOMEM; | ||||
| 	} | ||||
| 	memset((void *)base, 0, (2 + FEC_RBD_NUM) * | ||||
| 			sizeof(struct fec_bd) + DB_ALIGNMENT); | ||||
| 	base += (DB_ALIGNMENT-1); | ||||
| 	base &= ~(DB_ALIGNMENT-1); | ||||
| 
 | ||||
| 	fec->rbd_base = (struct fec_bd *)base; | ||||
| 
 | ||||
| 	base += FEC_RBD_NUM * sizeof(struct fec_bd); | ||||
| 
 | ||||
| 	fec->tbd_base = (struct fec_bd *)base; | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * Set interrupt mask register | ||||
| 	 */ | ||||
| 	writel(0x00000000, &fec->eth->imask); | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * Clear FEC-Lite interrupt event register(IEVENT) | ||||
| 	 */ | ||||
| 	writel(0xffffffff, &fec->eth->ievent); | ||||
| 
 | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * Set FEC-Lite receive control register(R_CNTRL): | ||||
| 	 */ | ||||
| 	if (fec->xcv_type == SEVENWIRE) { | ||||
| 		/*
 | ||||
| 		 * Frame length=1518; 7-wire mode | ||||
| 		 */ | ||||
| 		writel(0x05ee0020, &fec->eth->r_cntrl);	/* FIXME 0x05ee0000 */ | ||||
| 	} else { | ||||
| 		/*
 | ||||
| 		 * Frame length=1518; MII mode; | ||||
| 		 */ | ||||
| 		writel(0x05ee0024, &fec->eth->r_cntrl);	/* FIXME 0x05ee0004 */ | ||||
| 		/*
 | ||||
| 		 * Set MII_SPEED = (1/(mii_speed * 2)) * System Clock | ||||
| 		 * and do not drop the Preamble. | ||||
| 		 */ | ||||
| 		writel((((imx_get_ahbclk() / 1000000) + 2) / 5) << 1, | ||||
| 				&fec->eth->mii_speed); | ||||
| 		debug("fec_init: mii_speed %#lx\n", | ||||
| 				(((imx_get_ahbclk() / 1000000) + 2) / 5) << 1); | ||||
| 	} | ||||
| 	/*
 | ||||
| 	 * Set Opcode/Pause Duration Register | ||||
| 	 */ | ||||
| 	writel(0x00010020, &fec->eth->op_pause);	/* FIXME 0xffff0020; */ | ||||
| 	writel(0x2, &fec->eth->x_wmrk); | ||||
| 	/*
 | ||||
| 	 * Set multicast address filter | ||||
| 	 */ | ||||
| 	writel(0x00000000, &fec->eth->gaddr1); | ||||
| 	writel(0x00000000, &fec->eth->gaddr2); | ||||
| 
 | ||||
| 
 | ||||
| 	/* clear MIB RAM */ | ||||
| 	long *mib_ptr = (long *)(IMX_FEC_BASE + 0x200); | ||||
| 	while (mib_ptr <= (long *)(IMX_FEC_BASE + 0x2FC)) | ||||
| 		*mib_ptr++ = 0; | ||||
| 
 | ||||
| 	/* FIFO receive start register */ | ||||
| 	writel(0x520, &fec->eth->r_fstart); | ||||
| 
 | ||||
| 	/* size and address of each buffer */ | ||||
| 	writel(FEC_MAX_PKT_SIZE, &fec->eth->emrbr); | ||||
| 	writel((uint32_t)fec->tbd_base, &fec->eth->etdsr); | ||||
| 	writel((uint32_t)fec->rbd_base, &fec->eth->erdsr); | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * Initialize RxBD/TxBD rings | ||||
| 	 */ | ||||
| 	if (fec_rbd_init(fec, FEC_RBD_NUM, FEC_MAX_PKT_SIZE) < 0) { | ||||
| 		free(fec->base_ptr); | ||||
| 		return -ENOMEM; | ||||
| 	} | ||||
| 	fec_tbd_init(fec); | ||||
| 
 | ||||
| 
 | ||||
| 	if (fec->xcv_type != SEVENWIRE) | ||||
| 		miiphy_restart_aneg(dev); | ||||
| 
 | ||||
| 	fec_open(dev); | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * Halt the FEC engine | ||||
|  * @param[in] dev Our device to handle | ||||
|  */ | ||||
| static void fec_halt(struct eth_device *dev) | ||||
| { | ||||
| 	struct fec_priv *fec = &gfec; | ||||
| 	int counter = 0xffff; | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * issue graceful stop command to the FEC transmitter if necessary | ||||
| 	 */ | ||||
| 	writel(FEC_ECNTRL_RESET | readl(&fec->eth->x_cntrl), | ||||
| 			&fec->eth->x_cntrl); | ||||
| 
 | ||||
| 	debug("eth_halt: wait for stop regs\n"); | ||||
| 	/*
 | ||||
| 	 * wait for graceful stop to register | ||||
| 	 */ | ||||
| 	while ((counter--) && (!(readl(&fec->eth->ievent) & FEC_IEVENT_GRA))) | ||||
| 		;	/* FIXME ensure time */ | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * Disable SmartDMA tasks | ||||
| 	 */ | ||||
| 	fec_tx_task_disable(fec); | ||||
| 	fec_rx_task_disable(fec); | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * Disable the Ethernet Controller | ||||
| 	 * Note: this will also reset the BD index counter! | ||||
| 	 */ | ||||
| 	writel(0, &fec->eth->ecntrl); | ||||
| 	fec->rbd_index = 0; | ||||
| 	fec->tbd_index = 0; | ||||
| 	free(fec->rdb_ptr); | ||||
| 	free(fec->base_ptr); | ||||
| 	debug("eth_halt: done\n"); | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * Transmit one frame | ||||
|  * @param[in] dev Our ethernet device to handle | ||||
|  * @param[in] packet Pointer to the data to be transmitted | ||||
|  * @param[in] length Data count in bytes | ||||
|  * @return 0 on success | ||||
|  */ | ||||
| static int fec_send(struct eth_device *dev, volatile void* packet, int length) | ||||
| { | ||||
| 	unsigned int status; | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * This routine transmits one frame.  This routine only accepts | ||||
| 	 * 6-byte Ethernet addresses. | ||||
| 	 */ | ||||
| 	struct fec_priv *fec = (struct fec_priv *)dev->priv; | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * Check for valid length of data. | ||||
| 	 */ | ||||
| 	if ((length > 1500) || (length <= 0)) { | ||||
| 		printf("Payload (%d) to large!\n", length); | ||||
| 		return -1; | ||||
| 	} | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * Setup the transmit buffer | ||||
| 	 * Note: We are always using the first buffer for transmission, | ||||
| 	 * the second will be empty and only used to stop the DMA engine | ||||
| 	 */ | ||||
| 	writew(length, &fec->tbd_base[fec->tbd_index].data_length); | ||||
| 	writel((uint32_t)packet, &fec->tbd_base[fec->tbd_index].data_pointer); | ||||
| 	/*
 | ||||
| 	 * update BD's status now | ||||
| 	 * This block: | ||||
| 	 * - is always the last in a chain (means no chain) | ||||
| 	 * - should transmitt the CRC | ||||
| 	 * - might be the last BD in the list, so the address counter should | ||||
| 	 *   wrap (-> keep the WRAP flag) | ||||
| 	 */ | ||||
| 	status = readw(&fec->tbd_base[fec->tbd_index].status) & FEC_TBD_WRAP; | ||||
| 	status |= FEC_TBD_LAST | FEC_TBD_TC | FEC_TBD_READY; | ||||
| 	writew(status, &fec->tbd_base[fec->tbd_index].status); | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * Enable SmartDMA transmit task | ||||
| 	 */ | ||||
| 	fec_tx_task_enable(fec); | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * wait until frame is sent . | ||||
| 	 */ | ||||
| 	while (readw(&fec->tbd_base[fec->tbd_index].status) & FEC_TBD_READY) { | ||||
| 		/* FIXME: Timeout */ | ||||
| 	} | ||||
| 	debug("fec_send: status 0x%x index %d\n", | ||||
| 			readw(&fec->tbd_base[fec->tbd_index].status), | ||||
| 			fec->tbd_index); | ||||
| 	/* for next transmission use the other buffer */ | ||||
| 	if (fec->tbd_index) | ||||
| 		fec->tbd_index = 0; | ||||
| 	else | ||||
| 		fec->tbd_index = 1; | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * Pull one frame from the card | ||||
|  * @param[in] dev Our ethernet device to handle | ||||
|  * @return Length of packet read | ||||
|  */ | ||||
| static int fec_recv(struct eth_device *dev) | ||||
| { | ||||
| 	struct fec_priv *fec = (struct fec_priv *)dev->priv; | ||||
| 	struct fec_bd *rbd = &fec->rbd_base[fec->rbd_index]; | ||||
| 	unsigned long ievent; | ||||
| 	int frame_length, len = 0; | ||||
| 	struct nbuf *frame; | ||||
| 	uint16_t bd_status; | ||||
| 	uchar buff[FEC_MAX_PKT_SIZE]; | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * Check if any critical events have happened | ||||
| 	 */ | ||||
| 	ievent = readl(&fec->eth->ievent); | ||||
| 	writel(ievent, &fec->eth->ievent); | ||||
| 	debug("fec_recv: ievent 0x%x\n", ievent); | ||||
| 	if (ievent & FEC_IEVENT_BABR) { | ||||
| 		fec_halt(dev); | ||||
| 		fec_init(dev, fec->bd); | ||||
| 		printf("some error: 0x%08lx\n", ievent); | ||||
| 		return 0; | ||||
| 	} | ||||
| 	if (ievent & FEC_IEVENT_HBERR) { | ||||
| 		/* Heartbeat error */ | ||||
| 		writel(0x00000001 | readl(&fec->eth->x_cntrl), | ||||
| 				&fec->eth->x_cntrl); | ||||
| 	} | ||||
| 	if (ievent & FEC_IEVENT_GRA) { | ||||
| 		/* Graceful stop complete */ | ||||
| 		if (readl(&fec->eth->x_cntrl) & 0x00000001) { | ||||
| 			fec_halt(dev); | ||||
| 			writel(~0x00000001 & readl(&fec->eth->x_cntrl), | ||||
| 					&fec->eth->x_cntrl); | ||||
| 			fec_init(dev, fec->bd); | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * ensure reading the right buffer status | ||||
| 	 */ | ||||
| 	bd_status = readw(&rbd->status); | ||||
| 	debug("fec_recv: status 0x%x\n", bd_status); | ||||
| 
 | ||||
| 	if (!(bd_status & FEC_RBD_EMPTY)) { | ||||
| 		if ((bd_status & FEC_RBD_LAST) && !(bd_status & FEC_RBD_ERR) && | ||||
| 			((readw(&rbd->data_length) - 4) > 14)) { | ||||
| 			/*
 | ||||
| 			 * Get buffer address and size | ||||
| 			 */ | ||||
| 			frame = (struct nbuf *)readl(&rbd->data_pointer); | ||||
| 			frame_length = readw(&rbd->data_length) - 4; | ||||
| 			/*
 | ||||
| 			 *  Fill the buffer and pass it to upper layers | ||||
| 			 */ | ||||
| 			memcpy(buff, frame->data, frame_length); | ||||
| 			NetReceive(buff, frame_length); | ||||
| 			len = frame_length; | ||||
| 		} else { | ||||
| 			if (bd_status & FEC_RBD_ERR) | ||||
| 				printf("error frame: 0x%08lx 0x%08x\n", | ||||
| 						(ulong)rbd->data_pointer, | ||||
| 						bd_status); | ||||
| 		} | ||||
| 		/*
 | ||||
| 		 * free the current buffer, restart the engine | ||||
| 		 * and move forward to the next buffer | ||||
| 		 */ | ||||
| 		fec_rbd_clean(fec->rbd_index == (FEC_RBD_NUM - 1) ? 1 : 0, rbd); | ||||
| 		fec_rx_task_enable(fec); | ||||
| 		fec->rbd_index = (fec->rbd_index + 1) % FEC_RBD_NUM; | ||||
| 	} | ||||
| 	debug("fec_recv: stop\n"); | ||||
| 
 | ||||
| 	return len; | ||||
| } | ||||
| 
 | ||||
| static int fec_probe(bd_t *bd) | ||||
| { | ||||
| 	struct pll_regs *pll = (struct pll_regs *)IMX_PLL_BASE; | ||||
| 	struct eth_device *edev; | ||||
| 	struct fec_priv *fec = &gfec; | ||||
| 	unsigned char ethaddr_str[20]; | ||||
| 	unsigned char ethaddr[6]; | ||||
| 	char *tmp = getenv("ethaddr"); | ||||
| 	char *end; | ||||
| 
 | ||||
| 	/* enable FEC clock */ | ||||
| 	writel(readl(&pll->pccr1) | PCCR1_HCLK_FEC, &pll->pccr1); | ||||
| 	writel(readl(&pll->pccr0) | PCCR0_FEC_EN, &pll->pccr0); | ||||
| 
 | ||||
| 	/* create and fill edev struct */ | ||||
| 	edev = (struct eth_device *)malloc(sizeof(struct eth_device)); | ||||
| 	if (!edev) { | ||||
| 		puts("fec_imx27: not enough malloc memory!\n"); | ||||
| 		return -ENOMEM; | ||||
| 	} | ||||
| 	edev->priv = fec; | ||||
| 	edev->init = fec_init; | ||||
| 	edev->send = fec_send; | ||||
| 	edev->recv = fec_recv; | ||||
| 	edev->halt = fec_halt; | ||||
| 
 | ||||
| 	fec->eth = (struct ethernet_regs *)IMX_FEC_BASE; | ||||
| 	fec->bd = bd; | ||||
| 
 | ||||
| 	fec->xcv_type = MII100; | ||||
| 
 | ||||
| 	/* Reset chip. */ | ||||
| 	writel(FEC_ECNTRL_RESET, &fec->eth->ecntrl); | ||||
| 	while (readl(&fec->eth->ecntrl) & 1) | ||||
| 		udelay(10); | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * Set interrupt mask register | ||||
| 	 */ | ||||
| 	writel(0x00000000, &fec->eth->imask); | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * Clear FEC-Lite interrupt event register(IEVENT) | ||||
| 	 */ | ||||
| 	writel(0xffffffff, &fec->eth->ievent); | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * Set FEC-Lite receive control register(R_CNTRL): | ||||
| 	 */ | ||||
| 	/*
 | ||||
| 	 * Frame length=1518; MII mode; | ||||
| 	 */ | ||||
| 	writel(0x05ee0024, &fec->eth->r_cntrl);	/* FIXME 0x05ee0004 */ | ||||
| 	/*
 | ||||
| 	 * Set MII_SPEED = (1/(mii_speed * 2)) * System Clock | ||||
| 	 * and do not drop the Preamble. | ||||
| 	 */ | ||||
| 	writel((((imx_get_ahbclk() / 1000000) + 2) / 5) << 1, | ||||
| 			&fec->eth->mii_speed); | ||||
| 	debug("fec_init: mii_speed %#lx\n", | ||||
| 			(((imx_get_ahbclk() / 1000000) + 2) / 5) << 1); | ||||
| 
 | ||||
| 	sprintf(edev->name, "FEC_MXC"); | ||||
| 
 | ||||
| 	miiphy_register(edev->name, fec_miiphy_read, fec_miiphy_write); | ||||
| 
 | ||||
| 	eth_register(edev); | ||||
| 
 | ||||
| 	if ((NULL != tmp) && (12 <= strlen(tmp))) { | ||||
| 		int i; | ||||
| 		/* convert MAC from string to int */ | ||||
| 		for (i = 0; i < 6; i++) { | ||||
| 			ethaddr[i] = tmp ? simple_strtoul(tmp, &end, 16) : 0; | ||||
| 			if (tmp) | ||||
| 				tmp = (*end) ? end + 1 : end; | ||||
| 		} | ||||
| 	} else if (fec_get_hwaddr(edev, ethaddr) == 0) { | ||||
| 		printf("got MAC address from EEPROM: %pM\n", ethaddr); | ||||
| 		setenv("ethaddr", (char *)ethaddr_str); | ||||
| 	} | ||||
| 	memcpy(edev->enetaddr, ethaddr, 6); | ||||
| 	fec_set_hwaddr(edev, ethaddr); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| int fecmxc_initialize(bd_t *bd) | ||||
| { | ||||
| 	int lout = 1; | ||||
| 
 | ||||
| 	debug("eth_init: fec_probe(bd)\n"); | ||||
| 	lout = fec_probe(bd); | ||||
| 
 | ||||
| 	return lout; | ||||
| } | ||||
| 
 | ||||
|  | @ -0,0 +1,304 @@ | |||
| /*
 | ||||
|  * (C) Copyright 2009 Ilya Yanok, Emcraft Systems Ltd <yanok@emcraft.com> | ||||
|  * (C) Copyright 2008 Armadeus Systems, nc | ||||
|  * (C) Copyright 2008 Eric Jarrige <eric.jarrige@armadeus.org> | ||||
|  * (C) Copyright 2007 Pengutronix, Sascha Hauer <s.hauer@pengutronix.de> | ||||
|  * (C) Copyright 2007 Pengutronix, Juergen Beisert <j.beisert@pengutronix.de> | ||||
|  * | ||||
|  * (C) Copyright 2003 | ||||
|  * Wolfgang Denk, DENX Software Engineering, wd@denx.de. | ||||
|  * | ||||
|  * This file is based on mpc4200fec.h | ||||
|  * (C) Copyright Motorola, Inc., 2000 | ||||
|  * | ||||
|  * 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 | ||||
|  * | ||||
|  */ | ||||
| 
 | ||||
| 
 | ||||
| #ifndef __FEC_MXC_H | ||||
| #define __FEC_MXC_H | ||||
| 
 | ||||
| /**
 | ||||
|  * Layout description of the FEC | ||||
|  */ | ||||
| struct ethernet_regs { | ||||
| 
 | ||||
| /* [10:2]addr = 00 */ | ||||
| 
 | ||||
| /*  Control and status Registers (offset 000-1FF) */ | ||||
| 
 | ||||
| 	uint32_t res0[1];		/* MBAR_ETH + 0x000 */ | ||||
| 	uint32_t ievent;		/* MBAR_ETH + 0x004 */ | ||||
| 	uint32_t imask;			/* MBAR_ETH + 0x008 */ | ||||
| 
 | ||||
| 	uint32_t res1[1];		/* MBAR_ETH + 0x00C */ | ||||
| 	uint32_t r_des_active;		/* MBAR_ETH + 0x010 */ | ||||
| 	uint32_t x_des_active;		/* MBAR_ETH + 0x014 */ | ||||
| 	uint32_t res2[3];		/* MBAR_ETH + 0x018-20 */ | ||||
| 	uint32_t ecntrl;		/* MBAR_ETH + 0x024 */ | ||||
| 
 | ||||
| 	uint32_t res3[6];		/* MBAR_ETH + 0x028-03C */ | ||||
| 	uint32_t mii_data;		/* MBAR_ETH + 0x040 */ | ||||
| 	uint32_t mii_speed;		/* MBAR_ETH + 0x044 */ | ||||
| 	uint32_t res4[7];		/* MBAR_ETH + 0x048-60 */ | ||||
| 	uint32_t mib_control;		/* MBAR_ETH + 0x064 */ | ||||
| 
 | ||||
| 	uint32_t res5[7];		/* MBAR_ETH + 0x068-80 */ | ||||
| 	uint32_t r_cntrl;		/* MBAR_ETH + 0x084 */ | ||||
| 	uint32_t res6[15];		/* MBAR_ETH + 0x088-C0 */ | ||||
| 	uint32_t x_cntrl;		/* MBAR_ETH + 0x0C4 */ | ||||
| 	uint32_t res7[7];		/* MBAR_ETH + 0x0C8-E0 */ | ||||
| 	uint32_t paddr1;		/* MBAR_ETH + 0x0E4 */ | ||||
| 	uint32_t paddr2;		/* MBAR_ETH + 0x0E8 */ | ||||
| 	uint32_t op_pause;		/* MBAR_ETH + 0x0EC */ | ||||
| 
 | ||||
| 	uint32_t res8[10];		/* MBAR_ETH + 0x0F0-114 */ | ||||
| 	uint32_t iaddr1;		/* MBAR_ETH + 0x118 */ | ||||
| 	uint32_t iaddr2;		/* MBAR_ETH + 0x11C */ | ||||
| 	uint32_t gaddr1;		/* MBAR_ETH + 0x120 */ | ||||
| 	uint32_t gaddr2;		/* MBAR_ETH + 0x124 */ | ||||
| 	uint32_t res9[7];		/* MBAR_ETH + 0x128-140 */ | ||||
| 
 | ||||
| 	uint32_t x_wmrk;		/* MBAR_ETH + 0x144 */ | ||||
| 	uint32_t res10[1];		/* MBAR_ETH + 0x148 */ | ||||
| 	uint32_t r_bound;		/* MBAR_ETH + 0x14C */ | ||||
| 	uint32_t r_fstart;		/* MBAR_ETH + 0x150 */ | ||||
| 	uint32_t res11[11];		/* MBAR_ETH + 0x154-17C */ | ||||
| 	uint32_t erdsr;			/* MBAR_ETH + 0x180 */ | ||||
| 	uint32_t etdsr;			/* MBAR_ETH + 0x184 */ | ||||
| 	uint32_t emrbr;			/* MBAR_ETH + 0x188 */ | ||||
| 	uint32_t res12[29];		/* MBAR_ETH + 0x18C-1FC */ | ||||
| 
 | ||||
| /*  MIB COUNTERS (Offset 200-2FF) */ | ||||
| 
 | ||||
| 	uint32_t rmon_t_drop;		/* MBAR_ETH + 0x200 */ | ||||
| 	uint32_t rmon_t_packets;	/* MBAR_ETH + 0x204 */ | ||||
| 	uint32_t rmon_t_bc_pkt;		/* MBAR_ETH + 0x208 */ | ||||
| 	uint32_t rmon_t_mc_pkt;		/* MBAR_ETH + 0x20C */ | ||||
| 	uint32_t rmon_t_crc_align;	/* MBAR_ETH + 0x210 */ | ||||
| 	uint32_t rmon_t_undersize;	/* MBAR_ETH + 0x214 */ | ||||
| 	uint32_t rmon_t_oversize;	/* MBAR_ETH + 0x218 */ | ||||
| 	uint32_t rmon_t_frag;		/* MBAR_ETH + 0x21C */ | ||||
| 	uint32_t rmon_t_jab;		/* MBAR_ETH + 0x220 */ | ||||
| 	uint32_t rmon_t_col;		/* MBAR_ETH + 0x224 */ | ||||
| 	uint32_t rmon_t_p64;		/* MBAR_ETH + 0x228 */ | ||||
| 	uint32_t rmon_t_p65to127;	/* MBAR_ETH + 0x22C */ | ||||
| 	uint32_t rmon_t_p128to255;	/* MBAR_ETH + 0x230 */ | ||||
| 	uint32_t rmon_t_p256to511;	/* MBAR_ETH + 0x234 */ | ||||
| 	uint32_t rmon_t_p512to1023;	/* MBAR_ETH + 0x238 */ | ||||
| 	uint32_t rmon_t_p1024to2047;	/* MBAR_ETH + 0x23C */ | ||||
| 	uint32_t rmon_t_p_gte2048;	/* MBAR_ETH + 0x240 */ | ||||
| 	uint32_t rmon_t_octets;		/* MBAR_ETH + 0x244 */ | ||||
| 	uint32_t ieee_t_drop;		/* MBAR_ETH + 0x248 */ | ||||
| 	uint32_t ieee_t_frame_ok;	/* MBAR_ETH + 0x24C */ | ||||
| 	uint32_t ieee_t_1col;		/* MBAR_ETH + 0x250 */ | ||||
| 	uint32_t ieee_t_mcol;		/* MBAR_ETH + 0x254 */ | ||||
| 	uint32_t ieee_t_def;		/* MBAR_ETH + 0x258 */ | ||||
| 	uint32_t ieee_t_lcol;		/* MBAR_ETH + 0x25C */ | ||||
| 	uint32_t ieee_t_excol;		/* MBAR_ETH + 0x260 */ | ||||
| 	uint32_t ieee_t_macerr;		/* MBAR_ETH + 0x264 */ | ||||
| 	uint32_t ieee_t_cserr;		/* MBAR_ETH + 0x268 */ | ||||
| 	uint32_t ieee_t_sqe;		/* MBAR_ETH + 0x26C */ | ||||
| 	uint32_t t_fdxfc;		/* MBAR_ETH + 0x270 */ | ||||
| 	uint32_t ieee_t_octets_ok;	/* MBAR_ETH + 0x274 */ | ||||
| 
 | ||||
| 	uint32_t res13[2];		/* MBAR_ETH + 0x278-27C */ | ||||
| 	uint32_t rmon_r_drop;		/* MBAR_ETH + 0x280 */ | ||||
| 	uint32_t rmon_r_packets;	/* MBAR_ETH + 0x284 */ | ||||
| 	uint32_t rmon_r_bc_pkt;		/* MBAR_ETH + 0x288 */ | ||||
| 	uint32_t rmon_r_mc_pkt;		/* MBAR_ETH + 0x28C */ | ||||
| 	uint32_t rmon_r_crc_align;	/* MBAR_ETH + 0x290 */ | ||||
| 	uint32_t rmon_r_undersize;	/* MBAR_ETH + 0x294 */ | ||||
| 	uint32_t rmon_r_oversize;	/* MBAR_ETH + 0x298 */ | ||||
| 	uint32_t rmon_r_frag;		/* MBAR_ETH + 0x29C */ | ||||
| 	uint32_t rmon_r_jab;		/* MBAR_ETH + 0x2A0 */ | ||||
| 
 | ||||
| 	uint32_t rmon_r_resvd_0;	/* MBAR_ETH + 0x2A4 */ | ||||
| 
 | ||||
| 	uint32_t rmon_r_p64;		/* MBAR_ETH + 0x2A8 */ | ||||
| 	uint32_t rmon_r_p65to127;	/* MBAR_ETH + 0x2AC */ | ||||
| 	uint32_t rmon_r_p128to255;	/* MBAR_ETH + 0x2B0 */ | ||||
| 	uint32_t rmon_r_p256to511;	/* MBAR_ETH + 0x2B4 */ | ||||
| 	uint32_t rmon_r_p512to1023;	/* MBAR_ETH + 0x2B8 */ | ||||
| 	uint32_t rmon_r_p1024to2047;	/* MBAR_ETH + 0x2BC */ | ||||
| 	uint32_t rmon_r_p_gte2048;	/* MBAR_ETH + 0x2C0 */ | ||||
| 	uint32_t rmon_r_octets;		/* MBAR_ETH + 0x2C4 */ | ||||
| 	uint32_t ieee_r_drop;		/* MBAR_ETH + 0x2C8 */ | ||||
| 	uint32_t ieee_r_frame_ok;	/* MBAR_ETH + 0x2CC */ | ||||
| 	uint32_t ieee_r_crc;		/* MBAR_ETH + 0x2D0 */ | ||||
| 	uint32_t ieee_r_align;		/* MBAR_ETH + 0x2D4 */ | ||||
| 	uint32_t r_macerr;		/* MBAR_ETH + 0x2D8 */ | ||||
| 	uint32_t r_fdxfc;		/* MBAR_ETH + 0x2DC */ | ||||
| 	uint32_t ieee_r_octets_ok;	/* MBAR_ETH + 0x2E0 */ | ||||
| 
 | ||||
| 	uint32_t res14[6];		/* MBAR_ETH + 0x2E4-2FC */ | ||||
| 
 | ||||
| 	uint32_t res15[64];		/* MBAR_ETH + 0x300-3FF */ | ||||
| }; | ||||
| 
 | ||||
| #define FEC_IEVENT_HBERR		0x80000000 | ||||
| #define FEC_IEVENT_BABR			0x40000000 | ||||
| #define FEC_IEVENT_BABT			0x20000000 | ||||
| #define FEC_IEVENT_GRA			0x10000000 | ||||
| #define FEC_IEVENT_TXF			0x08000000 | ||||
| #define FEC_IEVENT_TXB			0x04000000 | ||||
| #define FEC_IEVENT_RXF			0x02000000 | ||||
| #define FEC_IEVENT_RXB			0x01000000 | ||||
| #define FEC_IEVENT_MII			0x00800000 | ||||
| #define FEC_IEVENT_EBERR		0x00400000 | ||||
| #define FEC_IEVENT_LC			0x00200000 | ||||
| #define FEC_IEVENT_RL			0x00100000 | ||||
| #define FEC_IEVENT_UN			0x00080000 | ||||
| 
 | ||||
| #define FEC_IMASK_HBERR			0x80000000 | ||||
| #define FEC_IMASK_BABR			0x40000000 | ||||
| #define FEC_IMASKT_BABT			0x20000000 | ||||
| #define FEC_IMASK_GRA			0x10000000 | ||||
| #define FEC_IMASKT_TXF			0x08000000 | ||||
| #define FEC_IMASK_TXB			0x04000000 | ||||
| #define FEC_IMASKT_RXF			0x02000000 | ||||
| #define FEC_IMASK_RXB			0x01000000 | ||||
| #define FEC_IMASK_MII			0x00800000 | ||||
| #define FEC_IMASK_EBERR			0x00400000 | ||||
| #define FEC_IMASK_LC			0x00200000 | ||||
| #define FEC_IMASKT_RL			0x00100000 | ||||
| #define FEC_IMASK_UN			0x00080000 | ||||
| 
 | ||||
| 
 | ||||
| #define FEC_RCNTRL_MAX_FL_SHIFT		16 | ||||
| #define FEC_RCNTRL_LOOP			0x00000001 | ||||
| #define FEC_RCNTRL_DRT			0x00000002 | ||||
| #define FEC_RCNTRL_MII_MODE		0x00000004 | ||||
| #define FEC_RCNTRL_PROM			0x00000008 | ||||
| #define FEC_RCNTRL_BC_REJ		0x00000010 | ||||
| #define FEC_RCNTRL_FCE			0x00000020 | ||||
| 
 | ||||
| #define FEC_TCNTRL_GTS			0x00000001 | ||||
| #define FEC_TCNTRL_HBC			0x00000002 | ||||
| #define FEC_TCNTRL_FDEN			0x00000004 | ||||
| #define FEC_TCNTRL_TFC_PAUSE		0x00000008 | ||||
| #define FEC_TCNTRL_RFC_PAUSE		0x00000010 | ||||
| 
 | ||||
| #define FEC_ECNTRL_RESET		0x00000001	/* reset the FEC */ | ||||
| #define FEC_ECNTRL_ETHER_EN		0x00000002	/* enable the FEC */ | ||||
| 
 | ||||
| /**
 | ||||
|  * @brief Descriptor buffer alignment | ||||
|  * | ||||
|  * i.MX27 requires a 16 byte alignment (but for the first element only) | ||||
|  */ | ||||
| #define DB_ALIGNMENT		16 | ||||
| 
 | ||||
| /**
 | ||||
|  * @brief Data buffer alignment | ||||
|  * | ||||
|  * i.MX27 requires a four byte alignment for transmit and 16 bits | ||||
|  * alignment for receive so take 16 | ||||
|  * Note: Valid for member data_pointer in struct buffer_descriptor | ||||
|  */ | ||||
| #define DB_DATA_ALIGNMENT	16 | ||||
| 
 | ||||
| /**
 | ||||
|  * @brief Receive & Transmit Buffer Descriptor definitions | ||||
|  * | ||||
|  * Note: The first BD must be aligned (see DB_ALIGNMENT) | ||||
|  */ | ||||
| struct fec_bd { | ||||
| 	uint16_t data_length;		/* payload's length in bytes */ | ||||
| 	uint16_t status;		/* BD's staus (see datasheet) */ | ||||
| 	uint32_t data_pointer;		/* payload's buffer address */ | ||||
| }; | ||||
| 
 | ||||
| /**
 | ||||
|  * Supported phy types on this platform | ||||
|  */ | ||||
| enum xceiver_type { | ||||
| 	SEVENWIRE,	/* 7-wire       */ | ||||
| 	MII10,		/* MII 10Mbps   */ | ||||
| 	MII100		/* MII 100Mbps  */ | ||||
| }; | ||||
| 
 | ||||
| /**
 | ||||
|  * @brief i.MX27-FEC private structure | ||||
|  */ | ||||
| struct fec_priv { | ||||
| 	struct ethernet_regs *eth;	/* pointer to register'S base */ | ||||
| 	enum xceiver_type xcv_type;	/* transceiver type */ | ||||
| 	struct fec_bd *rbd_base;	/* RBD ring */ | ||||
| 	int rbd_index;			/* next receive BD to read */ | ||||
| 	struct fec_bd *tbd_base;	/* TBD ring */ | ||||
| 	int tbd_index;			/* next transmit BD to write */ | ||||
| 	bd_t *bd; | ||||
| 	void *rdb_ptr; | ||||
| 	void *base_ptr; | ||||
| }; | ||||
| 
 | ||||
| /**
 | ||||
|  * @brief Numbers of buffer descriptors for receiving | ||||
|  * | ||||
|  * The number defines the stocked memory buffers for the receiving task. | ||||
|  * Larger values makes no sense in this limited environment. | ||||
|  */ | ||||
| #define FEC_RBD_NUM		64 | ||||
| 
 | ||||
| /**
 | ||||
|  * @brief Define the ethernet packet size limit in memory | ||||
|  * | ||||
|  * Note: Do not shrink this number. This will force the FEC to spread larger | ||||
|  * frames in more than one BD. This is nothing to worry about, but the current | ||||
|  * driver can't handle it. | ||||
|  */ | ||||
| #define FEC_MAX_PKT_SIZE	1536 | ||||
| 
 | ||||
| /* Receive BD status bits */ | ||||
| #define FEC_RBD_EMPTY	0x8000	/* Receive BD status: Buffer is empty */ | ||||
| #define FEC_RBD_WRAP	0x2000	/* Receive BD status: Last BD in ring */ | ||||
| /* Receive BD status: Buffer is last in frame (useless here!) */ | ||||
| #define FEC_RBD_LAST	0x0800 | ||||
| #define FEC_RBD_MISS	0x0100	/* Receive BD status: Miss bit for prom mode */ | ||||
| /* Receive BD status: The received frame is broadcast frame */ | ||||
| #define FEC_RBD_BC	0x0080 | ||||
| /* Receive BD status: The received frame is multicast frame */ | ||||
| #define FEC_RBD_MC	0x0040 | ||||
| #define FEC_RBD_LG	0x0020	/* Receive BD status: Frame length violation */ | ||||
| #define FEC_RBD_NO	0x0010	/* Receive BD status: Nonoctet align frame */ | ||||
| #define FEC_RBD_CR	0x0004	/* Receive BD status: CRC error */ | ||||
| #define FEC_RBD_OV	0x0002	/* Receive BD status: Receive FIFO overrun */ | ||||
| #define FEC_RBD_TR	0x0001	/* Receive BD status: Frame is truncated */ | ||||
| #define FEC_RBD_ERR	(FEC_RBD_LG | FEC_RBD_NO | FEC_RBD_CR | \ | ||||
| 			FEC_RBD_OV | FEC_RBD_TR) | ||||
| 
 | ||||
| /* Transmit BD status bits */ | ||||
| #define FEC_TBD_READY	0x8000	/* Tansmit BD status: Buffer is ready */ | ||||
| #define FEC_TBD_WRAP	0x2000	/* Tansmit BD status: Mark as last BD in ring */ | ||||
| #define FEC_TBD_LAST	0x0800	/* Tansmit BD status: Buffer is last in frame */ | ||||
| #define FEC_TBD_TC	0x0400	/* Tansmit BD status: Transmit the CRC */ | ||||
| #define FEC_TBD_ABC	0x0200	/* Tansmit BD status: Append bad CRC */ | ||||
| 
 | ||||
| /* MII-related definitios */ | ||||
| #define FEC_MII_DATA_ST		0x40000000	/* Start of frame delimiter */ | ||||
| #define FEC_MII_DATA_OP_RD	0x20000000	/* Perform a read operation */ | ||||
| #define FEC_MII_DATA_OP_WR	0x10000000	/* Perform a write operation */ | ||||
| #define FEC_MII_DATA_PA_MSK	0x0f800000	/* PHY Address field mask */ | ||||
| #define FEC_MII_DATA_RA_MSK	0x007c0000	/* PHY Register field mask */ | ||||
| #define FEC_MII_DATA_TA		0x00020000	/* Turnaround */ | ||||
| #define FEC_MII_DATA_DATAMSK	0x0000ffff	/* PHY data field */ | ||||
| 
 | ||||
| #define FEC_MII_DATA_RA_SHIFT	18	/* MII Register address bits */ | ||||
| #define FEC_MII_DATA_PA_SHIFT	23	/* MII PHY address bits */ | ||||
| 
 | ||||
| #endif	/* __FEC_MXC_H */ | ||||
|  | @ -50,6 +50,7 @@ int e1000_initialize(bd_t *bis); | |||
| int eepro100_initialize(bd_t *bis); | ||||
| int eth_3com_initialize (bd_t * bis); | ||||
| int fec_initialize (bd_t *bis); | ||||
| int fecmxc_initialize (bd_t *bis); | ||||
| int greth_initialize(bd_t *bis); | ||||
| void gt6426x_eth_initialize(bd_t *bis); | ||||
| int inca_switch_initialize(bd_t *bis); | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue