summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorIvo Clarysse <ivo.clarysse@gmail.com>2009-04-27 11:47:07 +0200
committerSascha Hauer <s.hauer@pengutronix.de>2009-04-28 08:39:32 +0200
commit85d0c4625837a03919a216bb62f6bdccbff141f2 (patch)
tree36437508a8a03d04f9483a51d13e1697dd402289
parenta3f20a45454e9608d435d178e6f2fc8676f8a6fc (diff)
downloadbarebox-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.c481
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);