LKML Archive on lore.kernel.org
help / color / mirror / Atom feed
* [PATCH -mm 3/5] Blackfin: on-chip ethernet MAC controller driver
@ 2007-03-01  4:15 Wu, Bryan
  2007-03-01  8:53 ` Andrew Morton
  2007-03-01 15:52 ` Stephen Hemminger
  0 siblings, 2 replies; 6+ messages in thread
From: Wu, Bryan @ 2007-03-01  4:15 UTC (permalink / raw)
  To: Andrew Morton, jgarzik, linux-kernel, netdev

Hi folks,

Here is the blackfin on-chip ethernet MAC controller driver for Linux.

It's name is blackfin-driver-net-stamp537.patch

[PATCH] Blackfin: on-chip ethernet MAC controller driver

This patch implements the driver necessary use the Analog Devices
Blackfin processor's on-chip ethernet MAC controller.

Signed-off-by: Bryan Wu <bryan.wu@analog.com> 
---

 drivers/net/Kconfig    |   44 ++
 drivers/net/Makefile   |    1
 drivers/net/bfin_mac.c |  988 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 drivers/net/bfin_mac.h |  146 +++++++++
 4 files changed, 1179 insertions(+)

Index: linux-2.6/drivers/net/Kconfig
===================================================================
--- linux-2.6.orig/drivers/net/Kconfig	2007-03-01 11:39:14.000000000 +0800
+++ linux-2.6/drivers/net/Kconfig	2007-03-01 11:39:19.000000000 +0800
@@ -836,6 +836,50 @@
 	  module, say M here and read <file:Documentation/modules.txt> as well
 	  as <file:Documentation/networking/net-modules.txt>.
 
+config BFIN_MAC
+	tristate "Blackfin 536/537 on-chip mac support"
+	depends on NET_ETHERNET && (BF537 || BF536) && (!BF537_PORT_H)
+	select CRC32
+	select BFIN_MAC_USE_L1 if DMA_UNCACHED_NONE
+	help
+	  This is the driver for blackfin on-chip mac device. Say Y if you want it
+	  compiled into the kernel. This driver is also available as a module
+	  ( = code which can be inserted in and removed from the running kernel
+	  whenever you want). The module will be called bfin_mac.
+
+config BFIN_MAC_USE_L1
+	bool "Use L1 memory for rx/tx packets"
+	depends on BFIN_MAC && BF537
+	default y
+	help
+	  To get maximum network performace, you should use L1 memory as rx/tx buffers.
+	  Say N here if you want to reserve L1 memory for other uses.
+
+config BFIN_TX_DESC_NUM
+	int "Number of transmit buffer packets"
+	depends on BFIN_MAC
+	range 6 10 if BFIN_MAC_USE_L1
+	range 10 100
+	default "10"
+	help
+	  Set the number of buffer packets used in driver.
+
+config BFIN_RX_DESC_NUM
+	int "Number of receive buffer packets"
+	depends on BFIN_MAC
+	range 20 100 if BFIN_MAC_USE_L1
+	range 20 800
+	default "20"
+	help
+	  Set the number of buffer packets used in driver.
+
+config BFIN_MAC_RMII
+	bool "RMII PHY Interface (EXPERIMENTAL)"
+	depends on BFIN_MAC && EXPERIMENTAL
+	default n
+	help
+	  Use Reduced PHY MII Interface
+
 config SMC9194
 	tristate "SMC 9194 support"
 	depends on NET_VENDOR_SMC && (ISA || MAC && BROKEN)
Index: linux-2.6/drivers/net/Makefile
===================================================================
--- linux-2.6.orig/drivers/net/Makefile	2007-03-01 11:33:24.000000000 +0800
+++ linux-2.6/drivers/net/Makefile	2007-03-01 11:39:19.000000000 +0800
@@ -195,6 +195,7 @@
 obj-$(CONFIG_MYRI10GE) += myri10ge/
 obj-$(CONFIG_SMC91X) += smc91x.o
 obj-$(CONFIG_SMC911X) += smc911x.o
+obj-$(CONFIG_BFIN_MAC) += bfin_mac.o
 obj-$(CONFIG_DM9000) += dm9000.o
 obj-$(CONFIG_FEC_8XX) += fec_8xx/
 obj-$(CONFIG_PASEMI_MAC) += pasemi_mac.o
