LL-temac SGDMA and FIFO mode for Microblaze

  • This driver version is not fully compatible with latest U-BOOT mainline code. Please look at git tree to see almost latest u-boot version

with this driver — Michal Simek 2009/09/29 20:36

  • support both mode
  • don't forget to check your phy setting
  • Driver support ML505 and S3e board
  • latest U-BOOT bsp support both mode
  • Open lib_microblaze/cache.c and fill flush_cache function with this code
    • these code is not clear because there is not counted with caches address
void flush_cache (ulong addr, ulong size)
{
	int i;
	for (i = 0; i < size; i += 4)
		asm volatile (
				"wic	%0, r0;"
				"nop;"
				"wdc	%0, r0;"
				"nop;"
				:
				: "r" (addr + i)
				: "memory");
}
  • Add latest line to drivers/net/Makefile for enabling ll_temac driver

COBJS-$(CONFIG_XILINX_EMAC) += xilinx_emac.o

COBJS-$(CONFIG_XILINX_EMACLITE) += xilinx_emaclite.o
COBJS-$(CONFIG_XILINX_LL_TEMAC) += xilinx_ll_temac.o
  • Copy this code to drivers/net/xilinx_ll_temac.c
/*
 *
 * Xilinx xps_ll_temac ethernet driver for u-boot
 *
 * Author: Yoshio Kashiwagi kashiwagi@co-nss.co.jp
 *
 * Copyright (C) 2008 Michal Simek <monstr@monstr.eu>
 * June 2008 Microblaze optimalization, FIFO mode support
 *
 * Copyright (C) 2008 Nissin Systems Co.,Ltd.
 * March 2008 created
 *
 * 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.
 *
 */

#include <config.h>
#include <common.h>
#include <net.h>
#include <malloc.h>
#include <asm/processor.h>
#include <asm/io.h>

#ifdef XILINX_LLTEMAC_FIFO_BASEADDR
# define FIFO_MODE	1
#elif XILINX_LLTEMAC_SDMA_CTRL_BASEADDR
# define SDMA_MODE	1
#else
# error Unsupported mode
#endif

#ifdef SDMA_MODE
/* XPS_LL_TEMAC SDMA registers definition */
# define TX_NXTDESC_PTR		(XILINX_LLTEMAC_SDMA_CTRL_BASEADDR + 0x00)
# define TX_CURBUF_ADDR		(XILINX_LLTEMAC_SDMA_CTRL_BASEADDR + 0x04)
# define TX_CURBUF_LENGTH	(XILINX_LLTEMAC_SDMA_CTRL_BASEADDR + 0x08)
# define TX_CURDESC_PTR		(XILINX_LLTEMAC_SDMA_CTRL_BASEADDR + 0x0c)
# define TX_TAILDESC_PTR	(XILINX_LLTEMAC_SDMA_CTRL_BASEADDR + 0x10)
# define TX_CHNL_CTRL		(XILINX_LLTEMAC_SDMA_CTRL_BASEADDR + 0x14)
# define TX_IRQ_REG		(XILINX_LLTEMAC_SDMA_CTRL_BASEADDR + 0x18)
# define TX_CHNL_STS		(XILINX_LLTEMAC_SDMA_CTRL_BASEADDR + 0x1c)

# define RX_NXTDESC_PTR		(XILINX_LLTEMAC_SDMA_CTRL_BASEADDR + 0x20)
# define RX_CURBUF_ADDR		(XILINX_LLTEMAC_SDMA_CTRL_BASEADDR + 0x24)
# define RX_CURBUF_LENGTH	(XILINX_LLTEMAC_SDMA_CTRL_BASEADDR + 0x28)
# define RX_CURDESC_PTR		(XILINX_LLTEMAC_SDMA_CTRL_BASEADDR + 0x2c)
# define RX_TAILDESC_PTR	(XILINX_LLTEMAC_SDMA_CTRL_BASEADDR + 0x30)
# define RX_CHNL_CTRL		(XILINX_LLTEMAC_SDMA_CTRL_BASEADDR + 0x34)
# define RX_IRQ_REG		(XILINX_LLTEMAC_SDMA_CTRL_BASEADDR + 0x38)
# define RX_CHNL_STS		(XILINX_LLTEMAC_SDMA_CTRL_BASEADDR + 0x3c)

