[RFC V2 6/6] net: add DEC 21143 (tulip) Ethernet controller support

Antony Pavlov antonynpavlov at gmail.com
Thu Mar 8 14:50:32 EST 2012


This driver is based on the tulip driver from Linux, and on
the dc driver from *BSD.

Signed-off-by: Antony Pavlov <antonynpavlov at gmail.com>
---
 drivers/net/Kconfig     |    8 +
 drivers/pci/21143.c     |  769 +++++++++++++++++++++++++++++++++++++++++++++++
 drivers/pci/Makefile    |    1 +
 include/linux/pci_ids.h |    3 +
 4 files changed, 781 insertions(+), 0 deletions(-)
 create mode 100644 drivers/pci/21143.c

diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig
index ff18b5e..7b87501 100644
--- a/drivers/net/Kconfig
+++ b/drivers/net/Kconfig
@@ -108,6 +108,14 @@ config 8139
 	  This is a driver for the Fast Ethernet PCI network cards based on
 	  the RTL 8139 chips.
 
+config 21143
+	depends on PCI
+	bool "DECchip Tulip (dc21143) PCI Fast Ethernet Adapter support"
+	select MIIDEV
+	help
+	  This is a driver for the Fast Ethernet PCI network cards based on
+	  the DEC 21143 chips.
+
 source "drivers/net/usb/Kconfig"
 
 endmenu
