diff options
| -rw-r--r-- | cpu/arm926ejs/mx27/generic.c | 10 | ||||
| -rw-r--r-- | drivers/net/Makefile | 1 | ||||
| -rw-r--r-- | drivers/net/fec_mxc.c | 742 | ||||
| -rw-r--r-- | drivers/net/fec_mxc.h | 304 | ||||
| -rw-r--r-- | include/netdev.h | 1 | 
5 files changed, 1058 insertions, 0 deletions
| diff --git a/cpu/arm926ejs/mx27/generic.c b/cpu/arm926ejs/mx27/generic.c index bcf7899ba..47fa4b48e 100644 --- a/cpu/arm926ejs/mx27/generic.c +++ b/cpu/arm926ejs/mx27/generic.c @@ -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; diff --git a/drivers/net/Makefile b/drivers/net/Makefile index 1ce2936cb..34b56d825 100644 --- a/drivers/net/Makefile +++ b/drivers/net/Makefile @@ -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 diff --git a/drivers/net/fec_mxc.c b/drivers/net/fec_mxc.c new file mode 100644 index 000000000..faf008cea --- /dev/null +++ b/drivers/net/fec_mxc.c @@ -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; +} + diff --git a/drivers/net/fec_mxc.h b/drivers/net/fec_mxc.h new file mode 100644 index 000000000..6cb1bfc05 --- /dev/null +++ b/drivers/net/fec_mxc.h @@ -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 */ diff --git a/include/netdev.h b/include/netdev.h index d8056abd8..3e66586b4 100644 --- a/include/netdev.h +++ b/include/netdev.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); |