# define DMA_CONTROL_REG	(XILINX_LLTEMAC_SDMA_CTRL_BASEADDR + 0x40)
#endif

/* XPS_LL_TEMAC direct registers definition */
#define TEMAC_RAF0		(XILINX_LLTEMAC_BASEADDR + 0x00)
#define TEMAC_TPF0		(XILINX_LLTEMAC_BASEADDR + 0x04)
#define TEMAC_IFGP0		(XILINX_LLTEMAC_BASEADDR + 0x08)
#define TEMAC_IS0		(XILINX_LLTEMAC_BASEADDR + 0x0c)
#define TEMAC_IP0		(XILINX_LLTEMAC_BASEADDR + 0x10)
#define TEMAC_IE0		(XILINX_LLTEMAC_BASEADDR + 0x14)

#define TEMAC_MSW0		(XILINX_LLTEMAC_BASEADDR + 0x20)
#define TEMAC_LSW0		(XILINX_LLTEMAC_BASEADDR + 0x24)
#define TEMAC_CTL0		(XILINX_LLTEMAC_BASEADDR + 0x28)
#define TEMAC_RDY0		(XILINX_LLTEMAC_BASEADDR + 0x2c)

#define XTE_RSE_MIIM_RR_MASK	0x0002
#define XTE_RSE_MIIM_WR_MASK	0x0004
#define XTE_RSE_CFG_RR_MASK	0x0020
#define XTE_RSE_CFG_WR_MASK	0x0040

/* XPS_LL_TEMAC indirect registers offset definition */

#define RCW0	0x200
#define RCW1	0x240
#define TC	0x280
#define FCC	0x2c0
#define EMMC	0x300
#define PHYC	0x320
#define MC	0x340
#define UAW0	0x380
#define UAW1	0x384
#define MAW0	0x388
#define MAW1	0x38c
#define AFM	0x390
#define TIS	0x3a0
#define TIE	0x3a4
#define MIIMWD	0x3b0
#define MIIMAI	0x3b4

#define CNTLREG_WRITE_ENABLE_MASK	0x8000
#define CNTLREG_EMAC1SEL_MASK		0x0400
#define CNTLREG_ADDRESSCODE_MASK	0x03ff

#define MDIO_ENABLE_MASK	0x40
#define MDIO_CLOCK_DIV_MASK	0x3F
#define MDIO_CLOCK_DIV_100MHz	0x28

#define ETHER_MTU		1520

#ifdef SDMA_MODE
/* CDMAC descriptor status bit definitions */
# define BDSTAT_ERROR_MASK		0x80
# define BDSTAT_INT_ON_END_MASK		0x40
# define BDSTAT_STOP_ON_END_MASK	0x20
# define BDSTAT_COMPLETED_MASK		0x10
# define BDSTAT_SOP_MASK		0x08
# define BDSTAT_EOP_MASK		0x04
# define BDSTAT_CHANBUSY_MASK		0x02
# define BDSTAT_CHANRESET_MASK		0x01

/* SDMA Buffer Descriptor */

typedef struct cdmac_bd_t {
	struct cdmac_bd_t *next_p;
	unsigned char *phys_buf_p;
	unsigned long buf_len;
	unsigned char stat;
	unsigned char app1_1;
	unsigned short app1_2;
	unsigned long app2;
	unsigned long app3;
	unsigned long app4;
	unsigned long app5;
} cdmac_bd __attribute((aligned(32))) ;

static cdmac_bd	tx_bd;
static cdmac_bd	rx_bd;
#endif

