diff options
author | Ivo Clarysse <ivo.clarysse@gmail.com> | 2009-04-27 11:47:07 +0200 |
---|---|---|
committer | Sascha Hauer <s.hauer@pengutronix.de> | 2009-04-28 08:39:32 +0200 |
commit | 85d0c4625837a03919a216bb62f6bdccbff141f2 (patch) | |
tree | 36437508a8a03d04f9483a51d13e1697dd402289 | |
parent | a3f20a45454e9608d435d178e6f2fc8676f8a6fc (diff) | |
download | barebox-85d0c4625837a03919a216bb62f6bdccbff141f2.tar.gz barebox-85d0c4625837a03919a216bb62f6bdccbff141f2.tar.xz |
u-boot-v2: CS89x0 ethernet support
Support for CS89X0 ethernet controllers.
Tested with CS8900A ref. F on MX21ADS.
Signed-off-by: Ivo Clarysse <ivo.clarysse@gmail.com>
-rw-r--r-- | drivers/net/cs8900.c | 481 |
1 files changed, 481 insertions, 0 deletions
diff --git a/drivers/net/cs8900.c b/drivers/net/cs8900.c new file mode 100644 index 0000000000..d32a7ce197 --- /dev/null +++ b/drivers/net/cs8900.c @@ -0,0 +1,481 @@ +/* + * cs8900.c + * + * Cirrus Logic Crystal CS89X0 Ethernet driver for u-boot v2 + * Copyright (C) 2009 Ivo Clarysse <ivo.clarysse@gmail.com> + * + * Based on cs8900.c from u-boot mainline, + * (C) 2003 Wolfgang Denk, wd@denx.de + * (C) Copyright 2002 Sysgo Real-Time Solutions, GmbH <www.elinos.com> + * Marius Groeger <mgroeger@sysgo.de> + * Copyright (C) 1999 Ben Williamson <benw@pobox.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. + */ + +#include <common.h> +#include <driver.h> +#include <init.h> +#include <net.h> +#include <malloc.h> +#include <clock.h> +#include <asm/io.h> +#include <errno.h> + +/* I/O ports for I/O Space operation */ +#define CS8900_RTDATA0 0x0000 /* Receive/Transmit Data (Port 0) */ +#define CS8900_RTDATA1 0x0002 /* Receive/Transmit Data (Port 1) */ +#define CS8900_TXCMD 0x0004 /* Transmit Command */ +#define CS8900_TXLEN 0x0006 /* Transmit Length */ +#define CS8900_ISQ 0x0008 /* Interrupt Status Queue */ +#define CS8900_PP_PTR 0x000a /* PacketPage Pointer */ +#define CS8900_PP_DATA0 0x000c /* PacketPage Data (Port 0) */ +#define CS8900_PP_DATA1 0x000e /* PacketPage Data (Port 1) */ + +/* PacketPage registers */ + +/* Bus Interface registers */ +#define PP_BI_VID 0x0000 /* Vendor (EISA registration #) */ +#define PP_BI_PID 0x0002 /* Product ID */ +#define PP_BI_IOBASE 0x0020 /* I/O Base Address */ +#define PP_BI_IRQ 0x0022 /* Interrupt Number */ +#define PP_BI_DMACH 0x0024 /* DMA Channel Number */ +#define PP_BI_DMASOF 0x0026 /* DMA Start of Frame */ +#define PP_BI_DMAFC 0x0028 /* DMA Frame Count (12bit) */ +#define PP_BI_RXDMAC 0x002A /* RxDMA Byte Count */ +#define PP_BI_MBAR 0x002C /* Memory Base Address Register (20bit) */ +#define PP_BI_BPBA 0x0030 /* Boot PROM Base Address (32bit) */ +#define PP_BI_BPAM 0x0034 /* Boot PROM Address Mark (32bit) */ +#define PP_BI_EE_CMD 0x0040 /* EEPROM Command */ +#define PP_BI_EE_DAT 0x0042 /* EEPROM Data */ +#define PP_BI_RFBC 0x0050 /* Received Frame Byte Counter */ + +/* Status and Control registers */ +#define PP_REG_ISQ 0x0120 /* Register 0: ISQ */ +#define PP_REG_01 0x0100 /* Register 1: -reserved- */ +#define PP_REG_02 0x0122 /* Register 2: -reserved- */ +#define PP_REG_RXCFG 0x0102 /* Register 3: RxCFG */ +#define PP_REG_RXEVENT 0x0124 /* Register 4: RxEvent/RxEventalt */ +#define PP_REG_RXCTL 0x0104 /* Register 5: RxCTL */ +#define PP_REG_06 0x0126 /* Register 6: -reserved- */ +#define PP_REG_TXCFG 0x0106 /* Register 7: TxCFG */ +#define PP_REG_TXEVENT 0x0128 /* Register 8: TxEvent */ +#define PP_REG_TXCMD 0x0108 /* Register 9: TxCmd (Transmit Command Status) */ +#define PP_REG_0A 0x012A /* Register A: -reserved- */ +#define PP_REG_BUFCFG 0x010A /* Register B: BufCFG */ +#define PP_REG_BUFEVENT 0x012C /* Register C: BufEvent */ +#define PP_REG_0C 0x010C /* Register D: -reserved- */ +#define PP_REG_0E 0x012E /* Register E: -reserved- */ +#define PP_REG_0F 0x010E /* Register F: -reserved- */ +#define PP_REG_RXMISS 0x0130 /* Register 10: RxMISS */ +#define PP_REG_11 0x0110 /* Register 11: -reserved- */ +#define PP_REG_TXCOL 0x0132 /* Register 12: TxCOL */ +#define PP_REG_LINECTL 0x0112 /* Register 13: LineCTL */ +#define PP_REG_LINEST 0x0134 /* Register 14: LineST */ +#define PP_REG_SELFCTL 0x0114 /* Register 15: SelfCTL */ +#define PP_REG_SELFST 0x0136 /* Register 16: SelfST */ +#define PP_REG_BUSCTL 0x0116 /* Register 17: BusCTL */ +#define PP_REG_BUSST 0x0138 /* Register 18: BusST */ +#define PP_REG_TESTCTL 0x0118 /* Register 19: TestCTL */ +#define PP_REG_1A 0x013A /* Register 1A: -reserved- */ +#define PP_REG_1B 0x011A /* Register 1B: -reserved- */ +#define PP_REG_TDR 0x013C /* Register 1C: TDR */ +#define PP_REG_1D 0x011C /* Register 1D: -reserved- */ +#define PP_REG_1E 0x013E /* Register 1E: -reserved- */ +#define PP_REG_1F 0x011E /* Register 1F: -reserved- */ + +/* Initiate Transmit registers */ +#define PP_IT_TXCMD 0x0144 /* Transmit Command */ +#define PP_IT_TXLEN 0x0146 /* Transmit Length */ + +/* Address Filter registers */ +#define PP_AF_LAF 0x0150 /* Logical Address Filter */ +#define PP_AF_IA 0x0158 /* Individual address (IEEE address) */ + +/* Frame Location */ +#define PP_FL_RXSTATUS 0x0400 /* RXStatus */ +#define PP_FL_RXLENGTH 0x0402 /* RxLength */ +#define PP_FL_RXLOC 0x0404 /* Receive Frame Location */ +#define PP_FL_TXLOC 0x0A00 /* Transmit Frame Location */ + +/* Register bit masks */ +#define RXEVENT_RXOK 0x0100 +#define RXCTL_RXOKA 0x0100 +#define RXCTL_MULTICASTA 0x0200 +#define RXCTL_INDIVIDUALA 0x0400 +#define RXCTL_BROADCASTA 0x0800 +#define TXCMD_TXSTART_5 0x0000 /* Start Tx after 5 bytes */ +#define TXCMD_TXSTART_381 0x0040 /* Start Tx after 381 bytes */ +#define TXCMD_TXSTART_1021 0x0080 /* Start Tx after 1021 bytes */ +#define TXCMD_TXSTART_FULL 0x00C0 /* Start Tx after all bytes loaded */ +#define LINECTL_SERRXON 0x0040 +#define LINECTL_SERTXON 0x0080 +#define LINEST_LINKOK 0x0080 +#define LINEST_AUI 0x0100 +#define LINEST_10BT 0x0200 +#define LINEST_POLARITYOK 0x1000 +#define LINEST_CRS 0x4000 +#define SELFCTL_RESET 0x0040 +#define SELFST_33VACTIVE 0x0040 +#define SELFST_INITD 0x0080 +#define SELFST_SIBUSY 0x0100 +#define SELFST_EEPROMP 0x0200 +#define SELFST_EEPROMOK 0x0400 +#define SELFST_ELPRESENT 0x0800 +#define SELFST_EESIZE 0x1000 +#define BUSST_TXBIDERR 0x0080 +#define BUSST_RDY4TXNOW 0x0100 + +#define CRYSTAL_SEMI_EISA_ID 0x630E + +/* Origin: CS8900A datasheet */ +#define CS8X0_PID_CS8900A_B 0x0700 +#define CS8X0_PID_CS8900A_C 0x0800 +#define CS8X0_PID_CS8900A_D 0x0900 +#define CS8X0_PID_CS8900A_F 0x0A00 +/* Origin: CS8920A datasheet */ +#define CS8X0_PID_CS8920_B 0x6100 +#define CS8X0_PID_CS8920_C 0x6200 +#define CS8X0_PID_CS8920_D 0x6300 +#define CS8X0_PID_CS8920A_AB 0x6400 +#define CS8X0_PID_CS8920A_C 0x6500 + +typedef enum { + CS8900, + CS8900A, + CS8920, + CS8920A, +} cs89x0_chip_type_t; + +struct cs89x0_chip { + cs89x0_chip_type_t chip_type; + const char *name; +}; + +struct cs89x0_product { + u16 product_id; + cs89x0_chip_type_t chip_type; + const char *rev_name; +}; + +static struct cs89x0_chip cs89x0_chips[] = { + {CS8900A, "CS8900A"}, + {CS8920A, "CS8920A"}, /* EOL: March 30, 2003 */ + {CS8900, "CS8900"}, /* EOL: September 31, 1999 */ + {CS8920, "CS8920"}, /* EOL: December 9, 1998 */ +}; + +static struct cs89x0_product cs89x0_product_ids[] = { + {CS8X0_PID_CS8900A_F, CS8900A, "F"}, + {CS8X0_PID_CS8900A_D, CS8900A, "D"}, + {CS8X0_PID_CS8900A_C, CS8900A, "C"}, + {CS8X0_PID_CS8900A_B, CS8900A, "B"}, + {CS8X0_PID_CS8920A_C, CS8920A, "C"}, + {CS8X0_PID_CS8920A_AB, CS8920A, "A/B"}, + {CS8X0_PID_CS8920_D, CS8920, "D"}, + {CS8X0_PID_CS8920_C, CS8920, "C"}, + {CS8X0_PID_CS8920_B, CS8920, "B"}, +}; + +struct cs8900_priv { + void *regs; + struct cs89x0_product *product; + struct cs89x0_chip *chip; +}; + +/* Read a 16-bit value from PacketPage Memory using I/O Space operation */ +static u16 cs8900_ior(struct cs8900_priv *priv, int reg) +{ + u16 result; + writew(reg, priv->regs + CS8900_PP_PTR); + result = readw(priv->regs + CS8900_PP_DATA0); + return result; +} + +/* Write a 16-bit value to PacketPage Memory using I/O Space operation */ +static void cs8900_iow(struct cs8900_priv *priv, int reg, u16 value) +{ + writew(reg, priv->regs + CS8900_PP_PTR); + writew(value, priv->regs + CS8900_PP_DATA0); +} + +static void cs8900_reginit(struct cs8900_priv *priv) +{ + /* receive only error free packets addressed to this card */ + cs8900_iow(priv, PP_REG_RXCTL, + RXCTL_BROADCASTA | RXCTL_INDIVIDUALA | RXCTL_RXOKA); + /* do not generate any interrupts on receive operations */ + cs8900_iow(priv, PP_REG_RXCFG, 0); + /* do not generate any interrupts on transmit operations */ + cs8900_iow(priv, PP_REG_TXCFG, 0); + /* do not generate any interrupts on buffer operations */ + cs8900_iow(priv, PP_REG_BUFCFG, 0); + /* enable transmitter/receiver mode */ + cs8900_iow(priv, PP_REG_LINECTL, + LINECTL_SERRXON | LINECTL_SERTXON); +} + +static void cs8900_reset(struct cs8900_priv *priv) +{ + u16 v; + uint64_t tmo; + + v = cs8900_ior(priv, PP_REG_SELFCTL); + v |= SELFCTL_RESET; + cs8900_iow(priv, PP_REG_SELFCTL, v); + + /* wait 200ms */ + udelay(200000); + + tmo = get_time_ns(); + while ((cs8900_ior(priv, PP_REG_SELFST) & SELFST_INITD) == 0) { + if (is_timeout(tmo, 1 * SECOND)) { + printf("%s: timeout\n", __FUNCTION__); + break; + } + } +} + + +static int cs8900_dev_init(struct eth_device *dev) +{ + return 0; +} + +static int cs8900_open(struct eth_device *dev) +{ + struct cs8900_priv *priv = (struct cs8900_priv *) dev->priv; + cs8900_reset(priv); + cs8900_reginit(priv); + return 0; +} + +static int cs8900_send(struct eth_device *dev, void *eth_data, + int data_length) +{ + struct cs8900_priv *priv = (struct cs8900_priv *) dev->priv; + u16 *addr; + u16 v; + + + writew(TXCMD_TXSTART_FULL, priv->regs + CS8900_TXCMD); + writew(data_length, priv->regs + CS8900_TXLEN); + + v = cs8900_ior(priv, PP_REG_BUSST); + + if (v & BUSST_TXBIDERR) { + printf("%s: frame error\n", __FUNCTION__); + return -1; + } + if ((v & BUSST_RDY4TXNOW) == 0) { + printf("%s: busy\n", __FUNCTION__); + return -1; + } + + for (addr = eth_data; data_length > 0; data_length -= 2) { + writew(*addr++, priv->regs + CS8900_RTDATA0); + } + + return 0; +} + +static int cs8900_recv(struct eth_device *dev) +{ + struct cs8900_priv *priv = (struct cs8900_priv *) dev->priv; + int len = 0; + u16 status; + u16 *addr; + int i; + + status = cs8900_ior(priv, PP_REG_RXEVENT); + if ((status & RXEVENT_RXOK) == 0) { + /* No packet received. */ + return 0; + } + + status = readw(priv->regs + CS8900_RTDATA0); + len = readw(priv->regs + CS8900_RTDATA0); + + for (addr = (u16 *) NetRxPackets[0], i = len >> 1; i > 0; i--) { + *addr++ = readw(priv->regs + CS8900_RTDATA0); + } + if (len & 1) { + *addr++ = readw(priv->regs + CS8900_RTDATA0); + } + NetReceive(NetRxPackets[0], len); + + return len; +} + +static void cs8900_halt(struct eth_device *dev) +{ + struct cs8900_priv *priv = (struct cs8900_priv *) dev->priv; + cs8900_iow(priv, PP_REG_BUSCTL, 0); + cs8900_iow(priv, PP_REG_TESTCTL, 0); + cs8900_iow(priv, PP_REG_SELFCTL, 0); + cs8900_iow(priv, PP_REG_LINECTL, 0); + cs8900_iow(priv, PP_REG_BUFCFG, 0); + cs8900_iow(priv, PP_REG_TXCFG, 0); + cs8900_iow(priv, PP_REG_RXCTL, 0); + cs8900_iow(priv, PP_REG_RXCFG, 0); +} + +static int cs8900_get_ethaddr(struct eth_device *dev, unsigned char *mac) +{ + struct cs8900_priv *priv = (struct cs8900_priv *) dev->priv; + int i; + for (i = 0; i < 6 / 2; i++) { + u16 v; + v = cs8900_ior(priv, PP_AF_IA + i * 2); + mac[i * 2] = v & 0xFF; + mac[i * 2 + 1] = v >> 8; + } + + return 0; +} + +static int cs8900_set_ethaddr(struct eth_device *dev, unsigned char *mac) +{ + struct cs8900_priv *priv = (struct cs8900_priv *) dev->priv; + int i; + for (i = 0; i < 6 / 2; i++) { + u16 v; + v = (mac[i * 2 + 1] << 8) | mac[i * 2]; + cs8900_iow(priv, PP_AF_IA + i * 2, v); + } + return 0; +} + +static const char *yesno_str(int v) +{ + return (v) ? "yes" : "no"; +} + +static void cs8900_info(struct device_d *dev) +{ + struct eth_device *edev = (struct eth_device *) dev->type_data; + struct cs8900_priv *priv = (struct cs8900_priv *) edev->priv; + u16 v; + + printf("%s Rev. %s (PID: 0x%04x) at 0x%08x\n", priv->chip->name, + priv->product->rev_name, priv->product->product_id, + priv->regs); + + v = cs8900_ior(priv, PP_REG_LINEST); + printf("Register 14: LineST 0x%04x\n", v); + printf(" Link OK: %s\n", yesno_str(v & LINEST_LINKOK)); + printf(" Using AUI: %s\n", yesno_str(v & LINEST_AUI)); + printf(" Using 10Base-T: %s\n", yesno_str(v & LINEST_10BT)); + printf(" Polarity OK: %s\n", yesno_str(v & LINEST_POLARITYOK)); + printf(" Incoming frame: %s\n", yesno_str(v & LINEST_CRS)); + + v = cs8900_ior(priv, PP_REG_SELFST); + printf("Register 16: SelfST 0x%04x\n", v); + printf(" Voltage: %sV\n", (v & SELFST_33VACTIVE) ? "3.3" : "5"); + printf(" Initialization complete: %s\n", + yesno_str(v & SELFST_INITD)); + printf(" EEPROM busy: %s\n", yesno_str(v & SELFST_SIBUSY)); + printf(" EEPROM present: %s\n", yesno_str(v & SELFST_EEPROMP)); + printf(" EEPROM checksum OK: %s\n", + yesno_str(v & SELFST_EEPROMOK)); + printf(" EL present: %s\n", yesno_str(v & SELFST_ELPRESENT)); + printf(" EEPROM size: %s words\n", + (v & SELFST_EESIZE) ? "64" : "128 or 256"); +} + +static int cs8900_check_id(struct cs8900_priv *priv) +{ + int result = -1; + struct cs89x0_product *product = NULL; + struct cs89x0_chip *chip = NULL; + int i; + u16 v; + v = cs8900_ior(priv, PP_BI_VID); + if (v != CRYSTAL_SEMI_EISA_ID) { + printf("No CS89x0 variant found at 0x%08x vid: 0x%04x\n", + priv->regs, v); + return -1; + } + v = cs8900_ior(priv, PP_BI_PID); + for (i = 0; i < ARRAY_SIZE(cs89x0_product_ids); i++) { + if (cs89x0_product_ids[i].product_id == v) { + product = &(cs89x0_product_ids[i]); + break; + } + } + if (product == NULL) { + printf("Unable to identify CS89x0 variant 0x%04x\n", v); + goto out; + } + for (i = 0; i < ARRAY_SIZE(cs89x0_chips); i++) { + if (cs89x0_chips[i].chip_type == product->chip_type) { + chip = &(cs89x0_chips[i]); + break; + } + } + if (chip == NULL) { + printf("Invalid chip type %d\n", product->chip_type); + goto out; + } + printf("%s Rev. %s (PID: 0x%04x) found at 0x%08x\n", chip->name, + product->rev_name, v, priv->regs); + priv->chip = chip; + priv->product = product; + result = 0; + out: + return result; +} + +static int cs8900_probe(struct device_d *dev) +{ + struct eth_device *edev; + struct cs8900_priv *priv; + + debug("cs8900_init()\n"); + + priv = (struct cs8900_priv *) malloc(sizeof(*priv)); + priv->regs = (u16 *) dev->map_base; + if (cs8900_check_id(priv)) { + free(priv); + return -1; + } + + edev = (struct eth_device *) malloc(sizeof(struct eth_device)); + dev->type_data = edev; + edev->priv = priv; + edev->dev = dev; + + edev->init = cs8900_dev_init; + edev->open = cs8900_open; + edev->send = cs8900_send; + edev->recv = cs8900_recv; + edev->halt = cs8900_halt; + edev->get_ethaddr = cs8900_get_ethaddr; + edev->set_ethaddr = cs8900_set_ethaddr; + + eth_register(edev); + return 0; +} + +static struct driver_d cs8900_driver = { + .name = "cs8900", + .probe = cs8900_probe, + .info = cs8900_info, + .type = DEVICE_TYPE_ETHER, +}; + +static int cs8900_init(void) +{ + register_driver(&cs8900_driver); + return 0; +} + +device_initcall(cs8900_init); |