mirror of
				git://git.openwrt.org/openwrt/openwrt.git
				synced 2025-10-30 21:44:27 -04:00 
			
		
		
		
	This adds u-boot for nbg460n ar71xx target, as it is required as second stage bootloader. Signed-off-by: Michael Kurz <michi.kurz@googlemail.com> SVN-Revision: 24418
		
			
				
	
	
		
			810 lines
		
	
	
		
			19 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			810 lines
		
	
	
		
			19 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /*
 | |
|  *  Atheros AR71xx built-in ethernet mac driver
 | |
|  *
 | |
|  *  Copyright (C) 2010 Michael Kurz <michi.kurz@googlemail.com>
 | |
|  *  Copyright (C) 2008-2010 Gabor Juhos <juhosg@openwrt.org>
 | |
|  *  Copyright (C) 2008 Imre Kaloz <kaloz@openwrt.org>
 | |
|  *
 | |
|  *  Based on Atheros' AG7100 driver
 | |
|  *
 | |
|  *  This program is free software; you can redistribute it and/or modify it
 | |
|  *  under the terms of the GNU General Public License version 2 as published
 | |
|  *  by the Free Software Foundation.
 | |
|  */
 | |
|  
 | |
| #include <common.h>
 | |
| #include <malloc.h>
 | |
| #include <net.h>
 | |
| #include <miiphy.h>
 | |
| 
 | |
| #include <asm/ar71xx.h>
 | |
| 
 | |
| #include "ag71xx.h"
 | |
| 
 | |
| #ifdef AG71XX_DEBUG
 | |
| #define DBG(fmt,args...)		printf(fmt ,##args)
 | |
| #else
 | |
| #define DBG(fmt,args...)
 | |
| #endif
 | |
| 
 | |
| 
 | |
| static struct ag71xx agtable[] = {
 | |
| 	{
 | |
| 		.mac_base = KSEG1ADDR(AR71XX_GE0_BASE),
 | |
| 		.mii_ctrl = KSEG1ADDR(AR71XX_MII_BASE + MII_REG_MII0_CTRL),
 | |
| 		.mii_if = CONFIG_AG71XX_MII0_IIF,
 | |
| 	} , {
 | |
| 		.mac_base = KSEG1ADDR(AR71XX_GE1_BASE),
 | |
| 		.mii_ctrl = KSEG1ADDR(AR71XX_MII_BASE + MII_REG_MII1_CTRL),
 | |
| 		.mii_if = CONFIG_AG71XX_MII1_IIF,
 | |
| 	}
 | |
| };
 | |
| 
 | |
| static int ag71xx_ring_alloc(struct ag71xx_ring *ring, unsigned int size)
 | |
| {
 | |
| 	int err;
 | |
| 	int i;
 | |
| 	int rsize;
 | |
| 
 | |
| 	ring->desc_size = sizeof(struct ag71xx_desc);
 | |
| 	if (ring->desc_size % (CONFIG_SYS_CACHELINE_SIZE)) {
 | |
| 		rsize = roundup(ring->desc_size, CONFIG_SYS_CACHELINE_SIZE);
 | |
| 		DBG("ag71xx: ring %p, desc size %u rounded to %u\n",
 | |
| 			ring, ring->desc_size,
 | |
| 			rsize);
 | |
| 		ring->desc_size = rsize;
 | |
| 	}
 | |
| 
 | |
| 	ring->descs_cpu = (u8 *) malloc((size * ring->desc_size)
 | |
| 		+ CONFIG_SYS_CACHELINE_SIZE - 1);
 | |
| 	if (!ring->descs_cpu) {
 | |
| 		err = -1;
 | |
| 		goto err;
 | |
| 	}
 | |
| 	ring->descs_cpu = (u8 *) UNCACHED_SDRAM((((u32) ring->descs_cpu + 
 | |
| 		CONFIG_SYS_CACHELINE_SIZE - 1) & ~(CONFIG_SYS_CACHELINE_SIZE - 1)));
 | |
|     ring->descs_dma = (u8 *) virt_to_phys(ring->descs_cpu);
 | |
| 
 | |
| 	ring->size = size;
 | |
| 
 | |
| 	ring->buf = malloc(size * sizeof(*ring->buf));
 | |
| 	if (!ring->buf) {
 | |
| 		err = -1;
 | |
| 		goto err;
 | |
| 	}
 | |
|     memset(ring->buf, 0, size * sizeof(*ring->buf));
 | |
| 
 | |
| 	for (i = 0; i < size; i++) {
 | |
| 		ring->buf[i].desc =
 | |
| 			(struct ag71xx_desc *)&ring->descs_cpu[i * ring->desc_size];
 | |
| 		DBG("ag71xx: ring %p, desc %d at %p\n",
 | |
| 			ring, i, ring->buf[i].desc);
 | |
| 	}
 | |
| 
 | |
| 	flush_cache( (u32) ring->buf, size * sizeof(*ring->buf));
 | |
| 	
 | |
| 	return 0;
 | |
| 
 | |
|  err:
 | |
| 	return err;
 | |
| }
 | |
| 
 | |
| static void ag71xx_ring_tx_init(struct ag71xx *ag)
 | |