#ifdef FIFO_MODE
typedef struct ll_fifo_s {
	int isr; /* Interrupt Status Register 0x0 */
	int ier; /* Interrupt Enable Register 0x4 */
	int tdfr; /* Transmit data FIFO reset 0x8 */
	int tdfv; /* Transmit data FIFO Vacancy 0xC */
	int tdfd; /* Transmit data FIFO 32bit wide data write port 0x10 */
	int tlf; /* Write Transmit Length FIFO 0x14 */
	int rdfr; /* Read Receive data FIFO reset 0x18 */
	int rdfo; /* Receive data FIFO Occupancy 0x1C */
	int rdfd; /* Read Receive data FIFO 32bit wide data read port 0x20 */
	int rlf; /* Read Receive Length FIFO 0x24 */
	int llr; /* Read LocalLink reset 0x28 */
} ll_fifo_s;

ll_fifo_s *ll_fifo = (ll_fifo_s *) (XILINX_LLTEMAC_FIFO_BASEADDR);
#endif

static unsigned char tx_buffer[ETHER_MTU] __attribute((aligned(32)));
static unsigned char rx_buffer[ETHER_MTU] __attribute((aligned(32)));

#if !defined(CONFIG_NET_MULTI)
//static struct eth_device *xps_ll_dev = NULL;
#endif

struct xps_ll_temac_private {
	int idx;
	unsigned char dev_addr[6];
};

/* undirect hostif write to ll_temac */
static void xps_ll_temac_hostif_set(int emac, int phy_addr,
					int reg_addr, int phy_data)
{
	out_be32((u32 *)TEMAC_LSW0, phy_data);
	out_be32((u32 *)TEMAC_CTL0, CNTLREG_WRITE_ENABLE_MASK | MIIMWD);
	out_be32((u32 *)TEMAC_LSW0, (phy_addr << 5) | (reg_addr));
	out_be32((u32 *)TEMAC_CTL0, \
			CNTLREG_WRITE_ENABLE_MASK | MIIMAI | (emac << 10));
	while(! (in_be32((u32 *)TEMAC_RDY0) & XTE_RSE_MIIM_WR_MASK));
}

/* undirect hostif read from ll_temac */
static unsigned int xps_ll_temac_hostif_get(int emac, int phy_addr, int reg_addr)
{
	out_be32((u32 *)TEMAC_LSW0, (phy_addr << 5) | (reg_addr));
	out_be32((u32 *)TEMAC_CTL0, MIIMAI | (emac << 10));
	while(! (in_be32((u32 *)TEMAC_RDY0) & XTE_RSE_MIIM_RR_MASK));
	return in_be32((u32 *)TEMAC_LSW0);
}

/* undirect write to ll_temac */
static void xps_ll_temac_indirect_set(int emac, int reg_offset, int reg_data)
{
	out_be32((u32 *)TEMAC_LSW0, reg_data);
	out_be32((u32 *)TEMAC_CTL0, \
			CNTLREG_WRITE_ENABLE_MASK | (emac << 10) | reg_offset);
	while(! (in_be32((u32 *)TEMAC_RDY0) & XTE_RSE_CFG_WR_MASK));
}

/* undirect read from ll_temac */
int xps_ll_temac_indirect_get(int emac, int reg_offset)
{
	out_be32((u32 *)TEMAC_CTL0, (emac << 10) | reg_offset);
	while(! (in_be32((u32 *)TEMAC_RDY0) & XTE_RSE_CFG_RR_MASK));
	return in_be32((u32 *)TEMAC_LSW0);
}

#ifdef DEBUG
/* read from phy */
static void read_phy_reg (int phy_addr)
{
	int j, result;
	printf("phy%d ",phy_addr);
	for ( j = 0; j < 32; j++) {
		result = xps_ll_temac_hostif_get(0, phy_addr, j);
		printf("%d: 0x%x ", j, result);
	}
	puts("\n");
}
#endif

