2005-10-29 00:25:58 +04:00
/*
* Freescale Ethernet controllers
*
2007-09-18 20:05:35 +04:00
* Copyright ( c ) 2005 Intracom S . A .
2005-10-29 00:25:58 +04:00
* by Pantelis Antoniou < panto @ intracom . gr >
*
2007-09-18 20:05:35 +04:00
* 2005 ( c ) MontaVista Software , Inc .
2005-10-29 00:25:58 +04:00
* Vitaly Bordug < vbordug @ ru . mvista . com >
*
2007-09-18 20:05:35 +04:00
* This file is licensed under the terms of the GNU General Public License
* version 2. This program is licensed " as is " without any warranty of any
2005-10-29 00:25:58 +04:00
* kind , whether express or implied .
*/
# include <linux/module.h>
# include <linux/kernel.h>
# include <linux/types.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/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/ethtool.h>
# include <linux/bitops.h>
# include <linux/fs.h>
2005-11-09 16:00:16 +03:00
# include <linux/platform_device.h>
2008-06-12 17:32:13 +04:00
# include <linux/of_device.h>
2005-10-29 00:25:58 +04:00
# include <asm/irq.h>
# include <asm/uaccess.h>
# ifdef CONFIG_8xx
# include <asm/8xx_immap.h>
# include <asm/pgtable.h>
# include <asm/mpc8xx.h>
2008-01-25 17:31:42 +03:00
# include <asm/cpm1.h>
2005-10-29 00:25:58 +04:00
# endif
# include "fs_enet.h"
2006-08-15 10:00:30 +04:00
# include "fec.h"
2005-10-29 00:25:58 +04:00
/*************************************************/
# if defined(CONFIG_CPM1)
/* for a CPM1 __raw_xxx's are sufficient */
# define __fs_out32(addr, x) __raw_writel(x, addr)
# define __fs_out16(addr, x) __raw_writew(x, addr)
# define __fs_in32(addr) __raw_readl(addr)
# define __fs_in16(addr) __raw_readw(addr)
# else
/* for others play it safe */
# define __fs_out32(addr, x) out_be32(addr, x)
# define __fs_out16(addr, x) out_be16(addr, x)
# define __fs_in32(addr) in_be32(addr)
# define __fs_in16(addr) in_be16(addr)
# endif
/* write */
# define FW(_fecp, _reg, _v) __fs_out32(&(_fecp)->fec_ ## _reg, (_v))
/* read */
# define FR(_fecp, _reg) __fs_in32(&(_fecp)->fec_ ## _reg)
/* set bits */
# define FS(_fecp, _reg, _v) FW(_fecp, _reg, FR(_fecp, _reg) | (_v))
/* clear bits */
# define FC(_fecp, _reg, _v) FW(_fecp, _reg, FR(_fecp, _reg) & ~(_v))
/*
2006-08-15 10:00:30 +04:00
* Delay to wait for FEC reset command to complete ( in us )
2005-10-29 00:25:58 +04:00
*/
# define FEC_RESET_DELAY 50
2007-10-01 23:20:58 +04:00
static int whack_reset ( fec_t __iomem * fecp )
2005-10-29 00:25:58 +04:00
{
int i ;
FW ( fecp , ecntrl , FEC_ECNTRL_PINMUX | FEC_ECNTRL_RESET ) ;
for ( i = 0 ; i < FEC_RESET_DELAY ; i + + ) {
if ( ( FR ( fecp , ecntrl ) & FEC_ECNTRL_RESET ) = = 0 )
return 0 ; /* OK */
udelay ( 1 ) ;
}
return - 1 ;
}
static int do_pd_setup ( struct fs_enet_private * fep )
{
2007-10-02 19:55:58 +04:00
struct of_device * ofdev = to_of_device ( fep - > dev ) ;
fep - > interrupt = of_irq_to_resource ( ofdev - > node , 0 , NULL ) ;
if ( fep - > interrupt = = NO_IRQ )
return - EINVAL ;
fep - > fec . fecp = of_iomap ( ofdev - > node , 0 ) ;
if ( ! fep - > fcc . fccp )
return - EINVAL ;
return 0 ;
2005-10-29 00:25:58 +04:00
}
# define FEC_NAPI_RX_EVENT_MSK (FEC_ENET_RXF | FEC_ENET_RXB)
# define FEC_RX_EVENT (FEC_ENET_RXF)
# define FEC_TX_EVENT (FEC_ENET_TXF)
# define FEC_ERR_EVENT_MSK (FEC_ENET_HBERR | FEC_ENET_BABR | \
FEC_ENET_BABT | FEC_ENET_EBERR )
static int setup_data ( struct net_device * dev )
{
struct fs_enet_private * fep = netdev_priv ( dev ) ;
if ( do_pd_setup ( fep ) ! = 0 )
return - EINVAL ;
fep - > fec . hthi = 0 ;
fep - > fec . htlo = 0 ;
fep - > ev_napi_rx = FEC_NAPI_RX_EVENT_MSK ;
fep - > ev_rx = FEC_RX_EVENT ;
fep - > ev_tx = FEC_TX_EVENT ;
fep - > ev_err = FEC_ERR_EVENT_MSK ;
return 0 ;
}
static int allocate_bd ( struct net_device * dev )
{
struct fs_enet_private * fep = netdev_priv ( dev ) ;
const struct fs_platform_info * fpi = fep - > fpi ;
2007-09-18 20:05:35 +04:00
2007-10-01 23:20:58 +04:00
fep - > ring_base = ( void __force __iomem * ) dma_alloc_coherent ( fep - > dev ,
2005-10-29 00:25:58 +04:00
( fpi - > tx_ring + fpi - > rx_ring ) *
sizeof ( cbd_t ) , & fep - > ring_mem_addr ,
GFP_KERNEL ) ;
if ( fep - > ring_base = = NULL )
return - ENOMEM ;
return 0 ;
}
static void free_bd ( struct net_device * dev )
{
struct fs_enet_private * fep = netdev_priv ( dev ) ;
const struct fs_platform_info * fpi = fep - > fpi ;
if ( fep - > ring_base )
dma_free_coherent ( fep - > dev , ( fpi - > tx_ring + fpi - > rx_ring )
* sizeof ( cbd_t ) ,
2007-10-01 23:20:58 +04:00
( void __force * ) fep - > ring_base ,
2005-10-29 00:25:58 +04:00
fep - > ring_mem_addr ) ;
}
static void cleanup_data ( struct net_device * dev )
{
/* nothing */
}
static void set_promiscuous_mode ( struct net_device * dev )
{
struct fs_enet_private * fep = netdev_priv ( dev ) ;
2007-10-01 23:20:58 +04:00
fec_t __iomem * fecp = fep - > fec . fecp ;
2005-10-29 00:25:58 +04:00
FS ( fecp , r_cntrl , FEC_RCNTRL_PROM ) ;
}
static void set_multicast_start ( struct net_device * dev )
{
struct fs_enet_private * fep = netdev_priv ( dev ) ;
fep - > fec . hthi = 0 ;
fep - > fec . htlo = 0 ;
}
static void set_multicast_one ( struct net_device * dev , const u8 * mac )
{
struct fs_enet_private * fep = netdev_priv ( dev ) ;
int temp , hash_index , i , j ;
u32 crc , csrVal ;
u8 byte , msb ;
crc = 0xffffffff ;
for ( i = 0 ; i < 6 ; i + + ) {
byte = mac [ i ] ;
for ( j = 0 ; j < 8 ; j + + ) {
msb = crc > > 31 ;
crc < < = 1 ;
if ( msb ^ ( byte & 0x1 ) )
crc ^ = FEC_CRC_POLY ;
byte > > = 1 ;
}
}
temp = ( crc & 0x3f ) > > 1 ;
hash_index = ( ( temp & 0x01 ) < < 4 ) |
( ( temp & 0x02 ) < < 2 ) |
( ( temp & 0x04 ) ) |
( ( temp & 0x08 ) > > 2 ) |
( ( temp & 0x10 ) > > 4 ) ;
csrVal = 1 < < hash_index ;
if ( crc & 1 )
fep - > fec . hthi | = csrVal ;
else
fep - > fec . htlo | = csrVal ;
}
static void set_multicast_finish ( struct net_device * dev )
{
struct fs_enet_private * fep = netdev_priv ( dev ) ;
2007-10-01 23:20:58 +04:00
fec_t __iomem * fecp = fep - > fec . fecp ;
2005-10-29 00:25:58 +04:00
/* if all multi or too many multicasts; just enable all */
if ( ( dev - > flags & IFF_ALLMULTI ) ! = 0 | |
2010-02-08 07:30:35 +03:00
netdev_mc_count ( dev ) > FEC_MAX_MULTICAST_ADDRS ) {
2005-10-29 00:25:58 +04:00
fep - > fec . hthi = 0xffffffffU ;
fep - > fec . htlo = 0xffffffffU ;
}
FC ( fecp , r_cntrl , FEC_RCNTRL_PROM ) ;
FW ( fecp , hash_table_high , fep - > fec . hthi ) ;
FW ( fecp , hash_table_low , fep - > fec . htlo ) ;
}
static void set_multicast_list ( struct net_device * dev )
{
struct dev_mc_list * pmc ;
if ( ( dev - > flags & IFF_PROMISC ) = = 0 ) {
set_multicast_start ( dev ) ;
2010-02-22 12:22:26 +03:00
netdev_for_each_mc_addr ( pmc , dev )
2005-10-29 00:25:58 +04:00
set_multicast_one ( dev , pmc - > dmi_addr ) ;
set_multicast_finish ( dev ) ;
} else
set_promiscuous_mode ( dev ) ;
}
static void restart ( struct net_device * dev )
{
struct fs_enet_private * fep = netdev_priv ( dev ) ;
2007-10-01 23:20:58 +04:00
fec_t __iomem * fecp = fep - > fec . fecp ;
2005-10-29 00:25:58 +04:00
const struct fs_platform_info * fpi = fep - > fpi ;
dma_addr_t rx_bd_base_phys , tx_bd_base_phys ;
int r ;
u32 addrhi , addrlo ;
2006-08-15 10:00:30 +04:00
struct mii_bus * mii = fep - > phydev - > bus ;
struct fec_info * fec_inf = mii - > priv ;
2005-10-29 00:25:58 +04:00
r = whack_reset ( fep - > fec . fecp ) ;
if ( r ! = 0 )
2010-02-26 15:00:47 +03:00
dev_err ( fep - > dev , " FEC Reset FAILED! \n " ) ;
2005-10-29 00:25:58 +04:00
/*
2006-08-15 10:00:30 +04:00
* Set station address .
2005-10-29 00:25:58 +04:00
*/
addrhi = ( ( u32 ) dev - > dev_addr [ 0 ] < < 24 ) |
( ( u32 ) dev - > dev_addr [ 1 ] < < 16 ) |
( ( u32 ) dev - > dev_addr [ 2 ] < < 8 ) |
( u32 ) dev - > dev_addr [ 3 ] ;
addrlo = ( ( u32 ) dev - > dev_addr [ 4 ] < < 24 ) |
( ( u32 ) dev - > dev_addr [ 5 ] < < 16 ) ;
FW ( fecp , addr_low , addrhi ) ;
FW ( fecp , addr_high , addrlo ) ;
/*
2007-09-18 20:05:35 +04:00
* Reset all multicast .
2005-10-29 00:25:58 +04:00
*/
FW ( fecp , hash_table_high , fep - > fec . hthi ) ;
FW ( fecp , hash_table_low , fep - > fec . htlo ) ;
/*
2007-09-18 20:05:35 +04:00
* Set maximum receive buffer size .
2005-10-29 00:25:58 +04:00
*/
FW ( fecp , r_buff_size , PKT_MAXBLR_SIZE ) ;
FW ( fecp , r_hash , PKT_MAXBUF_SIZE ) ;
/* get physical address */
rx_bd_base_phys = fep - > ring_mem_addr ;
tx_bd_base_phys = rx_bd_base_phys + sizeof ( cbd_t ) * fpi - > rx_ring ;
/*
2007-09-18 20:05:35 +04:00
* Set receive and transmit descriptor base .
2005-10-29 00:25:58 +04:00
*/
FW ( fecp , r_des_start , rx_bd_base_phys ) ;
FW ( fecp , x_des_start , tx_bd_base_phys ) ;
fs_init_bds ( dev ) ;
/*
2007-09-18 20:05:35 +04:00
* Enable big endian and don ' t care about SDMA FC .
2005-10-29 00:25:58 +04:00
*/
FW ( fecp , fun_code , 0x78000000 ) ;
/*
2006-08-15 10:00:30 +04:00
* Set MII speed .
2005-10-29 00:25:58 +04:00
*/
2006-08-15 10:00:30 +04:00
FW ( fecp , mii_speed , fec_inf - > mii_speed ) ;
2005-10-29 00:25:58 +04:00
/*
2006-08-15 10:00:30 +04:00
* Clear any outstanding interrupt .
2005-10-29 00:25:58 +04:00
*/
FW ( fecp , ievent , 0xffc0 ) ;
2007-01-27 11:00:04 +03:00
FW ( fecp , ivec , ( virq_to_hw ( fep - > interrupt ) / 2 ) < < 29 ) ;
2005-10-29 00:25:58 +04:00
FW ( fecp , r_cntrl , FEC_RCNTRL_MII_MODE ) ; /* MII enable */
/*
2006-08-15 10:00:30 +04:00
* adjust to duplex mode
2005-10-29 00:25:58 +04:00
*/
2006-08-15 10:00:30 +04:00
if ( fep - > phydev - > duplex ) {
2005-10-29 00:25:58 +04:00
FC ( fecp , r_cntrl , FEC_RCNTRL_DRT ) ;
FS ( fecp , x_cntrl , FEC_TCNTRL_FDEN ) ; /* FD enable */
} else {
FS ( fecp , r_cntrl , FEC_RCNTRL_DRT ) ;
FC ( fecp , x_cntrl , FEC_TCNTRL_FDEN ) ; /* FD disable */
}
/*
2007-09-18 20:05:35 +04:00
* Enable interrupts we wish to service .
2005-10-29 00:25:58 +04:00
*/
FW ( fecp , imask , FEC_ENET_TXF | FEC_ENET_TXB |
FEC_ENET_RXF | FEC_ENET_RXB ) ;
/*
2007-09-18 20:05:35 +04:00
* And last , enable the transmit and receive processing .
2005-10-29 00:25:58 +04:00
*/
FW ( fecp , ecntrl , FEC_ECNTRL_PINMUX | FEC_ECNTRL_ETHER_EN ) ;
FW ( fecp , r_des_active , 0x01000000 ) ;
}
static void stop ( struct net_device * dev )
{
struct fs_enet_private * fep = netdev_priv ( dev ) ;
2006-08-15 10:00:30 +04:00
const struct fs_platform_info * fpi = fep - > fpi ;
2007-10-01 23:20:58 +04:00
fec_t __iomem * fecp = fep - > fec . fecp ;
2006-08-15 10:00:30 +04:00
struct fec_info * feci = fep - > phydev - > bus - > priv ;
2005-10-29 00:25:58 +04:00
int i ;
if ( ( FR ( fecp , ecntrl ) & FEC_ECNTRL_ETHER_EN ) = = 0 )
return ; /* already down */
FW ( fecp , x_cntrl , 0x01 ) ; /* Graceful transmit stop */
for ( i = 0 ; ( ( FR ( fecp , ievent ) & 0x10000000 ) = = 0 ) & &
i < FEC_RESET_DELAY ; i + + )
udelay ( 1 ) ;
if ( i = = FEC_RESET_DELAY )
2010-02-26 15:00:47 +03:00
dev_warn ( fep - > dev , " FEC timeout on graceful transmit stop \n " ) ;
2005-10-29 00:25:58 +04:00
/*
2007-09-18 20:05:35 +04:00
* Disable FEC . Let only MII interrupts .
2005-10-29 00:25:58 +04:00
*/
FW ( fecp , imask , 0 ) ;
FC ( fecp , ecntrl , FEC_ECNTRL_ETHER_EN ) ;
fs_cleanup_bds ( dev ) ;
/* shut down FEC1? that's where the mii bus is */
2006-08-15 10:00:30 +04:00
if ( fpi - > has_phy ) {
2005-10-29 00:25:58 +04:00
FS ( fecp , r_cntrl , FEC_RCNTRL_MII_MODE ) ; /* MII enable */
FS ( fecp , ecntrl , FEC_ECNTRL_PINMUX | FEC_ECNTRL_ETHER_EN ) ;
FW ( fecp , ievent , FEC_ENET_MII ) ;
2006-08-15 10:00:30 +04:00
FW ( fecp , mii_speed , feci - > mii_speed ) ;
2005-10-29 00:25:58 +04:00
}
}
static void napi_clear_rx_event ( struct net_device * dev )
{
struct fs_enet_private * fep = netdev_priv ( dev ) ;
2007-10-01 23:20:58 +04:00
fec_t __iomem * fecp = fep - > fec . fecp ;
2005-10-29 00:25:58 +04:00
FW ( fecp , ievent , FEC_NAPI_RX_EVENT_MSK ) ;
}
static void napi_enable_rx ( struct net_device * dev )
{
struct fs_enet_private * fep = netdev_priv ( dev ) ;
2007-10-01 23:20:58 +04:00
fec_t __iomem * fecp = fep - > fec . fecp ;
2005-10-29 00:25:58 +04:00
FS ( fecp , imask , FEC_NAPI_RX_EVENT_MSK ) ;
}
static void napi_disable_rx ( struct net_device * dev )
{
struct fs_enet_private * fep = netdev_priv ( dev ) ;
2007-10-01 23:20:58 +04:00
fec_t __iomem * fecp = fep - > fec . fecp ;
2005-10-29 00:25:58 +04:00
FC ( fecp , imask , FEC_NAPI_RX_EVENT_MSK ) ;
}
static void rx_bd_done ( struct net_device * dev )
{
struct fs_enet_private * fep = netdev_priv ( dev ) ;
2007-10-01 23:20:58 +04:00
fec_t __iomem * fecp = fep - > fec . fecp ;
2005-10-29 00:25:58 +04:00
FW ( fecp , r_des_active , 0x01000000 ) ;
}
static void tx_kickstart ( struct net_device * dev )
{
struct fs_enet_private * fep = netdev_priv ( dev ) ;
2007-10-01 23:20:58 +04:00
fec_t __iomem * fecp = fep - > fec . fecp ;
2005-10-29 00:25:58 +04:00
FW ( fecp , x_des_active , 0x01000000 ) ;
}
static u32 get_int_events ( struct net_device * dev )
{
struct fs_enet_private * fep = netdev_priv ( dev ) ;
2007-10-01 23:20:58 +04:00
fec_t __iomem * fecp = fep - > fec . fecp ;
2005-10-29 00:25:58 +04:00
return FR ( fecp , ievent ) & FR ( fecp , imask ) ;
}
static void clear_int_events ( struct net_device * dev , u32 int_events )
{
struct fs_enet_private * fep = netdev_priv ( dev ) ;
2007-10-01 23:20:58 +04:00
fec_t __iomem * fecp = fep - > fec . fecp ;
2005-10-29 00:25:58 +04:00
FW ( fecp , ievent , int_events ) ;
}
static void ev_error ( struct net_device * dev , u32 int_events )
{
2010-02-26 15:00:47 +03:00
struct fs_enet_private * fep = netdev_priv ( dev ) ;
dev_warn ( fep - > dev , " FEC ERROR(s) 0x%x \n " , int_events ) ;
2005-10-29 00:25:58 +04:00
}
2007-10-01 23:20:58 +04:00
static int get_regs ( struct net_device * dev , void * p , int * sizep )
2005-10-29 00:25:58 +04:00
{
struct fs_enet_private * fep = netdev_priv ( dev ) ;
if ( * sizep < sizeof ( fec_t ) )
return - EINVAL ;
memcpy_fromio ( p , fep - > fec . fecp , sizeof ( fec_t ) ) ;
return 0 ;
}
2007-10-01 23:20:58 +04:00
static int get_regs_len ( struct net_device * dev )
2005-10-29 00:25:58 +04:00
{
return sizeof ( fec_t ) ;
}
2007-10-01 23:20:58 +04:00
static void tx_restart ( struct net_device * dev )
2005-10-29 00:25:58 +04:00
{
/* nothing */
}
/*************************************************************************/
const struct fs_ops fs_fec_ops = {
. setup_data = setup_data ,
. cleanup_data = cleanup_data ,
. set_multicast_list = set_multicast_list ,
. restart = restart ,
. stop = stop ,
. napi_clear_rx_event = napi_clear_rx_event ,
. napi_enable_rx = napi_enable_rx ,
. napi_disable_rx = napi_disable_rx ,
. rx_bd_done = rx_bd_done ,
. tx_kickstart = tx_kickstart ,
. get_int_events = get_int_events ,
. clear_int_events = clear_int_events ,
. ev_error = ev_error ,
. get_regs = get_regs ,
. get_regs_len = get_regs_len ,
. tx_restart = tx_restart ,
. allocate_bd = allocate_bd ,
. free_bd = free_bd ,
} ;