[U-Boot-Users] Xilinx PowerPC XPS_LL_TEMAC driver
Yoshio Kashiwagi
kashiwagi at co-nss.co.jp
Fri Apr 25 11:32:34 CEST 2008
Hi All,
I am writing XPS_LL_TEMAC for Xilinx PowerPC.
Can anyone give me an advice on which I should correct this source?
Thanks in advance,
Yoshio Kashiwagi - Nissin Systems
/*
*
* xilinx_ll_temac.c ethernet driver for u-boot
*
* Author: Yoshio Kashiwagi kashiwagi at co-nss.co.jp
*
* 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/errno.h>
#include <asm/processor.h>
#define S_DMA_CTRL_BASEADDR XPAR_LLTEMAC_0_LLINK_CONNECTED_BASEADDR
#define XPS_LLTEMAC_BASEADDR XPAR_LLTEMAC_0_BASEADDR
/* XPS_LL_TEMAC SDMA registers definition */
#define TX_NXTDESC_PTR (S_DMA_CTRL_BASEADDR + 0x00)
#define TX_CURBUF_ADDR (S_DMA_CTRL_BASEADDR + 0x04)
#define TX_CURBUF_LENGTH (S_DMA_CTRL_BASEADDR + 0x08)
#define TX_CURDESC_PTR (S_DMA_CTRL_BASEADDR + 0x0c)
#define TX_TAILDESC_PTR (S_DMA_CTRL_BASEADDR + 0x10)
#define TX_CHNL_CTRL (S_DMA_CTRL_BASEADDR + 0x14)
#define TX_IRQ_REG (S_DMA_CTRL_BASEADDR + 0x18)
#define TX_CHNL_STS (S_DMA_CTRL_BASEADDR + 0x1c)
#define RX_NXTDESC_PTR (S_DMA_CTRL_BASEADDR + 0x20)
#define RX_CURBUF_ADDR (S_DMA_CTRL_BASEADDR + 0x24)
#define RX_CURBUF_LENGTH (S_DMA_CTRL_BASEADDR + 0x28)
#define RX_CURDESC_PTR (S_DMA_CTRL_BASEADDR + 0x2c)
#define RX_TAILDESC_PTR (S_DMA_CTRL_BASEADDR + 0x30)
#define RX_CHNL_CTRL (S_DMA_CTRL_BASEADDR + 0x34)
#define RX_IRQ_REG (S_DMA_CTRL_BASEADDR + 0x38)
#define RX_CHNL_STS (S_DMA_CTRL_BASEADDR + 0x3c)
#define DMA_CONTROL_REG (S_DMA_CTRL_BASEADDR + 0x40)
/* XPS_LL_TEMAC direct registers definition */
#define TEMAC_RAF0 (XPS_LLTEMAC_BASEADDR + 0x00)
#define TEMAC_TPF0 (XPS_LLTEMAC_BASEADDR + 0x04)
#define TEMAC_IFGP0 (XPS_LLTEMAC_BASEADDR + 0x08)
#define TEMAC_IS0 (XPS_LLTEMAC_BASEADDR + 0x0c)
#define TEMAC_IP0 (XPS_LLTEMAC_BASEADDR + 0x10)
#define TEMAC_IE0 (XPS_LLTEMAC_BASEADDR + 0x14)
#define TEMAC_MSW0 (XPS_LLTEMAC_BASEADDR + 0x20)
#define TEMAC_LSW0 (XPS_LLTEMAC_BASEADDR + 0x24)
#define TEMAC_CTL0 (XPS_LLTEMAC_BASEADDR + 0x28)
#define TEMAC_RDY0 (XPS_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
#define CACHE_LINE_SIZE 32
/* SDMA 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;
static unsigned char tx_buffer[ETHER_MTU] __attribute((aligned(32)));
static unsigned char rx_buffer[ETHER_MTU] __attribute((aligned(32)));
struct xps_ll_temac_private {
int idx;
unsigned char dev_addr[6];
};
static void flush_dcache_range(unsigned int addr, unsigned size)
{
unsigned int end = addr & ~(CACHE_LINE_SIZE - 1);
if(size) {
while(addr < end) {
__asm__ __volatile__("dcbf 0,%0; sync;" : : "r" (addr));
addr += CACHE_LINE_SIZE;
}
}
}
static void invalidate_dcache_range(unsigned int addr, unsigned size)
{
unsigned int end = addr & ~(CACHE_LINE_SIZE - 1);
if(size) {
while(addr < end) {
__asm__ __volatile__("dcbi 0,%0; sync;" : : "r" (addr));
addr += CACHE_LINE_SIZE;
}
}
}
static unsigned int xps_ll_temac_hostif_get(int emac, int phy_addr, int
reg_addr)
{
*(unsigned int *)TEMAC_LSW0 = phy_addr << 5 | reg_addr;
*(unsigned int *)TEMAC_CTL0 = MIIMAI | emac << 10;
while(! (*(volatile unsigned int *)TEMAC_RDY0 & XTE_RSE_MIIM_RR_MASK)
);
return *(unsigned int *)TEMAC_LSW0;
}
static void xps_ll_temac_indirect_set(int emac, int reg_offset, int reg_
data)
{
*(unsigned int *)TEMAC_LSW0 = reg_data;
*(unsigned int *)TEMAC_CTL0 = CNTLREG_WRITE_ENABLE_MASK | emac << 10
| reg_offset;
while(! (*(volatile unsigned int *)TEMAC_RDY0 & XTE_RSE_CFG_WR_MASK))
;
}
static int xps_ll_temac_phy_ctrl(void)
{
static int phy_addr = -1;
static int link = 0;
int i;
unsigned int result;
if(phy_addr == -1) {
for(i = 0;i < 32;i++) {
result = xps_ll_temac_hostif_get(0, i, 1);
if((result & 0x0ffff) != 0x0ffff) {
phy_addr = i;
break;
}
}
} else {
result = xps_ll_temac_hostif_get(0, phy_addr, 1);
}
if((result & 0x24) != 0x24) {
if(link) {
link = 0;
printf("Link down\n");
}
return 0;
}
if(link == 0) {
link = 1;
} else {
return 1;
}
result = xps_ll_temac_hostif_get(0, phy_addr, 10);
if((result & 0x0800) == 0x0800) {
xps_ll_temac_indirect_set(0, EMMC, 0x80000000);
printf("1000BASE-T/FD\n");
return 1;
}
result = xps_ll_temac_hostif_get(0, phy_addr, 5);
if((result & 0x0100) == 0x0100) {
xps_ll_temac_indirect_set(0, EMMC, 0x40000000);
printf("100BASE-T/FD\n");
} else if((result & 0x0040) == 0x0040) {
xps_ll_temac_indirect_set(0, EMMC, 0x00000000);
printf("10BASE-T/FD\n");
} else {
printf("Half Duplex not supported\n");
}
return 1;
}
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_dcache_range((unsigned int)&rx_bd, sizeof(cdmac_bd));
*(unsigned int *)RX_CURDESC_PTR = (unsigned int)&rx_bd;
*(unsigned int *)RX_TAILDESC_PTR = (unsigned int)&rx_bd;
tx_bd.phys_buf_p = &tx_buffer[0];
tx_bd.next_p = &tx_bd;
flush_dcache_range((unsigned int)&tx_bd, sizeof(cdmac_bd));
*(unsigned int *)TX_CURDESC_PTR = (unsigned int)&tx_bd;
}
static int xps_ll_temac_send(unsigned char *buffer, int length)
{
if(xps_ll_temac_phy_ctrl() == 0) return 0;
memcpy(tx_buffer, buffer, length);
flush_dcache_range((unsigned int)tx_buffer, length);
tx_bd.stat = BDSTAT_SOP_MASK | BDSTAT_EOP_MASK | BDSTAT_STOP_ON_END_
MASK;
tx_bd.buf_len = length;
flush_dcache_range((unsigned int)&tx_bd, sizeof(cdmac_bd));
*(unsigned int *)TX_CURDESC_PTR = (unsigned int)&tx_bd;
*(unsigned int *)TX_TAILDESC_PTR = (unsigned int)&tx_bd; /* DMA
start */
do {
invalidate_dcache_range((unsigned int)&tx_bd, sizeof(cdmac_bd));
} while(!((volatile int)tx_bd.stat & BDSTAT_COMPLETED_MASK));
return length;
}
static int xps_ll_temac_recv(void)
{
int length;
invalidate_dcache_range((unsigned int)&rx_bd, sizeof(cdmac_bd));
if(!(rx_bd.stat & BDSTAT_COMPLETED_MASK)) return 0;
length = rx_bd.app5;
invalidate_dcache_range((unsigned int)rx_bd.phys_buf_p, length);
rx_bd.buf_len = ETHER_MTU;
rx_bd.stat = 0;
rx_bd.app5 = 0;
flush_dcache_range((unsigned int)&rx_bd, sizeof(cdmac_bd));
*(unsigned int *)RX_TAILDESC_PTR = (unsigned int)&rx_bd;
if(length > 0) {
NetReceive(rx_bd.phys_buf_p, length);
}
return length;
}
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 void xps_ll_temac_init(struct eth_device *dev, bd_t *bis)
{
struct xps_ll_temac_private *lp = (struct xps_ll_temac_private *)
dev->priv;
xps_ll_temac_bd_init();
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 */
}
int eth_init(bd_t *bis)
{
static int first = 1;
struct eth_device *dev;
struct xps_ll_temac_private *lp;
int i;
if(!first) return 0;
first = 0;
dev = (struct eth_device *) calloc(1, sizeof(struct eth_device))
;
if (NULL == dev) return 0;
lp = (struct xps_ll_temac_private *) 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, XPS_LLTEMAC_BASEADDR);
for(i = 0;i < 3;i++) {
if(xps_ll_temac_phy_ctrl()) break;
udelay(10000); /* wait second */
}
return 1;
}
int eth_send(volatile void *packet, int length)
{
return xps_ll_temac_send((unsigned char *)packet, length);
}
int eth_rx(void)
{
return xps_ll_temac_recv();
}
void eth_halt(void)
{
}
More information about the U-Boot
mailing list