static int phy_addr = -1;
static int link = 0;

/* setting ll_temac and phy to proper setting */
static int xps_ll_temac_phy_ctrl(void)
{
	int i;
	unsigned int result;
	unsigned retries = 10;

/* phy reset */
//	xps_ll_temac_hostif_set(0, 0, 0, 0x8000);


	if(phy_addr == -1) {
		for(i = 31; i >= 0; i--) {
			result = xps_ll_temac_hostif_get(0, i, 1);
			if((result & 0x0ffff) != 0x0ffff) {
#ifdef DEBUG
				printf ("phy %x result %x\n", i, result);
#endif
				phy_addr = i;
				break;
			}
		}
	}

	//read_phy_reg(phy_addr);

/* marvell phy test */
/*	xps_ll_temac_hostif_set(0, 7, 25, 0x500); */

	/* wait for link up */
	while (retries-- &&
		((xps_ll_temac_hostif_get(0, phy_addr, 1) & 0x24) == 0x24))
		;

	if(link == 1) {
	/*	puts("link is setup\n"); */
		return 1;
	}

	i = (xps_ll_temac_hostif_get(0, phy_addr, 2) << 16) | \
		xps_ll_temac_hostif_get(0, phy_addr, 3);
//	printf("Phy ID 0x%x\n", i);

#ifdef CONFIG_S3E
		xps_ll_temac_hostif_set(0, phy_addr, 0, 0x8000); /* reset phy */
		xps_ll_temac_indirect_set(0, EMMC, 0x40000000);
		//printf("100BASE-T/FD\n");
		link = 1;
	return 1;
#endif

	/* Marwell 88e1111 id - ml505 */
	if (i == 0x1410cc2) {
		result = xps_ll_temac_hostif_get(0, phy_addr, 5);
		if((result & 0x8000) == 0x8000) {
			xps_ll_temac_indirect_set(0, EMMC, 0x80000000);
			printf("1000BASE-T/FD\n");
			link = 1;
		} else if((result & 0x4000) == 0x4000) {
			xps_ll_temac_indirect_set(0, EMMC, 0x40000000);
			printf("100BASE-T/FD\n");
			link = 1;
		} else {
			printf("Unsupported mode\n");
			link = 0;
		}
		return 1;
	}
	return 0;
}

#ifdef SDMA_MODE
/* bd init */
static void xps_ll_temac_bd_init(void)
{
	memset((void *)&tx_bd, 0, sizeof(cdmac_bd));
	memset((void *)&rx_bd, 0, sizeof(cdmac_bd));

	rx_bd.phys_buf_p = &rx_buffer[0];

	rx_bd.next_p = &rx_bd;
	rx_bd.buf_len = ETHER_MTU;
	flush_cache((u32)&rx_bd, sizeof(cdmac_bd));

	out_be32((u32 *)RX_CURDESC_PTR, (u32)&rx_bd);
	out_be32((u32 *)RX_TAILDESC_PTR, (u32)&rx_bd);
	out_be32((u32 *)RX_NXTDESC_PTR, (u32)&rx_bd); /* setup first fd */

	tx_bd.phys_buf_p = &tx_buffer[0];
	tx_bd.next_p = &tx_bd;

	flush_cache((u32)&tx_bd, sizeof(cdmac_bd));
	out_be32((u32 *)TX_CURDESC_PTR, (u32)&tx_bd);
}
#endif

