On Sun, 2007-07-15 at 02:49 +0800, Bryan Wu wrote:
> This patch implements the driver necessary use the Analog Devices
> Blackfin processor's on-chip ethernet MAC controller.
>
> - clean up coding style issues by Jeff's review
>
> Signed-off-by: Bryan Wu <[email protected]>
> Cc: Jeff Garzik <[email protected]>
> Signed-off-by: Andrew Morton <[email protected]>
> ---
> MAINTAINERS | 7 +
> drivers/net/Kconfig | 44 ++
> drivers/net/Makefile | 1 +
> drivers/net/bfin_mac.c | 1034 ++++++++++++++++++++++++++++++++++++++++++++++++
> drivers/net/bfin_mac.h | 144 +++++++
> 5 files changed, 1230 insertions(+), 0 deletions(-)
> create mode 100644 drivers/net/bfin_mac.c
> create mode 100644 drivers/net/bfin_mac.h
>
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 845fbf4..21a2265 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -754,6 +754,13 @@ L: [email protected] (subscribers-only)
> W: http://blackfin.uclinux.org
> S: Supported
>
> +BLACKFIN EMAC DRIVER
> +P: Bryan Wu
> +M: [email protected]
> +L: [email protected] (subscribers-only)
> +W: http://blackfin.uclinux.org
> +S: Supported
> +
> BLACKFIN RTC DRIVER
> P: Mike Frysinger
> M: [email protected]
> diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig
> index ba314ad..9356a6e 100644
> --- a/drivers/net/Kconfig
> +++ b/drivers/net/Kconfig
> @@ -838,6 +838,50 @@ config ULTRA32
> <file:Documentation/networking/net-modules.txt>. The module
> will be called smc-ultra32.
>
> +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.
Seems odd that this is a compile time option, is there any reason why
it's not a module parameter with a default of 1? It would also make the
code cleaner since you wouldn't have #ifdefs sprinkled around quite so
much. Basically, if I find out gee, I now want to use L1 for other uses
today, should I really have to recompile my kernel to do that?
You could handle the bfin_mac_alloc/free decision by having making them
pointers in the driver private structure, assigning those pointers to
the right function on card probe, and calling the pointer at runtime if
you don't want to have an "if (use_l1) { ... } else { ... }" in critical
paths.
> +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.
Why are these compile-time options and not module parameters with
defaults?
> +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)
> diff --git a/drivers/net/Makefile b/drivers/net/Makefile
> index a2241e6..c23a1b3 100644
> --- a/drivers/net/Makefile
> +++ b/drivers/net/Makefile
> @@ -198,6 +198,7 @@ obj-$(CONFIG_S2IO) += s2io.o
> 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
> diff --git a/drivers/net/bfin_mac.c b/drivers/net/bfin_mac.c
> new file mode 100644
> index 0000000..72c524f
> --- /dev/null
> +++ b/drivers/net/bfin_mac.c
> @@ -0,0 +1,1034 @@
> +/*
> + * File: drivers/net/bfin_mac.c
> + * Based on:
> + * Maintainer:
> + * Bryan Wu <[email protected]>
> + *
> + * Original author:
> + * Luke Yang <[email protected]>
> + *
> + * 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/irq.h>
> +#include <linux/io.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/dma.h>
> +#include <linux/dma-mapping.h>
> +
> +#include <asm/blackfin.h>
> +#include <asm/cacheflush.h>
> +
> +#include "bfin_mac.h"
> +
> +#undef DRV_DEBUG
> +
> +#define CARDNAME "bfin_mac"
> +
> +MODULE_LICENSE("GPL");
> +MODULE_AUTHOR("Bryan Wu, 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;
> + /* disabled */
> + tmp_desc_tx->desc_a.config.b_DMA_EN = 0;
> + /* read from memory */
> + tmp_desc_tx->desc_a.config.b_WNR = 0;
> + /* wordsize is 32 bits */
> + tmp_desc_tx->desc_a.config.b_WDSIZE = 2;
> + /* 6 half words is desc size. */
> + tmp_desc_tx->desc_a.config.b_NDSIZE = 6;
> + /* large desc flow */
> + tmp_desc_tx->desc_a.config.b_FLOW = 7;
> + 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;
> + /* enabled */
> + tmp_desc_tx->desc_b.config.b_DMA_EN = 1;
> + /* write to memory */
> + tmp_desc_tx->desc_b.config.b_WNR = 1;
> + /* wordsize is 32 bits */
> + tmp_desc_tx->desc_b.config.b_WDSIZE = 2;
> + /* disable interrupt */
> + tmp_desc_tx->desc_b.config.b_DI_EN = 0;
> + tmp_desc_tx->desc_b.config.b_NDSIZE = 6;
> + /* stop mode */
> + tmp_desc_tx->desc_b.config.b_FLOW = 7;
> + 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;
> + /* enabled */
> + tmp_desc_rx->desc_a.config.b_DMA_EN = 1;
> + /* Write to memory */
> + tmp_desc_rx->desc_a.config.b_WNR = 1;
> + /* wordsize is 32 bits */
> + tmp_desc_rx->desc_a.config.b_WDSIZE = 2;
> + /* 6 half words is desc size. */
> + tmp_desc_rx->desc_a.config.b_NDSIZE = 6;
> + /* large desc flow */
> + tmp_desc_rx->desc_a.config.b_FLOW = 7;
> + 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;
> + /* enabled */
> + tmp_desc_rx->desc_b.config.b_DMA_EN = 1;
> + /* Write to memory */
> + tmp_desc_rx->desc_b.config.b_WNR = 1;
> + /* wordsize is 32 bits */
> + tmp_desc_rx->desc_b.config.b_WDSIZE = 2;
> + tmp_desc_rx->desc_b.config.b_NDSIZE = 6;
> + /* enable interrupt */
> + tmp_desc_rx->desc_b.config.b_DI_EN = 1;
> + /* large mode */
> + tmp_desc_rx->desc_b.config.b_FLOW = 7;
> + 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 */
> +
^^^^ If this is a silicon or timing bug or something like that, please
note that in the comments. This also seems somewhat fragile. If you
have to write it twice, are you _sure_ that after the second write that
the value has been recognized by the NIC? If so, why? Put that in the
comment, or use a loop and keep writing it every so often, read the
value and break out when it's what you expect. Bound that with a
maximum number of tries.
> + 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();
> + /* read mode */
> + bfin_write_EMAC_STAADD(SET_PHYAD(PHYAddr) |
> + SET_REGAD(RegAddr) |
> + STABUSY);
> + 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);
> +
> + /* write mode */
> + bfin_write_EMAC_STAADD(SET_PHYAD(PHYAddr) |
> + SET_REGAD(RegAddr) |
> + STAOP |
> + STABUSY);
> +
> + 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 */
> +#ifdef DRV_DEBUG
> + write_phy_reg(lp->PhyAddr, PHYREG_MODECTL, phydat);
> +#endif
Please make DRV_DEBUG a config option like "BFIN_DEBUG" and just use
CONFIG_BFIN_DEBUG here instead of rolling your own.
> + }
> +
> + 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
> + */
> +
> + /* enable interrupts */
> + write_phy_reg(lp->PhyAddr, 30, 0x0ff);
> + /* enable PHY_INT */
> + sysctl = bfin_read_EMAC_SYSCTL();
> + sysctl |= 0x1;
> +#ifdef DRV_DEBUG
CONFIG_BFIN_DEBUG
> + 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\n");
> + printk(KERN_NOTICE CARDNAME
> + "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);
> +
> +#ifdef DRV_DEBUG
CONFIG_BFIN_DEBUG
> + 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
You'll want to replace CARDNAME with whatever the devicename currently
is. Makes it a lot easier to figure out what's actually having problems
at runtime.
> + ": 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;
> + /* this is important! */
> + blackfin_dcache_flush_range(data, (data + (skb->len)) + 2);
> +
> + } 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);
> + }
> +
> + /* enable this packet's dma */
> + current_tx_ptr->desc_a.config.b_DMA_EN = 1;
> +
> + 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
CARDNAME -> device name
> + ": 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;
> +
> +#ifdef DRV_DEBUG
CONFIG_BFIN_DEBUG
> + int i;
> + if (len >= 64) {
> + for (i = 0; i < len; i++) {
> + printk(KERN_DEBUG "%.2x-", ((unsigned char *)pkt)[i]);
> + if (((i % 8) == 0) && (i != 0))
> + printk(KERN_DEBUG "\n");
> + }
> + printk(KERN_DEBUG"\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
Do runtime checking of the silicon version. _Don't_ make it a compile
time option.
> + 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");
CARDNAME -> device name if you have a device name yet, otherwise
CARDNAME is fine
Dan
> + 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/mach-bf537/boards/eth_mac.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;
> +#ifdef DVR_DEBUG
> + 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;
> + }
> +
> + SET_MODULE_OWNER(ndev);
> + SET_NETDEV_DEV(ndev, &pdev->dev);
> +
> + platform_set_drvdata(pdev, ndev);
> +
> + if (bf537mac_probe(ndev) != 0) {
> + platform_set_drvdata(pdev, NULL);
> + free_netdev(ndev);
> + printk(KERN_WARNING CARDNAME ": not found\n");
> + return -ENODEV;
> + }
> +
> + 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);
> diff --git a/drivers/net/bfin_mac.h b/drivers/net/bfin_mac.h
> new file mode 100644
> index 0000000..2d5d145
> --- /dev/null
> +++ b/drivers/net/bfin_mac.h
> @@ -0,0 +1,144 @@
> +/*
> + * File: drivers/net/bfin_mac.c
> + * Based on:
> + * Maintainer:
> + * Bryan Wu <[email protected]>
> + *
> + * Original author:
> + * Luke Yang <[email protected]>
> + *
> + * 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
> +
> +struct dma_config_reg {
> + 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 */
> +};
> +
> +struct dma_descriptor {
> + struct dma_descriptor *next_dma_desc;
> + unsigned long start_addr;
> + struct dma_config_reg config;
> + unsigned short x_count;
> +};
> +
> +struct status_area_rx {
> +#if defined(BFIN_MAC_CSUM_OFFLOAD)
> + unsigned short ip_hdr_csum; /* ip header checksum */
> + /* ip payload(udp or tcp or others) checksum */
> + unsigned short ip_payload_csum;
> +#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;
> + 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;
> + unsigned char packet[1560];
> + 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);
-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
[Index of Archives]
[Kernel Newbies]
[Netfilter]
[Bugtraq]
[Photo]
[Stuff]
[Gimp]
[Yosemite News]
[MIPS Linux]
[ARM Linux]
[Linux Security]
[Linux RAID]
[Video 4 Linux]
[Linux for the blind]
[Linux Resources]