diff options
Diffstat (limited to 'drivers/net/inca-ip_sw.c')
| -rw-r--r-- | drivers/net/inca-ip_sw.c | 817 | 
1 files changed, 817 insertions, 0 deletions
| diff --git a/drivers/net/inca-ip_sw.c b/drivers/net/inca-ip_sw.c new file mode 100644 index 000000000..e4aaed6af --- /dev/null +++ b/drivers/net/inca-ip_sw.c @@ -0,0 +1,817 @@ +/* + * INCA-IP internal switch ethernet driver. + * + * (C) Copyright 2003-2004 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * See file CREDITS for list of people who contributed to this + * project. + * + * 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> + +#if defined(CONFIG_CMD_NET) \ +	&& defined(CONFIG_NET_MULTI) && defined(CONFIG_INCA_IP_SWITCH) + +#include <malloc.h> +#include <net.h> +#include <asm/inca-ip.h> +#include <asm/addrspace.h> + + +#define NUM_RX_DESC	PKTBUFSRX +#define NUM_TX_DESC	3 +#define TOUT_LOOP	1000000 + + +#define DELAY	udelay(10000) +  /* Sometimes the store word instruction hangs while writing to one +   * of the Switch registers. Moving the instruction into a separate +   * function somehow makes the problem go away. +   */ +static void SWORD(volatile u32 * reg, u32 value) +{ +	*reg = value; +} + +#define DMA_WRITE_REG(reg, value) *((volatile u32 *)reg) = (u32)value; +#define DMA_READ_REG(reg, value)    value = (u32)*((volatile u32*)reg) +#define SW_WRITE_REG(reg, value)   \ +	SWORD(reg, value);\ +	DELAY;\ +	SWORD(reg, value); + +#define SW_READ_REG(reg, value)	   \ +	value = (u32)*((volatile u32*)reg);\ +	DELAY;\ +	value = (u32)*((volatile u32*)reg); + +#define INCA_DMA_TX_POLLING_TIME	0x07 +#define INCA_DMA_RX_POLLING_TIME	0x07 + +#define INCA_DMA_TX_HOLD		0x80000000 +#define INCA_DMA_TX_EOP			0x40000000 +#define INCA_DMA_TX_SOP			0x20000000 +#define INCA_DMA_TX_ICPT		0x10000000 +#define INCA_DMA_TX_IEOP		0x08000000 + +#define INCA_DMA_RX_C			0x80000000 +#define INCA_DMA_RX_SOP			0x40000000 +#define INCA_DMA_RX_EOP			0x20000000 + +#define INCA_SWITCH_PHY_SPEED_10H	0x1 +#define INCA_SWITCH_PHY_SPEED_10F	0x5 +#define INCA_SWITCH_PHY_SPEED_100H	0x2 +#define INCA_SWITCH_PHY_SPEED_100F	0x6 + +/************************ Auto MDIX settings ************************/ +#define INCA_IP_AUTO_MDIX_LAN_PORTS_DIR		INCA_IP_Ports_P1_DIR +#define INCA_IP_AUTO_MDIX_LAN_PORTS_ALTSEL	INCA_IP_Ports_P1_ALTSEL +#define INCA_IP_AUTO_MDIX_LAN_PORTS_OUT		INCA_IP_Ports_P1_OUT +#define INCA_IP_AUTO_MDIX_LAN_GPIO_PIN_RXTX	16 + +#define WAIT_SIGNAL_RETRIES			100 +#define WAIT_LINK_RETRIES			100 +#define LINK_RETRY_DELAY			2000  /* ms */ +/********************************************************************/ + +typedef struct +{ +	union { +		struct { +			volatile u32 HOLD		:1; +			volatile u32 ICpt		:1; +			volatile u32 IEop		:1; +			volatile u32 offset		:3; +			volatile u32 reserved0		:4; +			volatile u32 NFB		:22; +		}field; + +		volatile u32 word; +	}params; + +	volatile u32 nextRxDescPtr; + +	volatile u32 RxDataPtr; + +	union { +		struct { +			volatile u32 C			:1; +			volatile u32 Sop		:1; +			volatile u32 Eop		:1; +			volatile u32 reserved3		:12; +			volatile u32 NBT		:17; +		}field; + +		volatile u32 word; +	}status; + +} inca_rx_descriptor_t; + + +typedef struct +{ +	union { +		struct { +			volatile u32 HOLD		:1; +			volatile u32 Eop		:1; +			volatile u32 Sop		:1; +			volatile u32 ICpt		:1; +			volatile u32 IEop		:1; +			volatile u32 reserved0		:5; +			volatile u32 NBA		:22; +		}field; + +		volatile u32 word; +	}params; + +	volatile u32 nextTxDescPtr; + +	volatile u32 TxDataPtr; + +	volatile u32 C			:1; +	volatile u32 reserved3		:31; + +} inca_tx_descriptor_t; + + +static inca_rx_descriptor_t rx_ring[NUM_RX_DESC] __attribute__ ((aligned(16))); +static inca_tx_descriptor_t tx_ring[NUM_TX_DESC] __attribute__ ((aligned(16))); + +static int tx_new, rx_new, tx_hold, rx_hold; +static int tx_old_hold = -1; +static int initialized	= 0; + + +static int inca_switch_init(struct eth_device *dev, bd_t * bis); +static int inca_switch_send(struct eth_device *dev, volatile void *packet, int length); +static int inca_switch_recv(struct eth_device *dev); +static void inca_switch_halt(struct eth_device *dev); +static void inca_init_switch_chip(void); +static void inca_dma_init(void); +static int inca_amdix(void); + + +int inca_switch_initialize(bd_t * bis) +{ +	struct eth_device *dev; + +#if 0 +	printf("Entered inca_switch_initialize()\n"); +#endif + +	if (!(dev = (struct eth_device *) malloc (sizeof *dev))) { +		printf("Failed to allocate memory\n"); +		return 0; +	} +	memset(dev, 0, sizeof(*dev)); + +	inca_dma_init(); + +	inca_init_switch_chip(); + +#if defined(CONFIG_INCA_IP_SWITCH_AMDIX) +	inca_amdix(); +#endif + +	sprintf(dev->name, "INCA-IP Switch"); +	dev->init = inca_switch_init; +	dev->halt = inca_switch_halt; +	dev->send = inca_switch_send; +	dev->recv = inca_switch_recv; + +	eth_register(dev); + +#if 0 +	printf("Leaving inca_switch_initialize()\n"); +#endif + +	return 1; +} + + +static int inca_switch_init(struct eth_device *dev, bd_t * bis) +{ +	int i; +	u32 v, regValue; +	u16 wTmp; + +#if 0 +	printf("Entering inca_switch_init()\n"); +#endif + +	/* Set MAC address. +	 */ +	wTmp = (u16)dev->enetaddr[0]; +	regValue = (wTmp << 8) | dev->enetaddr[1]; + +	SW_WRITE_REG(INCA_IP_Switch_PMAC_SA1, regValue); + +	wTmp = (u16)dev->enetaddr[2]; +	regValue = (wTmp << 8) | dev->enetaddr[3]; +	regValue = regValue << 16; +	wTmp = (u16)dev->enetaddr[4]; +	regValue |= (wTmp<<8) | dev->enetaddr[5]; + +	SW_WRITE_REG(INCA_IP_Switch_PMAC_SA2, regValue); + +	/* Initialize the descriptor rings. +	 */ +	for (i = 0; i < NUM_RX_DESC; i++) { +		inca_rx_descriptor_t * rx_desc = KSEG1ADDR(&rx_ring[i]); +		memset(rx_desc, 0, sizeof(rx_ring[i])); + +		/* Set maximum size of receive buffer. +		 */ +		rx_desc->params.field.NFB = PKTSIZE_ALIGN; + +		/* Set the offset of the receive buffer. Zero means +		 * that the offset mechanism is not used. +		 */ +		rx_desc->params.field.offset = 0; + +		/* Check if it is the last descriptor. +		 */ +		if (i == (NUM_RX_DESC - 1)) { +			/* Let the last descriptor point to the first +			 * one. +			 */ +			rx_desc->nextRxDescPtr = KSEG1ADDR((u32)rx_ring); +		} else { +			/* Set the address of the next descriptor. +			 */ +			rx_desc->nextRxDescPtr = (u32)KSEG1ADDR(&rx_ring[i+1]); +		} + +		rx_desc->RxDataPtr = (u32)KSEG1ADDR(NetRxPackets[i]); +	} + +#if 0 +	printf("rx_ring = 0x%08X 0x%08X\n", (u32)rx_ring, (u32)&rx_ring[0]); +	printf("tx_ring = 0x%08X 0x%08X\n", (u32)tx_ring, (u32)&tx_ring[0]); +#endif + +	for (i = 0; i < NUM_TX_DESC; i++) { +		inca_tx_descriptor_t * tx_desc = KSEG1ADDR(&tx_ring[i]); + +		memset(tx_desc, 0, sizeof(tx_ring[i])); + +		tx_desc->params.word	   = 0; +		tx_desc->params.field.HOLD = 1; +		tx_desc->C		   = 1; + +			/* Check if it is the last descriptor. +			 */ +		if (i == (NUM_TX_DESC - 1)) { +				/* Let the last descriptor point to the +				 * first one. +				 */ +			tx_desc->nextTxDescPtr = KSEG1ADDR((u32)tx_ring); +		} else { +				/* Set the address of the next descriptor. +				 */ +			tx_desc->nextTxDescPtr = (u32)KSEG1ADDR(&tx_ring[i+1]); +		} +	} + +	/* Initialize RxDMA. +	 */ +	DMA_READ_REG(INCA_IP_DMA_DMA_RXISR, v); +#if 0 +	printf("RX status = 0x%08X\n", v); +#endif + +	/* Writing to the FRDA of CHANNEL. +	 */ +	DMA_WRITE_REG(INCA_IP_DMA_DMA_RXFRDA0, (u32)rx_ring); + +	/* Writing to the COMMAND REG. +	 */ +	DMA_WRITE_REG(INCA_IP_DMA_DMA_RXCCR0, INCA_IP_DMA_DMA_RXCCR0_INIT); + +	/* Initialize TxDMA. +	 */ +	DMA_READ_REG(INCA_IP_DMA_DMA_TXISR, v); +#if 0 +	printf("TX status = 0x%08X\n", v); +#endif + +	/* Writing to the FRDA of CHANNEL. +	 */ +	DMA_WRITE_REG(INCA_IP_DMA_DMA_TXFRDA0, (u32)tx_ring); + +	tx_new = rx_new = 0; + +	tx_hold = NUM_TX_DESC - 1; +	rx_hold = NUM_RX_DESC - 1; + +#if 0 +	rx_ring[rx_hold].params.field.HOLD = 1; +#endif +	/* enable spanning tree forwarding, enable the CPU port */ +	/* ST_PT: +	 *	CPS (CPU port status)	0x3 (forwarding) +	 *	LPS (LAN port status)	0x3 (forwarding) +	 *	PPS (PC port status)	0x3 (forwarding) +	 */ +	SW_WRITE_REG(INCA_IP_Switch_ST_PT,0x3f); + +#if 0 +	printf("Leaving inca_switch_init()\n"); +#endif + +	return 0; +} + + +static int inca_switch_send(struct eth_device *dev, volatile void *packet, int length) +{ +	int		       i; +	int		       res	= -1; +	u32		       command; +	u32		       regValue; +	inca_tx_descriptor_t * tx_desc	= KSEG1ADDR(&tx_ring[tx_new]); + +#if 0 +	printf("Entered inca_switch_send()\n"); +#endif + +	if (length <= 0) { +		printf ("%s: bad packet size: %d\n", dev->name, length); +		goto Done; +	} + +	for(i = 0; tx_desc->C == 0; i++) { +		if (i >= TOUT_LOOP) { +			printf("%s: tx error buffer not ready\n", dev->name); +			goto Done; +		} +	} + +	if (tx_old_hold >= 0) { +		KSEG1ADDR(&tx_ring[tx_old_hold])->params.field.HOLD = 1; +	} +	tx_old_hold = tx_hold; + +	tx_desc->params.word = +			(INCA_DMA_TX_SOP | INCA_DMA_TX_EOP | INCA_DMA_TX_HOLD); + +	tx_desc->C = 0; +	tx_desc->TxDataPtr = (u32)packet; +	tx_desc->params.field.NBA = length; + +	KSEG1ADDR(&tx_ring[tx_hold])->params.field.HOLD = 0; + +	tx_hold = tx_new; +	tx_new	= (tx_new + 1) % NUM_TX_DESC; + + +	if (! initialized) { +		command = INCA_IP_DMA_DMA_TXCCR0_INIT; +		initialized = 1; +	} else { +		command = INCA_IP_DMA_DMA_TXCCR0_HR; +	} + +	DMA_READ_REG(INCA_IP_DMA_DMA_TXCCR0, regValue); +	regValue |= command; +#if 0 +	printf("regValue = 0x%x\n", regValue); +#endif +	DMA_WRITE_REG(INCA_IP_DMA_DMA_TXCCR0, regValue); + +#if 1 +	for(i = 0; KSEG1ADDR(&tx_ring[tx_hold])->C == 0; i++) { +		if (i >= TOUT_LOOP) { +			printf("%s: tx buffer not ready\n", dev->name); +			goto Done; +		} +	} +#endif +	res = length; +Done: +#if 0 +	printf("Leaving inca_switch_send()\n"); +#endif +	return res; +} + + +static int inca_switch_recv(struct eth_device *dev) +{ +	int		       length  = 0; +	inca_rx_descriptor_t * rx_desc; + +#if 0 +	printf("Entered inca_switch_recv()\n"); +#endif + +	for (;;) { +		rx_desc = KSEG1ADDR(&rx_ring[rx_new]); + +		if (rx_desc->status.field.C == 0) { +			break; +		} + +#if 0 +		rx_ring[rx_new].params.field.HOLD = 1; +#endif + +		if (! rx_desc->status.field.Eop) { +			printf("Partly received packet!!!\n"); +			break; +		} + +		length = rx_desc->status.field.NBT; +		rx_desc->status.word &= +			 ~(INCA_DMA_RX_EOP | INCA_DMA_RX_SOP | INCA_DMA_RX_C); +#if 0 +{ +  int i; +  for (i=0;i<length - 4;i++) { +    if (i % 16 == 0) printf("\n%04x: ", i); +    printf("%02X ", NetRxPackets[rx_new][i]); +  } +  printf("\n"); +} +#endif + +		if (length) { +#if 0 +			printf("Received %d bytes\n", length); +#endif +			NetReceive((void*)KSEG1ADDR(NetRxPackets[rx_new]), length - 4); +		} else { +#if 1 +			printf("Zero length!!!\n"); +#endif +		} + + +		KSEG1ADDR(&rx_ring[rx_hold])->params.field.HOLD = 0; + +		rx_hold = rx_new; + +		rx_new = (rx_new + 1) % NUM_RX_DESC; +	} + +#if 0 +	printf("Leaving inca_switch_recv()\n"); +#endif + +	return length; +} + + +static void inca_switch_halt(struct eth_device *dev) +{ +#if 0 +	printf("Entered inca_switch_halt()\n"); +#endif + +#if 1 +	initialized = 0; +#endif +#if 1 +	/* Disable forwarding to the CPU port. +	 */ +	SW_WRITE_REG(INCA_IP_Switch_ST_PT,0xf); + +	/* Close RxDMA channel. +	 */ +	DMA_WRITE_REG(INCA_IP_DMA_DMA_RXCCR0, INCA_IP_DMA_DMA_RXCCR0_OFF); + +	/* Close TxDMA channel. +	 */ +	DMA_WRITE_REG(INCA_IP_DMA_DMA_TXCCR0, INCA_IP_DMA_DMA_TXCCR0_OFF); + + +#endif +#if 0 +	printf("Leaving inca_switch_halt()\n"); +#endif +} + + +static void inca_init_switch_chip(void) +{ +	u32 regValue; + +	/* To workaround a problem with collision counter +	 * (see Errata sheet). +	 */ +	SW_WRITE_REG(INCA_IP_Switch_PC_TX_CTL, 0x00000001); +	SW_WRITE_REG(INCA_IP_Switch_LAN_TX_CTL, 0x00000001); + +#if 1 +	/* init MDIO configuration: +	 *	MDS (Poll speed):	0x01 (4ms) +	 *	PHY_LAN_ADDR:		0x06 +	 *	PHY_PC_ADDR:		0x05 +	 *	UEP (Use External PHY): 0x00 (Internal PHY is used) +	 *	PS (Port Select):	0x00 (PT/UMM for LAN) +	 *	PT (PHY Test):		0x00 (no test mode) +	 *	UMM (Use MDIO Mode):	0x00 (state machine is disabled) +	 */ +	SW_WRITE_REG(INCA_IP_Switch_MDIO_CFG, 0x4c50); + +	/* init PHY: +	 *	SL (Auto Neg. Speed for LAN) +	 *	SP (Auto Neg. Speed for PC) +	 *	LL (Link Status for LAN) +	 *	LP (Link Status for PC) +	 *	DL (Duplex Status for LAN) +	 *	DP (Duplex Status for PC) +	 *	PL (Auto Neg. Pause Status for LAN) +	 *	PP (Auto Neg. Pause Status for PC) +	 */ +	SW_WRITE_REG (INCA_IP_Switch_EPHY, 0xff); + +	/* MDIO_ACC: +	 *	RA (Request/Ack)  0x01 (Request) +	 *	RW (Read/Write)	  0x01 (Write) +	 *	PHY_ADDR	  0x05 (PC) +	 *	REG_ADDR	  0x00 (PHY_BCR: basic control register) +	 *	PHY_DATA	  0x8000 +	 *		      Reset		      - software reset +	 *		      LB (loop back)	      - normal +	 *		      SS (speed select)	      - 10 Mbit/s +	 *		      ANE (auto neg. enable)  - enable +	 *		      PD (power down)	      - normal +	 *		      ISO (isolate)	      - normal +	 *		      RAN (restart auto neg.) - normal +	 *		      DM (duplex mode)	      - half duplex +	 *		      CT (collision test)     - enable +	 */ +	SW_WRITE_REG(INCA_IP_Switch_MDIO_ACC, 0xc0a09000); + +	/* MDIO_ACC: +	 *	RA (Request/Ack)  0x01 (Request) +	 *	RW (Read/Write)	  0x01 (Write) +	 *	PHY_ADDR	  0x06 (LAN) +	 *	REG_ADDR	  0x00 (PHY_BCR: basic control register) +	 *	PHY_DATA	  0x8000 +	 *		      Reset		      - software reset +	 *		      LB (loop back)	      - normal +	 *		      SS (speed select)	      - 10 Mbit/s +	 *		      ANE (auto neg. enable)  - enable +	 *		      PD (power down)	      - normal +	 *		      ISO (isolate)	      - normal +	 *		      RAN (restart auto neg.) - normal +	 *		      DM (duplex mode)	      - half duplex +	 *		      CT (collision test)     - enable +	 */ +	SW_WRITE_REG(INCA_IP_Switch_MDIO_ACC, 0xc0c09000); + +#endif + +	/* Make sure the CPU port is disabled for now. We +	 * don't want packets to get stacked for us until +	 * we enable DMA and are prepared to receive them. +	 */ +	SW_WRITE_REG(INCA_IP_Switch_ST_PT,0xf); + +	SW_READ_REG(INCA_IP_Switch_ARL_CTL, regValue); + +	/* CRC GEN is enabled. +	 */ +	regValue |= 0x00000200; +	SW_WRITE_REG(INCA_IP_Switch_ARL_CTL, regValue); + +	/* ADD TAG is disabled. +	 */ +	SW_READ_REG(INCA_IP_Switch_PMAC_HD_CTL, regValue); +	regValue &= ~0x00000002; +	SW_WRITE_REG(INCA_IP_Switch_PMAC_HD_CTL, regValue); +} + + +static void inca_dma_init(void) +{ +	/* Switch off all DMA channels. +	 */ +	DMA_WRITE_REG(INCA_IP_DMA_DMA_RXCCR0, INCA_IP_DMA_DMA_RXCCR0_OFF); +	DMA_WRITE_REG(INCA_IP_DMA_DMA_RXCCR1, INCA_IP_DMA_DMA_RXCCR1_OFF); + +	DMA_WRITE_REG(INCA_IP_DMA_DMA_TXCCR0, INCA_IP_DMA_DMA_RXCCR0_OFF); +	DMA_WRITE_REG(INCA_IP_DMA_DMA_TXCCR1, INCA_IP_DMA_DMA_TXCCR1_OFF); +	DMA_WRITE_REG(INCA_IP_DMA_DMA_TXCCR2, INCA_IP_DMA_DMA_TXCCR2_OFF); + +	/* Setup TX channel polling time. +	 */ +	DMA_WRITE_REG(INCA_IP_DMA_DMA_TXPOLL, INCA_DMA_TX_POLLING_TIME); + +	/* Setup RX channel polling time. +	 */ +	DMA_WRITE_REG(INCA_IP_DMA_DMA_RXPOLL, INCA_DMA_RX_POLLING_TIME); + +	/* ERRATA: write reset value into the DMA RX IMR register. +	 */ +	DMA_WRITE_REG(INCA_IP_DMA_DMA_RXIMR, 0xFFFFFFFF); + +	/* Just in case: disable all transmit interrupts also. +	 */ +	DMA_WRITE_REG(INCA_IP_DMA_DMA_TXIMR, 0xFFFFFFFF); + +	DMA_WRITE_REG(INCA_IP_DMA_DMA_TXISR, 0xFFFFFFFF); +	DMA_WRITE_REG(INCA_IP_DMA_DMA_RXISR, 0xFFFFFFFF); +} + +#if defined(CONFIG_INCA_IP_SWITCH_AMDIX) +static int inca_amdix(void) +{ +	u32 phyReg1 = 0; +	u32 phyReg4 = 0; +	u32 phyReg5 = 0; +	u32 phyReg6 = 0; +	u32 phyReg31 = 0; +	u32 regEphy = 0; +	int mdi_flag; +	int retries; + +	/* Setup GPIO pins. +	 */ +	*INCA_IP_AUTO_MDIX_LAN_PORTS_DIR    |= (1 << INCA_IP_AUTO_MDIX_LAN_GPIO_PIN_RXTX); +	*INCA_IP_AUTO_MDIX_LAN_PORTS_ALTSEL |= (1 << INCA_IP_AUTO_MDIX_LAN_GPIO_PIN_RXTX); + +#if 0 +	/* Wait for signal. +	 */ +	retries = WAIT_SIGNAL_RETRIES; +	while (--retries) { +		SW_WRITE_REG(INCA_IP_Switch_MDIO_ACC, +				(0x1 << 31) |	/* RA		*/ +				(0x0 << 30) |	/* Read		*/ +				(0x6 << 21) |	/* LAN		*/ +				(17  << 16));	/* PHY_MCSR	*/ +		do { +			SW_READ_REG(INCA_IP_Switch_MDIO_ACC, phyReg1); +		} while (phyReg1 & (1 << 31)); + +		if (phyReg1 & (1 << 1)) { +			/* Signal detected */ +			break; +		} +	} + +	if (!retries) +		goto Fail; +#endif + +	/* Set MDI mode. +	 */ +	*INCA_IP_AUTO_MDIX_LAN_PORTS_OUT &= ~(1 << INCA_IP_AUTO_MDIX_LAN_GPIO_PIN_RXTX); +	mdi_flag = 1; + +	/* Wait for link. +	 */ +	retries = WAIT_LINK_RETRIES; +	while (--retries) { +		udelay(LINK_RETRY_DELAY * 1000); +		SW_WRITE_REG(INCA_IP_Switch_MDIO_ACC, +				(0x1 << 31) |	/* RA		*/ +				(0x0 << 30) |	/* Read		*/ +				(0x6 << 21) |	/* LAN		*/ +				(1   << 16));	/* PHY_BSR	*/ +		do { +			SW_READ_REG(INCA_IP_Switch_MDIO_ACC, phyReg1); +		} while (phyReg1 & (1 << 31)); + +		if (phyReg1 & (1 << 2)) { +			/* Link is up */ +			break; +		} else if (mdi_flag) { +			/* Set MDIX mode */ +			*INCA_IP_AUTO_MDIX_LAN_PORTS_OUT |= (1 << INCA_IP_AUTO_MDIX_LAN_GPIO_PIN_RXTX); +			mdi_flag = 0; +		} else { +			/* Set MDI mode */ +			*INCA_IP_AUTO_MDIX_LAN_PORTS_OUT &= ~(1 << INCA_IP_AUTO_MDIX_LAN_GPIO_PIN_RXTX); +			mdi_flag = 1; +		} +	} + +	if (!retries) { +		goto Fail; +	} else { +		SW_WRITE_REG(INCA_IP_Switch_MDIO_ACC, +				(0x1 << 31) |	/* RA		*/ +				(0x0 << 30) |	/* Read		*/ +				(0x6 << 21) |	/* LAN		*/ +				(1   << 16));	/* PHY_BSR	*/ +		do { +			SW_READ_REG(INCA_IP_Switch_MDIO_ACC, phyReg1); +		} while (phyReg1 & (1 << 31)); + +		/* Auto-negotiation / Parallel detection complete +		 */ +		if (phyReg1 & (1 << 5)) { +			SW_WRITE_REG(INCA_IP_Switch_MDIO_ACC, +				(0x1 << 31) |	/* RA		*/ +				(0x0 << 30) |	/* Read		*/ +				(0x6 << 21) |	/* LAN		*/ +				(31  << 16));	/* PHY_SCSR	*/ +			do { +				SW_READ_REG(INCA_IP_Switch_MDIO_ACC, phyReg31); +			} while (phyReg31 & (1 << 31)); + +			switch ((phyReg31 >> 2) & 0x7) { +			case INCA_SWITCH_PHY_SPEED_10H: +				/* 10Base-T Half-duplex */ +				regEphy = 0; +				break; +			case INCA_SWITCH_PHY_SPEED_10F: +				/* 10Base-T Full-duplex */ +				regEphy = INCA_IP_Switch_EPHY_DL; +				break; +			case INCA_SWITCH_PHY_SPEED_100H: +				/* 100Base-TX Half-duplex */ +				regEphy = INCA_IP_Switch_EPHY_SL; +				break; +			case INCA_SWITCH_PHY_SPEED_100F: +				/* 100Base-TX Full-duplex */ +				regEphy = INCA_IP_Switch_EPHY_SL | INCA_IP_Switch_EPHY_DL; +				break; +			} + +			/* In case of Auto-negotiation, +			 * update the negotiated PAUSE support status +			 */ +			if (phyReg1 & (1 << 3)) { +				SW_WRITE_REG(INCA_IP_Switch_MDIO_ACC, +					(0x1 << 31) |	/* RA		*/ +					(0x0 << 30) |	/* Read		*/ +					(0x6 << 21) |	/* LAN		*/ +					(6   << 16));	/* PHY_ANER	*/ +				do { +					SW_READ_REG(INCA_IP_Switch_MDIO_ACC, phyReg6); +				} while (phyReg6 & (1 << 31)); + +				/* We are Autoneg-able. +				 * Is Link partner also able to autoneg? +				 */ +				if (phyReg6 & (1 << 0)) { +					SW_WRITE_REG(INCA_IP_Switch_MDIO_ACC, +						(0x1 << 31) |	/* RA		*/ +						(0x0 << 30) |	/* Read		*/ +						(0x6 << 21) |	/* LAN		*/ +						(4   << 16));	/* PHY_ANAR	*/ +					do { +						SW_READ_REG(INCA_IP_Switch_MDIO_ACC, phyReg4); +					} while (phyReg4 & (1 << 31)); + +					/* We advertise PAUSE capab. +					 * Does link partner also advertise it? +					 */ +					if (phyReg4 & (1 << 10)) { +						SW_WRITE_REG(INCA_IP_Switch_MDIO_ACC, +							(0x1 << 31) |	/* RA		*/ +							(0x0 << 30) |	/* Read		*/ +							(0x6 << 21) |	/* LAN		*/ +							(5   << 16));	/* PHY_ANLPAR	*/ +						do { +							SW_READ_REG(INCA_IP_Switch_MDIO_ACC, phyReg5); +						} while (phyReg5 & (1 << 31)); + +						/* Link partner is PAUSE capab. +						 */ +						if (phyReg5 & (1 << 10)) { +							regEphy |= INCA_IP_Switch_EPHY_PL; +						} +					} +				} + +			} + +			/* Link is up */ +			regEphy |= INCA_IP_Switch_EPHY_LL; + +			SW_WRITE_REG(INCA_IP_Switch_EPHY, regEphy); +		} +	} + +	return 0; + +Fail: +	printf("No Link on LAN port\n"); +	return -1; +} +#endif /* CONFIG_INCA_IP_SWITCH_AMDIX */ + +#endif |