#ifdef SDMA_MODE
static int xps_ll_temac_send_sdma(unsigned char *buffer, int length)
{
	if( xps_ll_temac_phy_ctrl() == 0)
		return 0;

	memcpy (tx_buffer, buffer, length);
	flush_cache ((u32)tx_buffer, length);

	tx_bd.stat = BDSTAT_SOP_MASK | BDSTAT_EOP_MASK | BDSTAT_STOP_ON_END_MASK;
	tx_bd.buf_len = length;
	flush_cache ((u32)&tx_bd, sizeof(cdmac_bd));

	out_be32((u32 *)TX_CURDESC_PTR, (u32)&tx_bd);
	out_be32((u32 *)TX_TAILDESC_PTR, (u32)&tx_bd); /* DMA start */

	do {
		/* FIXME for system without cache - udelay(10000); */
		flush_cache ((u32)&tx_bd, sizeof(cdmac_bd));
	} while(!(((volatile int)tx_bd.stat) & BDSTAT_COMPLETED_MASK));

	return length;
}


static int xps_ll_temac_recv_sdma(void)
{
	int length;

	/* FIXME for system without cache - udelay(10000); */
	flush_cache ((u32)&rx_bd, sizeof(cdmac_bd));

	if(!(rx_bd.stat & BDSTAT_COMPLETED_MASK)) {
		return 0;
	}

	length = rx_bd.app5;
	/* FIXME for system without cache - udelay(10000); */
	flush_cache ((u32)rx_bd.phys_buf_p, length);

	rx_bd.buf_len = ETHER_MTU;
	rx_bd.stat = 0;
	rx_bd.app5 = 0;

	/* FIXME for system without cache - udelay(10000); */
	flush_cache ((u32)&rx_bd, sizeof(cdmac_bd));
	out_be32((u32 *)RX_TAILDESC_PTR, (u32)&rx_bd);

	if(length > 0) {
		NetReceive(rx_bd.phys_buf_p, length);
	}

	return length;
}
#endif


#ifdef FIFO_MODE
void debugll(int count)
{
	printf ("%d fifo isr 0x%08x, fifo_ier 0x%08x, fifo_rdfr 0x%08x, fifo_rdfo 0x%08x fifo_rlr 0x%08x\n",count, ll_fifo->isr, \
	ll_fifo->ier, ll_fifo->rdfr, ll_fifo->rdfo, ll_fifo->rlf);

}

static int xps_ll_temac_send_fifo(unsigned char *buffer, int length)
{
	u32 *buf = buffer;
	u32 len, i, val;

	len = (length / 4) + 1;

	for (i = 0; i < len; i++) {
		val = *buf++;
		ll_fifo->tdfd = val;
	}

	ll_fifo->tlf = length;

	return length;
}

static int xps_ll_temac_recv_fifo()
{
	int len, len2, i, val;
	int *buf;
	buf = &rx_buffer;

	if (ll_fifo->isr & 0x04000000 ) {
		ll_fifo->isr = 0xffffffff; /* reset isr */
	
		/* while (ll_fifo->isr); */
		len = ll_fifo->rlf & 0x7FF;
		len2 = (len / 4) + 1;
	
		for (i = 0; i < len2; i++) {
			val = ll_fifo->rdfd;
			*buf++ = val ;
		}
	
		/* debugll(1); */
		NetReceive (&rx_buffer, len);
	}
	return 0;
}
#endif

/* setup mac addr */
static int xps_ll_temac_addr_setup(struct xps_ll_temac_private * lp)
{
	char * env_p;
	char * end;
	int i, val;

	env_p = getenv("ethaddr");
	if (env_p == NULL) {
		printf("cannot get enviroment for \"ethaddr\".\n");
		return -1;
	}

	for (i = 0; i < 6; i++) {
		lp->dev_addr[i] = env_p ? simple_strtoul(env_p, &end, 16) : 0;
		if (env_p) env_p = (*end) ? end + 1 : end;
	}

	/* set up unicast MAC address filter */
	val = ((lp->dev_addr[3] << 24) | (lp->dev_addr[2] << 16) |
		(lp->dev_addr[1] << 8) | (lp->dev_addr[0] ));
	xps_ll_temac_indirect_set(0, UAW0, val);
	val = (lp->dev_addr[5] << 8) | lp->dev_addr[4] ;
	xps_ll_temac_indirect_set(0, UAW1, val);

	return 0;
}