Index: linux-2.6/drivers/net/bfin_mac.c
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ linux-2.6/drivers/net/bfin_mac.c	2007-03-01 11:39:19.000000000 +0800
@@ -0,0 +1,988 @@
+/*
+ * File:         drivers/net/bfin_mac.c
+ * Based on:
+ * Author:       Luke Yang <luke.yang@analog.com>
+ *
+ * Created:
+ * Description:
+ *
+ * Rev:          $Id: bfin_mac.c,v 1.60 2006/12/16 11:23:56 hennerich Exp $
+ *
+ * Modified:
+ *               Copyright 2004-2006 Analog Devices Inc.
+ *
+ * Bugs:         Enter bugs at http://blackfin.uclinux.org/
+ *
+ * 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, 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 ;  see the file COPYING.
+ * If not, write to the Free Software Foundation,
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/timer.h>
+#include <linux/errno.h>
+#include <linux/ioport.h>
+#include <linux/crc32.h>
+#include <linux/device.h>
+#include <linux/spinlock.h>
+#include <linux/ethtool.h>
+#include <linux/mii.h>
+
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+
+#include <linux/platform_device.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/dma.h>
+#include <linux/dma-mapping.h>
+
+#include <asm/irq.h>
+#include <asm/blackfin.h>
+#include <asm/delay.h>
+
+#include "bfin_mac.h"
+
+#define CARDNAME "bfin_mac"
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Luke Yang");
+MODULE_DESCRIPTION("Blackfin MAC Driver");
+
+#if defined(CONFIG_BFIN_MAC_USE_L1)
+# define bfin_mac_alloc(dma_handle, size)  l1_data_sram_zalloc(size)
+# define bfin_mac_free(dma_handle, ptr)    l1_data_sram_free(ptr)
+#else
+# define bfin_mac_alloc(dma_handle, size)  dma_alloc_coherent(NULL, size, dma_handle, GFP_DMA)
+# define bfin_mac_free(dma_handle, ptr)    dma_free_coherent(NULL, sizeof(*ptr), ptr, dma_handle)
+#endif
+
+#define PKT_BUF_SZ 1580
+
+static void desc_list_free(void);
+
+/* pointers to maintain transmit list */
+struct net_dma_desc_tx *tx_list_head;
+struct net_dma_desc_tx *tx_list_tail;
+struct net_dma_desc_rx *rx_list_head;
+struct net_dma_desc_rx *rx_list_tail;
+struct net_dma_desc_rx *current_rx_ptr;
+struct net_dma_desc_tx *current_tx_ptr;
+struct net_dma_desc_tx *tx_desc;
+struct net_dma_desc_rx *rx_desc;
+
+extern void get_bf537_ether_addr(char *addr);
+
+static int desc_list_init(void)
+{
+	struct net_dma_desc_tx *tmp_desc_tx;
+	struct net_dma_desc_rx *tmp_desc_rx;
+	int i;
+	struct sk_buff *new_skb;
+#if !defined(CONFIG_BFIN_MAC_USE_L1)
+	dma_addr_t dma_handle;
+#endif
+
+	tx_desc =
+	    bfin_mac_alloc(&dma_handle,
+			   sizeof(struct net_dma_desc_tx) *
+			   CONFIG_BFIN_TX_DESC_NUM);
+	if (tx_desc == NULL)
+		goto init_error;
+
+	rx_desc =
+	    bfin_mac_alloc(&dma_handle,
+			   sizeof(struct net_dma_desc_rx) *
+			   CONFIG_BFIN_RX_DESC_NUM);
+	if (rx_desc == NULL)
+		goto init_error;
+
+	/* init tx_list */
+	for (i = 0; i < CONFIG_BFIN_TX_DESC_NUM; i++) {
+
+		tmp_desc_tx = tx_desc + i;
+
+		if (i == 0) {
+			tx_list_head = tmp_desc_tx;
+			tx_list_tail = tmp_desc_tx;
+		}
+
+		tmp_desc_tx->desc_a.start_addr =
+		    (unsigned long)tmp_desc_tx->packet;
+		tmp_desc_tx->desc_a.x_count = 0;
+		tmp_desc_tx->desc_a.config.b_DMA_EN = 0;	//disabled
+		tmp_desc_tx->desc_a.config.b_WNR = 0;	//read from memory
+		tmp_desc_tx->desc_a.config.b_WDSIZE = 2;	//wordsize is 32 bits
+		tmp_desc_tx->desc_a.config.b_NDSIZE = 6;	//6 half words is desc size.
+		tmp_desc_tx->desc_a.config.b_FLOW = 7;	//large desc flow
+		tmp_desc_tx->desc_a.next_dma_desc = &(tmp_desc_tx->desc_b);
+
+		tmp_desc_tx->desc_b.start_addr =
+		    (unsigned long)(&(tmp_desc_tx->status));
+		tmp_desc_tx->desc_b.x_count = 0;
+		tmp_desc_tx->desc_b.config.b_DMA_EN = 1;	//enabled
+		tmp_desc_tx->desc_b.config.b_WNR = 1;	//write to memory
+		tmp_desc_tx->desc_b.config.b_WDSIZE = 2;	//wordsize is 32 bits
+		tmp_desc_tx->desc_b.config.b_DI_EN = 0;	//disable interrupt
+		tmp_desc_tx->desc_b.config.b_NDSIZE = 6;
+		tmp_desc_tx->desc_b.config.b_FLOW = 7;	//stop mode
+		tmp_desc_tx->skb = NULL;
+		tx_list_tail->desc_b.next_dma_desc = &(tmp_desc_tx->desc_a);
+		tx_list_tail->next = tmp_desc_tx;
+
+		tx_list_tail = tmp_desc_tx;
+	}
+	tx_list_tail->next = tx_list_head;	/* tx_list is a circle */
+	tx_list_tail->desc_b.next_dma_desc = &(tx_list_head->desc_a);
+	current_tx_ptr = tx_list_head;
+
+	/* init rx_list */
+	for (i = 0; i < CONFIG_BFIN_RX_DESC_NUM; i++) {
+
+		tmp_desc_rx = rx_desc + i;
+
+		if (i == 0) {
+			rx_list_head = tmp_desc_rx;
+			rx_list_tail = tmp_desc_rx;
+		}
+
+		/* allocat a new skb for next time receive */
+		new_skb = dev_alloc_skb(PKT_BUF_SZ + 2);
+		if (!new_skb) {
+			printk(KERN_NOTICE CARDNAME
+			       ": init: low on mem - packet dropped\n");
+			goto init_error;
+		}
+		skb_reserve(new_skb, 2);
+		tmp_desc_rx->skb = new_skb;
+		/* since RXDWA is enabled */
+		tmp_desc_rx->desc_a.start_addr =
+		    (unsigned long)new_skb->data - 2;
+		tmp_desc_rx->desc_a.x_count = 0;
+		tmp_desc_rx->desc_a.config.b_DMA_EN = 1;	//enabled
+		tmp_desc_rx->desc_a.config.b_WNR = 1;	//Write to memory
+		tmp_desc_rx->desc_a.config.b_WDSIZE = 2;	//wordsize is 32 bits
+		tmp_desc_rx->desc_a.config.b_NDSIZE = 6;	//6 half words is desc size.
+		tmp_desc_rx->desc_a.config.b_FLOW = 7;	//large desc flow
+		tmp_desc_rx->desc_a.next_dma_desc = &(tmp_desc_rx->desc_b);
+
+		tmp_desc_rx->desc_b.start_addr =
+		    (unsigned long)(&(tmp_desc_rx->status));
+		tmp_desc_rx->desc_b.x_count = 0;
+		tmp_desc_rx->desc_b.config.b_DMA_EN = 1;	//enabled
+		tmp_desc_rx->desc_b.config.b_WNR = 1;	//Write to memory
+		tmp_desc_rx->desc_b.config.b_WDSIZE = 2;	//wordsize is 32 bits
+		tmp_desc_rx->desc_b.config.b_NDSIZE = 6;
+		tmp_desc_rx->desc_b.config.b_DI_EN = 1;	//enable interrupt
+		tmp_desc_rx->desc_b.config.b_FLOW = 7;	//large mode
+		rx_list_tail->desc_b.next_dma_desc = &(tmp_desc_rx->desc_a);
+
+		rx_list_tail->next = tmp_desc_rx;
+		rx_list_tail = tmp_desc_rx;
+	}
+	rx_list_tail->next = rx_list_head;	/* rx_list is a circle */
+	rx_list_tail->desc_b.next_dma_desc = &(rx_list_head->desc_a);
+	current_rx_ptr = rx_list_head;
+
+	return 0;
+
+      init_error:
+	desc_list_free();
+	printk(KERN_ERR CARDNAME ": kmalloc failed\n");
+	return -ENOMEM;
+}
+
+static void desc_list_free(void)
+{
+	struct net_dma_desc_rx *tmp_desc_rx;
+	struct net_dma_desc_tx *tmp_desc_tx;
+	int i;
+#if !defined(CONFIG_BFIN_MAC_USE_L1)
+	dma_addr_t dma_handle = 0;
+#endif
+
+	if (tx_desc != NULL) {
+		tmp_desc_tx = tx_list_head;
+		for (i = 0; i < CONFIG_BFIN_TX_DESC_NUM; i++) {
+			if (tmp_desc_tx != NULL) {
+				if (tmp_desc_tx->skb) {
+					dev_kfree_skb(tmp_desc_tx->skb);
+					tmp_desc_tx->skb = NULL;
+				}
+
+			}
+			tmp_desc_tx = tmp_desc_tx->next;
+		}
+		bfin_mac_free(dma_handle, tx_desc);
+	}
+
+	if (rx_desc != NULL) {
+		tmp_desc_rx = rx_list_head;
+		for (i = 0; i < CONFIG_BFIN_RX_DESC_NUM; i++) {
+			if (tmp_desc_rx != NULL) {
+				if (tmp_desc_rx->skb) {
+					dev_kfree_skb(tmp_desc_rx->skb);
+					tmp_desc_rx->skb = NULL;
+				}
+			}
+			tmp_desc_rx = tmp_desc_rx->next;
+		}
+		bfin_mac_free(dma_handle, rx_desc);
+	}
+}
+
+/*---PHY CONTROL AND CONFIGURATION-----------------------------------------*/
+
+//
+//Set FER regs to MUX in Ethernet pins
+//
+
+static void SetupPinMux(void)
+{
+	unsigned int fer_val;
+
+	// FER reg bug work-around
+	// read it once
+	fer_val = bfin_read_PORTH_FER();
+
+#if defined(CONFIG_BFIN_MAC_RMII)
+	fer_val = 0xC373;
+#else
+	fer_val = 0xffff;
+#endif
+	// write it twice to the same value
+
+	bfin_write_PORTH_FER(fer_val);
+	bfin_write_PORTH_FER(fer_val);
+}
+
+//
+//Wait until the previous MDC/MDIO transaction has completed
+//
+
+static void PollMdcDone(void)
+{
+	// poll the STABUSY bit
+	while ((bfin_read_EMAC_STAADD()) & STABUSY) {
+	};
+}
+
+//
+//Read an off-chip register in a PHY through the MDC/MDIO port
+//
+
+static u16 RdPHYReg(u16 PHYAddr, u16 RegAddr)
+{
+	PollMdcDone();
+	bfin_write_EMAC_STAADD(SET_PHYAD(PHYAddr) | SET_REGAD(RegAddr) | STABUSY);	// read mode
+	PollMdcDone();
+
+	return (u16) bfin_read_EMAC_STADAT();
+}
+
+//
+//Write an off-chip register in a PHY through the MDC/MDIO port
+//
+
+static void RawWrPHYReg(u16 PHYAddr, u16 RegAddr, u32 Data)
+{
+
+	bfin_write_EMAC_STADAT(Data);
+
+	bfin_write_EMAC_STAADD(SET_PHYAD(PHYAddr) | SET_REGAD(RegAddr) | STAOP | STABUSY);	//write mode
+
+	PollMdcDone();
+}
+
+static void WrPHYReg(u16 PHYAddr, u16 RegAddr, u32 Data)
+{
+	PollMdcDone();
+	RawWrPHYReg(PHYAddr, RegAddr, Data);
+}
+
+//
+//set up the phy
+//
+static void bf537mac_setphy(struct net_device *dev)
+{
+	u16 phydat;
+	u32 sysctl;
+	struct bf537mac_local *lp = netdev_priv(dev);
+
+	//printk(KERN_DEBUG CARDNAME ": start settting up phy\n");
+
+	//Program PHY registers
+	phydat = 0;
+
+	// issue a reset
+	RawWrPHYReg(lp->PhyAddr, PHYREG_MODECTL, 0x8000);
+
+	// wait half a second
+	udelay(500);
+
+	phydat = RdPHYReg(lp->PhyAddr, PHYREG_MODECTL);
+
+	// advertise flow control supported
+	phydat = RdPHYReg(lp->PhyAddr, PHYREG_ANAR);
+	phydat |= (1 << 10);
+	WrPHYReg(lp->PhyAddr, PHYREG_ANAR, phydat);
+
+	phydat = 0;
+	if (lp->Negotiate) {
+		phydat |= 0x1000;	// enable auto negotiation
+	} else {
+		if (lp->FullDuplex) {
+			phydat |= (1 << 8);	// full duplex
+		} else {
+			phydat &= (~(1 << 8));	// half duplex
+		}
+		if (!lp->Port10) {
+			phydat |= (1 << 13);	// 100 Mbps
+		} else {
+			phydat &= (~(1 << 13));	// 10 Mbps
+		}
+	}
+
+	if (lp->Loopback) {
+		phydat |= (1 << 14);	// enable TX->RX loopback
+		//WrPHYReg(lp->PhyAddr, PHYREG_MODECTL, phydat);
+	}
+
+	WrPHYReg(lp->PhyAddr, PHYREG_MODECTL, phydat);
+	udelay(500);
+
+	phydat = RdPHYReg(lp->PhyAddr, PHYREG_MODECTL);
+	// check for SMSC PHY
+	if ((RdPHYReg(lp->PhyAddr, PHYREG_PHYID1) == 0x7)
+	    && ((RdPHYReg(lp->PhyAddr, PHYREG_PHYID2) & 0xfff0) == 0xC0A0)) {
+		// we have SMSC PHY so reqest interrupt on link down condition
+		WrPHYReg(lp->PhyAddr, 30, 0x0ff);	// enable interrupts
+		// enable PHY_INT
+		sysctl = bfin_read_EMAC_SYSCTL();
+		sysctl |= 0x1;
+		//bfin_write_EMAC_SYSCTL(sysctl);
+	}
+}
+
+/**************************************************************************/
+void SetupSystemRegs(struct net_device *dev)
+{
+	int PHYADDR;
+	unsigned short sysctl, phydat;
+	u32 opmode;
+	struct bf537mac_local *lp = netdev_priv(dev);
+	int count = 0;
+
+	PHYADDR = lp->PhyAddr;
+
+	/* Enable PHY output */
+	if (!(bfin_read_VR_CTL() & PHYCLKOE))
+		bfin_write_VR_CTL(bfin_read_VR_CTL() | PHYCLKOE);
+
+	/* MDC  = 2.5 MHz */
+	sysctl = SET_MDCDIV(24);
+	/* Odd word alignment for Receive Frame DMA word */
+	/* Configure checksum support and rcve frame word alignment */
+#if defined(BFIN_MAC_CSUM_OFFLOAD)
+	sysctl |= RXDWA | RXCKS;
+#else
+	sysctl |= RXDWA;
+#endif
+	bfin_write_EMAC_SYSCTL(sysctl);
+	/* auto negotiation on  */
+	/* full duplex          */
+	/* 100 Mbps             */
+	phydat = PHY_ANEG_EN | PHY_DUPLEX | PHY_SPD_SET;
+	WrPHYReg(PHYADDR, PHYREG_MODECTL, phydat);
+
+	/* test if full duplex supported */
+	do {
+		msleep(100);
+		phydat = RdPHYReg(PHYADDR, PHYREG_MODESTAT);
+		if (count > 30) {
+			printk(KERN_NOTICE CARDNAME
+			       ": Link is down, please check your network connection\n");
+			break;
+		}
+		count++;
+	} while (!(phydat & 0x0004));
+
+	phydat = RdPHYReg(PHYADDR, PHYREG_ANLPAR);
+
+	if ((phydat & 0x0100) || (phydat & 0x0040)) {
+		opmode = FDMODE;
+	} else {
+		opmode = 0;
+		printk(KERN_INFO CARDNAME ": Network is set to half duplex\n");
+	}
+
+#if defined(CONFIG_BFIN_MAC_RMII)
+	opmode |= RMII; /* For Now only 100MBit are supported */
+#endif
+
+	bfin_write_EMAC_OPMODE(opmode);
+
+	//bfin_write_EMAC_MMC_CTL(RSTC | CROLL | MMCE);
+	bfin_write_EMAC_MMC_CTL(RSTC | CROLL);
+
+	/* Initialize the TX DMA channel registers */
+	bfin_write_DMA2_X_COUNT(0);
+	bfin_write_DMA2_X_MODIFY(4);
+	bfin_write_DMA2_Y_COUNT(0);
+	bfin_write_DMA2_Y_MODIFY(0);
+
+	/* Initialize the RX DMA channel registers */
+	bfin_write_DMA1_X_COUNT(0);
+	bfin_write_DMA1_X_MODIFY(4);
+	bfin_write_DMA1_Y_COUNT(0);
+	bfin_write_DMA1_Y_MODIFY(0);
+}
+
+void SetupMacAddr(u8 * mac_addr)
+{
+	// this depends on a little-endian machine
+	bfin_write_EMAC_ADDRLO(*(u32 *) & mac_addr[0]);
+	bfin_write_EMAC_ADDRHI(*(u16 *) & mac_addr[4]);
+}
+
+static void adjust_tx_list(void)
+{
+	if (tx_list_head->status.status_word != 0
+	    && current_tx_ptr != tx_list_head) {
+		goto adjust_head;	// released something, just return;
+	}
+
+	/* if nothing released, check wait condition */
+	/* current's next can not be the head, otherwise the dma will not stop as we want */
+	if (current_tx_ptr->next->next == tx_list_head) {
+		while (tx_list_head->status.status_word == 0) {
+			udelay(10);
+			if (tx_list_head->status.status_word != 0
+			    || !(bfin_read_DMA2_IRQ_STATUS() & 0x08)) {
+				goto adjust_head;
+			}
+		}
+		if (tx_list_head->status.status_word != 0) {
+			goto adjust_head;
+		}
+	}
+
+	return;
+
+      adjust_head:
+	do {
+		tx_list_head->desc_a.config.b_DMA_EN = 0;
+		tx_list_head->status.status_word = 0;
+		if (tx_list_head->skb) {
+			dev_kfree_skb(tx_list_head->skb);
+			tx_list_head->skb = NULL;
+		} else {
+			printk(KERN_ERR CARDNAME
+			       ": no sk_buff in a transmitted frame!\n");
+		}
+		tx_list_head = tx_list_head->next;
+	} while (tx_list_head->status.status_word != 0
+		 && current_tx_ptr != tx_list_head);
+	return;
+
+}
+
+static int bf537mac_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+	struct bf537mac_local *lp = netdev_priv(dev);
+	unsigned int data;
+
+	current_tx_ptr->skb = skb;
+	// Is skb->data always 16-bit aligned? Do we need to memcpy((char *)(tail->packet + 2),skb->data,len)?
+	if ((((unsigned int)(skb->data)) & 0x02) == 2) {
+		//move skb->data to current_tx_ptr payload
+		data = (unsigned int)(skb->data) - 2;
+		*((unsigned short *)data) = (unsigned short)(skb->len);
+		current_tx_ptr->desc_a.start_addr = (unsigned long)data;
+		blackfin_dcache_flush_range(data, (data + (skb->len)) + 2);	//this is important!
+
+	} else {
+		*((unsigned short *)(current_tx_ptr->packet)) =
+		    (unsigned short)(skb->len);
+		memcpy((char *)(current_tx_ptr->packet + 2), skb->data,
+		       (skb->len));
+		current_tx_ptr->desc_a.start_addr =
+		    (unsigned long)current_tx_ptr->packet;
+		if (current_tx_ptr->status.status_word != 0)
+			current_tx_ptr->status.status_word = 0;
+		blackfin_dcache_flush_range((unsigned int)current_tx_ptr->
+					    packet,
+					    (unsigned int)(current_tx_ptr->
+							   packet + skb->len) +
+					    2);
+	}
+
+	current_tx_ptr->desc_a.config.b_DMA_EN = 1;	//enable this packet's dma
+
+	if (bfin_read_DMA2_IRQ_STATUS() & 0x08) {	//tx dma is running, just return
+		goto out;
+	} else {		//tx dma is not running
+		bfin_write_DMA2_NEXT_DESC_PTR(&(current_tx_ptr->desc_a));
+		bfin_write_DMA2_CONFIG(*((unsigned short *)(&(current_tx_ptr->desc_a.config))));	// dma enabled, read from memory, size is 6
+		// Turn on the EMAC tx
+		bfin_write_EMAC_OPMODE(bfin_read_EMAC_OPMODE() | TE);
+	}
+
+      out:
+	adjust_tx_list();
+	current_tx_ptr = current_tx_ptr->next;
+	dev->trans_start = jiffies;
+	lp->stats.tx_packets++;
+	lp->stats.tx_bytes += (skb->len);
+	return 0;
+}
+
+static void bf537mac_rx(struct net_device *dev)
+{
+	struct sk_buff *skb, *new_skb;
+	struct bf537mac_local *lp = netdev_priv(dev);
+	unsigned short len;
+
+	/* allocat a new skb for next time receive */
+	skb = current_rx_ptr->skb;
+	new_skb = dev_alloc_skb(PKT_BUF_SZ + 2);
+	if (!new_skb) {
+		printk(KERN_NOTICE CARDNAME
+		       ": rx: low on mem - packet dropped\n");
+		lp->stats.rx_dropped++;
+		goto out;
+	}
+	/* reserve 2 bytes for RXDWA padding */
+	skb_reserve(new_skb, 2);
+	current_rx_ptr->skb = new_skb;
+	current_rx_ptr->desc_a.start_addr = (unsigned long)new_skb->data - 2;
+/*
+	int i;
+	if (len >= 64) {
+		for (i=0;i<len;i++) {
+			printk("%.2x-",((unsigned char *)pkt)[i]);
+			if (((i%8)==0) && (i!=0)) printk("\n");
+		}
+	printk("\n");
+	}
+*/
+
+	len = (unsigned short)((current_rx_ptr->status.status_word) & RX_FRLEN);
+	skb_put(skb, len);
+	blackfin_dcache_invalidate_range((unsigned long)skb->head,
+					 (unsigned long)skb->tail);
+
+	dev->last_rx = jiffies;
+	skb->dev = dev;
+	skb->protocol = eth_type_trans(skb, dev);
+#if defined(BFIN_MAC_CSUM_OFFLOAD)
+	skb->csum = current_rx_ptr->status.ip_payload_csum;
+	skb->ip_summed = CHECKSUM_PARTIAL;
+#endif
+
+	netif_rx(skb);
+	lp->stats.rx_packets++;
+	lp->stats.rx_bytes += len;
+	current_rx_ptr->status.status_word = 0x00000000;
+	current_rx_ptr = current_rx_ptr->next;
+
+      out:
+	return;
+}
+
+/* interrupt routine to handle rx and error signal */
+static irqreturn_t bf537mac_interrupt(int irq, void *dev_id)
+{
+	struct net_device *dev = dev_id;
+	int number = 0;
+
+      get_one_packet:
+	if (current_rx_ptr->status.status_word == 0) {	// no more new packet received
+		if (number == 0) {
+			if (current_rx_ptr->next->status.status_word != 0) {
+				current_rx_ptr = current_rx_ptr->next;
+				goto real_rx;
+			}
+		}
+		bfin_write_DMA1_IRQ_STATUS(bfin_read_DMA1_IRQ_STATUS() |
+					   DMA_DONE | DMA_ERR);
+		return IRQ_HANDLED;
+	}
+
+      real_rx:
+	bf537mac_rx(dev);
+	number++;
+	goto get_one_packet;
+}
+
+#ifdef CONFIG_NET_POLL_CONTROLLER
+static void bf537mac_poll(struct net_device *dev)
+{
+	disable_irq(IRQ_MAC_RX);
+	bf537mac_interrupt(IRQ_MAC_RX, dev);
+	enable_irq(IRQ_MAC_RX);
+}
+#endif				/* CONFIG_NET_POLL_CONTROLLER */
+
+static void bf537mac_reset(void)
+{
+	unsigned int opmode;
+
+	opmode = bfin_read_EMAC_OPMODE();
+	opmode &= (~RE);
+	opmode &= (~TE);
+	/* Turn off the EMAC */
+	bfin_write_EMAC_OPMODE(bfin_read_EMAC_OPMODE() & opmode);
+}
+
+/*
+ * Enable Interrupts, Receive, and Transmit
+ */
+static int bf537mac_enable(struct net_device *dev)
+{
+	u32 opmode;
+	//u32 pkt_status;
+
+	//printk("%s: %s\n", dev->name, __FUNCTION__);
+
+	/* Set RX DMA */
+	bfin_write_DMA1_NEXT_DESC_PTR(&(rx_list_head->desc_a));
+	bfin_write_DMA1_CONFIG(*
+			       ((unsigned short
+				 *)(&(rx_list_head->desc_a.config))));
+
+	/* Wait MII done */
+	PollMdcDone();
+
+	/* We enable only RX here */
+	/* ASTP   : Enable Automatic Pad Stripping
+	   PR     : Promiscuous Mode for test
+	   PSF    : Receive frames with total length less than 64 bytes.
+	   FDMODE : Full Duplex Mode
+	   LB     : Internal Loopback for test
+	   RE     : Receiver Enable */
+	opmode = bfin_read_EMAC_OPMODE();
+	if (opmode & FDMODE)
+		opmode |= PSF;
+	else
+		opmode |= DRO | DC | PSF;
+	opmode |= RE;
+
+#if defined(CONFIG_BFIN_MAC_RMII)
+	opmode |= RMII; /* For Now only 100MBit are supported */
+#ifdef CONFIG_BF_REV_0_2
+	opmode |= TE;
+#endif
+#endif
+	/* Turn on the EMAC rx */
+	bfin_write_EMAC_OPMODE(opmode);
+
+	return 0;
+}
+
+/* Our watchdog timed out. Called by the networking layer */
+static void bf537mac_timeout(struct net_device *dev)
+{
+	//printk("%s: %s\n", dev->name, __FUNCTION__);
+
+	bf537mac_reset();
+
+	/* reset tx queue */
+	tx_list_tail = tx_list_head->next;
+
+	bf537mac_enable(dev);
+
+	/* We can accept TX packets again */
+	dev->trans_start = jiffies;
+	netif_wake_queue(dev);
+}
+
+/*
+ * Get the current statistics.
+ * This may be called with the card open or closed.
+ */
+static struct net_device_stats *bf537mac_query_statistics(struct net_device
+							  *dev)
+{
+	struct bf537mac_local *lp = netdev_priv(dev);
+
+	//printk("%s: %s\n", dev->name, __FUNCTION__);
+
+	return &lp->stats;
+}
+
+/*
+ * This routine will, depending on the values passed to it,
+ * either make it accept multicast packets, go into
+ * promiscuous mode (for TCPDUMP and cousins) or accept
+ * a select set of multicast packets
+ */
+static void bf537mac_set_multicast_list(struct net_device *dev)
+{
+	u32 sysctl;
+
+	if (dev->flags & IFF_PROMISC) {
+		printk(KERN_INFO "%s: set to promisc mode\n", dev->name);
+		sysctl = bfin_read_EMAC_OPMODE();
+		sysctl |= RAF;
+		bfin_write_EMAC_OPMODE(sysctl);
+	} else if (dev->flags & IFF_ALLMULTI || dev->mc_count > 16) {
+		/* accept all multicast */
+		sysctl = bfin_read_EMAC_OPMODE();
+		sysctl |= PAM;
+		bfin_write_EMAC_OPMODE(sysctl);
+	} else if (dev->mc_count) {
+		/* set multicast */
+	} else {
+		/* clear promisc or multicast mode */
+		sysctl = bfin_read_EMAC_OPMODE();
+		sysctl &= ~(RAF | PAM);
+		bfin_write_EMAC_OPMODE(sysctl);
+	}
+}
+
+/*
+ * this puts the device in an inactive state
+ */
+static void bf537mac_shutdown(struct net_device *dev)
+{
+	/* Turn off the EMAC */
+	bfin_write_EMAC_OPMODE(0x00000000);
+	/* Turn off the EMAC RX DMA */
+	bfin_write_DMA1_CONFIG(0x0000);
+	bfin_write_DMA2_CONFIG(0x0000);
+}
+
+/*
+ * Open and Initialize the interface
+ *
+ * Set up everything, reset the card, etc..
+ */
+static int bf537mac_open(struct net_device *dev)
+{
+	//printk("%s: %s\n", dev->name, __FUNCTION__);
+
+	/*
+	 * Check that the address is valid.  If its not, refuse
+	 * to bring the device up.  The user must specify an
+	 * address using ifconfig eth0 hw ether xx:xx:xx:xx:xx:xx
+	 */
+	if (!is_valid_ether_addr(dev->dev_addr)) {
+		printk(KERN_WARNING CARDNAME ": no valid ethernet hw addr\n");
+		return -EINVAL;
+	}
+
+	/* initial rx and tx list */
+	desc_list_init();
+
+	bf537mac_setphy(dev);
+	SetupSystemRegs(dev);
+	bf537mac_reset();
+	bf537mac_enable(dev);
+
+	//printk(KERN_DEBUG CARDNAME ": hardware init finished\n");
+	netif_start_queue(dev);
+	netif_carrier_on(dev);
+
+	return 0;
+}
+
+/*
+ *
+ * this makes the board clean up everything that it can
+ * and not talk to the outside world.   Caused by
+ * an 'ifconfig ethX down'
+ */
+static int bf537mac_close(struct net_device *dev)
+{
+	//printk("%s: %s\n", dev->name, __FUNCTION__);
+
+	netif_stop_queue(dev);
+	netif_carrier_off(dev);
+
+	/* clear everything */
+	bf537mac_shutdown(dev);
+
+	/* free the rx/tx buffers */
+	desc_list_free();
+
+	return 0;
+}
+
+static int __init bf537mac_probe(struct net_device *dev)
+{
+	struct bf537mac_local *lp = netdev_priv(dev);
+	int retval;
+
+	/* Grab the MAC address in the MAC */
+	*(u32 *) (&(dev->dev_addr[0])) = bfin_read_EMAC_ADDRLO();
+	*(u16 *) (&(dev->dev_addr[4])) = (u16) bfin_read_EMAC_ADDRHI();
+
+/* probe mac */
+	/*todo: how to proble? which is revision_register */
+	bfin_write_EMAC_ADDRLO(0x12345678);
+	if (bfin_read_EMAC_ADDRLO() != 0x12345678) {
+		//printk(CARDNAME ": can't detect bf537 mac!\n");
+		retval = -ENODEV;
+		goto err_out;
+	}
+
+	/*Is it valid? (Did bootloader initialize it?) */
+	if (!is_valid_ether_addr(dev->dev_addr)) {
+		/* Grab the MAC from the board somehow - this is done in the
+		   arch/blackfin/boards/bf537/boardname.c */
+		get_bf537_ether_addr(dev->dev_addr);
+	}
+
+	/* If still not valid, get a random one */
+	if (!is_valid_ether_addr(dev->dev_addr)) {
+		random_ether_addr(dev->dev_addr);
+	}
+
+	SetupMacAddr(dev->dev_addr);
+
+	/* Fill in the fields of the device structure with ethernet values. */
+	ether_setup(dev);
+
+	dev->open = bf537mac_open;
+	dev->stop = bf537mac_close;
+	dev->hard_start_xmit = bf537mac_hard_start_xmit;
+	dev->tx_timeout = bf537mac_timeout;
+	dev->get_stats = bf537mac_query_statistics;
+	dev->set_multicast_list = bf537mac_set_multicast_list;
+	//  dev->ethtool_ops = &bf537mac_ethtool_ops;
+#ifdef CONFIG_NET_POLL_CONTROLLER
+	dev->poll_controller = bf537mac_poll;
+#endif
+
+	/* fill in some of the fields */
+	lp->version = 1;
+	lp->PhyAddr = 0x01;
+	lp->CLKIN = 25;
+	lp->FullDuplex = 0;
+	lp->Negotiate = 1;
+	lp->FlowControl = 0;
+	spin_lock_init(&lp->lock);
+
+	// set the GPIO pins to Ethernet mode
+	SetupPinMux();
+
+	/* now, enable interrupts */
+	/* register irq handler */
+	if (request_irq
+	    (IRQ_MAC_RX, bf537mac_interrupt, IRQF_DISABLED | IRQF_SHARED,
+	     "BFIN537_MAC_RX", dev)) {
+		printk(KERN_WARNING CARDNAME
+		       ": Unable to attach BlackFin MAC RX interrupt\n");
+		return -EBUSY;
+	}
+
+	/* Enable PHY output early*/
+	if (!(bfin_read_VR_CTL() & PHYCLKOE))
+		bfin_write_VR_CTL(bfin_read_VR_CTL() | PHYCLKOE);
+
+	retval = register_netdev(dev);
+	if (retval == 0) {
+		/* now, print out the card info, in a short format.. */
+		printk(KERN_INFO "Blackfin mac net device registered\n");
+	}
+
+      err_out:
+	return retval;
+}
+
+static int bfin_mac_probe(struct platform_device *pdev)
+{
+	struct net_device *ndev;
+
+	ndev = alloc_etherdev(sizeof(struct bf537mac_local));
+
+	if (!ndev) {
+		printk(KERN_WARNING CARDNAME ": could not allocate device\n");
+		return -ENOMEM;
+	}
+
+	if (bf537mac_probe(ndev) != 0) {
+		platform_set_drvdata(pdev, NULL);
+		free_netdev(ndev);
+		printk(KERN_WARNING CARDNAME ": not found\n");
+		return -ENODEV;
+	}
+
+	SET_MODULE_OWNER(ndev);
+	SET_NETDEV_DEV(ndev, &pdev->dev);
+
+	platform_set_drvdata(pdev, ndev);
+
+	return 0;
+}
+
+static int bfin_mac_remove(struct platform_device *pdev)
+{
+	struct net_device *ndev = platform_get_drvdata(pdev);
+
+	platform_set_drvdata(pdev, NULL);
+
+	unregister_netdev(ndev);
+
+	free_irq(IRQ_MAC_RX, ndev);
+
+	free_netdev(ndev);
+
+	return 0;
+}
+
+static int bfin_mac_suspend(struct platform_device *pdev, pm_message_t state)
+{
+	return 0;
+}
+
+static int bfin_mac_resume(struct platform_device *pdev)
+{
+	return 0;
+}
+
+static struct platform_driver bfin_mac_driver = {
+	.probe = bfin_mac_probe,
+	.remove = bfin_mac_remove,
+	.resume = bfin_mac_resume,
+	.suspend = bfin_mac_suspend,
+	.driver = {
+		   .name = CARDNAME,
+		   },
+};
+
+static int __init bfin_mac_init(void)
+{
+	return platform_driver_register(&bfin_mac_driver);
+}
+
+module_init(bfin_mac_init);
+
+static void __exit bfin_mac_cleanup(void)
+{
+	platform_driver_unregister(&bfin_mac_driver);
+}
+
+module_exit(bfin_mac_cleanup);
Index: linux-2.6/drivers/net/bfin_mac.h
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ linux-2.6/drivers/net/bfin_mac.h	2007-03-01 11:39:19.000000000 +0800
@@ -0,0 +1,146 @@
+/*
+ * File:         drivers/net/bfin_mac.h
+ * Based on:
+ * Author:       Luke Yang <luke.yang@analog.com>
+ *
+ * Created:
+ * Description:
+ *
+ * Rev:          $Id: bfin_mac.h,v 1.8 2006/08/22 10:50:04 magicyang Exp $
+ *
+ * Modified:
+ *               Copyright 2004-2006 Analog Devices Inc.
+ *
+ * Bugs:         Enter bugs at http://blackfin.uclinux.org/
+ *
+ * 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, 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 ;  see the file COPYING.
+ * If not, write to the Free Software Foundation,
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+// -----------------------------------------------------------------------
+//                     PHY REGISTER NAMES                               //
+// -----------------------------------------------------------------------
+#define PHYREG_MODECTL          0x0000
+#define PHYREG_MODESTAT         0x0001
+#define PHYREG_PHYID1           0x0002
+#define PHYREG_PHYID2           0x0003
+#define PHYREG_ANAR                     0x0004
+#define PHYREG_ANLPAR           0x0005
+#define PHYREG_ANER                     0x0006
+#define PHYREG_NSR                      0x0010
+#define PHYREG_LBREMR           0x0011
+#define PHYREG_REC                      0x0012
+#define PHYREG_10CFG            0x0013
+#define PHYREG_PHY1_1           0x0014
+#define PHYREG_PHY1_2           0x0015
+#define PHYREG_PHY2                     0x0016
+#define PHYREG_TW_1                     0x0017
+#define PHYREG_TW_2                     0x0018
+#define PHYREG_TEST                     0x0019
+
+#define PHY_RESET               0x8000
+#define PHY_ANEG_EN             0x1000
+#define PHY_DUPLEX              0x0100
+#define PHY_SPD_SET             0x2000
+
+#define BFIN_MAC_CSUM_OFFLOAD
+
+typedef struct _DMA_CONFIG {
+	unsigned short b_DMA_EN:1;	//Bit 0 : DMA Enable
+	unsigned short b_WNR:1;	//Bit 1 : DMA Direction
+	unsigned short b_WDSIZE:2;	//Bit 2 & 3 : DMA Tranfer Word size
+	unsigned short b_DMA2D:1;	//Bit 4 : DMA Mode 2D or 1D
+	unsigned short b_RESTART:1;	//Bit 5 : Retain the FIFO
+	unsigned short b_DI_SEL:1;	//Bit 6 : Data Interrupt Timing Select
+	unsigned short b_DI_EN:1;	//Bit 7 : Data Interrupt Enable
+	unsigned short b_NDSIZE:4;	//Bit 8 to 11 : Flex descriptor Size
+	unsigned short b_FLOW:3;	//Bit 12 to 14 : FLOW
+} DMA_CONFIG_REG;
+
+struct dma_descriptor {
+	struct dma_descriptor *next_dma_desc;
+	unsigned long start_addr;
+	DMA_CONFIG_REG config;
+	unsigned short x_count;
+};
+
+/*
+struct status_area {
+  unsigned short ip_hdr_chksum;         // the IP header checksum
+  unsigned short ip_payload_chksum;     // the IP header and payload checksum
+  unsigned long  status_word;           // the frame status word
+};
+*/
+struct status_area_rx {
+#if defined(BFIN_MAC_CSUM_OFFLOAD)
+	unsigned short ip_hdr_csum;	// ip header checksum
+	unsigned short ip_payload_csum;	// ip payload(udp or tcp or others) checksum
+#endif
+	unsigned long status_word;	// the frame status word
+};
+
+struct status_area_tx {
+	unsigned long status_word;	// the frame status word
+};
+
+/* use two descriptors for a packet */
+struct net_dma_desc_rx {
+	struct net_dma_desc_rx *next;
+	struct sk_buff *skb;
+	struct dma_descriptor desc_a;
+	struct dma_descriptor desc_b;
+	volatile struct status_area_rx status;
+};
+
+/* use two descriptors for a packet */
+struct net_dma_desc_tx {
+	struct net_dma_desc_tx *next;
+	struct sk_buff *skb;
+	struct dma_descriptor desc_a;
+	struct dma_descriptor desc_b;
+	volatile unsigned char packet[1560];
+	volatile struct status_area_tx status;
+};
+
+struct bf537mac_local {
+	/*
+	 * these are things that the kernel wants me to keep, so users
+	 * can find out semi-useless statistics of how well the card is
+	 * performing
+	 */
+	struct net_device_stats stats;
+
+	int version;
+
+	int FlowEnabled;	// record if data flow is active
+	int EtherIntIVG;	// IVG for the ethernet interrupt
+	int RXIVG;		// IVG for the RX completion
+	int TXIVG;		// IVG for the TX completion
+	int PhyAddr;		// PHY address
+	int OpMode;		// set these bits n the OPMODE regs
+	int Port10;		// set port speed to 10 Mbit/s
+	int GenChksums;		// IP checksums to be calculated
+	int NoRcveLnth;		// dont insert recv length at start of buffer
+	int StripPads;		// remove trailing pad bytes
+	int FullDuplex;		// set full duplex mode
+	int Negotiate;		// enable auto negotiation
+	int Loopback;		// loopback at the PHY
+	int Cache;		// Buffers may be cached
+	int FlowControl;	// flow control active
+	int CLKIN;		// clock in value in MHZ
+	unsigned short IntMask;	// interrupt mask
+	unsigned char Mac[6];	// MAC address of the board
+	spinlock_t lock;
+};
---

Thanks
-Bryan


^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [PATCH -mm 3/5] Blackfin: on-chip ethernet MAC controller driver
  2007-03-01  4:15 [PATCH -mm 3/5] Blackfin: on-chip ethernet MAC controller driver Wu, Bryan
@ 2007-03-01  8:53 ` Andrew Morton
  2007-03-01 15:52 ` Stephen Hemminger
  1 sibling, 0 replies; 6+ messages in thread
From: Andrew Morton @ 2007-03-01  8:53 UTC (permalink / raw)
  To: bryan.wu; +Cc: jgarzik, linux-kernel, netdev

On Thu, 01 Mar 2007 12:15:14 +0800 "Wu, Bryan" <bryan.wu@analog.com> wrote:

> Hi folks,
> 
> Here is the blackfin on-chip ethernet MAC controller driver for Linux.
> 
> It's name is blackfin-driver-net-stamp537.patch
> 
> [PATCH] Blackfin: on-chip ethernet MAC controller driver
> 
> This patch implements the driver necessary use the Analog Devices
> Blackfin processor's on-chip ethernet MAC controller.
> 
> ...
>
> +
> +extern void get_bf537_ether_addr(char *addr);

Please don't put extern declarations in .c code.  Put them in a header file
which is included by the file which provides the definition and by all
files which refer to the symbol.

Please review the entire blackfin patchset for more occurrences of this
mistake.

> +static int bf537mac_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
> +{
> +	struct bf537mac_local *lp = netdev_priv(dev);
> +	unsigned int data;
> +
> +	current_tx_ptr->skb = skb;
> +	// Is skb->data always 16-bit aligned? Do we need to memcpy((char *)(tail->packet + 2),skb->data,len)?
> +	if ((((unsigned int)(skb->data)) & 0x02) == 2) {
> +		//move skb->data to current_tx_ptr payload
> +		data = (unsigned int)(skb->data) - 2;
> +		*((unsigned short *)data) = (unsigned short)(skb->len);
> +		current_tx_ptr->desc_a.start_addr = (unsigned long)data;
> +		blackfin_dcache_flush_range(data, (data + (skb->len)) + 2);	//this is important!
> +
> +	} else {
> +		*((unsigned short *)(current_tx_ptr->packet)) =
> +		    (unsigned short)(skb->len);
> +		memcpy((char *)(current_tx_ptr->packet + 2), skb->data,
> +		       (skb->len));
> +		current_tx_ptr->desc_a.start_addr =
> +		    (unsigned long)current_tx_ptr->packet;
> +		if (current_tx_ptr->status.status_word != 0)
> +			current_tx_ptr->status.status_word = 0;
> +		blackfin_dcache_flush_range((unsigned int)current_tx_ptr->
> +					    packet,
> +					    (unsigned int)(current_tx_ptr->
> +							   packet + skb->len) +
> +					    2);
> +	}

hm, I'd have thought that networking provided a way of avoiding that
memcpy, but it has been too long..


> +	current_tx_ptr->desc_a.config.b_DMA_EN = 1;	//enable this packet's dma
> +
> +	if (bfin_read_DMA2_IRQ_STATUS() & 0x08) {	//tx dma is running, just return
> +		goto out;
> +	} else {		//tx dma is not running
> +		bfin_write_DMA2_NEXT_DESC_PTR(&(current_tx_ptr->desc_a));
> +		bfin_write_DMA2_CONFIG(*((unsigned short *)(&(current_tx_ptr->desc_a.config))));	// dma enabled, read from memory, size is 6
> +		// Turn on the EMAC tx
> +		bfin_write_EMAC_OPMODE(bfin_read_EMAC_OPMODE() | TE);
> +	}
> +
> +      out:
> +	adjust_tx_list();
> +	current_tx_ptr = current_tx_ptr->next;
> +	dev->trans_start = jiffies;
> +	lp->stats.tx_packets++;
> +	lp->stats.tx_bytes += (skb->len);
> +	return 0;
> +}


^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [PATCH -mm 3/5] Blackfin: on-chip ethernet MAC controller driver
  2007-03-01  4:15 [PATCH -mm 3/5] Blackfin: on-chip ethernet MAC controller driver Wu, Bryan
  2007-03-01  8:53 ` Andrew Morton
@ 2007-03-01 15:52 ` Stephen Hemminger
  2007-03-01 18:21   ` Mike Frysinger
                     ` (2 more replies)
  1 sibling, 3 replies; 6+ messages in thread
From: Stephen Hemminger @ 2007-03-01 15:52 UTC (permalink / raw)
  To: bryan.wu; +Cc: Andrew Morton, jgarzik, linux-kernel, netdev

Wu, Bryan wrote:
> Hi folks,
>
> Here is the blackfin on-chip ethernet MAC controller driver for Linux.
>
> It's name is blackfin-driver-net-stamp537.patch
>
> [PATCH] Blackfin: on-chip ethernet MAC controller driver
>
> This patch implements the driver necessary use the Analog Devices
> Blackfin processor's on-chip ethernet MAC controller.
>
> Signed-off-by: Bryan Wu <bryan.wu@analog.com> 
> ---
>
>  drivers/net/Kconfig    |   44 ++
>  drivers/net/Makefile   |    1
>  drivers/net/bfin_mac.c |  988 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
>  drivers/net/bfin_mac.h |  146 +++++++++
>  4 files changed, 1179 insertions(+)
>
> Index: linux-2.6/drivers/net/Kconfig
> ===================================================================
> --- linux-2.6.orig/drivers/net/Kconfig	2007-03-01 11:39:14.000000000 +0800
> +++ linux-2.6/drivers/net/Kconfig	2007-03-01 11:39:19.000000000 +0800
> @@ -836,6 +836,50 @@
>  	  module, say M here and read <file:Documentation/modules.txt> as well
>  	  as <file:Documentation/networking/net-modules.txt>.
>  
> +config BFIN_MAC
> +	tristate "Blackfin 536/537 on-chip mac support"
> +	depends on NET_ETHERNET && (BF537 || BF536) && (!BF537_PORT_H)
> +	select CRC32
> +	select BFIN_MAC_USE_L1 if DMA_UNCACHED_NONE
> +	help
> +	  This is the driver for blackfin on-chip mac device. Say Y if you want it
> +	  compiled into the kernel. This driver is also available as a module
> +	  ( = code which can be inserted in and removed from the running kernel
> +	  whenever you want). The module will be called bfin_mac.
> +
> +config BFIN_MAC_USE_L1
> +	bool "Use L1 memory for rx/tx packets"
> +	depends on BFIN_MAC && BF537
> +	default y
> +	help
> +	  To get maximum network performace, you should use L1 memory as rx/tx buffers.
> +	  Say N here if you want to reserve L1 memory for other uses.
> +
> +config BFIN_TX_DESC_NUM
> +	int "Number of transmit buffer packets"
> +	depends on BFIN_MAC
> +	range 6 10 if BFIN_MAC_USE_L1
> +	range 10 100
> +	default "10"
> +	help
> +	  Set the number of buffer packets used in driver.
> +
> +config BFIN_RX_DESC_NUM
> +	int "Number of receive buffer packets"
> +	depends on BFIN_MAC
> +	range 20 100 if BFIN_MAC_USE_L1
> +	range 20 800
> +	default "20"
> +	help
> +	  Set the number of buffer packets used in driver
>   
The regular practice is to put these in a device include file, and not 
make them kernel configurable.
Why would you want to make it tuneable at compile time?
> +
> +config BFIN_MAC_RMII
> +	bool "RMII PHY Interface (EXPERIMENTAL)"
> +	depends on BFIN_MAC && EXPERIMENTAL
> +	default n
> +	help
> +	  Use Reduced PHY MII Interface
> +
>  config SMC9194
>  	tristate "SMC 9194 support"
>  	depends on NET_VENDOR_SMC && (ISA || MAC && BROKEN)
> Index: linux-2.6/drivers/net/Makefile
> ===================================================================
> --- linux-2.6.orig/drivers/net/Makefile	2007-03-01 11:33:24.000000000 +0800
> +++ linux-2.6/drivers/net/Makefile	2007-03-01 11:39:19.000000000 +0800
> @@ -195,6 +195,7 @@
>  obj-$(CONFIG_MYRI10GE) += myri10ge/
>  obj-$(CONFIG_SMC91X) += smc91x.o
>  obj-$(CONFIG_SMC911X) += smc911x.o
> +obj-$(CONFIG_BFIN_MAC) += bfin_mac.o
>  obj-$(CONFIG_DM9000) += dm9000.o
>  obj-$(CONFIG_FEC_8XX) += fec_8xx/
>  obj-$(CONFIG_PASEMI_MAC) += pasemi_mac.o
> Index: linux-2.6/drivers/net/bfin_mac.c
> ===================================================================
> --- /dev/null	1970-01-01 00:00:00.000000000 +0000
> +++ linux-2.6/drivers/net/bfin_mac.c	2007-03-01 11:39:19.000000000 +0800
> @@ -0,0 +1,988 @@
> +/*
> + * File:         drivers/net/bfin_mac.c
> + * Based on:
> + * Author:       Luke Yang <luke.yang@analog.com>
> + *
> + * Created:
> + * Description:
> + *
> + * Rev:          $Id: bfin_mac.c,v 1.60 2006/12/16 11:23:56 hennerich Exp $
> + *
> + * Modified:
> + *               Copyright 2004-2006 Analog Devices Inc.
> + *
> + * Bugs:         Enter bugs at http://blackfin.uclinux.org/
> + *
> + * 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, 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 ;  see the file COPYING.
> + * If not, write to the Free Software Foundation,
> + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
> + */
> +
> +#include <linux/init.h>
> +#include <linux/module.h>
> +#include <linux/kernel.h>
> +#include <linux/sched.h>
> +#include <linux/slab.h>
> +#include <linux/delay.h>
> +#include <linux/timer.h>
> +#include <linux/errno.h>
> +#include <linux/ioport.h>
> +#include <linux/crc32.h>
> +#include <linux/device.h>
> +#include <linux/spinlock.h>
> +#include <linux/ethtool.h>
> +#include <linux/mii.h>
> +
> +#include <linux/netdevice.h>
> +#include <linux/etherdevice.h>
> +#include <linux/skbuff.h>
> +
> +#include <linux/platform_device.h>
> +#include <linux/netdevice.h>
> +#include <linux/etherdevice.h>
> +#include <linux/skbuff.h>
> +
> +#include <asm/io.h>
> +#include <asm/irq.h>
> +#include <asm/dma.h>
> +#include <linux/dma-mapping.h>
> +
> +#include <asm/irq.h>
> +#include <asm/blackfin.h>
> +#include <asm/delay.h>
> +
> +#include "bfin_mac.h"
> +
> +#define CARDNAME "bfin_mac"
> +
> +MODULE_LICENSE("GPL");
> +MODULE_AUTHOR("Luke Yang");
> +MODULE_DESCRIPTION("Blackfin MAC Driver");
> +
> +#if defined(CONFIG_BFIN_MAC_USE_L1)
> +# define bfin_mac_alloc(dma_handle, size)  l1_data_sram_zalloc(size)
> +# define bfin_mac_free(dma_handle, ptr)    l1_data_sram_free(ptr)
> +#else
> +# define bfin_mac_alloc(dma_handle, size)  dma_alloc_coherent(NULL, size, dma_handle, GFP_DMA)
> +# define bfin_mac_free(dma_handle, ptr)    dma_free_coherent(NULL, sizeof(*ptr), ptr, dma_handle)
> +#endif
> +
> +#define PKT_BUF_SZ 1580
> +
> +static void desc_list_free(void);
> +
> +/* pointers to maintain transmit list */
> +struct net_dma_desc_tx *tx_list_head;
> +struct net_dma_desc_tx *tx_list_tail;
> +struct net_dma_desc_rx *rx_list_head;
> +struct net_dma_desc_rx *rx_list_tail;
> +struct net_dma_desc_rx *current_rx_ptr;
> +struct net_dma_desc_tx *current_tx_ptr;
> +struct net_dma_desc_tx *tx_desc;
> +struct net_dma_desc_rx *rx_desc;
>   

These should all be static
> +
> +extern void get_bf537_ether_addr(char *addr);
> +
> +static int desc_list_init(void)
> +{
> +	struct net_dma_desc_tx *tmp_desc_tx;
> +	struct net_dma_desc_rx *tmp_desc_rx;
> +	int i;
> +	struct sk_buff *new_skb;
> +#if !defined(CONFIG_BFIN_MAC_USE_L1)
> +	dma_addr_t dma_handle;
> +#endif
> +
> +	tx_desc =
> +	    bfin_mac_alloc(&dma_handle,
> +			   sizeof(struct net_dma_desc_tx) *
> +			   CONFIG_BFIN_TX_DESC_NUM);
> +	if (tx_desc == NULL)
> +		goto init_error;
> +
> +	rx_desc =
> +	    bfin_mac_alloc(&dma_handle,
> +			   sizeof(struct net_dma_desc_rx) *
> +			   CONFIG_BFIN_RX_DESC_NUM);
> +	if (rx_desc == NULL)
> +		goto init_error;
> +
> +	/* init tx_list */
> +	for (i = 0; i < CONFIG_BFIN_TX_DESC_NUM; i++) {
> +
> +		tmp_desc_tx = tx_desc + i;
> +
> +		if (i == 0) {
> +			tx_list_head = tmp_desc_tx;
> +			tx_list_tail = tmp_desc_tx;
> +		}
> +
> +		tmp_desc_tx->desc_a.start_addr =
> +		    (unsigned long)tmp_desc_tx->packet;
> +		tmp_desc_tx->desc_a.x_count = 0;
> +		tmp_desc_tx->desc_a.config.b_DMA_EN = 0;	//disabled
>   
Coding Style for Linux kernel is to *not* use C++ style comments (ie. //)

> +		tmp_desc_tx->desc_a.config.b_WNR = 0;	//read from memory
> +		tmp_desc_tx->desc_a.config.b_WDSIZE = 2;	//wordsize is 32 bits
> +		tmp_desc_tx->desc_a.config.b_NDSIZE = 6;	//6 half words is desc size.
> +		tmp_desc_tx->desc_a.config.b_FLOW = 7;	//large desc flow
> +		tmp_desc_tx->desc_a.next_dma_desc = &(tmp_desc_tx->desc_b);
> +
> +		tmp_desc_tx->desc_b.start_addr =
> +		    (unsigned long)(&(tmp_desc_tx->status));
> +		tmp_desc_tx->desc_b.x_count = 0;
> +		tmp_desc_tx->desc_b.config.b_DMA_EN = 1;	//enabled
> +		tmp_desc_tx->desc_b.config.b_WNR = 1;	//write to memory
> +		tmp_desc_tx->desc_b.config.b_WDSIZE = 2;	//wordsize is 32 bits
> +		tmp_desc_tx->desc_b.config.b_DI_EN = 0;	//disable interrupt
> +		tmp_desc_tx->desc_b.config.b_NDSIZE = 6;
> +		tmp_desc_tx->desc_b.config.b_FLOW = 7;	//stop mode
> +		tmp_desc_tx->skb = NULL;
> +		tx_list_tail->desc_b.next_dma_desc = &(tmp_desc_tx->desc_a);
> +		tx_list_tail->next = tmp_desc_tx;
> +
> +		tx_list_tail = tmp_desc_tx;
> +	}
> +	tx_list_tail->next = tx_list_head;	/* tx_list is a circle */
> +	tx_list_tail->desc_b.next_dma_desc = &(tx_list_head->desc_a);
> +	current_tx_ptr = tx_list_head;
> +
> +	/* init rx_list */
> +	for (i = 0; i < CONFIG_BFIN_RX_DESC_NUM; i++) {
> +
> +		tmp_desc_rx = rx_desc + i;
> +
> +		if (i == 0) {
> +			rx_list_head = tmp_desc_rx;
> +			rx_list_tail = tmp_desc_rx;
> +		}
> +
> +		/* allocat a new skb for next time receive */
> +		new_skb = dev_alloc_skb(PKT_BUF_SZ + 2);
> +		if (!new_skb) {
> +			printk(KERN_NOTICE CARDNAME
> +			       ": init: low on mem - packet dropped\n");
> +			goto init_error;
> +		}
> +		skb_reserve(new_skb, 2);
> +		tmp_desc_rx->skb = new_skb;
> +		/* since RXDWA is enabled */
> +		tmp_desc_rx->desc_a.start_addr =
> +		    (unsigned long)new_skb->data - 2;
> +		tmp_desc_rx->desc_a.x_count = 0;
> +		tmp_desc_rx->desc_a.config.b_DMA_EN = 1;	//enabled
> +		tmp_desc_rx->desc_a.config.b_WNR = 1;	//Write to memory
> +		tmp_desc_rx->desc_a.config.b_WDSIZE = 2;	//wordsize is 32 bits
> +		tmp_desc_rx->desc_a.config.b_NDSIZE = 6;	//6 half words is desc size.
> +		tmp_desc_rx->desc_a.config.b_FLOW = 7;	//large desc flow
> +		tmp_desc_rx->desc_a.next_dma_desc = &(tmp_desc_rx->desc_b);
> +
> +		tmp_desc_rx->desc_b.start_addr =
> +		    (unsigned long)(&(tmp_desc_rx->status));
> +		tmp_desc_rx->desc_b.x_count = 0;
> +		tmp_desc_rx->desc_b.config.b_DMA_EN = 1;	//enabled
> +		tmp_desc_rx->desc_b.config.b_WNR = 1;	//Write to memory
> +		tmp_desc_rx->desc_b.config.b_WDSIZE = 2;	//wordsize is 32 bits
> +		tmp_desc_rx->desc_b.config.b_NDSIZE = 6;
> +		tmp_desc_rx->desc_b.config.b_DI_EN = 1;	//enable interrupt
> +		tmp_desc_rx->desc_b.config.b_FLOW = 7;	//large mode
> +		rx_list_tail->desc_b.next_dma_desc = &(tmp_desc_rx->desc_a);
> +
> +		rx_list_tail->next = tmp_desc_rx;
> +		rx_list_tail = tmp_desc_rx;
> +	}
> +	rx_list_tail->next = rx_list_head;	/* rx_list is a circle */
> +	rx_list_tail->desc_b.next_dma_desc = &(rx_list_head->desc_a);
> +	current_rx_ptr = rx_list_head;
> +
> +	return 0;
> +
> +      init_error:
> +	desc_list_free();
> +	printk(KERN_ERR CARDNAME ": kmalloc failed\n");
> +	return -ENOMEM;
> +}
> +
> +static void desc_list_free(void)
> +{
> +	struct net_dma_desc_rx *tmp_desc_rx;
> +	struct net_dma_desc_tx *tmp_desc_tx;
> +	int i;
> +#if !defined(CONFIG_BFIN_MAC_USE_L1)
> +	dma_addr_t dma_handle = 0;
> +#endif
> +
> +	if (tx_desc != NULL) {
> +		tmp_desc_tx = tx_list_head;
> +		for (i = 0; i < CONFIG_BFIN_TX_DESC_NUM; i++) {
> +			if (tmp_desc_tx != NULL) {
> +				if (tmp_desc_tx->skb) {
> +					dev_kfree_skb(tmp_desc_tx->skb);
> +					tmp_desc_tx->skb = NULL;
> +				}
> +
> +			}
> +			tmp_desc_tx = tmp_desc_tx->next;
> +		}
> +		bfin_mac_free(dma_handle, tx_desc);
> +	}
> +
> +	if (rx_desc != NULL) {
> +		tmp_desc_rx = rx_list_head;
> +		for (i = 0; i < CONFIG_BFIN_RX_DESC_NUM; i++) {
> +			if (tmp_desc_rx != NULL) {
> +				if (tmp_desc_rx->skb) {
> +					dev_kfree_skb(tmp_desc_rx->skb);
> +					tmp_desc_rx->skb = NULL;
> +				}
> +			}
> +			tmp_desc_rx = tmp_desc_rx->next;
> +		}
> +		bfin_mac_free(dma_handle, rx_desc);
> +	}
> +}
> +
> +/*---PHY CONTROL AND CONFIGURATION-----------------------------------------*/
> +
> +//
> +//Set FER regs to MUX in Ethernet pins
> +//
> +
> +static void SetupPinMux(void)
> +{
> +	
Please do not use mixed case for function or structure member names (see 
Coding Style)

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [PATCH -mm 3/5] Blackfin: on-chip ethernet MAC controller driver
  2007-03-01 15:52 ` Stephen Hemminger
@ 2007-03-01 18:21   ` Mike Frysinger
  2007-03-01 18:35   ` Robin Getz
  2007-03-02  8:08   ` Wu, Bryan
  2 siblings, 0 replies; 6+ messages in thread
From: Mike Frysinger @ 2007-03-01 18:21 UTC (permalink / raw)
  To: Stephen Hemminger; +Cc: bryan.wu, Andrew Morton, jgarzik, linux-kernel, netdev

On 3/1/07, Stephen Hemminger <shemminger@linux-foundation.org> wrote:
> Wu, Bryan wrote:
> > +config BFIN_MAC
> > +     tristate "Blackfin 536/537 on-chip mac support"
> > +     depends on NET_ETHERNET && (BF537 || BF536) && (!BF537_PORT_H)
> > +     select CRC32
> > +     select BFIN_MAC_USE_L1 if DMA_UNCACHED_NONE
> > +     help
> > +       This is the driver for blackfin on-chip mac device. Say Y if you want it
> > +       compiled into the kernel. This driver is also available as a module
> > +       ( = code which can be inserted in and removed from the running kernel
> > +       whenever you want). The module will be called bfin_mac.
> > +
> > +config BFIN_MAC_USE_L1
> > +     bool "Use L1 memory for rx/tx packets"
> > +     depends on BFIN_MAC && BF537
> > +     default y
> > +     help
> > +       To get maximum network performace, you should use L1 memory as rx/tx buffers.
> > +       Say N here if you want to reserve L1 memory for other uses.
> > +
> > +config BFIN_TX_DESC_NUM
> > +     int "Number of transmit buffer packets"
> > +     depends on BFIN_MAC
> > +     range 6 10 if BFIN_MAC_USE_L1
> > +     range 10 100
> > +     default "10"
> > +     help
> > +       Set the number of buffer packets used in driver.
> > +
> > +config BFIN_RX_DESC_NUM
> > +     int "Number of receive buffer packets"
> > +     depends on BFIN_MAC
> > +     range 20 100 if BFIN_MAC_USE_L1
> > +     range 20 800
> > +     default "20"
> > +     help
> > +       Set the number of buffer packets used in driver
> >
> The regular practice is to put these in a device include file, and not
> make them kernel configurable.

we're working on moving some of this stuff into the board files rather
than Kconfig

> Why would you want to make it tuneable at compile time?

because it was easier to implement and deploy the first time around ;)
-mike

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [PATCH -mm 3/5] Blackfin: on-chip ethernet MAC controller driver
  2007-03-01 15:52 ` Stephen Hemminger
  2007-03-01 18:21   ` Mike Frysinger
@ 2007-03-01 18:35   ` Robin Getz
  2007-03-02  8:08   ` Wu, Bryan
  2 siblings, 0 replies; 6+ messages in thread
From: Robin Getz @ 2007-03-01 18:35 UTC (permalink / raw)
  To: Stephen Hemminger; +Cc: bryan.wu, Andrew Morton, jgarzik, linux-kernel, netdev

On Thu 1 Mar 2007 10:52, Stephen Hemminger pondered:
> Wu, Bryan wrote:
> > Hi folks,
> >
> > Here is the blackfin on-chip ethernet MAC controller driver for Linux.
> >
> > Index: linux-2.6/drivers/net/Kconfig
> > ===================================================================
> > --- linux-2.6.orig/drivers/net/Kconfig	2007-03-01 11:39:14.000000000
> > +0800 +++ linux-2.6/drivers/net/Kconfig	2007-03-01 11:39:19.000000000
> > +0800 @@ -836,6 +836,50 @@
> >  	  module, say M here and read <file:Documentation/modules.txt> as well
> >  	  as <file:Documentation/networking/net-modules.txt>.
> >
> > +config BFIN_MAC
> > +	tristate "Blackfin 536/537 on-chip mac support"
> > +	depends on NET_ETHERNET && (BF537 || BF536) && (!BF537_PORT_H)
> > +	select CRC32
> > +	select BFIN_MAC_USE_L1 if DMA_UNCACHED_NONE
> > +	help
> > +	  This is the driver for blackfin on-chip mac device. Say Y if you want
> > it +	  compiled into the kernel. This driver is also available as a
> > module +	  ( = code which can be inserted in and removed from the running
> > kernel +	  whenever you want). The module will be called bfin_mac.
> > +
> > +config BFIN_MAC_USE_L1
> > +	bool "Use L1 memory for rx/tx packets"
> > +	depends on BFIN_MAC && BF537
> > +	default y
> > +	help
> > +	  To get maximum network performace, you should use L1 memory as rx/tx
> > buffers. +	  Say N here if you want to reserve L1 memory for other uses.
> > +
> > +config BFIN_TX_DESC_NUM
> > +	int "Number of transmit buffer packets"
> > +	depends on BFIN_MAC
> > +	range 6 10 if BFIN_MAC_USE_L1
> > +	range 10 100
> > +	default "10"
> > +	help
> > +	  Set the number of buffer packets used in driver.
> > +
> > +config BFIN_RX_DESC_NUM
> > +	int "Number of receive buffer packets"
> > +	depends on BFIN_MAC
> > +	range 20 100 if BFIN_MAC_USE_L1
> > +	range 20 800
> > +	default "20"
> > +	help
> > +	  Set the number of buffer packets used in driver
>
> The regular practice is to put these in a device include file, and not
> make them kernel configurable.
> Why would you want to make it tuneable at compile time?

It is to attempt to make it easier for our end users to configure&use.

Most people who use it, want to make some tweaks to the configuration based on 
their embedded platform. Rather than forcing everyone to make changes to a .c 
or .h file, we let them manage it in the .config (which is platforms specific 
anyway).

-Robin

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [PATCH -mm 3/5] Blackfin: on-chip ethernet MAC controller driver
  2007-03-01 15:52 ` Stephen Hemminger
  2007-03-01 18:21   ` Mike Frysinger
  2007-03-01 18:35   ` Robin Getz
@ 2007-03-02  8:08   ` Wu, Bryan
  2 siblings, 0 replies; 6+ messages in thread
From: Wu, Bryan @ 2007-03-02  8:08 UTC (permalink / raw)
  To: Stephen Hemminger, Andrew Morton
  Cc: jgarzik, linux-kernel, netdev, Mike Frysinger, Robin Getz

On Thu, 2007-03-01 at 10:52 -0500, Stephen Hemminger wrote:
>     
> Please do not use mixed case for function or structure member names
> (see  
> Coding Style)
> 

Here is the updated version of this driver. 
a) Change to follow kernel coding style
b) rename some functions and structures
c) change '//' to '/* */'
d) use pr_debug()

[PATCH] Blackfin: on-chip ethernet MAC controller driver

This patch implements the driver necessary use the Analog Devices
Blackfin processor's on-chip ethernet MAC controller.

Signed-off-by: Bryan Wu <bryan.wu@analog.com>
---

 drivers/net/Kconfig    |   44 ++
 drivers/net/Makefile   |    1
 drivers/net/bfin_mac.c |  981 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 drivers/net/bfin_mac.h |  147 +++++++++
 4 files changed, 1173 insertions(+)

Index: linux-2.6/drivers/net/Kconfig
===================================================================
--- linux-2.6.orig/drivers/net/Kconfig	2007-03-02 13:39:00.000000000 +0800
+++ linux-2.6/drivers/net/Kconfig	2007-03-02 13:39:00.000000000 +0800
@@ -836,6 +836,50 @@
 	  module, say M here and read <file:Documentation/modules.txt> as well
 	  as <file:Documentation/networking/net-modules.txt>.
 
