📄 fcc_enet.c
字号:
/* * Fast Ethernet Controller (FCC) driver for Motorola MPC8260. * Copyright (c) 2000 MontaVista Software, Inc. Dan Malek (dmalek@jlc.net) * * This version of the driver is a combination of the 8xx fec and * 8260 SCC Ethernet drivers. This version has some additional * configuration options, which should probably be moved out of * here. This driver currently works for the EST SBC8260, * SBS Diablo/BCM, Embedded Planet RPX6, TQM8260, and others. * * Right now, I am very watseful with the buffers. I allocate memory * pages and then divide them into 2K frame buffers. This way I know I * have buffers large enough to hold one frame within one buffer descriptor. * Once I get this working, I will use 64 or 128 byte CPM buffers, which * will be much more memory efficient and will easily handle lots of * small packets. Since this is a cache coherent processor and CPM, * I could also preallocate SKB's and use them directly on the interface. * * 2004-12 Leo Li (leoli@freescale.com) * - Rework the FCC clock configuration part, make it easier to configure. * */#include <linux/config.h>#include <linux/kernel.h>#include <linux/sched.h>#include <linux/string.h>#include <linux/ptrace.h>#include <linux/errno.h>#include <linux/ioport.h>#include <linux/slab.h>#include <linux/interrupt.h>#include <linux/pci.h>#include <linux/init.h>#include <linux/delay.h>#include <linux/netdevice.h>#include <linux/etherdevice.h>#include <linux/skbuff.h>#include <linux/spinlock.h>#include <linux/mii.h>#include <linux/workqueue.h>#include <linux/bitops.h>#include <asm/immap_cpm2.h>#include <asm/pgtable.h>#include <asm/mpc8260.h>#include <asm/irq.h>#include <asm/uaccess.h>#include <asm/signal.h>/* We can't use the PHY interrupt if we aren't using MDIO. */#if !defined(CONFIG_USE_MDIO)#undef PHY_INTERRUPT#endif/* If we have a PHY interrupt, we will advertise both full-duplex and half- * duplex capabilities. If we don't have a PHY interrupt, then we will only * advertise half-duplex capabilities. */#define MII_ADVERTISE_HALF (ADVERTISE_100HALF | ADVERTISE_10HALF | \ ADVERTISE_CSMA)#define MII_ADVERTISE_ALL (ADVERTISE_100FULL | ADVERTISE_10FULL | \ MII_ADVERTISE_HALF)#ifdef PHY_INTERRUPT#define MII_ADVERTISE_DEFAULT MII_ADVERTISE_ALL#else#define MII_ADVERTISE_DEFAULT MII_ADVERTISE_HALF#endif#include <asm/cpm2.h>/* The transmitter timeout */#define TX_TIMEOUT (2*HZ)#ifdef CONFIG_USE_MDIO/* Forward declarations of some structures to support different PHYs */typedef struct { uint mii_data; void (*funct)(uint mii_reg, struct net_device *dev);} phy_cmd_t;typedef struct { uint id; char *name; const phy_cmd_t *config; const phy_cmd_t *startup; const phy_cmd_t *ack_int; const phy_cmd_t *shutdown;} phy_info_t;/* values for phy_status */#define PHY_CONF_ANE 0x0001 /* 1 auto-negotiation enabled */#define PHY_CONF_LOOP 0x0002 /* 1 loopback mode enabled */#define PHY_CONF_SPMASK 0x00f0 /* mask for speed */#define PHY_CONF_10HDX 0x0010 /* 10 Mbit half duplex supported */#define PHY_CONF_10FDX 0x0020 /* 10 Mbit full duplex supported */#define PHY_CONF_100HDX 0x0040 /* 100 Mbit half duplex supported */#define PHY_CONF_100FDX 0x0080 /* 100 Mbit full duplex supported */#define PHY_STAT_LINK 0x0100 /* 1 up - 0 down */#define PHY_STAT_FAULT 0x0200 /* 1 remote fault */#define PHY_STAT_ANC 0x0400 /* 1 auto-negotiation complete */#define PHY_STAT_SPMASK 0xf000 /* mask for speed */#define PHY_STAT_10HDX 0x1000 /* 10 Mbit half duplex selected */#define PHY_STAT_10FDX 0x2000 /* 10 Mbit full duplex selected */#define PHY_STAT_100HDX 0x4000 /* 100 Mbit half duplex selected */#define PHY_STAT_100FDX 0x8000 /* 100 Mbit full duplex selected */#endif /* CONFIG_USE_MDIO *//* The number of Tx and Rx buffers. These are allocated from the page * pool. The code may assume these are power of two, so it is best * to keep them that size. * We don't need to allocate pages for the transmitter. We just use * the skbuffer directly. */#define FCC_ENET_RX_PAGES 16#define FCC_ENET_RX_FRSIZE 2048#define FCC_ENET_RX_FRPPG (PAGE_SIZE / FCC_ENET_RX_FRSIZE)#define RX_RING_SIZE (FCC_ENET_RX_FRPPG * FCC_ENET_RX_PAGES)#define TX_RING_SIZE 16 /* Must be power of two */#define TX_RING_MOD_MASK 15 /* for this to work *//* The FCC stores dest/src/type, data, and checksum for receive packets. * size includes support for VLAN */#define PKT_MAXBUF_SIZE 1522#define PKT_MINBUF_SIZE 64/* Maximum input DMA size. Must be a should(?) be a multiple of 4. * size includes support for VLAN */#define PKT_MAXDMA_SIZE 1524/* Maximum input buffer size. Must be a multiple of 32.*/#define PKT_MAXBLR_SIZE 1536static int fcc_enet_open(struct net_device *dev);static int fcc_enet_start_xmit(struct sk_buff *skb, struct net_device *dev);static int fcc_enet_rx(struct net_device *dev);static irqreturn_t fcc_enet_interrupt(int irq, void *dev_id, struct pt_regs *);static int fcc_enet_close(struct net_device *dev);static struct net_device_stats *fcc_enet_get_stats(struct net_device *dev);/* static void set_multicast_list(struct net_device *dev); */static void fcc_restart(struct net_device *dev, int duplex);static void fcc_stop(struct net_device *dev);static int fcc_enet_set_mac_address(struct net_device *dev, void *addr);/* These will be configurable for the FCC choice. * Multiple ports can be configured. There is little choice among the * I/O pins to the PHY, except the clocks. We will need some board * dependent clock selection. * Why in the hell did I put these inside #ifdef's? I dunno, maybe to * help show what pins are used for each device. *//* Since the CLK setting changes greatly from board to board, I changed * it to a easy way. You just need to specify which CLK number to use. * Note that only limited choices can be make on each port. *//* FCC1 Clock Source Configuration. There are board specific. Can only choose from CLK9-12 */#ifdef CONFIG_SBC82xx#define F1_RXCLK 9#define F1_TXCLK 10#elif defined(CONFIG_ADS8272)#define F1_RXCLK 11#define F1_TXCLK 10#else#define F1_RXCLK 12#define F1_TXCLK 11#endif/* FCC2 Clock Source Configuration. There are board specific. Can only choose from CLK13-16 */#ifdef CONFIG_ADS8272#define F2_RXCLK 15#define F2_TXCLK 16#else#define F2_RXCLK 13#define F2_TXCLK 14#endif/* FCC3 Clock Source Configuration. There are board specific. Can only choose from CLK13-16 */#define F3_RXCLK 15#define F3_TXCLK 16/* Automatically generates register configurations */#define PC_CLK(x) ((uint)(1<<(x-1))) /* FCC CLK I/O ports */#define CMXFCR_RF1CS(x) ((uint)((x-5)<<27)) /* FCC1 Receive Clock Source */#define CMXFCR_TF1CS(x) ((uint)((x-5)<<24)) /* FCC1 Transmit Clock Source */#define CMXFCR_RF2CS(x) ((uint)((x-9)<<19)) /* FCC2 Receive Clock Source */#define CMXFCR_TF2CS(x) ((uint)((x-9)<<16)) /* FCC2 Transmit Clock Source */#define CMXFCR_RF3CS(x) ((uint)((x-9)<<11)) /* FCC3 Receive Clock Source */#define CMXFCR_TF3CS(x) ((uint)((x-9)<<8)) /* FCC3 Transmit Clock Source */#define PC_F1RXCLK PC_CLK(F1_RXCLK)#define PC_F1TXCLK PC_CLK(F1_TXCLK)#define CMX1_CLK_ROUTE (CMXFCR_RF1CS(F1_RXCLK) | CMXFCR_TF1CS(F1_TXCLK))#define CMX1_CLK_MASK ((uint)0xff000000)#define PC_F2RXCLK PC_CLK(F2_RXCLK)#define PC_F2TXCLK PC_CLK(F2_TXCLK)#define CMX2_CLK_ROUTE (CMXFCR_RF2CS(F2_RXCLK) | CMXFCR_TF2CS(F2_TXCLK))#define CMX2_CLK_MASK ((uint)0x00ff0000)#define PC_F3RXCLK PC_CLK(F3_RXCLK)#define PC_F3TXCLK PC_CLK(F3_TXCLK)#define CMX3_CLK_ROUTE (CMXFCR_RF3CS(F3_RXCLK) | CMXFCR_TF3CS(F3_TXCLK))#define CMX3_CLK_MASK ((uint)0x0000ff00)/* I/O Pin assignment for FCC1. I don't yet know the best way to do this, * but there is little variation among the choices. */#define PA1_COL ((uint)0x00000001)#define PA1_CRS ((uint)0x00000002)#define PA1_TXER ((uint)0x00000004)#define PA1_TXEN ((uint)0x00000008)#define PA1_RXDV ((uint)0x00000010)#define PA1_RXER ((uint)0x00000020)#define PA1_TXDAT ((uint)0x00003c00)#define PA1_RXDAT ((uint)0x0003c000)#define PA1_PSORA_BOUT (PA1_RXDAT | PA1_TXDAT)#define PA1_PSORA_BIN (PA1_COL | PA1_CRS | PA1_TXER | PA1_TXEN | \ PA1_RXDV | PA1_RXER)#define PA1_DIRA_BOUT (PA1_RXDAT | PA1_CRS | PA1_COL | PA1_RXER | PA1_RXDV)#define PA1_DIRA_BIN (PA1_TXDAT | PA1_TXEN | PA1_TXER)/* I/O Pin assignment for FCC2. I don't yet know the best way to do this, * but there is little variation among the choices. */#define PB2_TXER ((uint)0x00000001)#define PB2_RXDV ((uint)0x00000002)#define PB2_TXEN ((uint)0x00000004)#define PB2_RXER ((uint)0x00000008)#define PB2_COL ((uint)0x00000010)#define PB2_CRS ((uint)0x00000020)#define PB2_TXDAT ((uint)0x000003c0)#define PB2_RXDAT ((uint)0x00003c00)#define PB2_PSORB_BOUT (PB2_RXDAT | PB2_TXDAT | PB2_CRS | PB2_COL | \ PB2_RXER | PB2_RXDV | PB2_TXER)#define PB2_PSORB_BIN (PB2_TXEN)#define PB2_DIRB_BOUT (PB2_RXDAT | PB2_CRS | PB2_COL | PB2_RXER | PB2_RXDV)#define PB2_DIRB_BIN (PB2_TXDAT | PB2_TXEN | PB2_TXER)/* I/O Pin assignment for FCC3. I don't yet know the best way to do this, * but there is little variation among the choices. */#define PB3_RXDV ((uint)0x00004000)#define PB3_RXER ((uint)0x00008000)#define PB3_TXER ((uint)0x00010000)#define PB3_TXEN ((uint)0x00020000)#define PB3_COL ((uint)0x00040000)#define PB3_CRS ((uint)0x00080000)#ifndef CONFIG_RPX8260#define PB3_TXDAT ((uint)0x0f000000)#define PC3_TXDAT ((uint)0x00000000)#else#define PB3_TXDAT ((uint)0x0f000000)#define PC3_TXDAT 0#endif#define PB3_RXDAT ((uint)0x00f00000)#define PB3_PSORB_BOUT (PB3_RXDAT | PB3_TXDAT | PB3_CRS | PB3_COL | \ PB3_RXER | PB3_RXDV | PB3_TXER | PB3_TXEN)#define PB3_PSORB_BIN (0)#define PB3_DIRB_BOUT (PB3_RXDAT | PB3_CRS | PB3_COL | PB3_RXER | PB3_RXDV)#define PB3_DIRB_BIN (PB3_TXDAT | PB3_TXEN | PB3_TXER)#define PC3_PSORC_BOUT (PC3_TXDAT)#define PC3_PSORC_BIN (0)#define PC3_DIRC_BOUT (0)#define PC3_DIRC_BIN (PC3_TXDAT)/* MII status/control serial interface.*/#if defined(CONFIG_RPX8260)/* The EP8260 doesn't use Port C for MDIO */#define PC_MDIO ((uint)0x00000000)#define PC_MDCK ((uint)0x00000000)#elif defined(CONFIG_TQM8260)/* TQM8260 has MDIO and MDCK on PC30 and PC31 respectively */#define PC_MDIO ((uint)0x00000002)#define PC_MDCK ((uint)0x00000001)#elif defined(CONFIG_ADS8272)#define PC_MDIO ((uint)0x00002000)#define PC_MDCK ((uint)0x00001000)#elif defined(CONFIG_EST8260) || defined(CONFIG_ADS8260) || defined(CONFIG_PQ2FADS)#define PC_MDIO ((uint)0x00400000)#define PC_MDCK ((uint)0x00200000)#else#define PC_MDIO ((uint)0x00000004)#define PC_MDCK ((uint)0x00000020)#endif#if defined(CONFIG_USE_MDIO) && (!defined(PC_MDIO) || !defined(PC_MDCK))#error "Must define PC_MDIO and PC_MDCK if using MDIO"#endif/* PHY addresses *//* default to dynamic config of phy addresses */#define FCC1_PHY_ADDR 0#ifdef CONFIG_PQ2FADS#define FCC2_PHY_ADDR 0#else#define FCC2_PHY_ADDR 2#endif#define FCC3_PHY_ADDR 3/* A table of information for supporting FCCs. This does two things. * First, we know how many FCCs we have and they are always externally * numbered from zero. Second, it holds control register and I/O * information that could be different among board designs. */typedef struct fcc_info { uint fc_fccnum; uint fc_phyaddr; uint fc_cpmblock; uint fc_cpmpage; uint fc_proff; uint fc_interrupt; uint fc_trxclocks; uint fc_clockroute; uint fc_clockmask; uint fc_mdio; uint fc_mdck;} fcc_info_t;static fcc_info_t fcc_ports[] = {#ifdef CONFIG_FCC1_ENET { 0, FCC1_PHY_ADDR, CPM_CR_FCC1_SBLOCK, CPM_CR_FCC1_PAGE, PROFF_FCC1, SIU_INT_FCC1, (PC_F1RXCLK | PC_F1TXCLK), CMX1_CLK_ROUTE, CMX1_CLK_MASK, PC_MDIO, PC_MDCK },#endif#ifdef CONFIG_FCC2_ENET { 1, FCC2_PHY_ADDR, CPM_CR_FCC2_SBLOCK, CPM_CR_FCC2_PAGE, PROFF_FCC2, SIU_INT_FCC2, (PC_F2RXCLK | PC_F2TXCLK), CMX2_CLK_ROUTE, CMX2_CLK_MASK, PC_MDIO, PC_MDCK },#endif#ifdef CONFIG_FCC3_ENET { 2, FCC3_PHY_ADDR, CPM_CR_FCC3_SBLOCK, CPM_CR_FCC3_PAGE, PROFF_FCC3, SIU_INT_FCC3, (PC_F3RXCLK | PC_F3TXCLK), CMX3_CLK_ROUTE, CMX3_CLK_MASK, PC_MDIO, PC_MDCK },#endif};/* The FCC buffer descriptors track the ring buffers. The rx_bd_base and * tx_bd_base always point to the base of the buffer descriptors. The * cur_rx and cur_tx point to the currently available buffer. * The dirty_tx tracks the current buffer that is being sent by the * controller. The cur_tx and dirty_tx are equal under both completely * empty and completely full conditions. The empty/ready indicator in * the buffer descriptor determines the actual condition. */struct fcc_enet_private { /* The saved address of a sent-in-place packet/buffer, for skfree(). */ struct sk_buff* tx_skbuff[TX_RING_SIZE]; ushort skb_cur; ushort skb_dirty; /* CPM dual port RAM relative addresses. */ cbd_t *rx_bd_base; /* Address of Rx and Tx buffers. */ cbd_t *tx_bd_base; cbd_t *cur_rx, *cur_tx; /* The next free ring entry */ cbd_t *dirty_tx; /* The ring entries to be free()ed. */ volatile fcc_t *fccp; volatile fcc_enet_t *ep; struct net_device_stats stats; uint tx_free; spinlock_t lock;#ifdef CONFIG_USE_MDIO uint phy_id; uint phy_id_done; uint phy_status; phy_info_t *phy; struct work_struct phy_relink; struct work_struct phy_display_config; uint sequence_done; uint phy_addr;#endif /* CONFIG_USE_MDIO */ int link; int old_link; int full_duplex; fcc_info_t *fip;};static void init_fcc_shutdown(fcc_info_t *fip, struct fcc_enet_private *cep, volatile cpm2_map_t *immap);static void init_fcc_startup(fcc_info_t *fip, struct net_device *dev);static void init_fcc_ioports(fcc_info_t *fip, volatile iop_cpm2_t *io, volatile cpm2_map_t *immap);static void init_fcc_param(fcc_info_t *fip, struct net_device *dev, volatile cpm2_map_t *immap);#ifdef CONFIG_USE_MDIOstatic int mii_queue(struct net_device *dev, int request, void (*func)(uint, struct net_device *));static uint mii_send_receive(fcc_info_t *fip, uint cmd);static void mii_do_cmd(struct net_device *dev, const phy_cmd_t *c);/* Make MII read/write commands for the FCC.*/#define mk_mii_read(REG) (0x60020000 | (((REG) & 0x1f) << 18))#define mk_mii_write(REG, VAL) (0x50020000 | (((REG) & 0x1f) << 18) | \ ((VAL) & 0xffff))#define mk_mii_end 0#endif /* CONFIG_USE_MDIO */static intfcc_enet_start_xmit(struct sk_buff *skb, struct net_device *dev){ struct fcc_enet_private *cep = (struct fcc_enet_private *)dev->priv; volatile cbd_t *bdp; /* Fill in a Tx ring entry */ bdp = cep->cur_tx;#ifndef final_version if (!cep->tx_free || (bdp->cbd_sc & BD_ENET_TX_READY)) { /* Ooops. All transmit buffers are full. Bail out. * This should not happen, since the tx queue should be stopped. */ printk("%s: tx queue full!.\n", dev->name); return 1; }#endif /* Clear all of the status flags. */ bdp->cbd_sc &= ~BD_ENET_TX_STATS; /* If the frame is short, tell CPM to pad it. */ if (skb->len <= ETH_ZLEN) bdp->cbd_sc |= BD_ENET_TX_PAD; else bdp->cbd_sc &= ~BD_ENET_TX_PAD; /* Set buffer length and buffer pointer. */ bdp->cbd_datlen = skb->len; bdp->cbd_bufaddr = __pa(skb->data); spin_lock_irq(&cep->lock); /* Save skb pointer. */ cep->tx_skbuff[cep->skb_cur] = skb; cep->stats.tx_bytes += skb->len; cep->skb_cur = (cep->skb_cur+1) & TX_RING_MOD_MASK; /* Send it on its way. Tell CPM its ready, interrupt when done, * its the last BD of the frame, and to put the CRC on the end. */ bdp->cbd_sc |= (BD_ENET_TX_READY | BD_ENET_TX_INTR | BD_ENET_TX_LAST | BD_ENET_TX_TC);#if 0 /* Errata says don't do this. */ cep->fccp->fcc_ftodr = 0x8000;#endif dev->trans_start = jiffies; /* If this was the last BD in the ring, start at the beginning again. */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -