2014-02-10 10:31:42 +04:00
/* CoreChip-sz SR9800 one chip USB 2.0 Ethernet Devices
*
* Author : Liu Junliang < liujunliang_ljl @ 163. com >
*
* Based on asix_common . c , asix_devices . c
*
* 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
* kind , whether express or implied . *
*/
# include <linux/module.h>
# include <linux/kmod.h>
# include <linux/init.h>
# include <linux/netdevice.h>
# include <linux/etherdevice.h>
# include <linux/ethtool.h>
# include <linux/workqueue.h>
# include <linux/mii.h>
# include <linux/usb.h>
# include <linux/crc32.h>
# include <linux/usb/usbnet.h>
# include <linux/slab.h>
# include <linux/if_vlan.h>
# include "sr9800.h"
static int sr_read_cmd ( struct usbnet * dev , u8 cmd , u16 value , u16 index ,
u16 size , void * data )
{
int err ;
err = usbnet_read_cmd ( dev , cmd , SR_REQ_RD_REG , value , index ,
data , size ) ;
if ( ( err ! = size ) & & ( err > = 0 ) )
err = - EINVAL ;
return err ;
}
static int sr_write_cmd ( struct usbnet * dev , u8 cmd , u16 value , u16 index ,
u16 size , void * data )
{
int err ;
err = usbnet_write_cmd ( dev , cmd , SR_REQ_WR_REG , value , index ,
data , size ) ;
if ( ( err ! = size ) & & ( err > = 0 ) )
err = - EINVAL ;
return err ;
}
static void
sr_write_cmd_async ( struct usbnet * dev , u8 cmd , u16 value , u16 index ,
u16 size , void * data )
{
usbnet_write_cmd_async ( dev , cmd , SR_REQ_WR_REG , value , index , data ,
size ) ;
}
static int sr_rx_fixup ( struct usbnet * dev , struct sk_buff * skb )
{
int offset = 0 ;
2014-02-13 20:50:19 +04:00
/* This check is no longer done by usbnet */
if ( skb - > len < dev - > net - > hard_header_len )
return 0 ;
2014-02-10 10:31:42 +04:00
while ( offset + sizeof ( u32 ) < skb - > len ) {
struct sk_buff * sr_skb ;
u16 size ;
u32 header = get_unaligned_le32 ( skb - > data + offset ) ;
offset + = sizeof ( u32 ) ;
/* get the packet length */
size = ( u16 ) ( header & 0x7ff ) ;
if ( size ! = ( ( ~ header > > 16 ) & 0x07ff ) ) {
netdev_err ( dev - > net , " %s : Bad Header Length \n " ,
__func__ ) ;
return 0 ;
}
if ( ( size > dev - > net - > mtu + ETH_HLEN + VLAN_HLEN ) | |
( size + offset > skb - > len ) ) {
netdev_err ( dev - > net , " %s : Bad RX Length %d \n " ,
__func__ , size ) ;
return 0 ;
}
sr_skb = netdev_alloc_skb_ip_align ( dev - > net , size ) ;
if ( ! sr_skb )
return 0 ;
skb_put ( sr_skb , size ) ;
memcpy ( sr_skb - > data , skb - > data + offset , size ) ;
usbnet_skb_return ( dev , sr_skb ) ;
offset + = ( size + 1 ) & 0xfffe ;
}
if ( skb - > len ! = offset ) {
netdev_err ( dev - > net , " %s : Bad SKB Length %d \n " , __func__ ,
skb - > len ) ;
return 0 ;
}
return 1 ;
}
static struct sk_buff * sr_tx_fixup ( struct usbnet * dev , struct sk_buff * skb ,
gfp_t flags )
{
int headroom = skb_headroom ( skb ) ;
int tailroom = skb_tailroom ( skb ) ;
u32 padbytes = 0xffff0000 ;
u32 packet_len ;
int padlen ;
padlen = ( ( skb - > len + 4 ) % ( dev - > maxpacket - 1 ) ) ? 0 : 4 ;
if ( ( ! skb_cloned ( skb ) ) & & ( ( headroom + tailroom ) > = ( 4 + padlen ) ) ) {
if ( ( headroom < 4 ) | | ( tailroom < padlen ) ) {
skb - > data = memmove ( skb - > head + 4 , skb - > data ,
skb - > len ) ;
skb_set_tail_pointer ( skb , skb - > len ) ;
}
} else {
struct sk_buff * skb2 ;
skb2 = skb_copy_expand ( skb , 4 , padlen , flags ) ;
dev_kfree_skb_any ( skb ) ;
skb = skb2 ;
if ( ! skb )
return NULL ;
}
skb_push ( skb , 4 ) ;
packet_len = ( ( ( skb - > len - 4 ) ^ 0x0000ffff ) < < 16 ) + ( skb - > len - 4 ) ;
cpu_to_le32s ( & packet_len ) ;
skb_copy_to_linear_data ( skb , & packet_len , sizeof ( packet_len ) ) ;
if ( padlen ) {
cpu_to_le32s ( & padbytes ) ;
memcpy ( skb_tail_pointer ( skb ) , & padbytes , sizeof ( padbytes ) ) ;
skb_put ( skb , sizeof ( padbytes ) ) ;
}
2015-03-25 23:41:33 +03:00
usbnet_set_skb_tx_stats ( skb , 1 , 0 ) ;
2014-02-10 10:31:42 +04:00
return skb ;
}
static void sr_status ( struct usbnet * dev , struct urb * urb )
{
struct sr9800_int_data * event ;
int link ;
if ( urb - > actual_length < 8 )
return ;
event = urb - > transfer_buffer ;
link = event - > link & 0x01 ;
if ( netif_carrier_ok ( dev - > net ) ! = link ) {
usbnet_link_change ( dev , link , 1 ) ;
netdev_dbg ( dev - > net , " Link Status is: %d \n " , link ) ;
}
return ;
}
static inline int sr_set_sw_mii ( struct usbnet * dev )
{
int ret ;
ret = sr_write_cmd ( dev , SR_CMD_SET_SW_MII , 0x0000 , 0 , 0 , NULL ) ;
if ( ret < 0 )
netdev_err ( dev - > net , " Failed to enable software MII access \n " ) ;
return ret ;
}
static inline int sr_set_hw_mii ( struct usbnet * dev )
{
int ret ;
ret = sr_write_cmd ( dev , SR_CMD_SET_HW_MII , 0x0000 , 0 , 0 , NULL ) ;
if ( ret < 0 )
netdev_err ( dev - > net , " Failed to enable hardware MII access \n " ) ;
return ret ;
}
static inline int sr_get_phy_addr ( struct usbnet * dev )
{
u8 buf [ 2 ] ;
int ret ;
ret = sr_read_cmd ( dev , SR_CMD_READ_PHY_ID , 0 , 0 , 2 , buf ) ;
if ( ret < 0 ) {
netdev_err ( dev - > net , " %s : Error reading PHYID register:%02x \n " ,
__func__ , ret ) ;
goto out ;
}
netdev_dbg ( dev - > net , " %s : returning 0x%04x \n " , __func__ ,
* ( ( __le16 * ) buf ) ) ;
ret = buf [ 1 ] ;
out :
return ret ;
}
static int sr_sw_reset ( struct usbnet * dev , u8 flags )
{
int ret ;
ret = sr_write_cmd ( dev , SR_CMD_SW_RESET , flags , 0 , 0 , NULL ) ;
if ( ret < 0 )
netdev_err ( dev - > net , " Failed to send software reset:%02x \n " ,
ret ) ;
return ret ;
}
static u16 sr_read_rx_ctl ( struct usbnet * dev )
{
__le16 v ;
int ret ;
ret = sr_read_cmd ( dev , SR_CMD_READ_RX_CTL , 0 , 0 , 2 , & v ) ;
if ( ret < 0 ) {
netdev_err ( dev - > net , " Error reading RX_CTL register:%02x \n " ,
ret ) ;
goto out ;
}
ret = le16_to_cpu ( v ) ;
out :
return ret ;
}
static int sr_write_rx_ctl ( struct usbnet * dev , u16 mode )
{
int ret ;
netdev_dbg ( dev - > net , " %s : mode = 0x%04x \n " , __func__ , mode ) ;
ret = sr_write_cmd ( dev , SR_CMD_WRITE_RX_CTL , mode , 0 , 0 , NULL ) ;
if ( ret < 0 )
netdev_err ( dev - > net ,
" Failed to write RX_CTL mode to 0x%04x:%02x \n " ,
mode , ret ) ;
return ret ;
}
static u16 sr_read_medium_status ( struct usbnet * dev )
{
__le16 v ;
int ret ;
ret = sr_read_cmd ( dev , SR_CMD_READ_MEDIUM_STATUS , 0 , 0 , 2 , & v ) ;
if ( ret < 0 ) {
netdev_err ( dev - > net ,
" Error reading Medium Status register:%02x \n " , ret ) ;
return ret ; /* TODO: callers not checking for error ret */
}
return le16_to_cpu ( v ) ;
}
static int sr_write_medium_mode ( struct usbnet * dev , u16 mode )
{
int ret ;
netdev_dbg ( dev - > net , " %s : mode = 0x%04x \n " , __func__ , mode ) ;
ret = sr_write_cmd ( dev , SR_CMD_WRITE_MEDIUM_MODE , mode , 0 , 0 , NULL ) ;
if ( ret < 0 )
netdev_err ( dev - > net ,
" Failed to write Medium Mode mode to 0x%04x:%02x \n " ,
mode , ret ) ;
return ret ;
}
static int sr_write_gpio ( struct usbnet * dev , u16 value , int sleep )
{
int ret ;
netdev_dbg ( dev - > net , " %s : value = 0x%04x \n " , __func__ , value ) ;
ret = sr_write_cmd ( dev , SR_CMD_WRITE_GPIOS , value , 0 , 0 , NULL ) ;
if ( ret < 0 )
netdev_err ( dev - > net , " Failed to write GPIO value 0x%04x:%02x \n " ,
value , ret ) ;
if ( sleep )
msleep ( sleep ) ;
return ret ;
}
/* SR9800 have a 16-bit RX_CTL value */
static void sr_set_multicast ( struct net_device * net )
{
struct usbnet * dev = netdev_priv ( net ) ;
struct sr_data * data = ( struct sr_data * ) & dev - > data ;
u16 rx_ctl = SR_DEFAULT_RX_CTL ;
if ( net - > flags & IFF_PROMISC ) {
rx_ctl | = SR_RX_CTL_PRO ;
} else if ( net - > flags & IFF_ALLMULTI | |
netdev_mc_count ( net ) > SR_MAX_MCAST ) {
rx_ctl | = SR_RX_CTL_AMALL ;
} else if ( netdev_mc_empty ( net ) ) {
/* just broadcast and directed */
} else {
/* We use the 20 byte dev->data
* for our 8 byte filter buffer
* to avoid allocating memory that
* is tricky to free later
*/
struct netdev_hw_addr * ha ;
u32 crc_bits ;
memset ( data - > multi_filter , 0 , SR_MCAST_FILTER_SIZE ) ;
/* Build the multicast hash filter. */
netdev_for_each_mc_addr ( ha , net ) {
crc_bits = ether_crc ( ETH_ALEN , ha - > addr ) > > 26 ;
data - > multi_filter [ crc_bits > > 3 ] | =
1 < < ( crc_bits & 7 ) ;
}
sr_write_cmd_async ( dev , SR_CMD_WRITE_MULTI_FILTER , 0 , 0 ,
SR_MCAST_FILTER_SIZE , data - > multi_filter ) ;
rx_ctl | = SR_RX_CTL_AM ;
}
sr_write_cmd_async ( dev , SR_CMD_WRITE_RX_CTL , rx_ctl , 0 , 0 , NULL ) ;
}
static int sr_mdio_read ( struct net_device * net , int phy_id , int loc )
{
struct usbnet * dev = netdev_priv ( net ) ;
__le16 res ;
mutex_lock ( & dev - > phy_mutex ) ;
sr_set_sw_mii ( dev ) ;
sr_read_cmd ( dev , SR_CMD_READ_MII_REG , phy_id , ( __u16 ) loc , 2 , & res ) ;
sr_set_hw_mii ( dev ) ;
mutex_unlock ( & dev - > phy_mutex ) ;
netdev_dbg ( dev - > net ,
" %s : phy_id=0x%02x, loc=0x%02x, returns=0x%04x \n " , __func__ ,
phy_id , loc , le16_to_cpu ( res ) ) ;
return le16_to_cpu ( res ) ;
}
static void
sr_mdio_write ( struct net_device * net , int phy_id , int loc , int val )
{
struct usbnet * dev = netdev_priv ( net ) ;
__le16 res = cpu_to_le16 ( val ) ;
netdev_dbg ( dev - > net ,
" %s : phy_id=0x%02x, loc=0x%02x, val=0x%04x \n " , __func__ ,
phy_id , loc , val ) ;
mutex_lock ( & dev - > phy_mutex ) ;
sr_set_sw_mii ( dev ) ;
sr_write_cmd ( dev , SR_CMD_WRITE_MII_REG , phy_id , ( __u16 ) loc , 2 , & res ) ;
sr_set_hw_mii ( dev ) ;
mutex_unlock ( & dev - > phy_mutex ) ;
}
/* Get the PHY Identifier from the PHYSID1 & PHYSID2 MII registers */
static u32 sr_get_phyid ( struct usbnet * dev )
{
int phy_reg ;
u32 phy_id ;
int i ;
/* Poll for the rare case the FW or phy isn't ready yet. */
for ( i = 0 ; i < 100 ; i + + ) {
phy_reg = sr_mdio_read ( dev - > net , dev - > mii . phy_id , MII_PHYSID1 ) ;
if ( phy_reg ! = 0 & & phy_reg ! = 0xFFFF )
break ;
mdelay ( 1 ) ;
}
if ( phy_reg < = 0 | | phy_reg = = 0xFFFF )
return 0 ;
phy_id = ( phy_reg & 0xffff ) < < 16 ;
phy_reg = sr_mdio_read ( dev - > net , dev - > mii . phy_id , MII_PHYSID2 ) ;
if ( phy_reg < 0 )
return 0 ;
phy_id | = ( phy_reg & 0xffff ) ;
return phy_id ;
}
static void
sr_get_wol ( struct net_device * net , struct ethtool_wolinfo * wolinfo )
{
struct usbnet * dev = netdev_priv ( net ) ;
u8 opt ;
if ( sr_read_cmd ( dev , SR_CMD_READ_MONITOR_MODE , 0 , 0 , 1 , & opt ) < 0 ) {
wolinfo - > supported = 0 ;
wolinfo - > wolopts = 0 ;
return ;
}
wolinfo - > supported = WAKE_PHY | WAKE_MAGIC ;
wolinfo - > wolopts = 0 ;
if ( opt & SR_MONITOR_LINK )
wolinfo - > wolopts | = WAKE_PHY ;
if ( opt & SR_MONITOR_MAGIC )
wolinfo - > wolopts | = WAKE_MAGIC ;
}
static int
sr_set_wol ( struct net_device * net , struct ethtool_wolinfo * wolinfo )
{
struct usbnet * dev = netdev_priv ( net ) ;
u8 opt = 0 ;
if ( wolinfo - > wolopts & WAKE_PHY )
opt | = SR_MONITOR_LINK ;
if ( wolinfo - > wolopts & WAKE_MAGIC )
opt | = SR_MONITOR_MAGIC ;
if ( sr_write_cmd ( dev , SR_CMD_WRITE_MONITOR_MODE ,
opt , 0 , 0 , NULL ) < 0 )
return - EINVAL ;
return 0 ;
}
static int sr_get_eeprom_len ( struct net_device * net )
{
struct usbnet * dev = netdev_priv ( net ) ;
struct sr_data * data = ( struct sr_data * ) & dev - > data ;
return data - > eeprom_len ;
}
static int sr_get_eeprom ( struct net_device * net ,
struct ethtool_eeprom * eeprom , u8 * data )
{
struct usbnet * dev = netdev_priv ( net ) ;
__le16 * ebuf = ( __le16 * ) data ;
int ret ;
int i ;
/* Crude hack to ensure that we don't overwrite memory
* if an odd length is supplied
*/
if ( eeprom - > len % 2 )
return - EINVAL ;
eeprom - > magic = SR_EEPROM_MAGIC ;
/* sr9800 returns 2 bytes from eeprom on read */
for ( i = 0 ; i < eeprom - > len / 2 ; i + + ) {
ret = sr_read_cmd ( dev , SR_CMD_READ_EEPROM , eeprom - > offset + i ,
0 , 2 , & ebuf [ i ] ) ;
if ( ret < 0 )
return - EINVAL ;
}
return 0 ;
}
static void sr_get_drvinfo ( struct net_device * net ,
struct ethtool_drvinfo * info )
{
/* Inherit standard device info */
usbnet_get_drvinfo ( net , info ) ;
strncpy ( info - > driver , DRIVER_NAME , sizeof ( info - > driver ) ) ;
strncpy ( info - > version , DRIVER_VERSION , sizeof ( info - > version ) ) ;
}
static u32 sr_get_link ( struct net_device * net )
{
struct usbnet * dev = netdev_priv ( net ) ;
return mii_link_ok ( & dev - > mii ) ;
}
static int sr_ioctl ( struct net_device * net , struct ifreq * rq , int cmd )
{
struct usbnet * dev = netdev_priv ( net ) ;
return generic_mii_ioctl ( & dev - > mii , if_mii ( rq ) , cmd , NULL ) ;
}
static int sr_set_mac_address ( struct net_device * net , void * p )
{
struct usbnet * dev = netdev_priv ( net ) ;
struct sr_data * data = ( struct sr_data * ) & dev - > data ;
struct sockaddr * addr = p ;
if ( netif_running ( net ) )
return - EBUSY ;
if ( ! is_valid_ether_addr ( addr - > sa_data ) )
return - EADDRNOTAVAIL ;
memcpy ( net - > dev_addr , addr - > sa_data , ETH_ALEN ) ;
/* We use the 20 byte dev->data
* for our 6 byte mac buffer
* to avoid allocating memory that
* is tricky to free later
*/
memcpy ( data - > mac_addr , addr - > sa_data , ETH_ALEN ) ;
sr_write_cmd_async ( dev , SR_CMD_WRITE_NODE_ID , 0 , 0 , ETH_ALEN ,
data - > mac_addr ) ;
return 0 ;
}
static const struct ethtool_ops sr9800_ethtool_ops = {
. get_drvinfo = sr_get_drvinfo ,
. get_link = sr_get_link ,
. get_msglevel = usbnet_get_msglevel ,
. set_msglevel = usbnet_set_msglevel ,
. get_wol = sr_get_wol ,
. set_wol = sr_set_wol ,
. get_eeprom_len = sr_get_eeprom_len ,
. get_eeprom = sr_get_eeprom ,
. get_settings = usbnet_get_settings ,
. set_settings = usbnet_set_settings ,
. nway_reset = usbnet_nway_reset ,
} ;
static int sr9800_link_reset ( struct usbnet * dev )
{
struct ethtool_cmd ecmd = { . cmd = ETHTOOL_GSET } ;
u16 mode ;
mii_check_media ( & dev - > mii , 1 , 1 ) ;
mii_ethtool_gset ( & dev - > mii , & ecmd ) ;
mode = SR9800_MEDIUM_DEFAULT ;
if ( ethtool_cmd_speed ( & ecmd ) ! = SPEED_100 )
mode & = ~ SR_MEDIUM_PS ;
if ( ecmd . duplex ! = DUPLEX_FULL )
mode & = ~ SR_MEDIUM_FD ;
netdev_dbg ( dev - > net , " %s : speed: %u duplex: %d mode: 0x%04x \n " ,
__func__ , ethtool_cmd_speed ( & ecmd ) , ecmd . duplex , mode ) ;
sr_write_medium_mode ( dev , mode ) ;
return 0 ;
}
static int sr9800_set_default_mode ( struct usbnet * dev )
{
u16 rx_ctl ;
int ret ;
sr_mdio_write ( dev - > net , dev - > mii . phy_id , MII_BMCR , BMCR_RESET ) ;
sr_mdio_write ( dev - > net , dev - > mii . phy_id , MII_ADVERTISE ,
ADVERTISE_ALL | ADVERTISE_CSMA ) ;
mii_nway_restart ( & dev - > mii ) ;
ret = sr_write_medium_mode ( dev , SR9800_MEDIUM_DEFAULT ) ;
if ( ret < 0 )
goto out ;
ret = sr_write_cmd ( dev , SR_CMD_WRITE_IPG012 ,
SR9800_IPG0_DEFAULT | SR9800_IPG1_DEFAULT ,
SR9800_IPG2_DEFAULT , 0 , NULL ) ;
if ( ret < 0 ) {
netdev_dbg ( dev - > net , " Write IPG,IPG1,IPG2 failed: %d \n " , ret ) ;
goto out ;
}
/* Set RX_CTL to default values with 2k buffer, and enable cactus */
ret = sr_write_rx_ctl ( dev , SR_DEFAULT_RX_CTL ) ;
if ( ret < 0 )
goto out ;
rx_ctl = sr_read_rx_ctl ( dev ) ;
netdev_dbg ( dev - > net , " RX_CTL is 0x%04x after all initializations \n " ,
rx_ctl ) ;
rx_ctl = sr_read_medium_status ( dev ) ;
netdev_dbg ( dev - > net , " Medium Status:0x%04x after all initializations \n " ,
rx_ctl ) ;
return 0 ;
out :
return ret ;
}
static int sr9800_reset ( struct usbnet * dev )
{
struct sr_data * data = ( struct sr_data * ) & dev - > data ;
int ret , embd_phy ;
u16 rx_ctl ;
ret = sr_write_gpio ( dev ,
SR_GPIO_RSE | SR_GPIO_GPO_2 | SR_GPIO_GPO2EN , 5 ) ;
if ( ret < 0 )
goto out ;
embd_phy = ( ( sr_get_phy_addr ( dev ) & 0x1f ) = = 0x10 ? 1 : 0 ) ;
ret = sr_write_cmd ( dev , SR_CMD_SW_PHY_SELECT , embd_phy , 0 , 0 , NULL ) ;
if ( ret < 0 ) {
netdev_dbg ( dev - > net , " Select PHY #1 failed: %d \n " , ret ) ;
goto out ;
}
ret = sr_sw_reset ( dev , SR_SWRESET_IPPD | SR_SWRESET_PRL ) ;
if ( ret < 0 )
goto out ;
msleep ( 150 ) ;
ret = sr_sw_reset ( dev , SR_SWRESET_CLEAR ) ;
if ( ret < 0 )
goto out ;
msleep ( 150 ) ;
if ( embd_phy ) {
ret = sr_sw_reset ( dev , SR_SWRESET_IPRL ) ;
if ( ret < 0 )
goto out ;
} else {
ret = sr_sw_reset ( dev , SR_SWRESET_PRTE ) ;
if ( ret < 0 )
goto out ;
}
msleep ( 150 ) ;
rx_ctl = sr_read_rx_ctl ( dev ) ;
netdev_dbg ( dev - > net , " RX_CTL is 0x%04x after software reset \n " , rx_ctl ) ;
ret = sr_write_rx_ctl ( dev , 0x0000 ) ;
if ( ret < 0 )
goto out ;
rx_ctl = sr_read_rx_ctl ( dev ) ;
netdev_dbg ( dev - > net , " RX_CTL is 0x%04x setting to 0x0000 \n " , rx_ctl ) ;
ret = sr_sw_reset ( dev , SR_SWRESET_PRL ) ;
if ( ret < 0 )
goto out ;
msleep ( 150 ) ;
ret = sr_sw_reset ( dev , SR_SWRESET_IPRL | SR_SWRESET_PRL ) ;
if ( ret < 0 )
goto out ;
msleep ( 150 ) ;
ret = sr9800_set_default_mode ( dev ) ;
if ( ret < 0 )
goto out ;
/* Rewrite MAC address */
memcpy ( data - > mac_addr , dev - > net - > dev_addr , ETH_ALEN ) ;
ret = sr_write_cmd ( dev , SR_CMD_WRITE_NODE_ID , 0 , 0 , ETH_ALEN ,
data - > mac_addr ) ;
if ( ret < 0 )
goto out ;
return 0 ;
out :
return ret ;
}
static const struct net_device_ops sr9800_netdev_ops = {
. ndo_open = usbnet_open ,
. ndo_stop = usbnet_stop ,
. ndo_start_xmit = usbnet_start_xmit ,
. ndo_tx_timeout = usbnet_tx_timeout ,
. ndo_change_mtu = usbnet_change_mtu ,
. ndo_set_mac_address = sr_set_mac_address ,
. ndo_validate_addr = eth_validate_addr ,
. ndo_do_ioctl = sr_ioctl ,
. ndo_set_rx_mode = sr_set_multicast ,
} ;
static int sr9800_phy_powerup ( struct usbnet * dev )
{
int ret ;
/* set the embedded Ethernet PHY in power-down state */
ret = sr_sw_reset ( dev , SR_SWRESET_IPPD | SR_SWRESET_IPRL ) ;
if ( ret < 0 ) {
netdev_err ( dev - > net , " Failed to power down PHY : %d \n " , ret ) ;
return ret ;
}
msleep ( 20 ) ;
/* set the embedded Ethernet PHY in power-up state */
ret = sr_sw_reset ( dev , SR_SWRESET_IPRL ) ;
if ( ret < 0 ) {
netdev_err ( dev - > net , " Failed to reset PHY: %d \n " , ret ) ;
return ret ;
}
msleep ( 600 ) ;
/* set the embedded Ethernet PHY in reset state */
ret = sr_sw_reset ( dev , SR_SWRESET_CLEAR ) ;
if ( ret < 0 ) {
netdev_err ( dev - > net , " Failed to power up PHY: %d \n " , ret ) ;
return ret ;
}
msleep ( 20 ) ;
/* set the embedded Ethernet PHY in power-up state */
ret = sr_sw_reset ( dev , SR_SWRESET_IPRL ) ;
if ( ret < 0 ) {
netdev_err ( dev - > net , " Failed to reset PHY: %d \n " , ret ) ;
return ret ;
}
return 0 ;
}
static int sr9800_bind ( struct usbnet * dev , struct usb_interface * intf )
{
struct sr_data * data = ( struct sr_data * ) & dev - > data ;
u16 led01_mux , led23_mux ;
int ret , embd_phy ;
u32 phyid ;
u16 rx_ctl ;
data - > eeprom_len = SR9800_EEPROM_LEN ;
usbnet_get_endpoints ( dev , intf ) ;
/* LED Setting Rule :
* AABB : CCDD
* AA : MFA0 ( LED0 )
* BB : MFA1 ( LED1 )
* CC : MFA2 ( LED2 ) , Reserved for SR9800
* DD : MFA3 ( LED3 ) , Reserved for SR9800
*/
led01_mux = ( SR_LED_MUX_LINK_ACTIVE < < 8 ) | SR_LED_MUX_LINK ;
led23_mux = ( SR_LED_MUX_LINK_ACTIVE < < 8 ) | SR_LED_MUX_TX_ACTIVE ;
ret = sr_write_cmd ( dev , SR_CMD_LED_MUX , led01_mux , led23_mux , 0 , NULL ) ;
if ( ret < 0 ) {
netdev_err ( dev - > net , " set LINK LED failed : %d \n " , ret ) ;
goto out ;
}
/* Get the MAC address */
ret = sr_read_cmd ( dev , SR_CMD_READ_NODE_ID , 0 , 0 , ETH_ALEN ,
dev - > net - > dev_addr ) ;
if ( ret < 0 ) {
netdev_dbg ( dev - > net , " Failed to read MAC address: %d \n " , ret ) ;
return ret ;
}
netdev_dbg ( dev - > net , " mac addr : %pM \n " , dev - > net - > dev_addr ) ;
/* Initialize MII structure */
dev - > mii . dev = dev - > net ;
dev - > mii . mdio_read = sr_mdio_read ;
dev - > mii . mdio_write = sr_mdio_write ;
dev - > mii . phy_id_mask = 0x1f ;
dev - > mii . reg_num_mask = 0x1f ;
dev - > mii . phy_id = sr_get_phy_addr ( dev ) ;
dev - > net - > netdev_ops = & sr9800_netdev_ops ;
dev - > net - > ethtool_ops = & sr9800_ethtool_ops ;
embd_phy = ( ( dev - > mii . phy_id & 0x1f ) = = 0x10 ? 1 : 0 ) ;
/* Reset the PHY to normal operation mode */
ret = sr_write_cmd ( dev , SR_CMD_SW_PHY_SELECT , embd_phy , 0 , 0 , NULL ) ;
if ( ret < 0 ) {
netdev_dbg ( dev - > net , " Select PHY #1 failed: %d \n " , ret ) ;
return ret ;
}
/* Init PHY routine */
ret = sr9800_phy_powerup ( dev ) ;
if ( ret < 0 )
goto out ;
rx_ctl = sr_read_rx_ctl ( dev ) ;
netdev_dbg ( dev - > net , " RX_CTL is 0x%04x after software reset \n " , rx_ctl ) ;
ret = sr_write_rx_ctl ( dev , 0x0000 ) ;
if ( ret < 0 )
goto out ;
rx_ctl = sr_read_rx_ctl ( dev ) ;
netdev_dbg ( dev - > net , " RX_CTL is 0x%04x setting to 0x0000 \n " , rx_ctl ) ;
/* Read PHYID register *AFTER* the PHY was reset properly */
phyid = sr_get_phyid ( dev ) ;
netdev_dbg ( dev - > net , " PHYID=0x%08x \n " , phyid ) ;
/* medium mode setting */
ret = sr9800_set_default_mode ( dev ) ;
if ( ret < 0 )
goto out ;
if ( dev - > udev - > speed = = USB_SPEED_HIGH ) {
ret = sr_write_cmd ( dev , SR_CMD_BULKIN_SIZE ,
SR9800_BULKIN_SIZE [ SR9800_MAX_BULKIN_4K ] . byte_cnt ,
SR9800_BULKIN_SIZE [ SR9800_MAX_BULKIN_4K ] . threshold ,
0 , NULL ) ;
if ( ret < 0 ) {
netdev_err ( dev - > net , " Reset RX_CTL failed: %d \n " , ret ) ;
goto out ;
}
dev - > rx_urb_size =
SR9800_BULKIN_SIZE [ SR9800_MAX_BULKIN_4K ] . size ;
} else {
ret = sr_write_cmd ( dev , SR_CMD_BULKIN_SIZE ,
SR9800_BULKIN_SIZE [ SR9800_MAX_BULKIN_2K ] . byte_cnt ,
SR9800_BULKIN_SIZE [ SR9800_MAX_BULKIN_2K ] . threshold ,
0 , NULL ) ;
if ( ret < 0 ) {
netdev_err ( dev - > net , " Reset RX_CTL failed: %d \n " , ret ) ;
goto out ;
}
dev - > rx_urb_size =
SR9800_BULKIN_SIZE [ SR9800_MAX_BULKIN_2K ] . size ;
}
2014-02-13 11:09:58 +04:00
netdev_dbg ( dev - > net , " %s : setting rx_urb_size with : %zu \n " , __func__ ,
2014-02-10 10:31:42 +04:00
dev - > rx_urb_size ) ;
return 0 ;
out :
return ret ;
}
static const struct driver_info sr9800_driver_info = {
. description = " CoreChip SR9800 USB 2.0 Ethernet " ,
. bind = sr9800_bind ,
. status = sr_status ,
. link_reset = sr9800_link_reset ,
. reset = sr9800_reset ,
. flags = DRIVER_FLAG ,
. rx_fixup = sr_rx_fixup ,
. tx_fixup = sr_tx_fixup ,
} ;
static const struct usb_device_id products [ ] = {
{
USB_DEVICE ( 0x0fe6 , 0x9800 ) , /* SR9800 Device */
. driver_info = ( unsigned long ) & sr9800_driver_info ,
} ,
{ } , /* END */
} ;
MODULE_DEVICE_TABLE ( usb , products ) ;
static struct usb_driver sr_driver = {
. name = DRIVER_NAME ,
. id_table = products ,
. probe = usbnet_probe ,
. suspend = usbnet_suspend ,
. resume = usbnet_resume ,
. disconnect = usbnet_disconnect ,
. supports_autosuspend = 1 ,
} ;
module_usb_driver ( sr_driver ) ;
MODULE_AUTHOR ( " Liu Junliang <liujunliang_ljl@163.com " ) ;
MODULE_VERSION ( DRIVER_VERSION ) ;
MODULE_DESCRIPTION ( " SR9800 USB 2.0 USB2NET Dev : http://www.corechip-sz.com " ) ;
MODULE_LICENSE ( " GPL " ) ;