+config BFIN_MAC
+	tristate "Blackfin 536/537 on-chip mac support"
+	depends on NET_ETHERNET && (BF537 || BF536) && (!BF537_PORT_H)
+	select CRC32
+	select BFIN_MAC_USE_L1 if DMA_UNCACHED_NONE
+	help
+	  This is the driver for blackfin on-chip mac device. Say Y if you want it
+	  compiled into the kernel. This driver is also available as a module
+	  ( = code which can be inserted in and removed from the running kernel
+	  whenever you want). The module will be called bfin_mac.
+
+config BFIN_MAC_USE_L1
+	bool "Use L1 memory for rx/tx packets"
+	depends on BFIN_MAC && BF537
+	default y
+	help
+	  To get maximum network performace, you should use L1 memory as rx/tx buffers.
+	  Say N here if you want to reserve L1 memory for other uses.
+
+config BFIN_TX_DESC_NUM
+	int "Number of transmit buffer packets"
+	depends on BFIN_MAC
+	range 6 10 if BFIN_MAC_USE_L1
+	range 10 100
+	default "10"
+	help
+	  Set the number of buffer packets used in driver.
+
+config BFIN_RX_DESC_NUM
+	int "Number of receive buffer packets"
+	depends on BFIN_MAC
+	range 20 100 if BFIN_MAC_USE_L1
+	range 20 800
+	default "20"
+	help
+	  Set the number of buffer packets used in driver.
+
+config BFIN_MAC_RMII
+	bool "RMII PHY Interface (EXPERIMENTAL)"
+	depends on BFIN_MAC && EXPERIMENTAL
+	default n
+	help
+	  Use Reduced PHY MII Interface
+
 config SMC9194
 	tristate "SMC 9194 support"
 	depends on NET_VENDOR_SMC && (ISA || MAC && BROKEN)
Index: linux-2.6/drivers/net/Makefile
===================================================================
--- linux-2.6.orig/drivers/net/Makefile	2007-03-02 13:38:59.000000000 +0800
+++ linux-2.6/drivers/net/Makefile	2007-03-02 13:39:00.000000000 +0800
@@ -195,6 +195,7 @@
 obj-$(CONFIG_MYRI10GE) += myri10ge/
 obj-$(CONFIG_SMC91X) += smc91x.o
 obj-$(CONFIG_SMC911X) += smc911x.o
+obj-$(CONFIG_BFIN_MAC) += bfin_mac.o
 obj-$(CONFIG_DM9000) += dm9000.o
 obj-$(CONFIG_FEC_8XX) += fec_8xx/
 obj-$(CONFIG_PASEMI_MAC) += pasemi_mac.o