static int xps_ll_temac_init(struct eth_device *dev, bd_t *bis)
{
	struct xps_ll_temac_private *lp = (struct xps_ll_temac_private *)dev->priv;

#ifdef SDMA_MODE
	xps_ll_temac_bd_init();
#endif
#ifdef FIFO_MODE
	ll_fifo->tdfr = 0x000000a5; /* set fifo lenght */
	ll_fifo->rdfr = 0x000000a5;

	/* ll_fifo->isr = 0x0; */
	/* ll_fifo->ier = 0x0; */

	/* printf ("fifo isr 0x%08x, fifo_ier 0x%08x, fifo_tdfv 0x%08x, fifo_rdfo 0x%08x fifo_rlf 0x%08x\n", ll_fifo->isr, ll_fifo->ier, ll_fifo->tdfv, ll_fifo->rdfo,ll_fifo->rlf); */
#endif
	xps_ll_temac_indirect_set(0, MC, MDIO_ENABLE_MASK | MDIO_CLOCK_DIV_100MHz);

	xps_ll_temac_addr_setup(lp);
	xps_ll_temac_indirect_set(0, AFM, 0x00000000); /* Promiscuos mode disable */
	xps_ll_temac_indirect_set(0, RCW1, 0x10000000); /* Enable Receiver */
	xps_ll_temac_indirect_set(0, TC, 0x10000000); /* Enable Transmitter */

}

static int xps_ll_temac_halt(void)
{
	xps_ll_temac_indirect_set(0, RCW1, 0x00000000); /* Disable Receiver */
	xps_ll_temac_indirect_set(0, TC, 0x00000000); /* Disable Transmitter */

#ifdef SDMA_MODE
	out_be32((u32 *)DMA_CONTROL_REG, 0x00000001);
	while(in_be32((u32 *)DMA_CONTROL_REG) & 1);
#endif
#ifdef FIFO_MODE
	/* reset fifos */
#endif
}

void eth_halt(void){
	link = 0;
	/* puts ("halt device\n"); */
//	xps_ll_temac_halt();
}

int eth_init(bd_t *bis)
{
	static int first = 1;
	struct eth_device *dev;
	struct xps_ll_temac_private *lp;
#if DEBUG
	int i;
#endif

	if(!first)
		return 0;
	first = 0;
	dev = calloc(1, sizeof(struct eth_device));
	if (NULL == dev)
		return 0;

	lp = calloc(1, sizeof(struct xps_ll_temac_private));
	if (lp == NULL)
		return 0;
	dev->priv = lp;
	sprintf(dev->name, "eth0");

	xps_ll_temac_init(dev, bis);

	printf("%s: Xilinx XPS LocalLink Tri-Mode Ether MAC #%d at 0x%08X.\n",
		dev->name, 0, XILINX_LLTEMAC_BASEADDR);

#if DEBUG
	for(i = 0;i < 32;i++) {
		read_phy_reg(i);
	}
#endif
	xps_ll_temac_phy_ctrl();
	return 1;
}

int eth_send(volatile void *packet, int length)
{
#ifdef SDMA_MODE
	return xps_ll_temac_send_sdma((unsigned char *)packet, length);
#endif
#ifdef FIFO_MODE
	return xps_ll_temac_send_fifo((unsigned char *)packet, length);
#endif
}

int eth_rx(void)
{
#ifdef SDMA_MODE
	return xps_ll_temac_recv_sdma();
#endif
#ifdef FIFO_MODE
	return xps_ll_temac_recv_fifo();
#endif
}
 
uboot/ll_temac.txt · Last modified: 2010/06/20 09:30 by newacct
 
Except where otherwise noted, content on this wiki is licensed under the following license:CC Attribution-Noncommercial-Share Alike 3.0 Unported
Recent changes RSS feed Donate Powered by PHP Valid XHTML 1.0 Valid CSS Driven by DokuWiki