diff options
Diffstat (limited to 'drivers/ks8695eth.c')
-rw-r--r-- | drivers/ks8695eth.c | 236 |
1 files changed, 236 insertions, 0 deletions
diff --git a/drivers/ks8695eth.c b/drivers/ks8695eth.c new file mode 100644 index 0000000..89c766b --- /dev/null +++ b/drivers/ks8695eth.c @@ -0,0 +1,236 @@ +/* + * ks8695eth.c -- KS8695 ethernet driver + * + * (C) Copyright 2004-2005, Greg Ungerer <greg.ungerer@opengear.com> + * + * 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 <asm/io.h> +#include <asm/arch/platform.h> + +/****************************************************************************/ + +/* + * Hardware register access to the KS8695 LAN ethernet port + * (well, it is the 4 port switch really). + */ +#define ks8695_read(a) *((volatile unsigned long *) (KS8695_IO_BASE + (a))) +#define ks8695_write(a,v) *((volatile unsigned long *) (KS8695_IO_BASE + (a))) = (v) + +/****************************************************************************/ + +/* + * Define the descriptor in-memory data structures. + */ +struct ks8695_txdesc { + uint32_t owner; + uint32_t ctrl; + uint32_t addr; + uint32_t next; +}; + +struct ks8695_rxdesc { + uint32_t status; + uint32_t ctrl; + uint32_t addr; + uint32_t next; +}; + +/****************************************************************************/ + +/* + * Allocate local data structures to use for receiving and sending + * packets. Just to keep it all nice and simple. + */ + +#define TXDESCS 4 +#define RXDESCS 4 +#define BUFSIZE 2048 + +volatile struct ks8695_txdesc ks8695_tx[TXDESCS] __attribute__((aligned(256))); +volatile struct ks8695_rxdesc ks8695_rx[RXDESCS] __attribute__((aligned(256))); +volatile uint8_t ks8695_bufs[BUFSIZE*(TXDESCS+RXDESCS)] __attribute__((aligned(2048)));; + +/****************************************************************************/ + +/* + * Ideally we want to use the MAC address stored in flash. + * But we do some sanity checks in case they are not present + * first. + */ +unsigned char eth_mac[] = { + 0x00, 0x13, 0xc6, 0x00, 0x00, 0x00 +}; + +void ks8695_getmac(void) +{ + unsigned char *fp; + int i; + + /* Check if flash MAC is valid */ + fp = (unsigned char *) 0x0201c000; + for (i = 0; (i < 6); i++) { + if ((fp[i] != 0) && (fp[i] != 0xff)) + break; + } + + /* If we found a valid looking MAC address then use it */ + if (i < 6) + memcpy(ð_mac[0], fp, 6); +} + +/****************************************************************************/ + +void eth_reset(bd_t *bd) +{ + int i; + + debug ("%s(%d): eth_reset()\n", __FILE__, __LINE__); + + /* Reset the ethernet engines first */ + ks8695_write(KS8695_LAN_DMA_TX, 0x80000000); + ks8695_write(KS8695_LAN_DMA_RX, 0x80000000); + + ks8695_getmac(); + + /* Set MAC address */ + ks8695_write(KS8695_LAN_MAC_LOW, (eth_mac[5] | (eth_mac[4] << 8) | + (eth_mac[3] << 16) | (eth_mac[2] << 24))); + ks8695_write(KS8695_LAN_MAC_HIGH, (eth_mac[1] | (eth_mac[0] << 8))); + + /* Turn the 4 port switch on */ + i = ks8695_read(KS8695_SWITCH_CTRL0); + ks8695_write(KS8695_SWITCH_CTRL0, (i | 0x1)); + /* ks8695_write(KS8695_WAN_CONTROL, 0x3f000066); */ + + /* Initialize descriptor rings */ + for (i = 0; (i < TXDESCS); i++) { + ks8695_tx[i].owner = 0; + ks8695_tx[i].ctrl = 0; + ks8695_tx[i].addr = (uint32_t) &ks8695_bufs[i*BUFSIZE]; + ks8695_tx[i].next = (uint32_t) &ks8695_tx[i+1]; + } + ks8695_tx[TXDESCS-1].ctrl = 0x02000000; + ks8695_tx[TXDESCS-1].next = (uint32_t) &ks8695_tx[0]; + + for (i = 0; (i < RXDESCS); i++) { + ks8695_rx[i].status = 0x80000000; + ks8695_rx[i].ctrl = BUFSIZE - 4; + ks8695_rx[i].addr = (uint32_t) &ks8695_bufs[(i+TXDESCS)*BUFSIZE]; + ks8695_rx[i].next = (uint32_t) &ks8695_rx[i+1]; + } + ks8695_rx[RXDESCS-1].ctrl |= 0x00080000; + ks8695_rx[RXDESCS-1].next = (uint32_t) &ks8695_rx[0]; + + /* The KS8695 is pretty slow reseting the ethernets... */ + udelay(2000000); + + /* Enable the ethernet engine */ + ks8695_write(KS8695_LAN_TX_LIST, (uint32_t) &ks8695_tx[0]); + ks8695_write(KS8695_LAN_RX_LIST, (uint32_t) &ks8695_rx[0]); + ks8695_write(KS8695_LAN_DMA_TX, 0x3); + ks8695_write(KS8695_LAN_DMA_RX, 0x71); + ks8695_write(KS8695_LAN_DMA_RX_START, 0x1); + + printf("KS8695 ETHERNET: "); + for (i = 0; (i < 5); i++) { + bd->bi_enetaddr[i] = eth_mac[i]; + printf("%02x:", eth_mac[i]); + } + bd->bi_enetaddr[i] = eth_mac[i]; + printf("%02x\n", eth_mac[i]); +} + +/****************************************************************************/ + +int eth_init(bd_t *bd) +{ + debug ("%s(%d): eth_init()\n", __FILE__, __LINE__); + + eth_reset(bd); + return 0; +} + +/****************************************************************************/ + +void eth_halt(void) +{ + debug ("%s(%d): eth_halt()\n", __FILE__, __LINE__); + + /* Reset the ethernet engines */ + ks8695_write(KS8695_LAN_DMA_TX, 0x80000000); + ks8695_write(KS8695_LAN_DMA_RX, 0x80000000); +} + +/****************************************************************************/ + +int eth_rx(void) +{ + volatile struct ks8695_rxdesc *dp; + int i, len = 0; + + debug ("%s(%d): eth_rx()\n", __FILE__, __LINE__); + + for (i = 0; (i < RXDESCS); i++) { + dp= &ks8695_rx[i]; + if ((dp->status & 0x80000000) == 0) { + len = (dp->status & 0x7ff) - 4; + NetReceive((void *) dp->addr, len); + dp->status = 0x80000000; + ks8695_write(KS8695_LAN_DMA_RX_START, 0x1); + break; + } + } + + return len; +} + +/****************************************************************************/ + +int eth_send(volatile void *packet, int len) +{ + volatile struct ks8695_txdesc *dp; + static int next = 0; + + debug ("%s(%d): eth_send(packet=%x,len=%d)\n", __FILE__, __LINE__, + packet, len); + + dp = &ks8695_tx[next]; + memcpy((void *) dp->addr, packet, len); + + if (len < 64) { + memset(dp->addr+len, 0, 64-len); + len = 64; + } + + dp->ctrl = len | 0xe0000000; + dp->owner = 0x80000000; + + ks8695_write(KS8695_LAN_DMA_TX, 0x3); + ks8695_write(KS8695_LAN_DMA_TX_START, 0x1); + + if (++next >= TXDESCS) + next = 0; + + return len; +} + +/****************************************************************************/ |