2005-04-17 02:20:36 +04:00
/*
* Fast Ethernet Controller ( FEC ) driver for Motorola MPC8xx .
* Copyright ( c ) 1997 Dan Malek ( dmalek @ jlc . net )
*
2005-09-12 05:18:10 +04:00
* Right now , I am very wasteful with the buffers . I allocate memory
2005-04-17 02:20:36 +04:00
* pages and then divide them into 2 K 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 .
*
* Much better multiple PHY support by Magnus Damm .
* Copyright ( c ) 2000 Ericsson Radio Systems AB .
*
2005-11-07 07:09:50 +03:00
* Support for FEC controller of ColdFire processors .
* Copyright ( c ) 2001 - 2005 Greg Ungerer ( gerg @ snapgear . com )
2005-09-12 05:18:10 +04:00
*
* Bug fixes and cleanup by Philippe De Muyter ( phdm @ macqel . be )
2006-06-27 07:05:33 +04:00
* Copyright ( c ) 2004 - 2006 Macq Electronique SA .
2011-01-06 00:13:13 +03:00
*
* Copyright ( C ) 2010 Freescale Semiconductor , Inc .
2005-04-17 02:20:36 +04:00
*/
# include <linux/module.h>
# include <linux/kernel.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/workqueue.h>
# include <linux/bitops.h>
2009-01-29 02:03:05 +03:00
# include <linux/io.h>
# include <linux/irq.h>
2009-01-29 02:03:10 +03:00
# include <linux/clk.h>
2009-01-29 02:03:11 +03:00
# include <linux/platform_device.h>
2010-03-31 06:10:44 +04:00
# include <linux/phy.h>
2010-05-24 11:36:13 +04:00
# include <linux/fec.h>
2005-04-17 02:20:36 +04:00
2007-07-30 10:28:46 +04:00
# include <asm/cacheflush.h>
2009-01-29 02:03:10 +03:00
2011-01-06 00:13:13 +03:00
# ifndef CONFIG_ARM
2005-04-17 02:20:36 +04:00
# include <asm/coldfire.h>
# include <asm/mcfsim.h>
2009-01-29 02:03:10 +03:00
# endif
2009-01-29 02:03:05 +03:00
2005-04-17 02:20:36 +04:00
# include "fec.h"
2011-01-17 11:52:18 +03:00
# if defined(CONFIG_ARM)
2009-01-29 02:03:10 +03:00
# define FEC_ALIGNMENT 0xf
# else
# define FEC_ALIGNMENT 0x3
# endif
2011-01-06 00:13:13 +03:00
# define DRIVER_NAME "fec"
/* Controller is ENET-MAC */
# define FEC_QUIRK_ENET_MAC (1 << 0)
/* Controller needs driver to swap frame */
# define FEC_QUIRK_SWAP_FRAME (1 << 1)
static struct platform_device_id fec_devtype [ ] = {
{
. name = DRIVER_NAME ,
. driver_data = 0 ,
} , {
. name = " imx28-fec " ,
. driver_data = FEC_QUIRK_ENET_MAC | FEC_QUIRK_SWAP_FRAME ,
2011-02-23 02:28:46 +03:00
} ,
{ }
2011-01-06 00:13:13 +03:00
} ;
2011-01-06 00:13:11 +03:00
static unsigned char macaddr [ ETH_ALEN ] ;
module_param_array ( macaddr , byte , NULL , 0 ) ;
MODULE_PARM_DESC ( macaddr , " FEC Ethernet MAC address " ) ;
2005-04-17 02:20:36 +04:00
2011-01-06 00:13:11 +03:00
# if defined(CONFIG_M5272)
2005-04-17 02:20:36 +04:00
/*
* Some hardware gets it MAC address out of local flash memory .
* if this is non - zero then assume it is the address to get MAC from .
*/
# if defined(CONFIG_NETtel)
# define FEC_FLASHMAC 0xf0006006
# elif defined(CONFIG_GILBARCONAP) || defined(CONFIG_SCALES)
# define FEC_FLASHMAC 0xf0006000
# elif defined(CONFIG_CANCam)
# define FEC_FLASHMAC 0xf0020000
2005-09-12 05:18:10 +04:00
# elif defined (CONFIG_M5272C3)
# define FEC_FLASHMAC (0xffe04000 + 4)
# elif defined(CONFIG_MOD5272)
# define FEC_FLASHMAC 0xffc0406b
2005-04-17 02:20:36 +04:00
# else
# define FEC_FLASHMAC 0
# endif
2009-02-27 09:42:51 +03:00
# endif /* CONFIG_M5272 */
2009-01-29 02:03:11 +03:00
2005-04-17 02:20:36 +04:00
/* 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 it best
* to keep them that size .
* We don ' t need to allocate pages for the transmitter . We just use
* the skbuffer directly .
*/
# define FEC_ENET_RX_PAGES 8
# define FEC_ENET_RX_FRSIZE 2048
# define FEC_ENET_RX_FRPPG (PAGE_SIZE / FEC_ENET_RX_FRSIZE)
# define RX_RING_SIZE (FEC_ENET_RX_FRPPG * FEC_ENET_RX_PAGES)
# define FEC_ENET_TX_FRSIZE 2048
# define FEC_ENET_TX_FRPPG (PAGE_SIZE / FEC_ENET_TX_FRSIZE)
# define TX_RING_SIZE 16 /* Must be power of two */
# define TX_RING_MOD_MASK 15 /* for this to work */
2005-11-07 07:09:50 +03:00
# if (((RX_RING_SIZE + TX_RING_SIZE) * 8) > PAGE_SIZE)
2006-06-27 07:10:56 +04:00
# error "FEC: descriptor ring size constants too large"
2005-11-07 07:09:50 +03:00
# endif
2009-04-15 05:32:18 +04:00
/* Interrupt events/masks. */
2005-04-17 02:20:36 +04:00
# define FEC_ENET_HBERR ((uint)0x80000000) /* Heartbeat error */
# define FEC_ENET_BABR ((uint)0x40000000) /* Babbling receiver */
# define FEC_ENET_BABT ((uint)0x20000000) /* Babbling transmitter */
# define FEC_ENET_GRA ((uint)0x10000000) /* Graceful stop complete */
# define FEC_ENET_TXF ((uint)0x08000000) /* Full frame transmitted */
# define FEC_ENET_TXB ((uint)0x04000000) /* A buffer was transmitted */
# define FEC_ENET_RXF ((uint)0x02000000) /* Full frame received */
# define FEC_ENET_RXB ((uint)0x01000000) /* A buffer was received */
# define FEC_ENET_MII ((uint)0x00800000) /* MII interrupt */
# define FEC_ENET_EBERR ((uint)0x00400000) /* SDMA bus error */
2010-07-21 06:51:13 +04:00
# define FEC_DEFAULT_IMASK (FEC_ENET_TXF | FEC_ENET_RXF | FEC_ENET_MII)
2005-04-17 02:20:36 +04:00
/* The FEC stores dest/src/type, data, and checksum for receive packets.
*/
# define PKT_MAXBUF_SIZE 1518
# define PKT_MINBUF_SIZE 64
# define PKT_MAXBLR_SIZE 1520
/*
2006-06-27 07:10:56 +04:00
* The 5270 / 5271 / 5280 / 5282 / 532 x RX control register also contains maximum frame
2005-04-17 02:20:36 +04:00
* size bits . Other FEC hardware does not , so we need to take that into
* account when setting it .
*/
2005-11-07 07:09:50 +03:00
# if defined(CONFIG_M523x) || defined(CONFIG_M527x) || defined(CONFIG_M528x) || \
2011-01-17 11:52:18 +03:00
defined ( CONFIG_M520x ) | | defined ( CONFIG_M532x ) | | defined ( CONFIG_ARM )
2005-04-17 02:20:36 +04:00
# define OPT_FRAME_SIZE (PKT_MAXBUF_SIZE << 16)
# else
# define OPT_FRAME_SIZE 0
# endif
/* The FEC 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 fec_enet_private {
/* Hardware registers of the FEC device */
2009-04-15 07:11:30 +04:00
void __iomem * hwp ;
2005-04-17 02:20:36 +04:00
2007-07-30 10:29:09 +04:00
struct net_device * netdev ;
2009-01-29 02:03:11 +03:00
struct clk * clk ;
2005-04-17 02:20:36 +04:00
/* The saved address of a sent-in-place packet/buffer, for skfree(). */
unsigned char * tx_bounce [ TX_RING_SIZE ] ;
struct sk_buff * tx_skbuff [ TX_RING_SIZE ] ;
2009-04-15 05:32:24 +04:00
struct sk_buff * rx_skbuff [ RX_RING_SIZE ] ;
2005-04-17 02:20:36 +04:00
ushort skb_cur ;
ushort skb_dirty ;
2009-04-15 05:32:18 +04:00
/* CPM dual port RAM relative addresses */
2009-01-29 02:03:07 +03:00
dma_addr_t bd_dma ;
2009-04-15 05:32:18 +04:00
/* Address of Rx and Tx buffers */
2009-04-15 05:32:16 +04:00
struct bufdesc * rx_bd_base ;
struct bufdesc * tx_bd_base ;
/* The next free ring entry */
2011-01-19 22:26:39 +03:00
struct bufdesc * cur_rx , * cur_tx ;
2009-04-15 05:32:18 +04:00
/* The ring entries to be free()ed */
2009-04-15 05:32:16 +04:00
struct bufdesc * dirty_tx ;
2005-04-17 02:20:36 +04:00
uint tx_full ;
2008-05-01 08:08:12 +04:00
/* hold while accessing the HW like ringbuffer for tx/rx but not MAC */
spinlock_t hw_lock ;
2005-04-17 02:20:36 +04:00
2011-01-19 22:26:39 +03:00
struct platform_device * pdev ;
2005-04-17 02:20:36 +04:00
2010-03-31 06:10:44 +04:00
int opened ;
2005-04-17 02:20:36 +04:00
2010-03-31 06:10:44 +04:00
/* Phylib and MDIO interface */
2011-01-19 22:26:39 +03:00
struct mii_bus * mii_bus ;
struct phy_device * phy_dev ;
int mii_timeout ;
uint phy_speed ;
2010-05-24 11:36:13 +04:00
phy_interface_t phy_interface ;
2005-04-17 02:20:36 +04:00
int link ;
int full_duplex ;
2010-07-12 01:12:51 +04:00
struct completion mdio_done ;
2005-04-17 02:20:36 +04:00
} ;
2010-03-31 06:10:44 +04:00
/* FEC MII MMFR bits definition */
# define FEC_MMFR_ST (1 << 30)
# define FEC_MMFR_OP_READ (2 << 28)
# define FEC_MMFR_OP_WRITE (1 << 28)
# define FEC_MMFR_PA(v) ((v & 0x1f) << 23)
# define FEC_MMFR_RA(v) ((v & 0x1f) << 18)
# define FEC_MMFR_TA (2 << 16)
# define FEC_MMFR_DATA(v) (v & 0xffff)
2005-04-17 02:20:36 +04:00
2010-07-12 01:12:51 +04:00
# define FEC_MII_TIMEOUT 1000 /* us */
2005-04-17 02:20:36 +04:00
2009-04-15 05:32:18 +04:00
/* Transmitter timeout */
# define TX_TIMEOUT (2 * HZ)
2005-04-17 02:20:36 +04:00
2011-01-06 00:13:13 +03:00
static void * swap_buffer ( void * bufaddr , int len )
{
int i ;
unsigned int * buf = bufaddr ;
for ( i = 0 ; i < ( len + 3 ) / 4 ; i + + , buf + + )
* buf = cpu_to_be32 ( * buf ) ;
return bufaddr ;
}
2010-06-02 13:15:47 +04:00
static netdev_tx_t
2011-01-19 13:58:12 +03:00
fec_enet_start_xmit ( struct sk_buff * skb , struct net_device * ndev )
2005-04-17 02:20:36 +04:00
{
2011-01-19 13:58:12 +03:00
struct fec_enet_private * fep = netdev_priv ( ndev ) ;
2011-01-06 00:13:13 +03:00
const struct platform_device_id * id_entry =
platform_get_device_id ( fep - > pdev ) ;
2009-04-15 05:32:16 +04:00
struct bufdesc * bdp ;
2009-08-06 21:58:18 +04:00
void * bufaddr ;
2006-06-27 07:19:33 +04:00
unsigned short status ;
2008-05-01 08:08:12 +04:00
unsigned long flags ;
2005-04-17 02:20:36 +04:00
if ( ! fep - > link ) {
/* Link is down or autonegotiation is in progress. */
2009-06-12 10:22:29 +04:00
return NETDEV_TX_BUSY ;
2005-04-17 02:20:36 +04:00
}
2008-05-01 08:08:12 +04:00
spin_lock_irqsave ( & fep - > hw_lock , flags ) ;
2005-04-17 02:20:36 +04:00
/* Fill in a Tx ring entry */
bdp = fep - > cur_tx ;
2006-06-27 07:19:33 +04:00
status = bdp - > cbd_sc ;
2009-04-15 05:32:18 +04:00
2006-06-27 07:19:33 +04:00
if ( status & BD_ENET_TX_READY ) {
2005-04-17 02:20:36 +04:00
/* Ooops. All transmit buffers are full. Bail out.
2011-01-19 13:58:12 +03:00
* This should not happen , since ndev - > tbusy should be set .
2005-04-17 02:20:36 +04:00
*/
2011-01-19 13:58:12 +03:00
printk ( " %s: tx queue full!. \n " , ndev - > name ) ;
2008-05-01 08:08:12 +04:00
spin_unlock_irqrestore ( & fep - > hw_lock , flags ) ;
2009-06-12 10:22:29 +04:00
return NETDEV_TX_BUSY ;
2005-04-17 02:20:36 +04:00
}
2009-04-15 05:32:18 +04:00
/* Clear all of the status flags */
2006-06-27 07:19:33 +04:00
status & = ~ BD_ENET_TX_STATS ;
2005-04-17 02:20:36 +04:00
2009-04-15 05:32:18 +04:00
/* Set buffer length and buffer pointer */
2009-08-06 21:58:18 +04:00
bufaddr = skb - > data ;
2005-04-17 02:20:36 +04:00
bdp - > cbd_datlen = skb - > len ;
/*
2009-04-15 05:32:18 +04:00
* On some FEC implementations data must be aligned on
* 4 - byte boundaries . Use bounce buffers to copy data
* and get it aligned . Ugh .
2005-04-17 02:20:36 +04:00
*/
2009-08-06 21:58:18 +04:00
if ( ( ( unsigned long ) bufaddr ) & FEC_ALIGNMENT ) {
2005-04-17 02:20:36 +04:00
unsigned int index ;
index = bdp - fep - > tx_bd_base ;
2011-01-13 23:34:31 +03:00
memcpy ( fep - > tx_bounce [ index ] , skb - > data , skb - > len ) ;
2009-08-06 21:58:18 +04:00
bufaddr = fep - > tx_bounce [ index ] ;
2005-04-17 02:20:36 +04:00
}
2011-01-06 00:13:13 +03:00
/*
* Some design made an incorrect assumption on endian mode of
* the system that it ' s running on . As the result , driver has to
* swap every frame going to and coming from the controller .
*/
if ( id_entry - > driver_data & FEC_QUIRK_SWAP_FRAME )
swap_buffer ( bufaddr , skb - > len ) ;
2009-04-15 05:32:18 +04:00
/* Save skb pointer */
2005-04-17 02:20:36 +04:00
fep - > tx_skbuff [ fep - > skb_cur ] = skb ;
2011-01-19 13:58:12 +03:00
ndev - > stats . tx_bytes + = skb - > len ;
2005-04-17 02:20:36 +04:00
fep - > skb_cur = ( fep - > skb_cur + 1 ) & TX_RING_MOD_MASK ;
2006-09-13 21:24:59 +04:00
2005-04-17 02:20:36 +04:00
/* Push the data cache so the CPM does not get stale memory
* data .
*/
2011-01-20 11:26:38 +03:00
bdp - > cbd_bufaddr = dma_map_single ( & fep - > pdev - > dev , bufaddr ,
2009-04-15 05:32:24 +04:00
FEC_ENET_TX_FRSIZE , DMA_TO_DEVICE ) ;
2005-04-17 02:20:36 +04:00
2006-06-27 07:19:33 +04:00
/* Send it on its way. Tell FEC it's ready, interrupt when done,
* it ' s the last BD of the frame , and to put the CRC on the end .
2005-04-17 02:20:36 +04:00
*/
2006-06-27 07:19:33 +04:00
status | = ( BD_ENET_TX_READY | BD_ENET_TX_INTR
2005-04-17 02:20:36 +04:00
| BD_ENET_TX_LAST | BD_ENET_TX_TC ) ;
2006-06-27 07:19:33 +04:00
bdp - > cbd_sc = status ;
2005-04-17 02:20:36 +04:00
/* Trigger transmission start */
2009-04-15 07:11:30 +04:00
writel ( 0 , fep - > hwp + FEC_X_DES_ACTIVE ) ;
2005-04-17 02:20:36 +04:00
2009-04-15 05:32:18 +04:00
/* If this was the last BD in the ring, start at the beginning again. */
if ( status & BD_ENET_TX_WRAP )
2005-04-17 02:20:36 +04:00
bdp = fep - > tx_bd_base ;
2009-04-15 05:32:18 +04:00
else
2005-04-17 02:20:36 +04:00
bdp + + ;
if ( bdp = = fep - > dirty_tx ) {
fep - > tx_full = 1 ;
2011-01-19 13:58:12 +03:00
netif_stop_queue ( ndev ) ;
2005-04-17 02:20:36 +04:00
}
2009-04-15 05:32:16 +04:00
fep - > cur_tx = bdp ;
2005-04-17 02:20:36 +04:00
2008-05-01 08:08:12 +04:00
spin_unlock_irqrestore ( & fep - > hw_lock , flags ) ;
2005-04-17 02:20:36 +04:00
2009-06-23 10:03:08 +04:00
return NETDEV_TX_OK ;
2005-04-17 02:20:36 +04:00
}
2011-01-19 22:47:04 +03:00
/* This function is called to start or restart the FEC during a link
* change . This only happens when switching between half and full
* duplex .
*/
2005-04-17 02:20:36 +04:00
static void
2011-01-19 22:47:04 +03:00
fec_restart ( struct net_device * ndev , int duplex )
2005-04-17 02:20:36 +04:00
{
2011-01-19 13:58:12 +03:00
struct fec_enet_private * fep = netdev_priv ( ndev ) ;
2011-01-19 22:47:04 +03:00
const struct platform_device_id * id_entry =
platform_get_device_id ( fep - > pdev ) ;
int i ;
2011-01-26 00:11:25 +03:00
u32 temp_mac [ 2 ] ;
u32 rcntl = OPT_FRAME_SIZE | 0x04 ;
2005-04-17 02:20:36 +04:00
2011-01-19 22:47:04 +03:00
/* Whack a reset. We should wait for this. */
writel ( 1 , fep - > hwp + FEC_ECNTRL ) ;
udelay ( 10 ) ;
2005-04-17 02:20:36 +04:00
2011-01-19 22:47:04 +03:00
/*
* enet - mac reset will reset mac address registers too ,
* so need to reconfigure it .
*/
if ( id_entry - > driver_data & FEC_QUIRK_ENET_MAC ) {
memcpy ( & temp_mac , ndev - > dev_addr , ETH_ALEN ) ;
writel ( cpu_to_be32 ( temp_mac [ 0 ] ) , fep - > hwp + FEC_ADDR_LOW ) ;
writel ( cpu_to_be32 ( temp_mac [ 1 ] ) , fep - > hwp + FEC_ADDR_HIGH ) ;
}
2005-04-17 02:20:36 +04:00
2011-01-19 22:47:04 +03:00
/* Clear any outstanding interrupt. */
writel ( 0xffc00000 , fep - > hwp + FEC_IEVENT ) ;
2005-04-17 02:20:36 +04:00
2011-01-19 22:47:04 +03:00
/* Reset all multicast. */
writel ( 0 , fep - > hwp + FEC_GRP_HASH_TABLE_HIGH ) ;
writel ( 0 , fep - > hwp + FEC_GRP_HASH_TABLE_LOW ) ;
# ifndef CONFIG_M5272
writel ( 0 , fep - > hwp + FEC_HASH_TABLE_HIGH ) ;
writel ( 0 , fep - > hwp + FEC_HASH_TABLE_LOW ) ;
# endif
2005-04-17 02:20:36 +04:00
2011-01-19 22:47:04 +03:00
/* Set maximum receive buffer size. */
writel ( PKT_MAXBLR_SIZE , fep - > hwp + FEC_R_BUFF_SIZE ) ;
2005-04-17 02:20:36 +04:00
2011-01-19 22:47:04 +03:00
/* Set receive and transmit descriptor base. */
writel ( fep - > bd_dma , fep - > hwp + FEC_R_DES_START ) ;
writel ( ( unsigned long ) fep - > bd_dma + sizeof ( struct bufdesc ) * RX_RING_SIZE ,
fep - > hwp + FEC_X_DES_START ) ;
fep - > dirty_tx = fep - > cur_tx = fep - > tx_bd_base ;
fep - > cur_rx = fep - > rx_bd_base ;
/* Reset SKB transmit buffers. */
fep - > skb_cur = fep - > skb_dirty = 0 ;
for ( i = 0 ; i < = TX_RING_MOD_MASK ; i + + ) {
if ( fep - > tx_skbuff [ i ] ) {
dev_kfree_skb_any ( fep - > tx_skbuff [ i ] ) ;
fep - > tx_skbuff [ i ] = NULL ;
2005-04-17 02:20:36 +04:00
}
2011-01-19 22:47:04 +03:00
}
2010-07-12 01:12:51 +04:00
2011-01-19 22:47:04 +03:00
/* Enable MII mode */
if ( duplex ) {
2011-01-26 00:11:25 +03:00
/* FD enable */
2011-01-19 22:47:04 +03:00
writel ( 0x04 , fep - > hwp + FEC_X_CNTRL ) ;
} else {
2011-01-26 00:11:25 +03:00
/* No Rcv on Xmit */
rcntl | = 0x02 ;
2011-01-19 22:47:04 +03:00
writel ( 0x0 , fep - > hwp + FEC_X_CNTRL ) ;
}
2011-01-26 00:11:25 +03:00
2011-01-19 22:47:04 +03:00
fep - > full_duplex = duplex ;
/* Set MII speed */
writel ( fep - > phy_speed , fep - > hwp + FEC_MII_SPEED ) ;
/*
* The phy interface and speed need to get configured
* differently on enet - mac .
*/
if ( id_entry - > driver_data & FEC_QUIRK_ENET_MAC ) {
2011-01-26 00:11:25 +03:00
/* Enable flow control and length check */
rcntl | = 0x40000000 | 0x00000020 ;
2011-01-19 22:47:04 +03:00
/* MII or RMII */
if ( fep - > phy_interface = = PHY_INTERFACE_MODE_RMII )
2011-01-26 00:11:25 +03:00
rcntl | = ( 1 < < 8 ) ;
2011-01-19 22:47:04 +03:00
else
2011-01-26 00:11:25 +03:00
rcntl & = ~ ( 1 < < 8 ) ;
2011-01-19 22:47:04 +03:00
/* 10M or 100M */
if ( fep - > phy_dev & & fep - > phy_dev - > speed = = SPEED_100 )
2011-01-26 00:11:25 +03:00
rcntl & = ~ ( 1 < < 9 ) ;
2011-01-19 22:47:04 +03:00
else
2011-01-26 00:11:25 +03:00
rcntl | = ( 1 < < 9 ) ;
2011-01-19 22:47:04 +03:00
} else {
# ifdef FEC_MIIGSK_ENR
if ( fep - > phy_interface = = PHY_INTERFACE_MODE_RMII ) {
/* disable the gasket and wait */
writel ( 0 , fep - > hwp + FEC_MIIGSK_ENR ) ;
while ( readl ( fep - > hwp + FEC_MIIGSK_ENR ) & 4 )
udelay ( 1 ) ;
/*
* configure the gasket :
* RMII , 50 MHz , no loopback , no echo
*/
writel ( 1 , fep - > hwp + FEC_MIIGSK_CFGR ) ;
/* re-enable the gasket */
writel ( 2 , fep - > hwp + FEC_MIIGSK_ENR ) ;
2010-07-12 01:12:51 +04:00
}
2011-01-19 22:47:04 +03:00
# endif
}
2011-01-26 00:11:25 +03:00
writel ( rcntl , fep - > hwp + FEC_R_CNTRL ) ;
2008-05-01 08:08:12 +04:00
2011-01-19 22:47:04 +03:00
/* And last, enable the transmit and receive processing */
writel ( 2 , fep - > hwp + FEC_ECNTRL ) ;
writel ( 0 , fep - > hwp + FEC_R_DES_ACTIVE ) ;
/* Enable interrupts we wish to service */
writel ( FEC_DEFAULT_IMASK , fep - > hwp + FEC_IMASK ) ;
}
static void
fec_stop ( struct net_device * ndev )
{
struct fec_enet_private * fep = netdev_priv ( ndev ) ;
/* We cannot expect a graceful transmit stop without link !!! */
if ( fep - > link ) {
writel ( 1 , fep - > hwp + FEC_X_CNTRL ) ; /* Graceful transmit stop */
udelay ( 10 ) ;
if ( ! ( readl ( fep - > hwp + FEC_IEVENT ) & FEC_ENET_GRA ) )
printk ( " fec_stop : Graceful transmit stop did not complete ! \n " ) ;
}
/* Whack a reset. We should wait for this. */
writel ( 1 , fep - > hwp + FEC_ECNTRL ) ;
udelay ( 10 ) ;
writel ( fep - > phy_speed , fep - > hwp + FEC_MII_SPEED ) ;
writel ( FEC_DEFAULT_IMASK , fep - > hwp + FEC_IMASK ) ;
2005-04-17 02:20:36 +04:00
}
2011-01-19 22:47:04 +03:00
static void
fec_timeout ( struct net_device * ndev )
{
struct fec_enet_private * fep = netdev_priv ( ndev ) ;
ndev - > stats . tx_errors + + ;
fec_restart ( ndev , fep - > full_duplex ) ;
netif_wake_queue ( ndev ) ;
}
2005-04-17 02:20:36 +04:00
static void
2011-01-19 13:58:12 +03:00
fec_enet_tx ( struct net_device * ndev )
2005-04-17 02:20:36 +04:00
{
struct fec_enet_private * fep ;
2009-04-15 05:32:16 +04:00
struct bufdesc * bdp ;
2006-06-27 07:19:33 +04:00
unsigned short status ;
2005-04-17 02:20:36 +04:00
struct sk_buff * skb ;
2011-01-19 13:58:12 +03:00
fep = netdev_priv ( ndev ) ;
2009-09-02 03:14:16 +04:00
spin_lock ( & fep - > hw_lock ) ;
2005-04-17 02:20:36 +04:00
bdp = fep - > dirty_tx ;
2006-06-27 07:19:33 +04:00
while ( ( ( status = bdp - > cbd_sc ) & BD_ENET_TX_READY ) = = 0 ) {
2009-04-15 05:32:24 +04:00
if ( bdp = = fep - > cur_tx & & fep - > tx_full = = 0 )
break ;
2011-01-20 11:26:38 +03:00
dma_unmap_single ( & fep - > pdev - > dev , bdp - > cbd_bufaddr ,
FEC_ENET_TX_FRSIZE , DMA_TO_DEVICE ) ;
2009-04-15 05:32:24 +04:00
bdp - > cbd_bufaddr = 0 ;
2005-04-17 02:20:36 +04:00
skb = fep - > tx_skbuff [ fep - > skb_dirty ] ;
/* Check for errors. */
2006-06-27 07:19:33 +04:00
if ( status & ( BD_ENET_TX_HB | BD_ENET_TX_LC |
2005-04-17 02:20:36 +04:00
BD_ENET_TX_RL | BD_ENET_TX_UN |
BD_ENET_TX_CSL ) ) {
2011-01-19 13:58:12 +03:00
ndev - > stats . tx_errors + + ;
2006-06-27 07:19:33 +04:00
if ( status & BD_ENET_TX_HB ) /* No heartbeat */
2011-01-19 13:58:12 +03:00
ndev - > stats . tx_heartbeat_errors + + ;
2006-06-27 07:19:33 +04:00
if ( status & BD_ENET_TX_LC ) /* Late collision */
2011-01-19 13:58:12 +03:00
ndev - > stats . tx_window_errors + + ;
2006-06-27 07:19:33 +04:00
if ( status & BD_ENET_TX_RL ) /* Retrans limit */
2011-01-19 13:58:12 +03:00
ndev - > stats . tx_aborted_errors + + ;
2006-06-27 07:19:33 +04:00
if ( status & BD_ENET_TX_UN ) /* Underrun */
2011-01-19 13:58:12 +03:00
ndev - > stats . tx_fifo_errors + + ;
2006-06-27 07:19:33 +04:00
if ( status & BD_ENET_TX_CSL ) /* Carrier lost */
2011-01-19 13:58:12 +03:00
ndev - > stats . tx_carrier_errors + + ;
2005-04-17 02:20:36 +04:00
} else {
2011-01-19 13:58:12 +03:00
ndev - > stats . tx_packets + + ;
2005-04-17 02:20:36 +04:00
}
2006-06-27 07:19:33 +04:00
if ( status & BD_ENET_TX_READY )
2005-04-17 02:20:36 +04:00
printk ( " HEY! Enet xmit interrupt and TX_READY. \n " ) ;
2009-04-15 05:32:18 +04:00
2005-04-17 02:20:36 +04:00
/* Deferred means some collisions occurred during transmit,
* but we eventually sent the packet OK .
*/
2006-06-27 07:19:33 +04:00
if ( status & BD_ENET_TX_DEF )
2011-01-19 13:58:12 +03:00
ndev - > stats . collisions + + ;
2006-09-13 21:24:59 +04:00
2009-04-15 05:32:18 +04:00
/* Free the sk buffer associated with this last transmit */
2005-04-17 02:20:36 +04:00
dev_kfree_skb_any ( skb ) ;
fep - > tx_skbuff [ fep - > skb_dirty ] = NULL ;
fep - > skb_dirty = ( fep - > skb_dirty + 1 ) & TX_RING_MOD_MASK ;
2006-09-13 21:24:59 +04:00
2009-04-15 05:32:18 +04:00
/* Update pointer to next buffer descriptor to be transmitted */
2006-06-27 07:19:33 +04:00
if ( status & BD_ENET_TX_WRAP )
2005-04-17 02:20:36 +04:00
bdp = fep - > tx_bd_base ;
else
bdp + + ;
2006-09-13 21:24:59 +04:00
2009-04-15 05:32:18 +04:00
/* Since we have freed up a buffer, the ring is no longer full
2005-04-17 02:20:36 +04:00
*/
if ( fep - > tx_full ) {
fep - > tx_full = 0 ;
2011-01-19 13:58:12 +03:00
if ( netif_queue_stopped ( ndev ) )
netif_wake_queue ( ndev ) ;
2005-04-17 02:20:36 +04:00
}
}
2009-04-15 05:32:16 +04:00
fep - > dirty_tx = bdp ;
2009-09-02 03:14:16 +04:00
spin_unlock ( & fep - > hw_lock ) ;
2005-04-17 02:20:36 +04:00
}
/* During a receive, the cur_rx points to the current incoming buffer.
* When we update through the ring , if the next incoming buffer has
* not been given to the system , we just set the empty indicator ,
* effectively tossing the packet .
*/
static void
2011-01-19 13:58:12 +03:00
fec_enet_rx ( struct net_device * ndev )
2005-04-17 02:20:36 +04:00
{
2011-01-19 13:58:12 +03:00
struct fec_enet_private * fep = netdev_priv ( ndev ) ;
2011-01-06 00:13:13 +03:00
const struct platform_device_id * id_entry =
platform_get_device_id ( fep - > pdev ) ;
2009-04-15 05:32:16 +04:00
struct bufdesc * bdp ;
2006-06-27 07:19:33 +04:00
unsigned short status ;
2005-04-17 02:20:36 +04:00
struct sk_buff * skb ;
ushort pkt_len ;
__u8 * data ;
2006-09-13 21:24:59 +04:00
2006-06-27 07:19:33 +04:00
# ifdef CONFIG_M532x
flush_cache_all ( ) ;
2006-09-13 21:24:59 +04:00
# endif
2005-04-17 02:20:36 +04:00
2009-09-02 03:14:16 +04:00
spin_lock ( & fep - > hw_lock ) ;
2008-05-01 08:08:12 +04:00
2005-04-17 02:20:36 +04:00
/* First, grab all of the stats for the incoming packet.
* These get messed up if we get called due to a busy condition .
*/
bdp = fep - > cur_rx ;
2009-04-15 05:32:18 +04:00
while ( ! ( ( status = bdp - > cbd_sc ) & BD_ENET_RX_EMPTY ) ) {
2005-04-17 02:20:36 +04:00
2009-04-15 05:32:18 +04:00
/* Since we have allocated space to hold a complete frame,
* the last indicator should be set .
*/
if ( ( status & BD_ENET_RX_LAST ) = = 0 )
printk ( " FEC ENET: rcv is not +last \n " ) ;
2005-04-17 02:20:36 +04:00
2009-04-15 05:32:18 +04:00
if ( ! fep - > opened )
goto rx_processing_done ;
2005-04-17 02:20:36 +04:00
2009-04-15 05:32:18 +04:00
/* Check for errors. */
if ( status & ( BD_ENET_RX_LG | BD_ENET_RX_SH | BD_ENET_RX_NO |
2005-04-17 02:20:36 +04:00
BD_ENET_RX_CR | BD_ENET_RX_OV ) ) {
2011-01-19 13:58:12 +03:00
ndev - > stats . rx_errors + + ;
2009-04-15 05:32:18 +04:00
if ( status & ( BD_ENET_RX_LG | BD_ENET_RX_SH ) ) {
/* Frame too long or too short. */
2011-01-19 13:58:12 +03:00
ndev - > stats . rx_length_errors + + ;
2009-04-15 05:32:18 +04:00
}
if ( status & BD_ENET_RX_NO ) /* Frame alignment */
2011-01-19 13:58:12 +03:00
ndev - > stats . rx_frame_errors + + ;
2009-04-15 05:32:18 +04:00
if ( status & BD_ENET_RX_CR ) /* CRC Error */
2011-01-19 13:58:12 +03:00
ndev - > stats . rx_crc_errors + + ;
2009-04-15 05:32:18 +04:00
if ( status & BD_ENET_RX_OV ) /* FIFO overrun */
2011-01-19 13:58:12 +03:00
ndev - > stats . rx_fifo_errors + + ;
2005-04-17 02:20:36 +04:00
}
2009-04-15 05:32:18 +04:00
/* Report late collisions as a frame error.
* On this error , the BD is closed , but we don ' t know what we
* have in the buffer . So , just drop this frame on the floor .
*/
if ( status & BD_ENET_RX_CL ) {
2011-01-19 13:58:12 +03:00
ndev - > stats . rx_errors + + ;
ndev - > stats . rx_frame_errors + + ;
2009-04-15 05:32:18 +04:00
goto rx_processing_done ;
}
2005-04-17 02:20:36 +04:00
2009-04-15 05:32:18 +04:00
/* Process the incoming frame. */
2011-01-19 13:58:12 +03:00
ndev - > stats . rx_packets + + ;
2009-04-15 05:32:18 +04:00
pkt_len = bdp - > cbd_datlen ;
2011-01-19 13:58:12 +03:00
ndev - > stats . rx_bytes + = pkt_len ;
2009-04-15 05:32:18 +04:00
data = ( __u8 * ) __va ( bdp - > cbd_bufaddr ) ;
2005-04-17 02:20:36 +04:00
2011-01-20 11:26:38 +03:00
dma_unmap_single ( & fep - > pdev - > dev , bdp - > cbd_bufaddr ,
FEC_ENET_TX_FRSIZE , DMA_FROM_DEVICE ) ;
2009-01-29 02:03:09 +03:00
2011-01-06 00:13:13 +03:00
if ( id_entry - > driver_data & FEC_QUIRK_SWAP_FRAME )
swap_buffer ( data , pkt_len ) ;
2009-04-15 05:32:18 +04:00
/* This does 16 byte alignment, exactly what we need.
* The packet length includes FCS , but we don ' t want to
* include that when passing upstream as it messes up
* bridging applications .
*/
2009-04-15 05:32:21 +04:00
skb = dev_alloc_skb ( pkt_len - 4 + NET_IP_ALIGN ) ;
2005-04-17 02:20:36 +04:00
2009-04-15 05:32:21 +04:00
if ( unlikely ( ! skb ) ) {
2009-04-15 05:32:18 +04:00
printk ( " %s: Memory squeeze, dropping packet. \n " ,
2011-01-19 13:58:12 +03:00
ndev - > name ) ;
ndev - > stats . rx_dropped + + ;
2009-04-15 05:32:18 +04:00
} else {
2009-04-15 05:32:21 +04:00
skb_reserve ( skb , NET_IP_ALIGN ) ;
2009-04-15 05:32:18 +04:00
skb_put ( skb , pkt_len - 4 ) ; /* Make room */
skb_copy_to_linear_data ( skb , data , pkt_len - 4 ) ;
2011-01-19 13:58:12 +03:00
skb - > protocol = eth_type_trans ( skb , ndev ) ;
2009-04-15 05:32:18 +04:00
netif_rx ( skb ) ;
}
2009-04-15 05:32:24 +04:00
2011-01-20 11:26:38 +03:00
bdp - > cbd_bufaddr = dma_map_single ( & fep - > pdev - > dev , data ,
FEC_ENET_TX_FRSIZE , DMA_FROM_DEVICE ) ;
2009-04-15 05:32:18 +04:00
rx_processing_done :
/* Clear the status flags for this buffer */
status & = ~ BD_ENET_RX_STATS ;
2005-04-17 02:20:36 +04:00
2009-04-15 05:32:18 +04:00
/* Mark the buffer empty */
status | = BD_ENET_RX_EMPTY ;
bdp - > cbd_sc = status ;
2006-09-13 21:24:59 +04:00
2009-04-15 05:32:18 +04:00
/* Update BD pointer to next entry */
if ( status & BD_ENET_RX_WRAP )
bdp = fep - > rx_bd_base ;
else
bdp + + ;
/* Doing this here will keep the FEC running while we process
* incoming frames . On a heavily loaded network , we should be
* able to keep up at the expense of system resources .
*/
writel ( 0 , fep - > hwp + FEC_R_DES_ACTIVE ) ;
}
2009-04-15 05:32:16 +04:00
fep - > cur_rx = bdp ;
2005-04-17 02:20:36 +04:00
2009-09-02 03:14:16 +04:00
spin_unlock ( & fep - > hw_lock ) ;
2005-04-17 02:20:36 +04:00
}
2011-01-19 22:47:04 +03:00
static irqreturn_t
fec_enet_interrupt ( int irq , void * dev_id )
{
struct net_device * ndev = dev_id ;
struct fec_enet_private * fep = netdev_priv ( ndev ) ;
uint int_events ;
irqreturn_t ret = IRQ_NONE ;
do {
int_events = readl ( fep - > hwp + FEC_IEVENT ) ;
writel ( int_events , fep - > hwp + FEC_IEVENT ) ;
if ( int_events & FEC_ENET_RXF ) {
ret = IRQ_HANDLED ;
fec_enet_rx ( ndev ) ;
}
/* Transmit OK, or non-fatal error. Update the buffer
* descriptors . FEC handles all errors , we just discover
* them as part of the transmit process .
*/
if ( int_events & FEC_ENET_TXF ) {
ret = IRQ_HANDLED ;
fec_enet_tx ( ndev ) ;
}
if ( int_events & FEC_ENET_MII ) {
ret = IRQ_HANDLED ;
complete ( & fep - > mdio_done ) ;
}
} while ( int_events ) ;
return ret ;
}
2010-03-31 06:10:44 +04:00
/* ------------------------------------------------------------------------- */
2011-01-19 13:58:12 +03:00
static void __inline__ fec_get_mac ( struct net_device * ndev )
2005-04-17 02:20:36 +04:00
{
2011-01-19 13:58:12 +03:00
struct fec_enet_private * fep = netdev_priv ( ndev ) ;
2011-01-06 00:13:11 +03:00
struct fec_platform_data * pdata = fep - > pdev - > dev . platform_data ;
2010-03-31 06:10:44 +04:00
unsigned char * iap , tmpaddr [ ETH_ALEN ] ;
2005-04-17 02:20:36 +04:00
2011-01-06 00:13:11 +03:00
/*
* try to get mac address in following order :
*
* 1 ) module parameter via kernel command line in form
* fec . macaddr = 0x00 , 0x04 , 0x9f , 0x01 , 0x30 , 0xe0
*/
iap = macaddr ;
/*
* 2 ) from flash or fuse ( via platform data )
*/
if ( ! is_valid_ether_addr ( iap ) ) {
# ifdef CONFIG_M5272
if ( FEC_FLASHMAC )
iap = ( unsigned char * ) FEC_FLASHMAC ;
# else
if ( pdata )
memcpy ( iap , pdata - > mac , ETH_ALEN ) ;
# endif
}
/*
* 3 ) FEC mac registers set by bootloader
*/
if ( ! is_valid_ether_addr ( iap ) ) {
* ( ( unsigned long * ) & tmpaddr [ 0 ] ) =
be32_to_cpu ( readl ( fep - > hwp + FEC_ADDR_LOW ) ) ;
* ( ( unsigned short * ) & tmpaddr [ 4 ] ) =
be16_to_cpu ( readl ( fep - > hwp + FEC_ADDR_HIGH ) > > 16 ) ;
2010-03-31 06:10:44 +04:00
iap = & tmpaddr [ 0 ] ;
2005-04-17 02:20:36 +04:00
}
2011-01-19 13:58:12 +03:00
memcpy ( ndev - > dev_addr , iap , ETH_ALEN ) ;
2005-04-17 02:20:36 +04:00
2011-01-06 00:13:11 +03:00
/* Adjust MAC if using macaddr */
if ( iap = = macaddr )
2011-01-19 13:58:12 +03:00
ndev - > dev_addr [ ETH_ALEN - 1 ] = macaddr [ ETH_ALEN - 1 ] + fep - > pdev - > id ;
2005-04-17 02:20:36 +04:00
}
2010-03-31 06:10:44 +04:00
/* ------------------------------------------------------------------------- */
2005-04-17 02:20:36 +04:00
2010-03-31 06:10:44 +04:00
/*
* Phy section
*/
2011-01-19 13:58:12 +03:00
static void fec_enet_adjust_link ( struct net_device * ndev )
2005-04-17 02:20:36 +04:00
{
2011-01-19 13:58:12 +03:00
struct fec_enet_private * fep = netdev_priv ( ndev ) ;
2010-03-31 06:10:44 +04:00
struct phy_device * phy_dev = fep - > phy_dev ;
unsigned long flags ;
2005-04-17 02:20:36 +04:00
2010-03-31 06:10:44 +04:00
int status_change = 0 ;
2005-04-17 02:20:36 +04:00
2010-03-31 06:10:44 +04:00
spin_lock_irqsave ( & fep - > hw_lock , flags ) ;
2005-04-17 02:20:36 +04:00
2010-03-31 06:10:44 +04:00
/* Prevent a state halted on mii error */
if ( fep - > mii_timeout & & phy_dev - > state = = PHY_HALTED ) {
phy_dev - > state = PHY_RESUMING ;
goto spin_unlock ;
}
2005-04-17 02:20:36 +04:00
2010-03-31 06:10:44 +04:00
/* Duplex link change */
if ( phy_dev - > link ) {
if ( fep - > full_duplex ! = phy_dev - > duplex ) {
2011-01-19 13:58:12 +03:00
fec_restart ( ndev , phy_dev - > duplex ) ;
2010-03-31 06:10:44 +04:00
status_change = 1 ;
}
}
2005-04-17 02:20:36 +04:00
2010-03-31 06:10:44 +04:00
/* Link on or off change */
if ( phy_dev - > link ! = fep - > link ) {
fep - > link = phy_dev - > link ;
if ( phy_dev - > link )
2011-01-19 13:58:12 +03:00
fec_restart ( ndev , phy_dev - > duplex ) ;
2005-04-17 02:20:36 +04:00
else
2011-01-19 13:58:12 +03:00
fec_stop ( ndev ) ;
2010-03-31 06:10:44 +04:00
status_change = 1 ;
2005-04-17 02:20:36 +04:00
}
2006-09-13 21:24:59 +04:00
2010-03-31 06:10:44 +04:00
spin_unlock :
spin_unlock_irqrestore ( & fep - > hw_lock , flags ) ;
2005-04-17 02:20:36 +04:00
2010-03-31 06:10:44 +04:00
if ( status_change )
phy_print_status ( phy_dev ) ;
}
2005-04-17 02:20:36 +04:00
2010-03-31 06:10:44 +04:00
static int fec_enet_mdio_read ( struct mii_bus * bus , int mii_id , int regnum )
2005-04-17 02:20:36 +04:00
{
2010-03-31 06:10:44 +04:00
struct fec_enet_private * fep = bus - > priv ;
2010-07-12 01:12:51 +04:00
unsigned long time_left ;
2005-04-17 02:20:36 +04:00
2010-03-31 06:10:44 +04:00
fep - > mii_timeout = 0 ;
2010-07-12 01:12:51 +04:00
init_completion ( & fep - > mdio_done ) ;
2010-03-31 06:10:44 +04:00
/* start a read op */
writel ( FEC_MMFR_ST | FEC_MMFR_OP_READ |
FEC_MMFR_PA ( mii_id ) | FEC_MMFR_RA ( regnum ) |
FEC_MMFR_TA , fep - > hwp + FEC_MII_DATA ) ;
/* wait for end of transfer */
2010-07-12 01:12:51 +04:00
time_left = wait_for_completion_timeout ( & fep - > mdio_done ,
usecs_to_jiffies ( FEC_MII_TIMEOUT ) ) ;
if ( time_left = = 0 ) {
fep - > mii_timeout = 1 ;
printk ( KERN_ERR " FEC: MDIO read timeout \n " ) ;
return - ETIMEDOUT ;
2005-04-17 02:20:36 +04:00
}
2010-03-31 06:10:44 +04:00
/* return value */
return FEC_MMFR_DATA ( readl ( fep - > hwp + FEC_MII_DATA ) ) ;
2005-09-12 05:18:10 +04:00
}
2006-09-13 21:24:59 +04:00
2010-03-31 06:10:44 +04:00
static int fec_enet_mdio_write ( struct mii_bus * bus , int mii_id , int regnum ,
u16 value )
2005-04-17 02:20:36 +04:00
{
2010-03-31 06:10:44 +04:00
struct fec_enet_private * fep = bus - > priv ;
2010-07-12 01:12:51 +04:00
unsigned long time_left ;
2005-04-17 02:20:36 +04:00
2010-03-31 06:10:44 +04:00
fep - > mii_timeout = 0 ;
2010-07-12 01:12:51 +04:00
init_completion ( & fep - > mdio_done ) ;
2005-04-17 02:20:36 +04:00
2011-01-06 00:13:09 +03:00
/* start a write op */
writel ( FEC_MMFR_ST | FEC_MMFR_OP_WRITE |
2010-03-31 06:10:44 +04:00
FEC_MMFR_PA ( mii_id ) | FEC_MMFR_RA ( regnum ) |
FEC_MMFR_TA | FEC_MMFR_DATA ( value ) ,
fep - > hwp + FEC_MII_DATA ) ;
/* wait for end of transfer */
2010-07-12 01:12:51 +04:00
time_left = wait_for_completion_timeout ( & fep - > mdio_done ,
usecs_to_jiffies ( FEC_MII_TIMEOUT ) ) ;
if ( time_left = = 0 ) {
fep - > mii_timeout = 1 ;
printk ( KERN_ERR " FEC: MDIO write timeout \n " ) ;
return - ETIMEDOUT ;
2010-03-31 06:10:44 +04:00
}
2005-04-17 02:20:36 +04:00
2010-03-31 06:10:44 +04:00
return 0 ;
}
2005-04-17 02:20:36 +04:00
2010-03-31 06:10:44 +04:00
static int fec_enet_mdio_reset ( struct mii_bus * bus )
2005-04-17 02:20:36 +04:00
{
2010-03-31 06:10:44 +04:00
return 0 ;
2005-04-17 02:20:36 +04:00
}
2011-01-19 13:58:12 +03:00
static int fec_enet_mii_probe ( struct net_device * ndev )
2005-11-07 07:09:50 +03:00
{
2011-01-19 13:58:12 +03:00
struct fec_enet_private * fep = netdev_priv ( ndev ) ;
2010-03-31 06:10:44 +04:00
struct phy_device * phy_dev = NULL ;
2010-10-12 01:03:05 +04:00
char mdio_bus_id [ MII_BUS_ID_SIZE ] ;
char phy_name [ MII_BUS_ID_SIZE + 3 ] ;
int phy_id ;
2011-01-06 00:13:13 +03:00
int dev_id = fep - > pdev - > id ;
2005-11-07 07:09:50 +03:00
2010-05-28 14:40:39 +04:00
fep - > phy_dev = NULL ;
2010-10-12 01:03:05 +04:00
/* check for attached phy */
for ( phy_id = 0 ; ( phy_id < PHY_MAX_ADDR ) ; phy_id + + ) {
if ( ( fep - > mii_bus - > phy_mask & ( 1 < < phy_id ) ) )
continue ;
if ( fep - > mii_bus - > phy_map [ phy_id ] = = NULL )
continue ;
if ( fep - > mii_bus - > phy_map [ phy_id ] - > phy_id = = 0 )
continue ;
2011-01-06 00:13:13 +03:00
if ( dev_id - - )
continue ;
2010-10-12 01:03:05 +04:00
strncpy ( mdio_bus_id , fep - > mii_bus - > id , MII_BUS_ID_SIZE ) ;
break ;
2010-03-31 06:10:44 +04:00
}
2005-04-17 02:20:36 +04:00
2010-10-12 01:03:05 +04:00
if ( phy_id > = PHY_MAX_ADDR ) {
printk ( KERN_INFO " %s: no PHY, assuming direct connection "
2011-01-19 13:58:12 +03:00
" to switch \n " , ndev - > name ) ;
2010-10-12 01:03:05 +04:00
strncpy ( mdio_bus_id , " 0 " , MII_BUS_ID_SIZE ) ;
phy_id = 0 ;
}
snprintf ( phy_name , MII_BUS_ID_SIZE , PHY_ID_FMT , mdio_bus_id , phy_id ) ;
2011-01-19 13:58:12 +03:00
phy_dev = phy_connect ( ndev , phy_name , & fec_enet_adjust_link , 0 ,
2010-10-12 01:03:05 +04:00
PHY_INTERFACE_MODE_MII ) ;
if ( IS_ERR ( phy_dev ) ) {
2011-01-19 13:58:12 +03:00
printk ( KERN_ERR " %s: could not attach to PHY \n " , ndev - > name ) ;
2010-10-12 01:03:05 +04:00
return PTR_ERR ( phy_dev ) ;
2010-03-31 06:10:44 +04:00
}
2005-04-17 02:20:36 +04:00
2010-03-31 06:10:44 +04:00
/* mask with MAC supported features */
phy_dev - > supported & = PHY_BASIC_FEATURES ;
phy_dev - > advertising = phy_dev - > supported ;
2005-04-17 02:20:36 +04:00
2010-03-31 06:10:44 +04:00
fep - > phy_dev = phy_dev ;
fep - > link = 0 ;
fep - > full_duplex = 0 ;
2005-04-17 02:20:36 +04:00
2010-05-28 14:40:39 +04:00
printk ( KERN_INFO " %s: Freescale FEC PHY driver [%s] "
2011-01-19 13:58:12 +03:00
" (mii_bus:phy_addr=%s, irq=%d) \n " , ndev - > name ,
2010-05-28 14:40:39 +04:00
fep - > phy_dev - > drv - > name , dev_name ( & fep - > phy_dev - > dev ) ,
fep - > phy_dev - > irq ) ;
2010-03-31 06:10:44 +04:00
return 0 ;
2005-04-17 02:20:36 +04:00
}
2010-03-31 06:10:44 +04:00
static int fec_enet_mii_init ( struct platform_device * pdev )
2005-11-07 07:09:50 +03:00
{
2011-01-06 00:13:13 +03:00
static struct mii_bus * fec0_mii_bus ;
2011-01-19 13:58:12 +03:00
struct net_device * ndev = platform_get_drvdata ( pdev ) ;
struct fec_enet_private * fep = netdev_priv ( ndev ) ;
2011-01-06 00:13:13 +03:00
const struct platform_device_id * id_entry =
platform_get_device_id ( fep - > pdev ) ;
2010-03-31 06:10:44 +04:00
int err = - ENXIO , i ;
2006-06-27 07:10:56 +04:00
2011-01-06 00:13:13 +03:00
/*
* The dual fec interfaces are not equivalent with enet - mac .
* Here are the differences :
*
* - fec0 supports MII & RMII modes while fec1 only supports RMII
* - fec0 acts as the 1588 time master while fec1 is slave
* - external phys can only be configured by fec0
*
* That is to say fec1 can not work independently . It only works
* when fec0 is working . The reason behind this design is that the
* second interface is added primarily for Switch mode .
*
* Because of the last point above , both phys are attached on fec0
* mdio interface in board design , and need to be configured by
* fec0 mii_bus .
*/
if ( ( id_entry - > driver_data & FEC_QUIRK_ENET_MAC ) & & pdev - > id ) {
/* fec1 uses fec0 mii_bus */
fep - > mii_bus = fec0_mii_bus ;
return 0 ;
}
2010-03-31 06:10:44 +04:00
fep - > mii_timeout = 0 ;
2005-04-17 02:20:36 +04:00
2010-03-31 06:10:44 +04:00
/*
* Set MII speed to 2.5 MHz ( = clk_get_rate ( ) / 2 * phy_speed )
*/
fep - > phy_speed = DIV_ROUND_UP ( clk_get_rate ( fep - > clk ) , 5000000 ) < < 1 ;
writel ( fep - > phy_speed , fep - > hwp + FEC_MII_SPEED ) ;
2005-04-17 02:20:36 +04:00
2010-03-31 06:10:44 +04:00
fep - > mii_bus = mdiobus_alloc ( ) ;
if ( fep - > mii_bus = = NULL ) {
err = - ENOMEM ;
goto err_out ;
2005-04-17 02:20:36 +04:00
}
2010-03-31 06:10:44 +04:00
fep - > mii_bus - > name = " fec_enet_mii_bus " ;
fep - > mii_bus - > read = fec_enet_mdio_read ;
fep - > mii_bus - > write = fec_enet_mdio_write ;
fep - > mii_bus - > reset = fec_enet_mdio_reset ;
2010-10-12 01:03:05 +04:00
snprintf ( fep - > mii_bus - > id , MII_BUS_ID_SIZE , " %x " , pdev - > id + 1 ) ;
2010-03-31 06:10:44 +04:00
fep - > mii_bus - > priv = fep ;
fep - > mii_bus - > parent = & pdev - > dev ;
fep - > mii_bus - > irq = kmalloc ( sizeof ( int ) * PHY_MAX_ADDR , GFP_KERNEL ) ;
if ( ! fep - > mii_bus - > irq ) {
err = - ENOMEM ;
goto err_out_free_mdiobus ;
2005-04-17 02:20:36 +04:00
}
2010-03-31 06:10:44 +04:00
for ( i = 0 ; i < PHY_MAX_ADDR ; i + + )
fep - > mii_bus - > irq [ i ] = PHY_POLL ;
2005-04-17 02:20:36 +04:00
2010-03-31 06:10:44 +04:00
if ( mdiobus_register ( fep - > mii_bus ) )
goto err_out_free_mdio_irq ;
2005-04-17 02:20:36 +04:00
2011-01-06 00:13:13 +03:00
/* save fec0 mii_bus */
if ( id_entry - > driver_data & FEC_QUIRK_ENET_MAC )
fec0_mii_bus = fep - > mii_bus ;
2010-03-31 06:10:44 +04:00
return 0 ;
2005-04-17 02:20:36 +04:00
2010-03-31 06:10:44 +04:00
err_out_free_mdio_irq :
kfree ( fep - > mii_bus - > irq ) ;
err_out_free_mdiobus :
mdiobus_free ( fep - > mii_bus ) ;
err_out :
return err ;
2005-04-17 02:20:36 +04:00
}
2010-03-31 06:10:44 +04:00
static void fec_enet_mii_remove ( struct fec_enet_private * fep )
2005-04-17 02:20:36 +04:00
{
2010-03-31 06:10:44 +04:00
if ( fep - > phy_dev )
phy_disconnect ( fep - > phy_dev ) ;
mdiobus_unregister ( fep - > mii_bus ) ;
kfree ( fep - > mii_bus - > irq ) ;
mdiobus_free ( fep - > mii_bus ) ;
2005-04-17 02:20:36 +04:00
}
2011-01-19 13:58:12 +03:00
static int fec_enet_get_settings ( struct net_device * ndev ,
2010-03-31 06:10:44 +04:00
struct ethtool_cmd * cmd )
2005-04-17 02:20:36 +04:00
{
2011-01-19 13:58:12 +03:00
struct fec_enet_private * fep = netdev_priv ( ndev ) ;
2010-03-31 06:10:44 +04:00
struct phy_device * phydev = fep - > phy_dev ;
2005-04-17 02:20:36 +04:00
2010-03-31 06:10:44 +04:00
if ( ! phydev )
return - ENODEV ;
2005-04-17 02:20:36 +04:00
2010-03-31 06:10:44 +04:00
return phy_ethtool_gset ( phydev , cmd ) ;
2005-04-17 02:20:36 +04:00
}
2011-01-19 13:58:12 +03:00
static int fec_enet_set_settings ( struct net_device * ndev ,
2010-03-31 06:10:44 +04:00
struct ethtool_cmd * cmd )
2005-04-17 02:20:36 +04:00
{
2011-01-19 13:58:12 +03:00
struct fec_enet_private * fep = netdev_priv ( ndev ) ;
2010-03-31 06:10:44 +04:00
struct phy_device * phydev = fep - > phy_dev ;
2005-04-17 02:20:36 +04:00
2010-03-31 06:10:44 +04:00
if ( ! phydev )
return - ENODEV ;
2005-04-17 02:20:36 +04:00
2010-03-31 06:10:44 +04:00
return phy_ethtool_sset ( phydev , cmd ) ;
2005-04-17 02:20:36 +04:00
}
2011-01-19 13:58:12 +03:00
static void fec_enet_get_drvinfo ( struct net_device * ndev ,
2010-03-31 06:10:44 +04:00
struct ethtool_drvinfo * info )
2005-04-17 02:20:36 +04:00
{
2011-01-19 13:58:12 +03:00
struct fec_enet_private * fep = netdev_priv ( ndev ) ;
2006-09-13 21:24:59 +04:00
2010-03-31 06:10:44 +04:00
strcpy ( info - > driver , fep - > pdev - > dev . driver - > name ) ;
strcpy ( info - > version , " Revision: 1.0 " ) ;
2011-01-19 13:58:12 +03:00
strcpy ( info - > bus_info , dev_name ( & ndev - > dev ) ) ;
2005-04-17 02:20:36 +04:00
}
2010-03-31 06:10:44 +04:00
static struct ethtool_ops fec_enet_ethtool_ops = {
. get_settings = fec_enet_get_settings ,
. set_settings = fec_enet_set_settings ,
. get_drvinfo = fec_enet_get_drvinfo ,
. get_link = ethtool_op_get_link ,
} ;
2005-04-17 02:20:36 +04:00
2011-01-19 13:58:12 +03:00
static int fec_enet_ioctl ( struct net_device * ndev , struct ifreq * rq , int cmd )
2005-04-17 02:20:36 +04:00
{
2011-01-19 13:58:12 +03:00
struct fec_enet_private * fep = netdev_priv ( ndev ) ;
2010-03-31 06:10:44 +04:00
struct phy_device * phydev = fep - > phy_dev ;
2005-04-17 02:20:36 +04:00
2011-01-19 13:58:12 +03:00
if ( ! netif_running ( ndev ) )
2010-03-31 06:10:44 +04:00
return - EINVAL ;
2005-04-17 02:20:36 +04:00
2010-03-31 06:10:44 +04:00
if ( ! phydev )
return - ENODEV ;
2010-07-17 12:48:55 +04:00
return phy_mii_ioctl ( phydev , rq , cmd ) ;
2005-04-17 02:20:36 +04:00
}
2011-01-19 13:58:12 +03:00
static void fec_enet_free_buffers ( struct net_device * ndev )
2009-04-15 05:32:24 +04:00
{
2011-01-19 13:58:12 +03:00
struct fec_enet_private * fep = netdev_priv ( ndev ) ;
2009-04-15 05:32:24 +04:00
int i ;
struct sk_buff * skb ;
struct bufdesc * bdp ;
bdp = fep - > rx_bd_base ;
for ( i = 0 ; i < RX_RING_SIZE ; i + + ) {
skb = fep - > rx_skbuff [ i ] ;
if ( bdp - > cbd_bufaddr )
2011-01-20 11:26:38 +03:00
dma_unmap_single ( & fep - > pdev - > dev , bdp - > cbd_bufaddr ,
2009-04-15 05:32:24 +04:00
FEC_ENET_RX_FRSIZE , DMA_FROM_DEVICE ) ;
if ( skb )
dev_kfree_skb ( skb ) ;
bdp + + ;
}
bdp = fep - > tx_bd_base ;
for ( i = 0 ; i < TX_RING_SIZE ; i + + )
kfree ( fep - > tx_bounce [ i ] ) ;
}
2011-01-19 13:58:12 +03:00
static int fec_enet_alloc_buffers ( struct net_device * ndev )
2009-04-15 05:32:24 +04:00
{
2011-01-19 13:58:12 +03:00
struct fec_enet_private * fep = netdev_priv ( ndev ) ;
2009-04-15 05:32:24 +04:00
int i ;
struct sk_buff * skb ;
struct bufdesc * bdp ;
bdp = fep - > rx_bd_base ;
for ( i = 0 ; i < RX_RING_SIZE ; i + + ) {
skb = dev_alloc_skb ( FEC_ENET_RX_FRSIZE ) ;
if ( ! skb ) {
2011-01-19 13:58:12 +03:00
fec_enet_free_buffers ( ndev ) ;
2009-04-15 05:32:24 +04:00
return - ENOMEM ;
}
fep - > rx_skbuff [ i ] = skb ;
2011-01-20 11:26:38 +03:00
bdp - > cbd_bufaddr = dma_map_single ( & fep - > pdev - > dev , skb - > data ,
2009-04-15 05:32:24 +04:00
FEC_ENET_RX_FRSIZE , DMA_FROM_DEVICE ) ;
bdp - > cbd_sc = BD_ENET_RX_EMPTY ;
bdp + + ;
}
/* Set the last buffer to wrap. */
bdp - - ;
bdp - > cbd_sc | = BD_SC_WRAP ;
bdp = fep - > tx_bd_base ;
for ( i = 0 ; i < TX_RING_SIZE ; i + + ) {
fep - > tx_bounce [ i ] = kmalloc ( FEC_ENET_TX_FRSIZE , GFP_KERNEL ) ;
bdp - > cbd_sc = 0 ;
bdp - > cbd_bufaddr = 0 ;
bdp + + ;
}
/* Set the last buffer to wrap. */
bdp - - ;
bdp - > cbd_sc | = BD_SC_WRAP ;
return 0 ;
}
2005-04-17 02:20:36 +04:00
static int
2011-01-19 13:58:12 +03:00
fec_enet_open ( struct net_device * ndev )
2005-04-17 02:20:36 +04:00
{
2011-01-19 13:58:12 +03:00
struct fec_enet_private * fep = netdev_priv ( ndev ) ;
2009-04-15 05:32:24 +04:00
int ret ;
2005-04-17 02:20:36 +04:00
/* I should reset the ring buffers here, but I don't yet know
* a simple way to do that .
*/
2011-01-19 13:58:12 +03:00
ret = fec_enet_alloc_buffers ( ndev ) ;
2009-04-15 05:32:24 +04:00
if ( ret )
return ret ;
2010-05-28 14:40:39 +04:00
/* Probe and connect to PHY when open the interface */
2011-01-19 13:58:12 +03:00
ret = fec_enet_mii_probe ( ndev ) ;
2010-05-28 14:40:39 +04:00
if ( ret ) {
2011-01-19 13:58:12 +03:00
fec_enet_free_buffers ( ndev ) ;
2010-05-28 14:40:39 +04:00
return ret ;
}
2010-03-31 06:10:44 +04:00
phy_start ( fep - > phy_dev ) ;
2011-01-19 13:58:12 +03:00
netif_start_queue ( ndev ) ;
2005-04-17 02:20:36 +04:00
fep - > opened = 1 ;
2009-04-15 05:32:18 +04:00
return 0 ;
2005-04-17 02:20:36 +04:00
}
static int
2011-01-19 13:58:12 +03:00
fec_enet_close ( struct net_device * ndev )
2005-04-17 02:20:36 +04:00
{
2011-01-19 13:58:12 +03:00
struct fec_enet_private * fep = netdev_priv ( ndev ) ;
2005-04-17 02:20:36 +04:00
2009-04-15 05:32:18 +04:00
/* Don't know what to do yet. */
2005-04-17 02:20:36 +04:00
fep - > opened = 0 ;
2011-01-19 13:58:12 +03:00
netif_stop_queue ( ndev ) ;
fec_stop ( ndev ) ;
2005-04-17 02:20:36 +04:00
2011-01-17 22:04:23 +03:00
if ( fep - > phy_dev ) {
phy_stop ( fep - > phy_dev ) ;
2010-05-28 14:40:39 +04:00
phy_disconnect ( fep - > phy_dev ) ;
2011-01-17 22:04:23 +03:00
}
2010-05-28 14:40:39 +04:00
2011-01-19 22:26:39 +03:00
fec_enet_free_buffers ( ndev ) ;
2009-04-15 05:32:24 +04:00
2005-04-17 02:20:36 +04:00
return 0 ;
}
/* Set or clear the multicast filter for this adaptor.
* Skeleton taken from sunlance driver .
* The CPM Ethernet implementation allows Multicast as well as individual
* MAC address filtering . Some of the drivers check to make sure it is
* a group multicast address , and discard those that are not . I guess I
* will do the same for now , but just remove the test if you want
* individual filtering as well ( do the upper net layers want or support
* this kind of feature ? ) .
*/
# define HASH_BITS 6 /* #bits in hash */
# define CRC32_POLY 0xEDB88320
2011-01-19 13:58:12 +03:00
static void set_multicast_list ( struct net_device * ndev )
2005-04-17 02:20:36 +04:00
{
2011-01-19 13:58:12 +03:00
struct fec_enet_private * fep = netdev_priv ( ndev ) ;
2010-04-02 01:22:57 +04:00
struct netdev_hw_addr * ha ;
2010-02-22 12:22:26 +03:00
unsigned int i , bit , data , crc , tmp ;
2005-04-17 02:20:36 +04:00
unsigned char hash ;
2011-01-19 13:58:12 +03:00
if ( ndev - > flags & IFF_PROMISC ) {
2009-04-15 07:11:30 +04:00
tmp = readl ( fep - > hwp + FEC_R_CNTRL ) ;
tmp | = 0x8 ;
writel ( tmp , fep - > hwp + FEC_R_CNTRL ) ;
2009-04-15 05:32:19 +04:00
return ;
}
2005-04-17 02:20:36 +04:00
2009-04-15 05:32:19 +04:00
tmp = readl ( fep - > hwp + FEC_R_CNTRL ) ;
tmp & = ~ 0x8 ;
writel ( tmp , fep - > hwp + FEC_R_CNTRL ) ;
2011-01-19 13:58:12 +03:00
if ( ndev - > flags & IFF_ALLMULTI ) {
2009-04-15 05:32:19 +04:00
/* Catch all multicast addresses, so set the
* filter to all 1 ' s
*/
writel ( 0xffffffff , fep - > hwp + FEC_GRP_HASH_TABLE_HIGH ) ;
writel ( 0xffffffff , fep - > hwp + FEC_GRP_HASH_TABLE_LOW ) ;
return ;
}
/* Clear filter and add the addresses in hash register
*/
writel ( 0 , fep - > hwp + FEC_GRP_HASH_TABLE_HIGH ) ;
writel ( 0 , fep - > hwp + FEC_GRP_HASH_TABLE_LOW ) ;
2011-01-19 13:58:12 +03:00
netdev_for_each_mc_addr ( ha , ndev ) {
2009-04-15 05:32:19 +04:00
/* Only support group multicast for now */
2010-04-02 01:22:57 +04:00
if ( ! ( ha - > addr [ 0 ] & 1 ) )
2009-04-15 05:32:19 +04:00
continue ;
/* calculate crc32 value of mac address */
crc = 0xffffffff ;
2011-01-19 13:58:12 +03:00
for ( i = 0 ; i < ndev - > addr_len ; i + + ) {
2010-04-02 01:22:57 +04:00
data = ha - > addr [ i ] ;
2009-04-15 05:32:19 +04:00
for ( bit = 0 ; bit < 8 ; bit + + , data > > = 1 ) {
crc = ( crc > > 1 ) ^
( ( ( crc ^ data ) & 1 ) ? CRC32_POLY : 0 ) ;
2005-04-17 02:20:36 +04:00
}
}
2009-04-15 05:32:19 +04:00
/* only upper 6 bits (HASH_BITS) are used
* which point to specific bit in he hash registers
*/
hash = ( crc > > ( 32 - HASH_BITS ) ) & 0x3f ;
if ( hash > 31 ) {
tmp = readl ( fep - > hwp + FEC_GRP_HASH_TABLE_HIGH ) ;
tmp | = 1 < < ( hash - 32 ) ;
writel ( tmp , fep - > hwp + FEC_GRP_HASH_TABLE_HIGH ) ;
} else {
tmp = readl ( fep - > hwp + FEC_GRP_HASH_TABLE_LOW ) ;
tmp | = 1 < < hash ;
writel ( tmp , fep - > hwp + FEC_GRP_HASH_TABLE_LOW ) ;
}
2005-04-17 02:20:36 +04:00
}
}
2009-04-15 05:32:18 +04:00
/* Set a MAC change in hardware. */
2009-04-15 05:32:23 +04:00
static int
2011-01-19 13:58:12 +03:00
fec_set_mac_address ( struct net_device * ndev , void * p )
2005-04-17 02:20:36 +04:00
{
2011-01-19 13:58:12 +03:00
struct fec_enet_private * fep = netdev_priv ( ndev ) ;
2009-04-15 05:32:23 +04:00
struct sockaddr * addr = p ;
if ( ! is_valid_ether_addr ( addr - > sa_data ) )
return - EADDRNOTAVAIL ;
2011-01-19 13:58:12 +03:00
memcpy ( ndev - > dev_addr , addr - > sa_data , ndev - > addr_len ) ;
2005-04-17 02:20:36 +04:00
2011-01-19 13:58:12 +03:00
writel ( ndev - > dev_addr [ 3 ] | ( ndev - > dev_addr [ 2 ] < < 8 ) |
( ndev - > dev_addr [ 1 ] < < 16 ) | ( ndev - > dev_addr [ 0 ] < < 24 ) ,
2009-04-15 07:11:30 +04:00
fep - > hwp + FEC_ADDR_LOW ) ;
2011-01-19 13:58:12 +03:00
writel ( ( ndev - > dev_addr [ 5 ] < < 16 ) | ( ndev - > dev_addr [ 4 ] < < 24 ) ,
2010-05-05 11:55:48 +04:00
fep - > hwp + FEC_ADDR_HIGH ) ;
2009-04-15 05:32:23 +04:00
return 0 ;
2005-04-17 02:20:36 +04:00
}
2009-04-15 05:32:23 +04:00
static const struct net_device_ops fec_netdev_ops = {
. ndo_open = fec_enet_open ,
. ndo_stop = fec_enet_close ,
. ndo_start_xmit = fec_enet_start_xmit ,
. ndo_set_multicast_list = set_multicast_list ,
2009-07-09 21:59:01 +04:00
. ndo_change_mtu = eth_change_mtu ,
2009-04-15 05:32:23 +04:00
. ndo_validate_addr = eth_validate_addr ,
. ndo_tx_timeout = fec_timeout ,
. ndo_set_mac_address = fec_set_mac_address ,
2011-01-19 22:26:39 +03:00
. ndo_do_ioctl = fec_enet_ioctl ,
2009-04-15 05:32:23 +04:00
} ;
2005-04-17 02:20:36 +04:00
/*
* XXX : We need to clean up on failure exits here .
2009-01-29 02:03:11 +03:00
*
2005-04-17 02:20:36 +04:00
*/
2011-01-19 13:58:12 +03:00
static int fec_enet_init ( struct net_device * ndev )
2005-04-17 02:20:36 +04:00
{
2011-01-19 13:58:12 +03:00
struct fec_enet_private * fep = netdev_priv ( ndev ) ;
2009-04-15 05:32:24 +04:00
struct bufdesc * cbd_base ;
2010-02-05 11:56:20 +03:00
struct bufdesc * bdp ;
2009-04-15 05:32:24 +04:00
int i ;
2005-04-17 02:20:36 +04:00
2009-04-15 05:32:17 +04:00
/* Allocate memory for buffer descriptors. */
cbd_base = dma_alloc_coherent ( NULL , PAGE_SIZE , & fep - > bd_dma ,
GFP_KERNEL ) ;
if ( ! cbd_base ) {
2005-11-07 07:09:50 +03:00
printk ( " FEC: allocate descriptor memory failed? \n " ) ;
return - ENOMEM ;
}
2008-05-01 08:08:12 +04:00
spin_lock_init ( & fep - > hw_lock ) ;
2011-01-19 13:58:12 +03:00
fep - > netdev = ndev ;
2005-04-17 02:20:36 +04:00
2011-01-06 00:13:11 +03:00
/* Get the Ethernet address */
2011-01-19 13:58:12 +03:00
fec_get_mac ( ndev ) ;
2005-04-17 02:20:36 +04:00
2009-04-15 05:32:17 +04:00
/* Set receive and transmit descriptor base. */
2005-04-17 02:20:36 +04:00
fep - > rx_bd_base = cbd_base ;
fep - > tx_bd_base = cbd_base + RX_RING_SIZE ;
2009-04-15 05:32:18 +04:00
/* The FEC Ethernet specific entries in the device structure */
2011-01-19 13:58:12 +03:00
ndev - > watchdog_timeo = TX_TIMEOUT ;
ndev - > netdev_ops = & fec_netdev_ops ;
ndev - > ethtool_ops = & fec_enet_ethtool_ops ;
2010-02-05 11:56:20 +03:00
/* Initialize the receive buffer descriptors. */
bdp = fep - > rx_bd_base ;
for ( i = 0 ; i < RX_RING_SIZE ; i + + ) {
/* Initialize the BD for every fragment in the page. */
bdp - > cbd_sc = 0 ;
bdp + + ;
}
/* Set the last buffer to wrap */
bdp - - ;
bdp - > cbd_sc | = BD_SC_WRAP ;
/* ...and the same for transmit */
bdp = fep - > tx_bd_base ;
for ( i = 0 ; i < TX_RING_SIZE ; i + + ) {
/* Initialize the BD for every fragment in the page. */
bdp - > cbd_sc = 0 ;
bdp - > cbd_bufaddr = 0 ;
bdp + + ;
}
/* Set the last buffer to wrap */
bdp - - ;
bdp - > cbd_sc | = BD_SC_WRAP ;
2011-01-19 13:58:12 +03:00
fec_restart ( ndev , 0 ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
2009-01-29 02:03:11 +03:00
static int __devinit
fec_probe ( struct platform_device * pdev )
{
struct fec_enet_private * fep ;
2010-05-24 11:36:13 +04:00
struct fec_platform_data * pdata ;
2009-01-29 02:03:11 +03:00
struct net_device * ndev ;
int i , irq , ret = 0 ;
struct resource * r ;
r = platform_get_resource ( pdev , IORESOURCE_MEM , 0 ) ;
if ( ! r )
return - ENXIO ;
r = request_mem_region ( r - > start , resource_size ( r ) , pdev - > name ) ;
if ( ! r )
return - EBUSY ;
/* Init network device */
ndev = alloc_etherdev ( sizeof ( struct fec_enet_private ) ) ;
2011-01-13 23:44:18 +03:00
if ( ! ndev ) {
ret = - ENOMEM ;
goto failed_alloc_etherdev ;
}
2009-01-29 02:03:11 +03:00
SET_NETDEV_DEV ( ndev , & pdev - > dev ) ;
/* setup board info structure */
fep = netdev_priv ( ndev ) ;
2011-01-14 00:29:05 +03:00
fep - > hwp = ioremap ( r - > start , resource_size ( r ) ) ;
2010-03-31 06:10:44 +04:00
fep - > pdev = pdev ;
2009-01-29 02:03:11 +03:00
2011-01-14 00:29:05 +03:00
if ( ! fep - > hwp ) {
2009-01-29 02:03:11 +03:00
ret = - ENOMEM ;
goto failed_ioremap ;
}
platform_set_drvdata ( pdev , ndev ) ;
2010-05-24 11:36:13 +04:00
pdata = pdev - > dev . platform_data ;
if ( pdata )
fep - > phy_interface = pdata - > phy ;
2009-01-29 02:03:11 +03:00
/* This device has up to three irqs on some platforms */
for ( i = 0 ; i < 3 ; i + + ) {
irq = platform_get_irq ( pdev , i ) ;
if ( i & & irq < 0 )
break ;
ret = request_irq ( irq , fec_enet_interrupt , IRQF_DISABLED , pdev - > name , ndev ) ;
if ( ret ) {
2011-01-13 23:49:05 +03:00
while ( - - i > = 0 ) {
2009-01-29 02:03:11 +03:00
irq = platform_get_irq ( pdev , i ) ;
free_irq ( irq , ndev ) ;
}
goto failed_irq ;
}
}
fep - > clk = clk_get ( & pdev - > dev , " fec_clk " ) ;
if ( IS_ERR ( fep - > clk ) ) {
ret = PTR_ERR ( fep - > clk ) ;
goto failed_clk ;
}
clk_enable ( fep - > clk ) ;
2011-01-06 00:13:10 +03:00
ret = fec_enet_init ( ndev ) ;
2009-01-29 02:03:11 +03:00
if ( ret )
goto failed_init ;
2010-03-31 06:10:44 +04:00
ret = fec_enet_mii_init ( pdev ) ;
if ( ret )
goto failed_mii_init ;
2010-10-07 06:30:30 +04:00
/* Carrier starts down, phylib will bring it up */
netif_carrier_off ( ndev ) ;
2009-01-29 02:03:11 +03:00
ret = register_netdev ( ndev ) ;
if ( ret )
goto failed_register ;
return 0 ;
failed_register :
2010-03-31 06:10:44 +04:00
fec_enet_mii_remove ( fep ) ;
failed_mii_init :
2009-01-29 02:03:11 +03:00
failed_init :
clk_disable ( fep - > clk ) ;
clk_put ( fep - > clk ) ;
failed_clk :
for ( i = 0 ; i < 3 ; i + + ) {
irq = platform_get_irq ( pdev , i ) ;
if ( irq > 0 )
free_irq ( irq , ndev ) ;
}
failed_irq :
2011-01-14 00:29:05 +03:00
iounmap ( fep - > hwp ) ;
2009-01-29 02:03:11 +03:00
failed_ioremap :
free_netdev ( ndev ) ;
2011-01-13 23:44:18 +03:00
failed_alloc_etherdev :
release_mem_region ( r - > start , resource_size ( r ) ) ;
2009-01-29 02:03:11 +03:00
return ret ;
}
static int __devexit
fec_drv_remove ( struct platform_device * pdev )
{
struct net_device * ndev = platform_get_drvdata ( pdev ) ;
struct fec_enet_private * fep = netdev_priv ( ndev ) ;
2011-01-13 23:44:18 +03:00
struct resource * r ;
2009-01-29 02:03:11 +03:00
fec_stop ( ndev ) ;
2010-03-31 06:10:44 +04:00
fec_enet_mii_remove ( fep ) ;
2009-01-29 02:03:11 +03:00
clk_disable ( fep - > clk ) ;
clk_put ( fep - > clk ) ;
2011-01-14 00:29:05 +03:00
iounmap ( fep - > hwp ) ;
2009-01-29 02:03:11 +03:00
unregister_netdev ( ndev ) ;
free_netdev ( ndev ) ;
2011-01-13 23:44:18 +03:00
r = platform_get_resource ( pdev , IORESOURCE_MEM , 0 ) ;
BUG_ON ( ! r ) ;
release_mem_region ( r - > start , resource_size ( r ) ) ;
2011-01-25 20:03:37 +03:00
platform_set_drvdata ( pdev , NULL ) ;
2009-01-29 02:03:11 +03:00
return 0 ;
}
2010-06-02 13:27:04 +04:00
# ifdef CONFIG_PM
2009-01-29 02:03:11 +03:00
static int
2010-06-18 08:19:54 +04:00
fec_suspend ( struct device * dev )
2009-01-29 02:03:11 +03:00
{
2010-06-18 08:19:54 +04:00
struct net_device * ndev = dev_get_drvdata ( dev ) ;
2011-01-13 23:53:40 +03:00
struct fec_enet_private * fep = netdev_priv ( ndev ) ;
2009-01-29 02:03:11 +03:00
2011-01-13 23:53:40 +03:00
if ( netif_running ( ndev ) ) {
fec_stop ( ndev ) ;
netif_device_detach ( ndev ) ;
2009-01-29 02:03:11 +03:00
}
2011-01-13 23:53:40 +03:00
clk_disable ( fep - > clk ) ;
2009-01-29 02:03:11 +03:00
return 0 ;
}
static int
2010-06-18 08:19:54 +04:00
fec_resume ( struct device * dev )
2009-01-29 02:03:11 +03:00
{
2010-06-18 08:19:54 +04:00
struct net_device * ndev = dev_get_drvdata ( dev ) ;
2011-01-13 23:53:40 +03:00
struct fec_enet_private * fep = netdev_priv ( ndev ) ;
2009-01-29 02:03:11 +03:00
2011-01-13 23:53:40 +03:00
clk_enable ( fep - > clk ) ;
if ( netif_running ( ndev ) ) {
fec_restart ( ndev , fep - > full_duplex ) ;
netif_device_attach ( ndev ) ;
2009-01-29 02:03:11 +03:00
}
2011-01-13 23:53:40 +03:00
2009-01-29 02:03:11 +03:00
return 0 ;
}
2010-06-02 13:27:04 +04:00
static const struct dev_pm_ops fec_pm_ops = {
. suspend = fec_suspend ,
. resume = fec_resume ,
. freeze = fec_suspend ,
. thaw = fec_resume ,
. poweroff = fec_suspend ,
. restore = fec_resume ,
} ;
2010-06-18 08:19:54 +04:00
# endif
2010-06-02 13:27:04 +04:00
2009-01-29 02:03:11 +03:00
static struct platform_driver fec_driver = {
. driver = {
2011-01-06 00:13:13 +03:00
. name = DRIVER_NAME ,
2010-06-18 08:19:54 +04:00
. owner = THIS_MODULE ,
# ifdef CONFIG_PM
. pm = & fec_pm_ops ,
# endif
2009-01-29 02:03:11 +03:00
} ,
2011-01-06 00:13:13 +03:00
. id_table = fec_devtype ,
2010-06-18 08:19:54 +04:00
. probe = fec_probe ,
. remove = __devexit_p ( fec_drv_remove ) ,
2009-01-29 02:03:11 +03:00
} ;
static int __init
fec_enet_module_init ( void )
{
printk ( KERN_INFO " FEC Ethernet Driver \n " ) ;
return platform_driver_register ( & fec_driver ) ;
}
static void __exit
fec_enet_cleanup ( void )
{
platform_driver_unregister ( & fec_driver ) ;
}
module_exit ( fec_enet_cleanup ) ;
2005-04-17 02:20:36 +04:00
module_init ( fec_enet_module_init ) ;
MODULE_LICENSE ( " GPL " ) ;