| {
 | |
| 	struct ag71xx_ring *ring = &ag->tx_ring;
 | |
| 	int i;
 | |
| 
 | |
| 	for (i = 0; i < AG71XX_TX_RING_SIZE; i++) {
 | |
| 		ring->buf[i].desc->next = (u32) virt_to_phys((ring->descs_dma +
 | |
| 			ring->desc_size * ((i + 1) % AG71XX_TX_RING_SIZE)));
 | |
| 
 | |
| 		ring->buf[i].desc->ctrl = DESC_EMPTY;
 | |
| 		ring->buf[i].skb = NULL;
 | |
| 	}
 | |
| 
 | |
| 	ring->curr = 0;
 | |
| }
 | |
| 
 | |
| static void ag71xx_ring_rx_clean(struct ag71xx *ag)
 | |
| {
 | |
| 	struct ag71xx_ring *ring = &ag->rx_ring;
 | |
| 	int i;
 | |
| 
 | |
| 	if (!ring->buf)
 | |
| 		return;
 | |
| 
 | |
| 	for (i = 0; i < AG71XX_RX_RING_SIZE; i++) {
 | |
| 	    ring->buf[i].desc->data = (u32) virt_to_phys(NetRxPackets[i]);
 | |
| 	    flush_cache((u32) NetRxPackets[i], PKTSIZE_ALIGN);
 | |
|         ring->buf[i].desc->ctrl = DESC_EMPTY;
 | |
|     }
 | |
| 
 | |
| 	ring->curr = 0;
 | |
| }
 | |
| 
 | |
| static int ag71xx_ring_rx_init(struct ag71xx *ag)
 | |