Index: linux-2.6/drivers/net/bfin_mac.c
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ linux-2.6/drivers/net/bfin_mac.c	2007-03-02 13:52:39.000000000 +0800
@@ -0,0 +1,981 @@
+/*
+ * File:         drivers/net/bfin_mac.c
+ * Based on:
+ * Author:       Luke Yang <luke.yang@analog.com>
+ *
+ * Created:
+ * Description:
+ *
+ * Modified:
+ *               Copyright 2004-2006 Analog Devices Inc.
+ *
+ * Bugs:         Enter bugs at http://blackfin.uclinux.org/
+ *
+ * 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, 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 ;  see the file COPYING.
+ * If not, write to the Free Software Foundation,
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/timer.h>
+#include <linux/errno.h>
+#include <linux/ioport.h>
+#include <linux/crc32.h>
+#include <linux/device.h>
+#include <linux/spinlock.h>
+#include <linux/ethtool.h>
+#include <linux/mii.h>
+
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+
+#include <linux/platform_device.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/dma.h>
+#include <linux/dma-mapping.h>
+
+#include <asm/irq.h>
+#include <asm/blackfin.h>
+#include <asm/delay.h>
+
+#include "bfin_mac.h"
+
+#define CARDNAME "bfin_mac"
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Luke Yang");
+MODULE_DESCRIPTION("Blackfin MAC Driver");
+
+#if defined(CONFIG_BFIN_MAC_USE_L1)
+# define bfin_mac_alloc(dma_handle, size)  l1_data_sram_zalloc(size)
+# define bfin_mac_free(dma_handle, ptr)    l1_data_sram_free(ptr)
+#else
+# define bfin_mac_alloc(dma_handle, size)  dma_alloc_coherent(NULL, size, dma_handle, GFP_DMA)
+# define bfin_mac_free(dma_handle, ptr)    dma_free_coherent(NULL, sizeof(*ptr), ptr, dma_handle)
+#endif
+
+#define PKT_BUF_SZ 1580
+
+static void desc_list_free(void);
+
+/* pointers to maintain transmit list */
+static struct net_dma_desc_tx *tx_list_head;
+static struct net_dma_desc_tx *tx_list_tail;
+static struct net_dma_desc_rx *rx_list_head;
+static struct net_dma_desc_rx *rx_list_tail;
+static struct net_dma_desc_rx *current_rx_ptr;
+static struct net_dma_desc_tx *current_tx_ptr;
+static struct net_dma_desc_tx *tx_desc;
+static struct net_dma_desc_rx *rx_desc;
+
+static int desc_list_init(void)
+{
+	struct net_dma_desc_tx *tmp_desc_tx;
+	struct net_dma_desc_rx *tmp_desc_rx;
+	int i;
+	struct sk_buff *new_skb;
+#if !defined(CONFIG_BFIN_MAC_USE_L1)
+	dma_addr_t dma_handle;
+#endif
+
+	tx_desc =
+	    bfin_mac_alloc(&dma_handle,
+			   sizeof(struct net_dma_desc_tx) *
+			   CONFIG_BFIN_TX_DESC_NUM);
+	if (tx_desc == NULL)
+		goto init_error;
+
+	rx_desc =
+	    bfin_mac_alloc(&dma_handle,
+			   sizeof(struct net_dma_desc_rx) *
+			   CONFIG_BFIN_RX_DESC_NUM);
+	if (rx_desc == NULL)
+		goto init_error;
+
+	/* init tx_list */
+	for (i = 0; i < CONFIG_BFIN_TX_DESC_NUM; i++) {
+
+		tmp_desc_tx = tx_desc + i;
+
+		if (i == 0) {
+			tx_list_head = tmp_desc_tx;
+			tx_list_tail = tmp_desc_tx;
+		}
+
+		tmp_desc_tx->desc_a.start_addr =
+		    (unsigned long)tmp_desc_tx->packet;
+		tmp_desc_tx->desc_a.x_count = 0;
+		tmp_desc_tx->desc_a.config.b_DMA_EN = 0;	/* disabled */
+		tmp_desc_tx->desc_a.config.b_WNR = 0;		/* read from memory */
+		tmp_desc_tx->desc_a.config.b_WDSIZE = 2;	/* wordsize is 32 bits */
+		tmp_desc_tx->desc_a.config.b_NDSIZE = 6;	/* 6 half words is desc size. */
+		tmp_desc_tx->desc_a.config.b_FLOW = 7;		/* large desc flow */
+		tmp_desc_tx->desc_a.next_dma_desc = &(tmp_desc_tx->desc_b);
+
+		tmp_desc_tx->desc_b.start_addr =
+		    (unsigned long)(&(tmp_desc_tx->status));
+		tmp_desc_tx->desc_b.x_count = 0;
+		tmp_desc_tx->desc_b.config.b_DMA_EN = 1;	/* enabled */
+		tmp_desc_tx->desc_b.config.b_WNR = 1;		/* write to memory */
+		tmp_desc_tx->desc_b.config.b_WDSIZE = 2;	/* wordsize is 32 bits */
+		tmp_desc_tx->desc_b.config.b_DI_EN = 0;		/* disable interrupt */
+		tmp_desc_tx->desc_b.config.b_NDSIZE = 6;
+		tmp_desc_tx->desc_b.config.b_FLOW = 7;		/* stop mode */
+		tmp_desc_tx->skb = NULL;
+		tx_list_tail->desc_b.next_dma_desc = &(tmp_desc_tx->desc_a);
+		tx_list_tail->next = tmp_desc_tx;
+
+		tx_list_tail = tmp_desc_tx;
+	}
+	tx_list_tail->next = tx_list_head;	/* tx_list is a circle */
+	tx_list_tail->desc_b.next_dma_desc = &(tx_list_head->desc_a);
+	current_tx_ptr = tx_list_head;
+
+	/* init rx_list */
+	for (i = 0; i < CONFIG_BFIN_RX_DESC_NUM; i++) {
+
+		tmp_desc_rx = rx_desc + i;
+
+		if (i == 0) {
+			rx_list_head = tmp_desc_rx;
+			rx_list_tail = tmp_desc_rx;
+		}
+
+		/* allocat a new skb for next time receive */
+		new_skb = dev_alloc_skb(PKT_BUF_SZ + 2);
+		if (!new_skb) {
+			printk(KERN_NOTICE CARDNAME
+			       ": init: low on mem - packet dropped\n");
+			goto init_error;
+		}
+		skb_reserve(new_skb, 2);
+		tmp_desc_rx->skb = new_skb;
+		/* since RXDWA is enabled */
+		tmp_desc_rx->desc_a.start_addr =
+		    (unsigned long)new_skb->data - 2;
+		tmp_desc_rx->desc_a.x_count = 0;
+		tmp_desc_rx->desc_a.config.b_DMA_EN = 1;	/* enabled */
+		tmp_desc_rx->desc_a.config.b_WNR = 1;		/* Write to memory */
+		tmp_desc_rx->desc_a.config.b_WDSIZE = 2;	/* wordsize is 32 bits */
+		tmp_desc_rx->desc_a.config.b_NDSIZE = 6;	/* 6 half words is desc size. */
+		tmp_desc_rx->desc_a.config.b_FLOW = 7;		/* large desc flow */
+		tmp_desc_rx->desc_a.next_dma_desc = &(tmp_desc_rx->desc_b);
+
+		tmp_desc_rx->desc_b.start_addr =
+		    (unsigned long)(&(tmp_desc_rx->status));
+		tmp_desc_rx->desc_b.x_count = 0;
+		tmp_desc_rx->desc_b.config.b_DMA_EN = 1;	/* enabled */
+		tmp_desc_rx->desc_b.config.b_WNR = 1;		/* Write to memory */
+		tmp_desc_rx->desc_b.config.b_WDSIZE = 2;	/* wordsize is 32 bits */
+		tmp_desc_rx->desc_b.config.b_NDSIZE = 6;
+		tmp_desc_rx->desc_b.config.b_DI_EN = 1;		/* enable interrupt */
+		tmp_desc_rx->desc_b.config.b_FLOW = 7;		/* large mode */
+		rx_list_tail->desc_b.next_dma_desc = &(tmp_desc_rx->desc_a);
+
+		rx_list_tail->next = tmp_desc_rx;
+		rx_list_tail = tmp_desc_rx;
+	}
+	rx_list_tail->next = rx_list_head;	/* rx_list is a circle */
+	rx_list_tail->desc_b.next_dma_desc = &(rx_list_head->desc_a);
+	current_rx_ptr = rx_list_head;
+
+	return 0;
+
+      init_error:
+	desc_list_free();
+	printk(KERN_ERR CARDNAME ": kmalloc failed\n");
+	return -ENOMEM;
+}
+
+static void desc_list_free(void)
+{
+	struct net_dma_desc_rx *tmp_desc_rx;
+	struct net_dma_desc_tx *tmp_desc_tx;
+	int i;
+#if !defined(CONFIG_BFIN_MAC_USE_L1)
+	dma_addr_t dma_handle = 0;
+#endif
+
+	if (tx_desc != NULL) {
+		tmp_desc_tx = tx_list_head;
+		for (i = 0; i < CONFIG_BFIN_TX_DESC_NUM; i++) {
+			if (tmp_desc_tx != NULL) {
+				if (tmp_desc_tx->skb) {
+					dev_kfree_skb(tmp_desc_tx->skb);
+					tmp_desc_tx->skb = NULL;
+				}
+
+			}
+			tmp_desc_tx = tmp_desc_tx->next;
+		}
+		bfin_mac_free(dma_handle, tx_desc);
+	}
+
+	if (rx_desc != NULL) {
+		tmp_desc_rx = rx_list_head;
+		for (i = 0; i < CONFIG_BFIN_RX_DESC_NUM; i++) {
+			if (tmp_desc_rx != NULL) {
+				if (tmp_desc_rx->skb) {
+					dev_kfree_skb(tmp_desc_rx->skb);
+					tmp_desc_rx->skb = NULL;
+				}
+			}
+			tmp_desc_rx = tmp_desc_rx->next;
+		}
+		bfin_mac_free(dma_handle, rx_desc);
+	}
+}
+
+/*---PHY CONTROL AND CONFIGURATION-----------------------------------------*/
+
+/* Set FER regs to MUX in Ethernet pins */
+static void setup_pin_mux(void)
+{
+	unsigned int fer_val;
+
+	/* 
+	 * FER reg bug work-around 
+	 * read it once
+	 */
+	fer_val = bfin_read_PORTH_FER();
+
+#if defined(CONFIG_BFIN_MAC_RMII)
+	fer_val = 0xC373;
+#else
+	fer_val = 0xffff;
+#endif
+	/* write it twice to the same value */
+
+	bfin_write_PORTH_FER(fer_val);
+	bfin_write_PORTH_FER(fer_val);
+}
+
+/* Wait until the previous MDC/MDIO transaction has completed */
+static void poll_mdc_done(void)
+{
+	/* poll the STABUSY bit */
+	while ((bfin_read_EMAC_STAADD()) & STABUSY) {
+	};
+}
+
+/* Read an off-chip register in a PHY through the MDC/MDIO port */
+static u16 read_phy_reg(u16 PHYAddr, u16 RegAddr)
+{
+	poll_mdc_done();
+	bfin_write_EMAC_STAADD(SET_PHYAD(PHYAddr) | SET_REGAD(RegAddr) | STABUSY);	/* read mode */
+	poll_mdc_done();
+
+	return (u16) bfin_read_EMAC_STADAT();
+}
+
+/* Write an off-chip register in a PHY through the MDC/MDIO port */
+static void raw_write_phy_reg(u16 PHYAddr, u16 RegAddr, u32 Data)
+{
+	bfin_write_EMAC_STADAT(Data);
+
+	bfin_write_EMAC_STAADD(SET_PHYAD(PHYAddr) | SET_REGAD(RegAddr) | STAOP | STABUSY);	/* write mode */
+
+	poll_mdc_done();
+}
+
+static void write_phy_reg(u16 PHYAddr, u16 RegAddr, u32 Data)
+{
+	poll_mdc_done();
+	raw_write_phy_reg(PHYAddr, RegAddr, Data);
+}
+
+/* set up the phy */
+static void bf537mac_setphy(struct net_device *dev)
+{
+	u16 phydat;
+	u32 sysctl;
+	struct bf537mac_local *lp = netdev_priv(dev);
+
+	pr_debug("start settting up phy\n");
+
+	/* Program PHY registers */
+	phydat = 0;
+
+	/* issue a reset */
+	raw_write_phy_reg(lp->PhyAddr, PHYREG_MODECTL, 0x8000);
+
+	/* wait half a second */
+	udelay(500);
+
+	phydat = read_phy_reg(lp->PhyAddr, PHYREG_MODECTL);
+
+	/* advertise flow control supported */
+	phydat = read_phy_reg(lp->PhyAddr, PHYREG_ANAR);
+	phydat |= (1 << 10);
+	write_phy_reg(lp->PhyAddr, PHYREG_ANAR, phydat);
+
+	phydat = 0;
+	if (lp->Negotiate) {
+		phydat |= 0x1000;	/* enable auto negotiation */
+	} else {
+		if (lp->FullDuplex) {
+			phydat |= (1 << 8);	/* full duplex */
+		} else {
+			phydat &= (~(1 << 8));	/* half duplex */
+		}
+		if (!lp->Port10) {
+			phydat |= (1 << 13);	/* 100 Mbps */
+		} else {
+			phydat &= (~(1 << 13));	/* 10 Mbps */
+		}
+	}
+
+	if (lp->Loopback) {
+		phydat |= (1 << 14);	/* enable TX->RX loopback */
+#if 0
+		write_phy_reg(lp->PhyAddr, PHYREG_MODECTL, phydat);
+#endif
+	}
+
+	write_phy_reg(lp->PhyAddr, PHYREG_MODECTL, phydat);
+	udelay(500);
+
+	phydat = read_phy_reg(lp->PhyAddr, PHYREG_MODECTL);
+	/* check for SMSC PHY */
+	if ((read_phy_reg(lp->PhyAddr, PHYREG_PHYID1) == 0x7)
+	    && ((read_phy_reg(lp->PhyAddr, PHYREG_PHYID2) & 0xfff0) == 0xC0A0)) {
+		/* we have SMSC PHY so reqest interrupt on link down condition */
+		write_phy_reg(lp->PhyAddr, 30, 0x0ff);	/* enable interrupts */
+		/* enable PHY_INT */
+		sysctl = bfin_read_EMAC_SYSCTL();
+		sysctl |= 0x1;
+#if 0
+		bfin_write_EMAC_SYSCTL(sysctl);
+#endif
+	}
+}
+
+/**************************************************************************/
+void setup_system_regs(struct net_device *dev)
+{
+	int PHYADDR;
+	unsigned short sysctl, phydat;
+	u32 opmode;
+	struct bf537mac_local *lp = netdev_priv(dev);
+	int count = 0;
+
+	PHYADDR = lp->PhyAddr;
+
+	/* Enable PHY output */
+	if (!(bfin_read_VR_CTL() & PHYCLKOE))
+		bfin_write_VR_CTL(bfin_read_VR_CTL() | PHYCLKOE);
+
+	/* MDC  = 2.5 MHz */
+	sysctl = SET_MDCDIV(24);
+	/* Odd word alignment for Receive Frame DMA word */
+	/* Configure checksum support and rcve frame word alignment */
+#if defined(BFIN_MAC_CSUM_OFFLOAD)
+	sysctl |= RXDWA | RXCKS;
+#else
+	sysctl |= RXDWA;
+#endif
+	bfin_write_EMAC_SYSCTL(sysctl);
+	/* auto negotiation on  */
+	/* full duplex          */
+	/* 100 Mbps             */
+	phydat = PHY_ANEG_EN | PHY_DUPLEX | PHY_SPD_SET;
+	write_phy_reg(PHYADDR, PHYREG_MODECTL, phydat);
+
+	/* test if full duplex supported */
+	do {
+		msleep(100);
+		phydat = read_phy_reg(PHYADDR, PHYREG_MODESTAT);
+		if (count > 30) {
+			printk(KERN_NOTICE CARDNAME
+			       ": Link is down, please check your network connection\n");
+			break;
+		}
+		count++;
+	} while (!(phydat & 0x0004));
+
+	phydat = read_phy_reg(PHYADDR, PHYREG_ANLPAR);
+
+	if ((phydat & 0x0100) || (phydat & 0x0040)) {
+		opmode = FDMODE;
+	} else {
+		opmode = 0;
+		printk(KERN_INFO CARDNAME ": Network is set to half duplex\n");
+	}
+
+#if defined(CONFIG_BFIN_MAC_RMII)
+	opmode |= RMII; /* For Now only 100MBit are supported */
+#endif
+
+	bfin_write_EMAC_OPMODE(opmode);
+
+#if 0
+	bfin_write_EMAC_MMC_CTL(RSTC | CROLL | MMCE);
+#endif
+	bfin_write_EMAC_MMC_CTL(RSTC | CROLL);
+
+	/* Initialize the TX DMA channel registers */
+	bfin_write_DMA2_X_COUNT(0);
+	bfin_write_DMA2_X_MODIFY(4);
+	bfin_write_DMA2_Y_COUNT(0);
+	bfin_write_DMA2_Y_MODIFY(0);
+
+	/* Initialize the RX DMA channel registers */
+	bfin_write_DMA1_X_COUNT(0);
+	bfin_write_DMA1_X_MODIFY(4);
+	bfin_write_DMA1_Y_COUNT(0);
+	bfin_write_DMA1_Y_MODIFY(0);
+}
+
+void setup_mac_addr(u8 * mac_addr)
+{
+	/* this depends on a little-endian machine */
+	bfin_write_EMAC_ADDRLO(*(u32 *) & mac_addr[0]);
+	bfin_write_EMAC_ADDRHI(*(u16 *) & mac_addr[4]);
+}
+
+static void adjust_tx_list(void)
+{
+	if (tx_list_head->status.status_word != 0
+	    && current_tx_ptr != tx_list_head) {
+		goto adjust_head;	/* released something, just return; */
+	}
+
+	/* if nothing released, check wait condition */
+	/* current's next can not be the head, otherwise the dma will not stop as we want */
+	if (current_tx_ptr->next->next == tx_list_head) {
+		while (tx_list_head->status.status_word == 0) {
+			udelay(10);
+			if (tx_list_head->status.status_word != 0
+			    || !(bfin_read_DMA2_IRQ_STATUS() & 0x08)) {
+				goto adjust_head;
+			}
+		}
+		if (tx_list_head->status.status_word != 0) {
+			goto adjust_head;
+		}
+	}
+
+	return;
+
+      adjust_head:
+	do {
+		tx_list_head->desc_a.config.b_DMA_EN = 0;
+		tx_list_head->status.status_word = 0;
+		if (tx_list_head->skb) {
+			dev_kfree_skb(tx_list_head->skb);
+			tx_list_head->skb = NULL;
+		} else {
+			printk(KERN_ERR CARDNAME
+			       ": no sk_buff in a transmitted frame!\n");
+		}
+		tx_list_head = tx_list_head->next;
+	} while (tx_list_head->status.status_word != 0
+		 && current_tx_ptr != tx_list_head);
+	return;
+
+}
+
+static int bf537mac_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+	struct bf537mac_local *lp = netdev_priv(dev);
+	unsigned int data;
+
+	current_tx_ptr->skb = skb;
+	/* Is skb->data always 16-bit aligned? Do we need to memcpy((char *)(tail->packet + 2),skb->data,len)? */
+	if ((((unsigned int)(skb->data)) & 0x02) == 2) {
+		/* move skb->data to current_tx_ptr payload */
+		data = (unsigned int)(skb->data) - 2;
+		*((unsigned short *)data) = (unsigned short)(skb->len);
+		current_tx_ptr->desc_a.start_addr = (unsigned long)data;
+		blackfin_dcache_flush_range(data, (data + (skb->len)) + 2);	/* this is important! */
+
+	} else {
+		*((unsigned short *)(current_tx_ptr->packet)) =
+		    (unsigned short)(skb->len);
+		memcpy((char *)(current_tx_ptr->packet + 2), skb->data,
+		       (skb->len));
+		current_tx_ptr->desc_a.start_addr =
+		    (unsigned long)current_tx_ptr->packet;
+		if (current_tx_ptr->status.status_word != 0)
+			current_tx_ptr->status.status_word = 0;
+		blackfin_dcache_flush_range((unsigned int)current_tx_ptr->
+					    packet,
+					    (unsigned int)(current_tx_ptr->
+							   packet + skb->len) +
+					    2);
+	}
+
+	current_tx_ptr->desc_a.config.b_DMA_EN = 1;	/* enable this packet's dma */
+
+	if (bfin_read_DMA2_IRQ_STATUS() & 0x08) {	/* tx dma is running, just return */
+		goto out;
+	} else {
+		/* tx dma is not running */
+		bfin_write_DMA2_NEXT_DESC_PTR(&(current_tx_ptr->desc_a));
+		/* dma enabled, read from memory, size is 6 */
+		bfin_write_DMA2_CONFIG(*((unsigned short *)(&(current_tx_ptr->desc_a.config))));
+		/* Turn on the EMAC tx */
+		bfin_write_EMAC_OPMODE(bfin_read_EMAC_OPMODE() | TE);
+	}
+
+      out:
+	adjust_tx_list();
+	current_tx_ptr = current_tx_ptr->next;
+	dev->trans_start = jiffies;
+	lp->stats.tx_packets++;
+	lp->stats.tx_bytes += (skb->len);
+	return 0;
+}
+
+static void bf537mac_rx(struct net_device *dev)
+{
+	struct sk_buff *skb, *new_skb;
+	struct bf537mac_local *lp = netdev_priv(dev);
+	unsigned short len;
+
+	/* allocat a new skb for next time receive */
+	skb = current_rx_ptr->skb;
+	new_skb = dev_alloc_skb(PKT_BUF_SZ + 2);
+	if (!new_skb) {
+		printk(KERN_NOTICE CARDNAME
+		       ": rx: low on mem - packet dropped\n");
+		lp->stats.rx_dropped++;
+		goto out;
+	}
+	/* reserve 2 bytes for RXDWA padding */
+	skb_reserve(new_skb, 2);
+	current_rx_ptr->skb = new_skb;
+	current_rx_ptr->desc_a.start_addr = (unsigned long)new_skb->data - 2;
+
+#if 0
+	int i;
+	if (len >= 64) {
+		for (i=0;i<len;i++) {
+			printk("%.2x-",((unsigned char *)pkt)[i]);
+			if (((i%8)==0) && (i!=0)) printk("\n");
+		}
+	printk("\n");
+	}
+#endif
+
+	len = (unsigned short)((current_rx_ptr->status.status_word) & RX_FRLEN);
+	skb_put(skb, len);
+	blackfin_dcache_invalidate_range((unsigned long)skb->head,
+					 (unsigned long)skb->tail);
+
+	dev->last_rx = jiffies;
+	skb->dev = dev;
+	skb->protocol = eth_type_trans(skb, dev);
+#if defined(BFIN_MAC_CSUM_OFFLOAD)
+	skb->csum = current_rx_ptr->status.ip_payload_csum;
+	skb->ip_summed = CHECKSUM_PARTIAL;
+#endif
+
+	netif_rx(skb);
+	lp->stats.rx_packets++;
+	lp->stats.rx_bytes += len;
+	current_rx_ptr->status.status_word = 0x00000000;
+	current_rx_ptr = current_rx_ptr->next;
+
+      out:
+	return;
+}
+
+/* interrupt routine to handle rx and error signal */
+static irqreturn_t bf537mac_interrupt(int irq, void *dev_id)
+{
+	struct net_device *dev = dev_id;
+	int number = 0;
+
+      get_one_packet:
+	if (current_rx_ptr->status.status_word == 0) {	/* no more new packet received */
+		if (number == 0) {
+			if (current_rx_ptr->next->status.status_word != 0) {
+				current_rx_ptr = current_rx_ptr->next;
+				goto real_rx;
+			}
+		}
+		bfin_write_DMA1_IRQ_STATUS(bfin_read_DMA1_IRQ_STATUS() |
+					   DMA_DONE | DMA_ERR);
+		return IRQ_HANDLED;
+	}
+
+      real_rx:
+	bf537mac_rx(dev);
+	number++;
+	goto get_one_packet;
+}
+
+#ifdef CONFIG_NET_POLL_CONTROLLER
+static void bf537mac_poll(struct net_device *dev)
+{
+	disable_irq(IRQ_MAC_RX);
+	bf537mac_interrupt(IRQ_MAC_RX, dev);
+	enable_irq(IRQ_MAC_RX);
+}
+#endif				/* CONFIG_NET_POLL_CONTROLLER */
+
+static void bf537mac_reset(void)
+{
+	unsigned int opmode;
+
+	opmode = bfin_read_EMAC_OPMODE();
+	opmode &= (~RE);
+	opmode &= (~TE);
+	/* Turn off the EMAC */
+	bfin_write_EMAC_OPMODE(bfin_read_EMAC_OPMODE() & opmode);
+}
+
+/*
+ * Enable Interrupts, Receive, and Transmit
+ */
+static int bf537mac_enable(struct net_device *dev)
+{
+	u32 opmode;
+
+	pr_debug("%s: %s\n", dev->name, __FUNCTION__);
+
+	/* Set RX DMA */
+	bfin_write_DMA1_NEXT_DESC_PTR(&(rx_list_head->desc_a));
+	bfin_write_DMA1_CONFIG(*
+			       ((unsigned short
+				 *)(&(rx_list_head->desc_a.config))));
+
+	/* Wait MII done */
+	poll_mdc_done();
+
+	/* We enable only RX here */
+	/* ASTP   : Enable Automatic Pad Stripping
+	   PR     : Promiscuous Mode for test
+	   PSF    : Receive frames with total length less than 64 bytes.
+	   FDMODE : Full Duplex Mode
+	   LB     : Internal Loopback for test
+	   RE     : Receiver Enable */
+	opmode = bfin_read_EMAC_OPMODE();
+	if (opmode & FDMODE)
+		opmode |= PSF;
+	else
+		opmode |= DRO | DC | PSF;
+	opmode |= RE;
+
+#if defined(CONFIG_BFIN_MAC_RMII)
+	opmode |= RMII; /* For Now only 100MBit are supported */
+#ifdef CONFIG_BF_REV_0_2
+	opmode |= TE;
+#endif
+#endif
+	/* Turn on the EMAC rx */
+	bfin_write_EMAC_OPMODE(opmode);
+
+	return 0;
+}
+
+/* Our watchdog timed out. Called by the networking layer */
+static void bf537mac_timeout(struct net_device *dev)
+{
+	pr_debug("%s: %s\n", dev->name, __FUNCTION__);
+
+	bf537mac_reset();
+
+	/* reset tx queue */
+	tx_list_tail = tx_list_head->next;
+
+	bf537mac_enable(dev);
+
+	/* We can accept TX packets again */
+	dev->trans_start = jiffies;
+	netif_wake_queue(dev);
+}
+
+/*
+ * Get the current statistics.
+ * This may be called with the card open or closed.
+ */
+static struct net_device_stats *bf537mac_query_statistics(struct net_device
+							  *dev)
+{
+	struct bf537mac_local *lp = netdev_priv(dev);
+
+	pr_debug("%s: %s\n", dev->name, __FUNCTION__);
+
+	return &lp->stats;
+}
+
+/*
+ * This routine will, depending on the values passed to it,
+ * either make it accept multicast packets, go into
+ * promiscuous mode (for TCPDUMP and cousins) or accept
+ * a select set of multicast packets
+ */
+static void bf537mac_set_multicast_list(struct net_device *dev)
+{
+	u32 sysctl;
+
+	if (dev->flags & IFF_PROMISC) {
+		printk(KERN_INFO "%s: set to promisc mode\n", dev->name);
+		sysctl = bfin_read_EMAC_OPMODE();
+		sysctl |= RAF;
+		bfin_write_EMAC_OPMODE(sysctl);
+	} else if (dev->flags & IFF_ALLMULTI || dev->mc_count > 16) {
+		/* accept all multicast */
+		sysctl = bfin_read_EMAC_OPMODE();
+		sysctl |= PAM;
+		bfin_write_EMAC_OPMODE(sysctl);
+	} else if (dev->mc_count) {
+		/* set multicast */
+	} else {
+		/* clear promisc or multicast mode */
+		sysctl = bfin_read_EMAC_OPMODE();
+		sysctl &= ~(RAF | PAM);
+		bfin_write_EMAC_OPMODE(sysctl);
+	}
+}
+
+/*
+ * this puts the device in an inactive state
+ */
+static void bf537mac_shutdown(struct net_device *dev)
+{
+	/* Turn off the EMAC */
+	bfin_write_EMAC_OPMODE(0x00000000);
+	/* Turn off the EMAC RX DMA */
+	bfin_write_DMA1_CONFIG(0x0000);
+	bfin_write_DMA2_CONFIG(0x0000);
+}
+
+/*
+ * Open and Initialize the interface
+ *
+ * Set up everything, reset the card, etc..
+ */
+static int bf537mac_open(struct net_device *dev)
+{
+	pr_debug("%s: %s\n", dev->name, __FUNCTION__);
+
+	/*
+	 * Check that the address is valid.  If its not, refuse
+	 * to bring the device up.  The user must specify an
+	 * address using ifconfig eth0 hw ether xx:xx:xx:xx:xx:xx
+	 */
+	if (!is_valid_ether_addr(dev->dev_addr)) {
+		printk(KERN_WARNING CARDNAME ": no valid ethernet hw addr\n");
+		return -EINVAL;
+	}
+
+	/* initial rx and tx list */
+	desc_list_init();
+
+	bf537mac_setphy(dev);
+	setup_system_regs(dev);
+	bf537mac_reset();
+	bf537mac_enable(dev);
+
+	pr_debug("hardware init finished\n");
+	netif_start_queue(dev);
+	netif_carrier_on(dev);
+
+	return 0;
+}
+
+/*
+ *
+ * this makes the board clean up everything that it can
+ * and not talk to the outside world.   Caused by
+ * an 'ifconfig ethX down'
+ */
+static int bf537mac_close(struct net_device *dev)
+{
+	pr_debug("%s: %s\n", dev->name, __FUNCTION__);
+	
+	netif_stop_queue(dev);
+	netif_carrier_off(dev);
+
+	/* clear everything */
+	bf537mac_shutdown(dev);
+
+	/* free the rx/tx buffers */
+	desc_list_free();
+
+	return 0;
+}
+
+static int __init bf537mac_probe(struct net_device *dev)
+{
+	struct bf537mac_local *lp = netdev_priv(dev);
+	int retval;
+
+	/* Grab the MAC address in the MAC */
+	*(u32 *) (&(dev->dev_addr[0])) = bfin_read_EMAC_ADDRLO();
+	*(u16 *) (&(dev->dev_addr[4])) = (u16) bfin_read_EMAC_ADDRHI();
+
+/* probe mac */
+	/*todo: how to proble? which is revision_register */
+	bfin_write_EMAC_ADDRLO(0x12345678);
+	if (bfin_read_EMAC_ADDRLO() != 0x12345678) {
+		pr_debug("can't detect bf537 mac!\n");
+		retval = -ENODEV;
+		goto err_out;
+	}
+
+	/*Is it valid? (Did bootloader initialize it?) */
+	if (!is_valid_ether_addr(dev->dev_addr)) {
+		/* Grab the MAC from the board somehow - this is done in the
+		   arch/blackfin/boards/bf537/boardname.c */
+		get_bf537_ether_addr(dev->dev_addr);
+	}
+
+	/* If still not valid, get a random one */
+	if (!is_valid_ether_addr(dev->dev_addr)) {
+		random_ether_addr(dev->dev_addr);
+	}
+
+	setup_mac_addr(dev->dev_addr);
+
+	/* Fill in the fields of the device structure with ethernet values. */
+	ether_setup(dev);
+
+	dev->open = bf537mac_open;
+	dev->stop = bf537mac_close;
+	dev->hard_start_xmit = bf537mac_hard_start_xmit;
+	dev->tx_timeout = bf537mac_timeout;
+	dev->get_stats = bf537mac_query_statistics;
+	dev->set_multicast_list = bf537mac_set_multicast_list;
+#if 0
+	dev->ethtool_ops = &bf537mac_ethtool_ops;
+#endif
+#ifdef CONFIG_NET_POLL_CONTROLLER
+	dev->poll_controller = bf537mac_poll;
+#endif
+
+	/* fill in some of the fields */
+	lp->version = 1;
+	lp->PhyAddr = 0x01;
+	lp->CLKIN = 25;
+	lp->FullDuplex = 0;
+	lp->Negotiate = 1;
+	lp->FlowControl = 0;
+	spin_lock_init(&lp->lock);
+
+	/* set the GPIO pins to Ethernet mode */
+	setup_pin_mux();
+
+	/* now, enable interrupts */
+	/* register irq handler */
+	if (request_irq
+	    (IRQ_MAC_RX, bf537mac_interrupt, IRQF_DISABLED | IRQF_SHARED,
+	     "BFIN537_MAC_RX", dev)) {
+		printk(KERN_WARNING CARDNAME
+		       ": Unable to attach BlackFin MAC RX interrupt\n");
+		return -EBUSY;
+	}
+
+	/* Enable PHY output early */
+	if (!(bfin_read_VR_CTL() & PHYCLKOE))
+		bfin_write_VR_CTL(bfin_read_VR_CTL() | PHYCLKOE);
+
+	retval = register_netdev(dev);
+	if (retval == 0) {
+		/* now, print out the card info, in a short format.. */
+		printk(KERN_INFO "Blackfin mac net device registered\n");
+	}
+
+      err_out:
+	return retval;
+}
+
+static int bfin_mac_probe(struct platform_device *pdev)
+{
+	struct net_device *ndev;
+
+	ndev = alloc_etherdev(sizeof(struct bf537mac_local));
+
+	if (!ndev) {
+		printk(KERN_WARNING CARDNAME ": could not allocate device\n");
+		return -ENOMEM;
+	}
+
+	if (bf537mac_probe(ndev) != 0) {
+		platform_set_drvdata(pdev, NULL);
+		free_netdev(ndev);
+		printk(KERN_WARNING CARDNAME ": not found\n");
+		return -ENODEV;
+	}
+
+	SET_MODULE_OWNER(ndev);
+	SET_NETDEV_DEV(ndev, &pdev->dev);
+
+	platform_set_drvdata(pdev, ndev);
+
+	return 0;
+}
+
+static int bfin_mac_remove(struct platform_device *pdev)
+{
+	struct net_device *ndev = platform_get_drvdata(pdev);
+
+	platform_set_drvdata(pdev, NULL);
+
+	unregister_netdev(ndev);
+
+	free_irq(IRQ_MAC_RX, ndev);
+
+	free_netdev(ndev);
+
+	return 0;
+}
+
+static int bfin_mac_suspend(struct platform_device *pdev, pm_message_t state)
+{
+	return 0;
+}
+
+static int bfin_mac_resume(struct platform_device *pdev)
+{
+	return 0;
+}
+
+static struct platform_driver bfin_mac_driver = {
+	.probe = bfin_mac_probe,
+	.remove = bfin_mac_remove,
+	.resume = bfin_mac_resume,
+	.suspend = bfin_mac_suspend,
+	.driver = {
+		   .name = CARDNAME,
+		   },
+};
+
+static int __init bfin_mac_init(void)
+{
+	return platform_driver_register(&bfin_mac_driver);
+}
+
+module_init(bfin_mac_init);
+
+static void __exit bfin_mac_cleanup(void)
+{
+	platform_driver_unregister(&bfin_mac_driver);
+}
+
+module_exit(bfin_mac_cleanup);
Index: linux-2.6/drivers/net/bfin_mac.h
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ linux-2.6/drivers/net/bfin_mac.h	2007-03-02 13:52:49.000000000 +0800
@@ -0,0 +1,147 @@
+/*
+ * File:         drivers/net/bfin_mac.h
+ * Based on:
+ * Author:       Luke Yang <luke.yang@analog.com>
+ *
+ * Created:
+ * Description:
+ *
+ * Modified:
+ *               Copyright 2004-2006 Analog Devices Inc.
+ *
+ * Bugs:         Enter bugs at http://blackfin.uclinux.org/
+ *
+ * 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, 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 ;  see the file COPYING.
+ * If not, write to the Free Software Foundation,
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/* 
+ * PHY REGISTER NAMES
+ */
+#define PHYREG_MODECTL		0x0000
+#define PHYREG_MODESTAT		0x0001
+#define PHYREG_PHYID1		0x0002
+#define PHYREG_PHYID2		0x0003
+#define PHYREG_ANAR		0x0004
+#define PHYREG_ANLPAR		0x0005
+#define PHYREG_ANER		0x0006
+#define PHYREG_NSR		0x0010
+#define PHYREG_LBREMR		0x0011
+#define PHYREG_REC		0x0012
+#define PHYREG_10CFG		0x0013
+#define PHYREG_PHY1_1		0x0014
+#define PHYREG_PHY1_2		0x0015
+#define PHYREG_PHY2		0x0016
+#define PHYREG_TW_1		0x0017
+#define PHYREG_TW_2		0x0018
+#define PHYREG_TEST		0x0019
+
+#define PHY_RESET		0x8000
+#define PHY_ANEG_EN		0x1000
+#define PHY_DUPLEX		0x0100
+#define PHY_SPD_SET		0x2000
+
+#define BFIN_MAC_CSUM_OFFLOAD
+
+typedef struct _DMA_CONFIG {
+	unsigned short b_DMA_EN:1;	/* Bit 0 : DMA Enable */
+	unsigned short b_WNR:1;		/* Bit 1 : DMA Direction */
+	unsigned short b_WDSIZE:2;	/* Bit 2 & 3 : DMA Tranfer Word size */
+	unsigned short b_DMA2D:1;	/* Bit 4 : DMA Mode 2D or 1D */
+	unsigned short b_RESTART:1;	/* Bit 5 : Retain the FIFO */
+	unsigned short b_DI_SEL:1;	/* Bit 6 : Data Interrupt Timing Select */
+	unsigned short b_DI_EN:1;	/* Bit 7 : Data Interrupt Enable */
+	unsigned short b_NDSIZE:4;	/* Bit 8 to 11 : Flex descriptor Size */
+	unsigned short b_FLOW:3;	/* Bit 12 to 14 : FLOW */
+} DMA_CONFIG_REG;
+
+struct dma_descriptor {
+	struct dma_descriptor *next_dma_desc;
+	unsigned long start_addr;
+	DMA_CONFIG_REG config;
+	unsigned short x_count;
+};
+
+#if 0
+struct status_area {
+  unsigned short ip_hdr_chksum;         /* the IP header checksum */
+  unsigned short ip_payload_chksum;     /* the IP header and payload checksum */
+  unsigned long  status_word;           /* the frame status word */
+};
+#endif
+
+struct status_area_rx {
+#if defined(BFIN_MAC_CSUM_OFFLOAD)
+	unsigned short ip_hdr_csum;	/* ip header checksum */
+	unsigned short ip_payload_csum;	/* ip payload(udp or tcp or others) checksum */
+#endif
+	unsigned long status_word;	/* the frame status word */
+};
+
+struct status_area_tx {
+	unsigned long status_word;	/* the frame status word */
+};
+
+/* use two descriptors for a packet */
+struct net_dma_desc_rx {
+	struct net_dma_desc_rx *next;
+	struct sk_buff *skb;
+	struct dma_descriptor desc_a;
+	struct dma_descriptor desc_b;
+	volatile struct status_area_rx status;
+};
+
+/* use two descriptors for a packet */
+struct net_dma_desc_tx {
+	struct net_dma_desc_tx *next;
+	struct sk_buff *skb;
+	struct dma_descriptor desc_a;
+	struct dma_descriptor desc_b;
+	volatile unsigned char packet[1560];
+	volatile struct status_area_tx status;
+};
+
+struct bf537mac_local {
+	/*
+	 * these are things that the kernel wants me to keep, so users
+	 * can find out semi-useless statistics of how well the card is
+	 * performing
+	 */
+	struct net_device_stats stats;
+
+	int version;
+
+	int FlowEnabled;	/* record if data flow is active */
+	int EtherIntIVG;	/* IVG for the ethernet interrupt */
+	int RXIVG;		/* IVG for the RX completion */
+	int TXIVG;		/* IVG for the TX completion */
+	int PhyAddr;		/* PHY address */
+	int OpMode;		/* set these bits n the OPMODE regs */
+	int Port10;		/* set port speed to 10 Mbit/s */
+	int GenChksums;		/* IP checksums to be calculated */
+	int NoRcveLnth;		/* dont insert recv length at start of buffer */
+	int StripPads;		/* remove trailing pad bytes */
+	int FullDuplex;		/* set full duplex mode */
+	int Negotiate;		/* enable auto negotiation */
+	int Loopback;		/* loopback at the PHY */
+	int Cache;		/* Buffers may be cached */
+	int FlowControl;	/* flow control active */
+	int CLKIN;		/* clock in value in MHZ */
+	unsigned short IntMask;	/* interrupt mask */
+	unsigned char Mac[6];	/* MAC address of the board */
+	spinlock_t lock;
+};
+
+extern void get_bf537_ether_addr(char *addr);
_

Thanks
-Bryan

^ permalink raw reply	[flat|nested] 6+ messages in thread

end of thread, other threads:[~2007-03-02  8:09 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2007-03-01  4:15 [PATCH -mm 3/5] Blackfin: on-chip ethernet MAC controller driver Wu, Bryan
2007-03-01  8:53 ` Andrew Morton
2007-03-01 15:52 ` Stephen Hemminger
2007-03-01 18:21   ` Mike Frysinger
2007-03-01 18:35   ` Robin Getz
2007-03-02  8:08   ` Wu, Bryan

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).