diff --git a/drivers/pci/21143.c b/drivers/pci/21143.c
new file mode 100644
index 0000000..bd2268a
--- /dev/null
+++ b/drivers/pci/21143.c
@@ -0,0 +1,769 @@
+/*
+ * DEC/Intel 21143 (tulip) ethernet driver
+ *
+ * Copyright (C) 2011 Antony Pavlov <antonynpavlov at gmail.com>
+ *
+ * This file is part of barebox.
+ * See file CREDITS for list of people who contributed to this project.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ *
+ * 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 <command.h>
+#include <net.h>
+#include <miidev.h>
+#include <malloc.h>
+#include <init.h>
+#include <xfuncs.h>
+#include <errno.h>
+#include <clock.h>
+#include <asm/io.h>
+
+#include <linux/pci.h>
+
+#undef DEC21143_DEBUG
+#define DEC21143_DEBUG
+
+#define NUM_RX_DESC 1
+#define NUM_TX_DESC 1
+#define RX_BUFF_SZ  1600
+
+struct tx_desc {
+	u32 status;
+	u32 length;
+	u32 buffer1;
+	u32 buffer2;
+};
+
+struct rx_desc {
+	u32 status;
+	u32 length;
+	u32 buffer1;
+	u32 buffer2;
+};
+
+/* Descriptor bits. */
+#define TULIP_RDES0_OWN   (1 << 31) /* Own Bit: indicates that the descriptor is owned by the 21143 */
+#define RD_RER		0x02000000	/* Receive End Of Ring */
+#define RD_LS		0x00000100	/* Last Descriptor */
+#define RD_ES		0x00008000	/* Error Summary */
+
+#define TULIP_TDES0_OWN   (1 << 31) /* Own Bit: indicates that the descriptor is owned by the 21143 */
+#define TULIP_TDES1_LS    (1 << 30) /* Last Segment */
+#define TULIP_TDES1_FS    (1 << 29) /* First Segment */
+#define TULIP_TDES1_SET   (1 << 27) /* Setup Packet */
+#define TD_TER		0x02000000	/* Transmit End Of Ring */
+#define TD_ES		0x00008000	/* Error Summary */
+
+struct dec21143_priv {
+	void __iomem		*base;
+	struct pci_dev		*pci_dev;
+
+	struct mii_device	miidev;
+
+	struct rx_desc		*rx_ring;
+	struct tx_desc		*tx_ring;
+
+	int			tx_new;
+	int			rx_new;
+};
+
+/* FIXME: write 0 to CSR7 */
+
+#define TULIP_CSR0	0x00 /* Setting Register */
+ #define TULIP_CSR0_SWR		0x01 /* Software Reset */
+ #define TULIP_CSR0_TAP_MASK	(0x03 << 17)
+
+#define TULIP_CSR1	0x08 /* Transmit Poll Demand Register */
+
+#define TULIP_CSR2	0x10 /* Receive Poll Demand Register */
+#define POLL_DEMAND	1
+
+#define TULIP_CSR3	0x18 /* Start of Receive List */
+
+#define TULIP_CSR4	0x20 /* Start of Transmit List */
+
+#define TULIP_CSR5	0x28 /* Status Register */
+ #define TULIP_CSR5_RI	(1 << 6) /* Receive Interrupt */
+ #define TULIP_CSR5_TI	1 /* Transmit Interrupt */
+ #define TULIP_CSR5_TS_MASK	(0x07 << 20)
+ #define TULIP_CSR5_RS_MASK	(0x07 << 17)
+
+#define TULIP_CSR6	0x30 /* Operation Mode Register */
+ #define TULIP_CSR6_TXON	0x2000
+ #define TULIP_CSR6_RXON	0x0002
+
+#define TULIP_CSR7	0x38 /* Interrupt Enable Register */
+
+#define TULIP_CSR8	0x40 /* Unused */
+
+#define TULIP_CSR9	0x48 /* Boot ROM, Serial ROM, and MII Management Register */
+#define TULIP_MDIO_DATA0 0x00000
+#define TULIP_MDIO_DATA1 0x20000
+#define MDIO_ENB		0x00000		/* Ignore the 0x02000 databook setting. */
+#define MDIO_ENB_IN		0x40000
+#define MDIO_DATA_READ	0x80000
+
+ #define TULIP_CSR9_MDI         (1 << 19) /* MDIO Data In */
+ #define TULIP_CSR9_MDOM        (1 << 18) /* MDIO Operation Mode */
+ #define TULIP_CSR9_MDO         (1 << 17) /* MDIO Data Out */
+ #define TULIP_CSR9_MDC         (1 << 16) /* MDIO Clock */
+ #define TULIP_CSR9_RD          (1 << 14)
+ #define TULIP_CSR9_WR          (1 << 13)
+ #define TULIP_CSR9_SR          (1 << 11) /* Serial ROM Select */
+ #define TULIP_CSR9_SRDO        (1 << 3) /* Serial ROM Data Out */
+ #define TULIP_CSR9_SRDI        (1 << 2) /* Serial ROM Data In */
+ #define TULIP_CSR9_SRCK        (1 << 1) /* Serial ROM Clock */
+ #define TULIP_CSR9_SRCS        (1) /* Serial ROM Chip Select */
+
+#define TULIP_CSR10	0x50 /* Unused */
+#define TULIP_CSR11	0x58 /* Unused */
+#define TULIP_CSR12	0x60 /* Unused */
+#define TULIP_CSR13	0x68 /* Unused */
+#define TULIP_CSR14	0x70 /* Unused: SIA Transmit and Receive Register */
+#define TULIP_CSR15	0x78 /* Unused */
+
+/* Register bits. */
+/* CSR5 */
+#define STS_TS		0x00700000	/* Transmit Process State */
+#define STS_RS		0x000e0000	/* Receive Process State */
+
+/* CSR6 */
+#define OMR_PS		0x00040000	/* Port Select */
+#define OMR_SDP		0x02000000	/* SD Polarity - MUST BE ASSERTED */
+#define OMR_PM		0x00000080	/* Pass All Multicast */
+
+#define DC_W32(priv, reg, val)	writel(val, ((char *)(priv->base) + reg))
+#define DC_R32(priv, reg)	readl(((char *)(priv->base) + reg))
+
+/* Read and write the MII registers using software-generated serial
+   MDIO protocol.  It is just different enough from the EEPROM protocol
+   to not share code.  The maxium data clock rate is 2.5 MHz. */
+
+#define mdio_delay() (void)DC_R32(priv, TULIP_CSR9)
+
+static int dec21143_phy_read(struct mii_device *mdev, int phy_addr, int reg)
+{
+	struct eth_device *edev = mdev->edev;
+	struct dec21143_priv *priv = edev->priv;
+	int val;
+
+	int i;
+	int read_cmd = (0xf6 << 10) | (priv->miidev.address << 5) | reg;
+
+	val = 0;
+
+	/* Establish sync by sending at least 32 logic ones. */
+	for (i = 32; i >= 0; i--) {
+		DC_W32(priv, TULIP_CSR9, MDIO_ENB | TULIP_MDIO_DATA1);
+		mdio_delay();
+		DC_W32(priv, TULIP_CSR9, MDIO_ENB | TULIP_MDIO_DATA1 | TULIP_CSR9_MDC);
+		mdio_delay();
+	}
+
+	/* Shift the read command bits out. */
+	for (i = 15; i >= 0; i--) {
+		int dataval = (read_cmd & (1 << i)) ? TULIP_MDIO_DATA1 : TULIP_MDIO_DATA0;
+
+		DC_W32(priv, TULIP_CSR9, MDIO_ENB | dataval);
+		mdio_delay();
+		DC_W32(priv, TULIP_CSR9, MDIO_ENB | dataval | TULIP_CSR9_MDC);
+		mdio_delay();
+	}
+
+	/* Read the two transition, 16 data, and wire-idle bits. */
+	for (i = 19; i > 0; i--) {
+		DC_W32(priv, TULIP_CSR9, MDIO_ENB_IN);
+		mdio_delay();
+		val = (val << 1) | ((DC_R32(priv, TULIP_CSR9) & MDIO_DATA_READ) ? 1 : 0);
+		DC_W32(priv, TULIP_CSR9, MDIO_ENB_IN | TULIP_CSR9_MDC);
+		mdio_delay();
+	}
+
+	val = (val >> 1) & 0xffff;
+
+//#ifdef DEC21143_DEBUG
+#if 0
+	printf("%s: addr: 0x%02x reg: 0x%02x val: 0x%04x\n", __func__,
+			phy_addr, reg, val);
+#endif
+
+	return val;
+}
+
+static int dec21143_phy_write(struct mii_device *mdev, int phy_addr,
+	int reg, int val)
+{
+	struct eth_device *edev = mdev->edev;
+	struct dec21143_priv *priv = edev->priv;
+
+	int i;
+	int cmd = (0x5002 << 16) | (priv->miidev.address << 23) | (reg << 18) | val;
+
+#ifdef DEC21143_DEBUG
+	printf("%s: addr: 0x%02x reg: 0x%02x val: 0x%04x\n", __func__,
+	      phy_addr, reg, val);
+#endif
+
+	/* Establish sync by sending 32 logic ones. */
+	for (i = 32; i >= 0; i--) {
+		DC_W32(priv, TULIP_CSR9, MDIO_ENB | TULIP_MDIO_DATA1);
+		mdio_delay();
+		DC_W32(priv, TULIP_CSR9, MDIO_ENB | TULIP_MDIO_DATA1 | TULIP_CSR9_MDC);
+		mdio_delay();
+	}
+
+	/* Shift the command bits out. */
+	for (i = 31; i >= 0; i--) {
+		int dataval = (cmd & (1 << i)) ? TULIP_MDIO_DATA1 : TULIP_MDIO_DATA0;
+
+		DC_W32(priv, TULIP_CSR9, MDIO_ENB | dataval);
+		mdio_delay();
+		DC_W32(priv, TULIP_CSR9, MDIO_ENB | dataval | TULIP_CSR9_MDC);
+		mdio_delay();
+	}
+
+	/* Clear out extra bits. */
+	for (i = 2; i > 0; i--) {
+		DC_W32(priv, TULIP_CSR9, MDIO_ENB_IN);
+		mdio_delay();
+		DC_W32(priv, TULIP_CSR9, MDIO_ENB_IN | TULIP_CSR9_MDC);
+		mdio_delay();
+	}
+
+	return 0;
+}
+
+#define SETUP_FRAME_LEN 192
+#define ETH_ALEN	6
+#define	TOUT_LOOP	100
+
+/* FIXME */
+#define phys_to_bus(x) (0x0fffffff & (x))
+
+static void dump_rings(struct dec21143_priv *priv)
+{
+	int i;
+
+	printf("dump_rings(): tx_new = %d, rx_new = %d\n", priv->tx_new, priv->rx_new);
+
+	for (i = 0; i < NUM_RX_DESC; i++) {
+		printf("rx_ring[%d].status  = %08x\n", i, le32_to_cpu(priv->rx_ring[i].status));
+		printf("rx_ring[%d].length  = %08x\n", i, le32_to_cpu(priv->rx_ring[i].length));
+		printf("rx_ring[%d].buffer1 = %08x\n", i, le32_to_cpu(priv->rx_ring[i].buffer1));
+		printf("rx_ring[%d].buffer2 = %08x\n", i, le32_to_cpu(priv->rx_ring[i].buffer2));
+	}
+	printf("\n");
+
+	for (i = 0; i < NUM_TX_DESC; i++) {
+		printf("tx_ring[%d].status  = %08x\n", i, le32_to_cpu(priv->tx_ring[i].status));
+		printf("tx_ring[%d].length  = %08x\n", i, le32_to_cpu(priv->tx_ring[i].length));
+		printf("tx_ring[%d].buffer1 = %08x\n", i, le32_to_cpu(priv->tx_ring[i].buffer1));
+		printf("tx_ring[%d].buffer2 = %08x\n", i, le32_to_cpu(priv->tx_ring[i].buffer2));
+	}
+	printf("\n");
+	printf("TULIP_CSR0 = 0x%08x\n", DC_R32(priv, TULIP_CSR0));
+	printf("TULIP_CSR1 = 0x%08x\n", DC_R32(priv, TULIP_CSR1));
+	printf("TULIP_CSR2 = 0x%08x\n", DC_R32(priv, TULIP_CSR2));
+	printf("TULIP_CSR3 = 0x%08x\n", DC_R32(priv, TULIP_CSR3));
+	printf("TULIP_CSR4 = 0x%08x\n", DC_R32(priv, TULIP_CSR4));
+{
+	int t;
+	t = DC_R32(priv, TULIP_CSR5);
+	printf("TULIP_CSR5 = 0x%08x ", t);
+	if (t & 1) {
+		printf("<tx int>");
+	}
+	if (t & 2) {
+		printf("<tx stop>");
+	}
+	if (t & 4) {
+		printf("<tx bufun>");
+	}
+	printf("\n");
+}
+	printf("TULIP_CSR6 = 0x%08x\n", DC_R32(priv, TULIP_CSR6));
+
+}
+
+static void dec21143_send_setup_frame(struct eth_device *edev)
+{
+	struct dec21143_priv *priv = edev->priv;
+	int i;
+	char setup_frame[SETUP_FRAME_LEN];
+	char *pa = &setup_frame[15 * 6];
+	char *eaddrs[6];
+
+	/* add the broadcast address. */
+	memset(setup_frame, 0xff, SETUP_FRAME_LEN);
+
+	edev->get_ethaddr(edev, eaddrs);
+
+	/* Fill the final entry of the table with our physical address. */
+	*pa++ = eaddrs[0]; *pa++ = eaddrs[0];
+	*pa++ = eaddrs[1]; *pa++ = eaddrs[1];
+	*pa++ = eaddrs[2]; *pa++ = eaddrs[2];
+
+	dump_rings(priv);
+
+	for (i = 0; priv->tx_ring[priv->tx_new].status & cpu_to_le32(TULIP_TDES0_OWN); i++) {
+		if (i >= TOUT_LOOP) {
+			printf("%s: tx buffer not ready 0\n", edev->dev.name);
+			goto Done;
+		}
+	}
+
+	priv->tx_ring[priv->tx_new].buffer1 = cpu_to_le32(phys_to_bus((u32) &setup_frame[0]));
+	priv->tx_ring[priv->tx_new].length = cpu_to_le32(TD_TER | TULIP_TDES1_SET | SETUP_FRAME_LEN);
+	priv->tx_ring[priv->tx_new].status = cpu_to_le32(TULIP_TDES0_OWN);
+
+	DC_W32(priv, TULIP_CSR1, POLL_DEMAND);
+
+	dump_rings(priv);
+	for (i = 0; priv->tx_ring[priv->tx_new].status & cpu_to_le32(TULIP_TDES0_OWN); i++) {
+		if (i >= TOUT_LOOP) {
+			printf("%s: tx buffer not ready 1\n", edev->dev.name);
+			goto Done;
+		}
+	}
+
+	if (le32_to_cpu(priv->tx_ring[priv->tx_new].status) != 0x7FFFFFFF) {
+		printf("TX error status2 = 0x%08X\n", le32_to_cpu(priv->tx_ring[priv->tx_new].status));
+	}
+
+	priv->tx_new = (priv->tx_new + 1) % NUM_TX_DESC;
+
+	printf("============================== done\n");
+Done:
+	return;
+}
+
+/*  EEPROM_Ctrl bits. */
+#define EE_SHIFT_CLK	0x02	/* EEPROM shift clock. */
+#define EE_CS		0x01	/* EEPROM chip select. */
+#define EE_DATA_WRITE	0x04	/* Data from the Tulip to EEPROM. */
+#define EE_WRITE_0	0x01
+#define EE_WRITE_1	0x05
+#define EE_DATA_READ	0x08	/* Data from the EEPROM chip. */
+#define EE_ENB		(0x4800 | EE_CS)
+
+/* The EEPROM commands include the alway-set leading bit. */
+#define EE_READ_CMD		(6)
+
+#define eeprom_delay()	(void)DC_R32(priv, TULIP_CSR9)
+
+static int dec21143_read_eeprom(struct dec21143_priv *priv, int addr, u_int16_t *dest)
+{
+	int i;
+	unsigned retval = 0;
+	int addr_len = 6;
+	int read_cmd = addr | (EE_READ_CMD << addr_len);
+
+	DC_W32(priv, TULIP_CSR9, EE_ENB & ~EE_CS);
+	DC_W32(priv, TULIP_CSR9, EE_ENB);
+
+	/* Shift the read command bits out. */
+	for (i = 4 + addr_len; i >= 0; i--) {
+		short dataval = (read_cmd & (1 << i)) ? EE_DATA_WRITE : 0;
+		DC_W32(priv, TULIP_CSR9, EE_ENB | dataval);
+		eeprom_delay();
+		DC_W32(priv, TULIP_CSR9, EE_ENB | dataval | EE_SHIFT_CLK);
+		eeprom_delay();
+		retval = (retval << 1) | ((DC_R32(priv, TULIP_CSR9) & EE_DATA_READ) ? 1 : 0);
+	}
+	DC_W32(priv, TULIP_CSR9, EE_ENB);
+	eeprom_delay();
+
+	for (i = 16; i > 0; i--) {
+		DC_W32(priv, TULIP_CSR9, EE_ENB | EE_SHIFT_CLK);
+		eeprom_delay();
+		retval = (retval << 1) | ((DC_R32(priv, TULIP_CSR9) & EE_DATA_READ) ? 1 : 0);
+		DC_W32(priv, TULIP_CSR9, EE_ENB);
+		eeprom_delay();
+	}
+
+	/* Terminate the EEPROM access. */
+	DC_W32(priv, TULIP_CSR9, EE_ENB & ~EE_CS);
+
+	*dest = retval;
+
+	//return (tp->flags & HAS_SWAPPED_SEEPROM) ? swab16(retval) : retval;
+	return retval;
+}
+
+static int dec21143_get_ethaddr(struct eth_device *edev, unsigned char *m)
+{
+	struct dec21143_priv *priv = edev->priv;
+	int i;
+
+	for (i = 0; i < 6; i += sizeof(u16)) {
+		u_int16_t val;
+		dec21143_read_eeprom(priv, 10 + i/2, &val);
+		m[i] = val & 0xff;
+		m[i + 1] = val >> 8;
+	}
+
+	return 0;
+}
+
+static int dec21143_set_ethaddr(struct eth_device *edev,
+					unsigned char *mac_addr)
+{
+	struct dec21143_priv *priv = edev->priv;
+
+	return 0;
+}
+
+static int dec21143_init_dev(struct eth_device *edev)
+{
+	struct dec21143_priv *priv = edev->priv;
+	int i;
+
+#ifdef DEC21143_DEBUG
+	printf("%s\n", __func__);
+#endif
+
+	/* RESET_DE4X5(dev); */
+
+	i = DC_R32(priv, TULIP_CSR0);
+	udelay(1000);
+	DC_W32(priv, TULIP_CSR0, i | TULIP_CSR0_SWR);
+	udelay(1000);
+	DC_W32(priv, TULIP_CSR0, i);
+	udelay(1000);
+
+	for (i = 0; i < 5; i++) {
+		(void)DC_R32(priv, TULIP_CSR0);
+		udelay(10000);
+	}
+	udelay(1000);
+
+	if ((DC_R32(priv, TULIP_CSR5) & (STS_TS | STS_RS)) != 0) {
+		printf("Error: Cannot reset ethernet controller.\n");
+		return -1;
+	}
+
+	DC_W32(priv, TULIP_CSR6, OMR_SDP | OMR_PS | OMR_PM | 0x40);
+
+	for (i = 0; i < NUM_RX_DESC; i++) {
+		priv->rx_ring[i].status = cpu_to_le32(TULIP_RDES0_OWN);
+		priv->rx_ring[i].length = cpu_to_le32(RX_BUFF_SZ);
+		/* FIXME: VERY dirty */
+		priv->rx_ring[i].buffer1 = cpu_to_le32(phys_to_bus((u32) NetRxPackets[i]));
+#if 1
+		/* More that one descriptor */
+		priv->rx_ring[i].buffer2 = cpu_to_le32(phys_to_bus((u32) &priv->rx_ring[(i+1) % NUM_RX_DESC]));
+#else
+		priv->rx_ring[i].buffer2 = 0;
+#endif
+	}
+
+	for (i = 0; i < NUM_TX_DESC; i++) {
+		priv->tx_ring[i].status = 0;
+		priv->tx_ring[i].length = 0;
+		priv->tx_ring[i].buffer1 = 0;
+
+#if 0
+		priv->tx_ring[i].buffer2 = cpu_to_le32(phys_to_bus((u32) &priv->tx_ring[(i+1) % NUM_TX_DESC]));
+#endif
+		priv->tx_ring[i].buffer2 = 0;
+	}
+
+	/* Write the end of list marker to the descriptor lists. */
+	priv->rx_ring[NUM_RX_DESC - 1].length |= cpu_to_le32(RD_RER);
+	priv->tx_ring[NUM_TX_DESC - 1].length |= cpu_to_le32(TD_TER);
+
+//	priv->tx_ring[NUM_TX_DESC - 1].buffer2 = cpu_to_le32(phys_to_bus((u32) &priv->tx_ring[0]));
+
+	/* Tell the adapter where the TX/RX rings are located. */
+	/* FIXME: rx_ring to bus */
+	DC_W32(priv, TULIP_CSR3, phys_to_bus((u32) (priv->rx_ring)));
+	DC_W32(priv, TULIP_CSR4, phys_to_bus((u32) (priv->tx_ring)));
+
+	priv->rx_new = 0;
+	priv->tx_new = 0;
+
+	/* Start the chip's Tx to process setup frame. */
+	DC_W32(priv, TULIP_CSR6, DC_R32(priv, TULIP_CSR6)
+				| TULIP_CSR6_TXON | TULIP_CSR6_RXON);
+
+	dec21143_send_setup_frame(edev);
+
+	//miidev_restart_aneg(&priv->miidev);
+
+	return 0;
+}
+
+static int dec21143_eth_open(struct eth_device *edev)
+{
+	struct dec21143_priv *priv = edev->priv;
+
+	miidev_wait_aneg(&priv->miidev);
+	miidev_print_status(&priv->miidev);
+
+	return 0;
+}
+
+static void dec21143_eth_halt(struct eth_device *edev)
+{
+	struct dec21143_priv *priv = edev->priv;
+
+#ifdef DEC21143_DEBUG
+	printf("%s\n", __func__);
+#endif
+
+	/* Stop the Tx and Rx processes. */
+	DC_W32(priv, TULIP_CSR6, DC_R32(priv, TULIP_CSR6) &
+			~ (TULIP_CSR6_TXON | TULIP_CSR6_RXON));
+}
+
+static int dec21143_eth_send(struct eth_device *edev, void *packet,
+				int packet_length)
+{
+	struct dec21143_priv *priv = edev->priv;
+
+	int status = -1;
+	int i;
+
+#ifdef DEC21143_DEBUG
+//	printf("%s\n", __func__);
+//	memory_display(packet, 0, 70, 1);
+#endif
+
+	if (packet_length <= 0) {
+		printf("%s: bad packet size: %d\n", edev->dev.name, packet_length);
+		goto Done;
+	}
+
+	for (i = 0; priv->tx_ring[priv->tx_new].status & cpu_to_le32(TULIP_TDES0_OWN); i++) {
+		if (i >= TOUT_LOOP) {
+			printf("%s: tx error buffer not ready\n", edev->dev.name);
+			goto Done;
+		}
+	}
+
+	priv->tx_ring[priv->tx_new].buffer1 = cpu_to_le32(phys_to_bus((u32) packet));
+	priv->tx_ring[priv->tx_new].length = cpu_to_le32(TD_TER | TULIP_TDES1_LS | TULIP_TDES1_FS | packet_length);
+	priv->tx_ring[priv->tx_new].status = cpu_to_le32(TULIP_TDES0_OWN);
+
+	DC_W32(priv, TULIP_CSR1, POLL_DEMAND);
+
+	for(i = 0; priv->tx_ring[priv->tx_new].status & cpu_to_le32(TULIP_TDES0_OWN); i++) {
+		if (i >= TOUT_LOOP) {
+//			printf("%s: tx buffer not ready 2\n", edev->dev.name);
+			goto Done;
+		}
+	}
+
+	if (le32_to_cpu(priv->tx_ring[priv->tx_new].status) & TD_ES) {
+#if 0 /* test-only */
+		printf("TX error status = 0x%08X\n",
+			le32_to_cpu(priv->tx_ring[priv->tx_new].status));
+#endif
+		priv->tx_ring[priv->tx_new].status = 0x0;
+		goto Done;
+	}
+
+	status = packet_length;
+
+Done:
+	priv->tx_new = (priv->tx_new + 1) % NUM_TX_DESC;
+
+	return status;
+}
+
+static int dec21143_eth_rx(struct eth_device *edev)
+{
+	struct dec21143_priv *priv = edev->priv;
+
+	s32 status;
+	int length = 0;
+
+#ifdef DEC21143_DEBUG
+	//printf("%s rx_new=%d\n", __func__, priv->rx_new);
+	//dump_rings(priv);
+#endif
+
+	DC_W32(priv, TULIP_CSR2, POLL_DEMAND);
+
+	for ( ; ; ) {
+		status = (s32)le32_to_cpu(priv->rx_ring[priv->rx_new].status);
+
+		if (status & TULIP_RDES0_OWN) {
+			break;
+		}
+
+		if (status & RD_LS) {
+			/* Valid frame status. */
+			if (status & RD_ES) {
+				/* There was an error. */
+				printf("RX error status = 0x%08X\n", status);
+			} else {
+				/* A valid frame received. */
+				length = (le32_to_cpu(priv->rx_ring[priv->rx_new].status) >> 16);
+
+				/* Pass the packet up to the protocol layers. */
+				/* FIXME: VERY dirty */
+				//net_receive(0xa0000000 | cpu_to_le32(priv->rx_ring[priv->rx_new].buffer1), length);
+				/* Skip 4 byte CRC */
+				net_receive(NetRxPackets[priv->rx_new], length - 4);
+			}
+
+			/* Change buffer ownership for this frame, back to the adapter. */
+			priv->rx_ring[priv->rx_new].status = cpu_to_le32(TULIP_RDES0_OWN);
+			DC_W32(priv, TULIP_CSR2, POLL_DEMAND);
+		}
+
+		/* Update entry information. */
+		priv->rx_new = (priv->rx_new + 1) % NUM_RX_DESC;
+
+		break;
+	}
+
+	DC_W32(priv, TULIP_CSR2, POLL_DEMAND);
+
+	return length;
+}
+
+static int dec21143_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
+{
+	struct eth_device *edev;
+	struct dec21143_priv *priv;
+
+	u32 bar;
+	struct pci_bus *bus = pdev->bus;
+	struct device_d *dev = &pdev->dev;
+
+#ifdef DEC21143_DEBUG
+	printf("%s\n", __func__);
+#endif
+
+	/* enable pci device */
+	pci_read_config_dword(pdev, PCI_COMMAND, &bar);
+	pci_write_config_dword(pdev, PCI_COMMAND, bar | 0x03);
+
+	edev = xzalloc(sizeof(struct eth_device) +
+			sizeof(struct dec21143_priv));
+	dev->type_data = edev;
+	priv = (struct dec21143_priv *)(edev + 1);
+
+	edev->priv = priv;
+
+	/* FIXME: unused ? */
+	priv->pci_dev = pdev;
+
+	/* FIXME: pci_resource_start() */
+	pci_read_config_dword(pdev, PCI_BASE_ADDRESS_1, &bar);
+	priv->base = (void *)bus->ops->res_start(bus, bar);
+
+	printf("found dec21142/3 (rev %02x) at %02x: %04x (base=%p)\n",
+			pdev->revision,
+			pdev->devfn,
+			(pdev->class >> 8) & 0xffff,
+			priv->base);
+
+	edev->init = dec21143_init_dev;
+	edev->open = dec21143_eth_open;
+	edev->send = dec21143_eth_send;
+	edev->recv = dec21143_eth_rx;
+	edev->get_ethaddr = dec21143_get_ethaddr;
+	edev->set_ethaddr = dec21143_set_ethaddr;
+	edev->halt = dec21143_eth_halt;
+
+	priv->miidev.read = dec21143_phy_read;
+	priv->miidev.write = dec21143_phy_write;
+	priv->miidev.address = 0;
+	priv->miidev.flags = 0;
+	priv->miidev.edev = edev;
+
+	/* FIXME: there is no warranty what this memory can be seen from pci */
+	priv->rx_ring = xzalloc(sizeof(struct rx_desc) * NUM_RX_DESC);
+	priv->tx_ring = xzalloc(sizeof(struct tx_desc) * NUM_TX_DESC);
+
+	mii_register(&priv->miidev);
+	eth_register(edev);
+
+	return 0;
+}
+
+static DEFINE_PCI_DEVICE_TABLE(dec21143_pci_tbl) = {
+	{ PCI_DEVICE(PCI_VENDOR_ID_DEC,	PCI_DEVICE_ID_DEC_21142), },
+	{ },
+};
+
+static struct pci_driver dec21143_pci_eth_driver = {
+	.name = "dec21143_pci_eth",
+	.id_table = dec21143_pci_tbl,
+	.probe = dec21143_pci_probe,
+};
+
+static int dec21143_pci_init(void)
+{
+	pci_register_driver(&dec21143_pci_eth_driver);
+
+	return 0;
+}
+device_initcall(dec21143_pci_init);
+
+static int dec21143_eth_probe(struct device_d *dev)
+{
+	struct eth_device *edev;
+	struct dec21143_priv *priv;
+
+	edev = xzalloc(sizeof(struct eth_device) +
+			sizeof(struct dec21143_priv));
+	dev->type_data = edev;
+	priv = (struct dec21143_priv *)(edev + 1);
+
+	priv->base = dev_request_mem_region(dev, 0);
+
+	edev->priv = priv;
+
+	edev->init = dec21143_init_dev;
+	edev->open = dec21143_eth_open;
+	edev->send = dec21143_eth_send;
+	edev->recv = dec21143_eth_rx;
+	edev->get_ethaddr = dec21143_get_ethaddr;
+	edev->set_ethaddr = dec21143_set_ethaddr;
+	edev->halt = dec21143_eth_halt;
+
+	priv->miidev.read = dec21143_phy_read;
+	priv->miidev.write = dec21143_phy_write;
+	priv->miidev.address = 0;
+	priv->miidev.flags = 0;
+	priv->miidev.edev = edev;
+
+	priv->rx_ring = xzalloc(sizeof(struct rx_desc) * NUM_RX_DESC);
+	priv->tx_ring = xzalloc(sizeof(struct tx_desc) * NUM_TX_DESC);
+
+	mii_register(&priv->miidev);
+	eth_register(edev);
+
+	return 0;
+}
+
+static struct driver_d dec21143_eth_driver = {
+	.name = "dec21143_eth",
+	.probe = dec21143_eth_probe,
+};
+
+static int dec21143_eth_init(void)
+{
+	register_driver(&dec21143_eth_driver);
+
+	return 0;
+}
+device_initcall(dec21143_eth_init);
diff --git a/drivers/pci/Makefile b/drivers/pci/Makefile
index 6523385..a10f471 100644
--- a/drivers/pci/Makefile
+++ b/drivers/pci/Makefile
@@ -3,6 +3,7 @@
 #
 obj-y		+= pci.o bus.o
 obj-$(CONFIG_8139)	+= rtl8139.o
+obj-$(CONFIG_21143)	+= 21143.o
 
 ccflags-$(CONFIG_PCI_DEBUG) := -DDEBUG
 
diff --git a/include/linux/pci_ids.h b/include/linux/pci_ids.h
index 17ac7fd..a525653 100644
--- a/include/linux/pci_ids.h
+++ b/include/linux/pci_ids.h
@@ -137,5 +137,8 @@
 
 /* Vendors and devices.  Sort key: vendor first, device next. */
 
+#define PCI_VENDOR_ID_DEC		0x1011
+#define PCI_DEVICE_ID_DEC_21142		0x0019
+
 #define PCI_VENDOR_ID_REALTEK		0x10ec
 #define PCI_DEVICE_ID_REALTEK_8139	0x8139
-- 
1.7.8.3




More information about the barebox mailing list