| {
 | |
| 	struct ag71xx_ring *ring = &ag->rx_ring;
 | |
| 	unsigned int i;
 | |
| 
 | |
| 	for (i = 0; i < AG71XX_RX_RING_SIZE; i++) {
 | |
| 		ring->buf[i].desc->next = (u32) virt_to_phys((ring->descs_dma +
 | |
| 			ring->desc_size * ((i + 1) % AG71XX_RX_RING_SIZE)));
 | |
| 
 | |
| 		DBG("ag71xx: RX desc at %p, next is %08x\n",
 | |
| 			ring->buf[i].desc,
 | |
| 			ring->buf[i].desc->next);
 | |
| 	}
 | |
| 
 | |
| 	for (i = 0; i < AG71XX_RX_RING_SIZE; i++) {
 | |
| 		ring->buf[i].desc->data = (u32) virt_to_phys(NetRxPackets[i]);
 | |
| 		ring->buf[i].desc->ctrl = DESC_EMPTY;
 | |
| 	}
 | |
| 
 | |
| 	ring->curr = 0;
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static int ag71xx_rings_init(struct ag71xx *ag)
 | |
| {
 | |
| 	int ret;
 | |
| 
 | |
| 	ret = ag71xx_ring_alloc(&ag->tx_ring, AG71XX_TX_RING_SIZE);
 | |
| 	if (ret)
 | |
| 		return ret;
 | |
| 
 | |
| 	ag71xx_ring_tx_init(ag);
 | |
| 
 | |
| 	ret = ag71xx_ring_alloc(&ag->rx_ring, AG71XX_RX_RING_SIZE);
 | |
| 	if (ret)
 | |
| 		return ret;
 | |
| 
 | |
| 	ret = ag71xx_ring_rx_init(ag);
 | |
| 	return ret;
 | |
| }
 | |
| 
 | |
| static void ar71xx_set_pll(u32 cfg_reg, u32 pll_reg, u32 pll_val, u32 shift)
 | |
| {
 | |
| 	uint32_t base = KSEG1ADDR(AR71XX_PLL_BASE);
 | |
| 	u32 t;
 | |
| 
 | |
| 	t = readl(base + cfg_reg);
 | |
| 	t &= ~(3 << shift);
 | |
| 	t |=  (2 << shift);
 | |
| 	writel(t, base + cfg_reg);
 | |
| 	udelay(100);
 | |
| 
 | |
| 	writel(pll_val, base + pll_reg);
 | |
| 
 | |
| 	t |= (3 << shift);
 | |
| 	writel(t, base + cfg_reg);
 | |
| 	udelay(100);
 | |
| 
 | |
| 	t &= ~(3 << shift);
 | |
| 	writel(t, base + cfg_reg);
 | |
| 	udelay(100);
 | |
| 
 | |
| 	debug("ar71xx: pll_reg %#x: %#x\n", (unsigned int)(base + pll_reg),
 | |
|        readl(base + pll_reg));
 | |
| }
 | |
| 
 | |
| static void ar91xx_set_pll_ge0(int speed)
 | |
| {
 | |
| 	//u32 val = ar71xx_get_eth_pll(0, speed);
 | |
| 	u32 pll_val;
 | |
| 
 | |
| 	switch (speed) {
 | |
| 	case SPEED_10:
 | |
| 		pll_val = 0x00441099;
 | |
| 		break;
 | |
| 	case SPEED_100:
 | |
| 		pll_val = 0x13000a44;
 | |
| 		break;
 | |
| 	case SPEED_1000:
 | |
| 		pll_val = 0x1a000000;
 | |
| 		break;
 | |
| 	default:
 | |
| 		BUG();
 | |
| 	}
 | |
| 
 | |
| 	ar71xx_set_pll(AR91XX_PLL_REG_ETH_CONFIG, AR91XX_PLL_REG_ETH0_INT_CLOCK,
 | |
| 			 pll_val, AR91XX_ETH0_PLL_SHIFT);
 | |
| }
 | |
| 
 | |
| static void ar91xx_set_pll_ge1(int speed)
 | |
| {
 | |
| 	//u32 val = ar71xx_get_eth_pll(1, speed);
 | |
|     u32 pll_val;
 | |
| 
 | |
| 	switch (speed) {
 | |
| 	case SPEED_10:
 | |
| 		pll_val = 0x00441099;
 | |
| 		break;
 | |
| 	case SPEED_100:
 | |
| 		pll_val = 0x13000a44;
 | |
| 		break;
 | |
| 	case SPEED_1000:
 | |
| 		pll_val = 0x1a000000;
 | |
| 		break;
 | |
| 	default:
 | |
| 		BUG();
 | |
| 	}
 | |
| 
 | |
| 	ar71xx_set_pll(AR91XX_PLL_REG_ETH_CONFIG, AR91XX_PLL_REG_ETH1_INT_CLOCK,
 | |
| 			 pll_val, AR91XX_ETH1_PLL_SHIFT);
 | |
| }
 | |
| 
 | |
| static void ag71xx_hw_set_macaddr(struct ag71xx *ag, unsigned char *mac)
 | |
| {
 | |
| 	u32 t;
 | |
| 
 | |
| 	t = (((u32) mac[5]) << 24) | (((u32) mac[4]) << 16)
 | |
| 	  | (((u32) mac[3]) << 8) | ((u32) mac[2]);
 | |
| 
 | |
| 	ag71xx_wr(ag, AG71XX_REG_MAC_ADDR1, t);
 | |
| 
 | |
| 	t = (((u32) mac[1]) << 24) | (((u32) mac[0]) << 16);
 | |
| 	ag71xx_wr(ag, AG71XX_REG_MAC_ADDR2, t);
 | |
| }
 | |
| 
 | |
| static void ag71xx_dma_reset(struct ag71xx *ag)
 | |
| {
 | |
| 	u32 val;
 | |
| 	int i;
 | |
| 
 | |
| 	DBG("%s: txdesc reg: 0x%08x rxdesc reg: 0x%08x\n",
 | |
| 			ag->dev->name,
 | |
| 			ag71xx_rr(ag, AG71XX_REG_TX_DESC),
 | |
| 			ag71xx_rr(ag, AG71XX_REG_RX_DESC));
 | |
| 	
 | |
| 	/* stop RX and TX */
 | |
| 	ag71xx_wr(ag, AG71XX_REG_RX_CTRL, 0);
 | |
| 	ag71xx_wr(ag, AG71XX_REG_TX_CTRL, 0);
 | |
| 
 | |
| 	/* clear descriptor addresses */
 | |
| 	ag71xx_wr(ag, AG71XX_REG_TX_DESC, 0);
 | |
| 	ag71xx_wr(ag, AG71XX_REG_RX_DESC, 0);
 | |
| 
 | |
| 	/* clear pending RX/TX interrupts */
 | |
| 	for (i = 0; i < 256; i++) {
 | |
| 		ag71xx_wr(ag, AG71XX_REG_RX_STATUS, RX_STATUS_PR);
 | |
| 		ag71xx_wr(ag, AG71XX_REG_TX_STATUS, TX_STATUS_PS);
 | |
| 	}
 | |
| 
 | |
| 	/* clear pending errors */
 | |
| 	ag71xx_wr(ag, AG71XX_REG_RX_STATUS, RX_STATUS_BE | RX_STATUS_OF);
 | |
| 	ag71xx_wr(ag, AG71XX_REG_TX_STATUS, TX_STATUS_BE | TX_STATUS_UR);
 | |
| 
 | |
| 	val = ag71xx_rr(ag, AG71XX_REG_RX_STATUS);
 | |
| 	if (val)
 | |
| 		printf("%s: unable to clear DMA Rx status: %08x\n",
 | |
| 			ag->dev->name, val);
 | |
| 
 | |
| 	val = ag71xx_rr(ag, AG71XX_REG_TX_STATUS);
 | |
| 
 | |
| 	/* mask out reserved bits */
 | |
| 	val &= ~0xff000000;
 | |
| 
 | |
| 	if (val)
 | |
| 		printf("%s: unable to clear DMA Tx status: %08x\n",
 | |
| 			ag->dev->name, val);
 | |
| }
 | |
| 
 | |
| static void ag71xx_halt(struct eth_device *dev)
 | |
| {
 | |
|     struct ag71xx *ag = (struct ag71xx *) dev->priv;
 | |
| 
 | |
|     /* stop RX engine */
 | |
| 	ag71xx_wr(ag, AG71XX_REG_RX_CTRL, 0);
 | |
| 
 | |
| 	ag71xx_dma_reset(ag);
 | |
| }
 | |
| 
 | |
| #define MAX_WAIT        1000
 | |
| 
 | |
| static int ag71xx_send(struct eth_device *dev, volatile void *packet,
 | |
|                        int length)
 | |
| {
 | |
|     struct ag71xx *ag = (struct ag71xx *) dev->priv;
 | |
| 	struct ag71xx_ring *ring = &ag->tx_ring;
 | |
| 	struct ag71xx_desc *desc;
 | |
| 	int i;
 | |
| 
 | |
| 	i = ring->curr % AG71XX_TX_RING_SIZE;
 | |
| 	desc = ring->buf[i].desc;
 | |
| 
 | |
| 	if (!ag71xx_desc_empty(desc)) {
 | |
| 		printf("%s: tx buffer full\n", ag->dev->name);
 | |
| 		return 1;
 | |
| 	}
 | |
| 
 | |
| 	flush_cache((u32) packet, length);
 | |
|     desc->data = (u32) virt_to_phys(packet);
 | |
|     desc->ctrl = (length & DESC_PKTLEN_M);
 | |
| 	
 | |
| 	DBG("%s: sending %#08x length %#08x\n",
 | |
| 		ag->dev->name, desc->data, desc->ctrl);
 | |
| 	
 | |
| 	ring->curr++;
 | |
| 	if (ring->curr >= AG71XX_TX_RING_SIZE){
 | |
| 		ring->curr = 0;
 | |
| 	}
 | |
| 	
 | |
| 	/* enable TX engine */
 | |
| 	ag71xx_wr(ag, AG71XX_REG_TX_CTRL, TX_CTRL_TXE);
 | |
| 
 | |
|     for (i = 0; i < MAX_WAIT; i++)
 | |
|     {
 | |
|         if (ag71xx_desc_empty(desc))
 | |
|             break;
 | |
|         udelay(10);
 | |
|     }
 | |
|     if (i == MAX_WAIT) {
 | |
|         printf("%s: tx timed out!\n", ag->dev->name);
 | |
| 		return -1;
 | |
| 	}
 | |
| 	
 | |
| 	/* disable TX engine */
 | |
| 	ag71xx_wr(ag, AG71XX_REG_TX_CTRL, 0);
 | |
| 	desc->data = 0;
 | |
| 	desc->ctrl = DESC_EMPTY;
 | |
| 	
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static int ag71xx_recv(struct eth_device *dev)
 | |
| {
 | |
|     struct ag71xx *ag = (struct ag71xx *) dev->priv;
 | |
| 	struct ag71xx_ring *ring = &ag->rx_ring;
 | |
| 
 | |
|     for (;;) {
 | |
| 		unsigned int i = ring->curr % AG71XX_RX_RING_SIZE;
 | |
| 		struct ag71xx_desc *desc = ring->buf[i].desc;
 | |
| 		int pktlen;
 | |
| 		
 | |
| 		if (ag71xx_desc_empty(desc))
 | |
| 			break;
 | |
| 
 | |
| 		DBG("%s: rx packets, curr=%u\n", dev->name, ring->curr);
 | |
| 
 | |
|         pktlen = ag71xx_desc_pktlen(desc);
 | |
| 		pktlen -= ETH_FCS_LEN;
 | |
| 
 | |
| 
 | |
| 		NetReceive(NetRxPackets[i] , pktlen);
 | |
| 		flush_cache( (u32) NetRxPackets[i], PKTSIZE_ALIGN);
 | |
| 
 | |
|         ring->buf[i].desc->ctrl = DESC_EMPTY;
 | |
| 		ring->curr++;
 | |
| 		if (ring->curr >= AG71XX_RX_RING_SIZE){
 | |
| 			ring->curr = 0;
 | |
| 		}
 | |
| 
 | |
|     }
 | |
| 
 | |
| 	if ((ag71xx_rr(ag, AG71XX_REG_RX_CTRL) & RX_CTRL_RXE) == 0) {
 | |
| 		/* start RX engine */
 | |
| 		ag71xx_wr(ag, AG71XX_REG_RX_CTRL, RX_CTRL_RXE);
 | |
| 	}
 | |
| 	
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| #ifdef AG71XX_DEBUG
 | |
| static char *ag71xx_speed_str(struct ag71xx *ag)
 | |
| {
 | |
| 	switch (ag->speed) {
 | |
| 	case SPEED_1000:
 | |
| 		return "1000";
 | |
| 	case SPEED_100:
 | |
| 		return "100";
 | |
| 	case SPEED_10:
 | |
| 		return "10";
 | |
| 	}
 | |
| 
 | |
| 	return "?";
 | |
| }
 | |
| #endif
 | |
| 
 | |
| void ag71xx_link_adjust(struct ag71xx *ag)
 | |
| {
 | |
| 	u32 cfg2;
 | |
| 	u32 ifctl;
 | |
| 	u32 fifo5;
 | |
| 	u32 mii_speed;
 | |
| 
 | |
| 	if (!ag->link) {
 | |
| 		DBG("%s: link down\n", ag->dev->name);
 | |
| 		return;
 | |
| 	}
 | |
| 
 | |
| 	cfg2 = ag71xx_rr(ag, AG71XX_REG_MAC_CFG2);
 | |
| 	cfg2 &= ~(MAC_CFG2_IF_1000 | MAC_CFG2_IF_10_100 | MAC_CFG2_FDX);
 | |
| 	cfg2 |= (ag->duplex) ? MAC_CFG2_FDX : 0;
 | |
| 
 | |
| 	ifctl = ag71xx_rr(ag, AG71XX_REG_MAC_IFCTL);
 | |
| 	ifctl &= ~(MAC_IFCTL_SPEED);
 | |
| 
 | |
| 	fifo5 = ag71xx_rr(ag, AG71XX_REG_FIFO_CFG5);
 | |
| 	fifo5 &= ~FIFO_CFG5_BM;
 | |
| 
 | |
| 	switch (ag->speed) {
 | |
| 	case SPEED_1000:
 | |
| 		mii_speed =  MII_CTRL_SPEED_1000;
 | |
| 		cfg2 |= MAC_CFG2_IF_1000;
 | |
| 		fifo5 |= FIFO_CFG5_BM;
 | |
| 		break;
 | |
| 	case SPEED_100:
 | |
| 		mii_speed = MII_CTRL_SPEED_100;
 | |
| 		cfg2 |= MAC_CFG2_IF_10_100;
 | |
| 		ifctl |= MAC_IFCTL_SPEED;
 | |
| 		break;
 | |
| 	case SPEED_10:
 | |
| 		mii_speed = MII_CTRL_SPEED_10;
 | |
| 		cfg2 |= MAC_CFG2_IF_10_100;
 | |
| 		break;
 | |
| 	default:
 | |
| 		BUG();
 | |
| 		return;
 | |
| 	}
 | |
| 
 | |
|     ag71xx_wr(ag, AG71XX_REG_FIFO_CFG3, 0x00780fff);
 | |
| 
 | |
|     if (ag->macNum == 0)
 | |
|         ar91xx_set_pll_ge0(ag->speed);
 | |
|     else
 | |
|         ar91xx_set_pll_ge1(ag->speed);
 | |
| 
 | |
| 	ag71xx_mii_ctrl_set_speed(ag, mii_speed);
 | |
| 
 | |
| 	ag71xx_wr(ag, AG71XX_REG_MAC_CFG2, cfg2);
 | |
| 	ag71xx_wr(ag, AG71XX_REG_FIFO_CFG5, fifo5);
 | |
| 	ag71xx_wr(ag, AG71XX_REG_MAC_IFCTL, ifctl);
 | |
| 
 | |
|     DBG("%s: link up (%sMbps/%s duplex)\n",
 | |
|         ag->dev->name,
 | |
|         ag71xx_speed_str(ag),
 | |
|         (1 == ag->duplex) ? "Full" : "Half");
 | |
| 
 | |
| 	DBG("%s: fifo_cfg0=%#x, fifo_cfg1=%#x, fifo_cfg2=%#x\n",
 | |
| 		ag->dev->name,
 | |
| 		ag71xx_rr(ag, AG71XX_REG_FIFO_CFG0),
 | |
| 		ag71xx_rr(ag, AG71XX_REG_FIFO_CFG1),
 | |
| 		ag71xx_rr(ag, AG71XX_REG_FIFO_CFG2));
 | |
| 
 | |
| 	DBG("%s: fifo_cfg3=%#x, fifo_cfg4=%#x, fifo_cfg5=%#x\n",
 | |
| 		ag->dev->name,
 | |
| 		ag71xx_rr(ag, AG71XX_REG_FIFO_CFG3),
 | |
| 		ag71xx_rr(ag, AG71XX_REG_FIFO_CFG4),
 | |
| 		ag71xx_rr(ag, AG71XX_REG_FIFO_CFG5));
 | |
| 
 | |
| 	DBG("%s: mac_cfg2=%#x, mac_ifctl=%#x, mii_ctrl=%#x\n",
 | |
| 		ag->dev->name,
 | |
| 		ag71xx_rr(ag, AG71XX_REG_MAC_CFG2),
 | |
| 		ag71xx_rr(ag, AG71XX_REG_MAC_IFCTL),
 | |
| 		ag71xx_mii_ctrl_rr(ag));
 | |
| }
 | |
| 
 | |
| #if defined(CONFIG_MII) || defined(CONFIG_CMD_MII)
 | |
| static int ag71xx_getMiiSpeed(struct ag71xx *ag) 
 | |
| {
 | |
|     uint16_t phyreg, cap;
 | |
| 
 | |
|     if (miiphy_read(ag->phyname, ag->phyid,
 | |
|                     PHY_BMSR, &phyreg)) {
 | |
|         puts("PHY_BMSR read failed, assuming no link\n");
 | |
|         return -1;
 | |
|     }
 | |
| 
 | |
|     if ((phyreg & PHY_BMSR_LS) == 0) {
 | |
|         return -1;
 | |
|     }
 | |
| 
 | |
|     if (miiphy_read(ag->phyname, ag->phyid,
 | |
|                 PHY_1000BTSR, &phyreg))
 | |
|         return -1;
 | |
| 
 | |
|     if (phyreg & PHY_1000BTSR_1000FD) {
 | |
|         ag->speed = SPEED_1000;
 | |
|         ag->duplex = 1;
 | |
|     } else if (phyreg & PHY_1000BTSR_1000HD) {
 | |
|         ag->speed = SPEED_1000;
 | |
|         ag->duplex = 0;
 | |
|     } else {
 | |
|         if (miiphy_read(ag->phyname, ag->phyid,
 | |
|                 PHY_ANAR, &cap))
 | |
|             return -1;
 | |
| 
 | |
|         if (miiphy_read(ag->phyname, ag->phyid,
 | |
|                 PHY_ANLPAR, &phyreg))
 | |
|             return -1;
 | |
| 
 | |
|         cap &= phyreg;
 | |
|         if (cap & PHY_ANLPAR_TXFD) {
 | |
|             ag->speed = SPEED_100;
 | |
|             ag->duplex = 1;
 | |
|         } else if (cap & PHY_ANLPAR_TX) {
 | |
|             ag->speed = SPEED_100;
 | |
|             ag->duplex = 0;
 | |
|         } else if (cap & PHY_ANLPAR_10FD) {
 | |
|             ag->speed = SPEED_10;
 | |
|             ag->duplex = 1;
 | |
|         } else {
 | |
|             ag->speed = SPEED_10;
 | |
|             ag->duplex = 0;
 | |
|         }
 | |
|     }
 | |
| 	
 | |
| 	ag->link = 1;
 | |
| 	
 | |
| 	return 0;
 | |
| }
 | |
| #endif
 | |
| 
 | |
| static int ag71xx_hw_start(struct eth_device *dev, bd_t * bd)
 | |
| {
 | |
| 	struct ag71xx *ag = (struct ag71xx *) dev->priv;
 | |
| 
 | |
| 	ag71xx_dma_reset(ag);
 | |
| 
 | |
|     ag71xx_ring_rx_clean(ag);
 | |
| 	ag71xx_ring_tx_init(ag);
 | |
| 	
 | |
| 	ag71xx_wr(ag, AG71XX_REG_TX_DESC, 
 | |
| 				(u32) virt_to_phys(ag->tx_ring.descs_dma));
 | |
| 	ag71xx_wr(ag, AG71XX_REG_RX_DESC,
 | |
| 				(u32) virt_to_phys(ag->rx_ring.descs_dma));
 | |
| 
 | |
| 	ag71xx_hw_set_macaddr(ag, ag->dev->enetaddr);
 | |
| 
 | |
|     if (ag->phyfixed) {
 | |
|         ag->link = 1;
 | |
|         ag->duplex = 1;
 | |
|         ag->speed = SPEED_1000;
 | |
|     } else {
 | |
| 
 | |
| #if (defined(CONFIG_MII) || defined(CONFIG_CMD_MII))
 | |
| 		if (ag71xx_getMiiSpeed(ag))
 | |
| 			return -1;
 | |
| #else
 | |
| 		/* only fixed, without mii */
 | |
| 		return -1;
 | |
| #endif
 | |
| 
 | |
|     }
 | |
|     ag71xx_link_adjust(ag);
 | |
| 	
 | |
| 	DBG("%s: txdesc reg: %#08x rxdesc reg: %#08x\n",
 | |
| 		ag->dev->name,
 | |
| 		ag71xx_rr(ag, AG71XX_REG_TX_DESC),
 | |
| 		ag71xx_rr(ag, AG71XX_REG_RX_DESC));
 | |
| 	
 | |
| 	/* start RX engine */
 | |
| 	ag71xx_wr(ag, AG71XX_REG_RX_CTRL, RX_CTRL_RXE);
 | |
| 	
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| #define FIFO_CFG0_INIT	(FIFO_CFG0_ALL << FIFO_CFG0_ENABLE_SHIFT)
 | |
| 
 | |
| #define FIFO_CFG4_INIT	(FIFO_CFG4_DE | FIFO_CFG4_DV | FIFO_CFG4_FC | \
 | |
| 			 FIFO_CFG4_CE | FIFO_CFG4_CR | FIFO_CFG4_LM | \
 | |
| 			 FIFO_CFG4_LO | FIFO_CFG4_OK | FIFO_CFG4_MC | \
 | |
| 			 FIFO_CFG4_BC | FIFO_CFG4_DR | FIFO_CFG4_LE | \
 | |
| 			 FIFO_CFG4_CF | FIFO_CFG4_PF | FIFO_CFG4_UO | \
 | |
| 			 FIFO_CFG4_VT)
 | |
| 
 | |
| #define FIFO_CFG5_INIT	(FIFO_CFG5_DE | FIFO_CFG5_DV | FIFO_CFG5_FC | \
 | |
| 			 FIFO_CFG5_CE | FIFO_CFG5_LO | FIFO_CFG5_OK | \
 | |
| 			 FIFO_CFG5_MC | FIFO_CFG5_BC | FIFO_CFG5_DR | \
 | |
| 			 FIFO_CFG5_CF | FIFO_CFG5_PF | FIFO_CFG5_VT | \
 | |
| 			 FIFO_CFG5_LE | FIFO_CFG5_FT | FIFO_CFG5_16 | \
 | |
| 			 FIFO_CFG5_17 | FIFO_CFG5_SF)
 | |
| 
 | |
| static int ag71xx_hw_init(struct ag71xx *ag)
 | |
| {
 | |
|     int ret = 0;
 | |
| 	uint32_t reg;
 | |
| 	uint32_t mask, mii_type;
 | |
| 
 | |
|     if (ag->macNum == 0) {
 | |
|         mask = (RESET_MODULE_GE0_MAC | RESET_MODULE_GE0_PHY);
 | |
|         mii_type = 0x13;
 | |
|     } else {
 | |
|         mask = (RESET_MODULE_GE1_MAC | RESET_MODULE_GE1_PHY);
 | |
|         mii_type = 0x11;
 | |
|     }
 | |
| 
 | |
|     // mac soft reset
 | |
|     ag71xx_sb(ag, AG71XX_REG_MAC_CFG1, MAC_CFG1_SR);
 | |
|     udelay(20);
 | |
| 	
 | |
| 	// device stop
 | |
| 	reg = ar71xx_reset_rr(AR91XX_RESET_REG_RESET_MODULE);
 | |
| 	ar71xx_reset_wr(AR91XX_RESET_REG_RESET_MODULE, reg | mask);
 | |
| 	udelay(100 * 1000);
 | |
| 	
 | |
|     // device start
 | |
|     reg = ar71xx_reset_rr(AR91XX_RESET_REG_RESET_MODULE);
 | |
|     ar71xx_reset_wr(AR91XX_RESET_REG_RESET_MODULE, reg & ~mask);
 | |
|     udelay(100 * 1000);
 | |
| 
 | |
|     /* setup MAC configuration registers */
 | |
|     ag71xx_wr(ag, AG71XX_REG_MAC_CFG1, (MAC_CFG1_RXE | MAC_CFG1_TXE));
 | |
| 
 | |
|     ag71xx_sb(ag, AG71XX_REG_MAC_CFG2,
 | |
|           MAC_CFG2_PAD_CRC_EN | MAC_CFG2_LEN_CHECK);
 | |
| 
 | |
|     /* setup FIFO configuration register 0 */
 | |
|     ag71xx_wr(ag, AG71XX_REG_FIFO_CFG0, FIFO_CFG0_INIT);
 | |
| 
 | |
|     /* setup MII interface type */
 | |
|     ag71xx_mii_ctrl_set_if(ag, ag->mii_if);
 | |
| 
 | |
|     /* setup mdio clock divisor */
 | |
|     ag71xx_wr(ag, AG71XX_REG_MII_CFG, MII_CFG_CLK_DIV_20);
 | |
| 	
 | |
| 	/* setup FIFO configuration registers */
 | |
| 	ag71xx_sb(ag, AG71XX_REG_FIFO_CFG4, FIFO_CFG4_INIT);
 | |
|     ag71xx_wr(ag, AG71XX_REG_FIFO_CFG1, 0x0fff0000);
 | |
|     ag71xx_wr(ag, AG71XX_REG_FIFO_CFG2, 0x00001fff);
 | |
|     ag71xx_wr(ag, AG71XX_REG_FIFO_CFG5, FIFO_CFG5_INIT);
 | |
| 
 | |
|     ag71xx_dma_reset(ag);
 | |
| 
 | |
|     ret = ag71xx_rings_init(ag);
 | |
|     if (ret)
 | |
|         return -1;
 | |
| 
 | |
| 	ag71xx_wr(ag, AG71XX_REG_TX_DESC, 
 | |
| 				(u32) virt_to_phys(ag->tx_ring.descs_dma));
 | |
| 	ag71xx_wr(ag, AG71XX_REG_RX_DESC,
 | |
| 				(u32) virt_to_phys(ag->rx_ring.descs_dma));
 | |
| 		
 | |
| 	ag71xx_hw_set_macaddr(ag, ag->dev->enetaddr);
 | |
| 	
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| #if defined(CONFIG_MII) || defined(CONFIG_CMD_MII)
 | |
| #define AG71XX_MDIO_RETRY	1000
 | |
| #define AG71XX_MDIO_DELAY	5
 | |
| 
 | |
| static inline struct ag71xx *ag71xx_name2mac(char *devname)
 | |
| {
 | |
|     if (strcmp(devname, agtable[0].dev->name) == 0)
 | |
|         return &agtable[0];
 | |
|     else if (strcmp(devname, agtable[1].dev->name) == 0)
 | |
|         return &agtable[1];
 | |
|     else
 | |
|         return NULL;
 | |
| }
 | |
| 
 | |
| static inline void ag71xx_mdio_wr(struct ag71xx *ag, unsigned reg,
 | |
| 				  u32 value)
 | |
| {
 | |
| 	uint32_t r;
 | |
| 
 | |
| 	r = ag->mac_base + reg;
 | |
| 	writel(value, r);
 | |
| 
 | |
| 	/* flush write */
 | |
| 	(void) readl(r);
 | |
| }
 | |
| 
 | |
| static inline u32 ag71xx_mdio_rr(struct ag71xx *ag, unsigned reg)
 | |
| {
 | |
| 	return readl(ag->mac_base + reg);
 | |
| }
 | |
| 
 | |
| static int ag71xx_mdio_read(char *devname, unsigned char addr,
 | |
|                             unsigned char reg, unsigned short *val)
 | |
| {
 | |
| 	struct ag71xx *ag = ag71xx_name2mac(devname);
 | |
| 	uint16_t regData;
 | |
| 	int i;
 | |
| 
 | |
| 	ag71xx_mdio_wr(ag, AG71XX_REG_MII_CMD, MII_CMD_WRITE);
 | |
| 	ag71xx_mdio_wr(ag, AG71XX_REG_MII_ADDR,
 | |
| 			((addr & 0xff) << MII_ADDR_SHIFT) | (reg & 0xff));
 | |
| 	ag71xx_mdio_wr(ag, AG71XX_REG_MII_CMD, MII_CMD_READ);
 | |
| 
 | |
| 	i = AG71XX_MDIO_RETRY;
 | |
| 	while (ag71xx_mdio_rr(ag, AG71XX_REG_MII_IND) & MII_IND_BUSY) {
 | |
| 		if (i-- == 0) {
 | |
| 			printf("%s: mii_read timed out\n",
 | |
| 				ag->dev->name);
 | |
| 			return -1;
 | |
| 		}
 | |
| 		udelay(AG71XX_MDIO_DELAY);
 | |
| 	}
 | |
| 
 | |
| 	regData = (uint16_t) ag71xx_mdio_rr(ag, AG71XX_REG_MII_STATUS) & 0xffff;
 | |
| 	ag71xx_mdio_wr(ag, AG71XX_REG_MII_CMD, MII_CMD_WRITE);
 | |
| 
 | |
| 	DBG("mii_read: addr=%04x, reg=%04x, value=%04x\n", addr, reg, regData);
 | |
| 
 | |
|     if (val)
 | |
|         *val = regData;
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static int ag71xx_mdio_write(char *devname, unsigned char addr,
 | |
|                             unsigned char reg, unsigned short val)
 | |
| {
 | |
| 	struct ag71xx *ag = ag71xx_name2mac(devname);
 | |
| 	int i;
 | |
| 
 | |
|     if (ag == NULL)
 | |
|         return 1;
 | |
| 
 | |
| 	DBG("mii_write: addr=%04x, reg=%04x, value=%04x\n", addr, reg, val);
 | |
| 
 | |
| 	ag71xx_mdio_wr(ag, AG71XX_REG_MII_ADDR,
 | |
| 			((addr & 0xff) << MII_ADDR_SHIFT) | (reg & 0xff));
 | |
| 	ag71xx_mdio_wr(ag, AG71XX_REG_MII_CTRL, val);
 | |
| 
 | |
| 	i = AG71XX_MDIO_RETRY;
 | |
| 	while (ag71xx_mdio_rr(ag, AG71XX_REG_MII_IND) & MII_IND_BUSY) {
 | |
| 		if (i-- == 0) {
 | |
| 			printf("%s: mii_write timed out\n",
 | |
| 				ag->dev->name);
 | |
| 			break;
 | |
| 		}
 | |
| 		udelay(AG71XX_MDIO_DELAY);
 | |
| 	}
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| #endif
 | |
| 
 | |
| int ag71xx_register(bd_t * bis, char *phyname[], uint16_t phyid[], uint16_t phyfixed[])
 | |
| {
 | |
|     int i, num = 0;
 | |
|     u8 used_ports[MAX_AG71XX_DEVS] = CONFIG_AG71XX_PORTS;
 | |
| 
 | |
| 	for (i = 0; i < MAX_AG71XX_DEVS; i++) {
 | |
| 		/*skip if port is configured not to use */
 | |
| 		if (used_ports[i] == 0)
 | |
| 			continue;
 | |
| 
 | |
| 		agtable[i].dev = malloc(sizeof(struct eth_device));
 | |
| 		if (agtable[i].dev == NULL) {
 | |
| 			puts("malloc failed\n");
 | |
| 			return 0;
 | |
|         }
 | |
| 		memset(agtable[i].dev, 0, sizeof(struct eth_device));
 | |
| 		sprintf(agtable[i].dev->name, "eth%d", i);
 | |
| 
 | |
| 		agtable[i].dev->iobase = 0;
 | |
| 		agtable[i].dev->init = ag71xx_hw_start;
 | |
| 		agtable[i].dev->halt = ag71xx_halt;
 | |
| 		agtable[i].dev->send = ag71xx_send;
 | |
| 		agtable[i].dev->recv = ag71xx_recv;
 | |
| 		agtable[i].dev->priv = (void *) (&agtable[i]);
 | |
| 		agtable[i].macNum = i;
 | |
| 		eth_register(agtable[i].dev);
 | |
| #if defined(CONFIG_MII) || defined(CONFIG_CMD_MII)
 | |
| 
 | |
|         if ((phyname == NULL) || (phyid == NULL) || (phyfixed == NULL))
 | |
|             return -1;
 | |
| 
 | |
|         agtable[i].phyname = strdup(phyname[i]);
 | |
|         agtable[i].phyid = phyid[i];
 | |
|         agtable[i].phyfixed = phyfixed[i];
 | |
| 
 | |
|         miiphy_register(agtable[i].dev->name, ag71xx_mdio_read,
 | |
| 			ag71xx_mdio_write);
 | |
| #endif
 | |
| 
 | |
| 		if (ag71xx_hw_init(&agtable[i]))
 | |
| 			continue;
 | |
| 
 | |
|         num++;
 | |
| 	}
 | |
| 
 | |
|     return num;
 | |
| }
 |