2012-07-13 09:26:31 +04:00
/*
* ASIX AX88172A based USB 2.0 Ethernet Devices
* Copyright ( C ) 2012 OMICRON electronics GmbH
*
* Supports external PHYs via phylib . Based on the driver for the
* AX88772 . Original copyrights follow :
*
* Copyright ( C ) 2003 - 2006 David Hollis < dhollis @ davehollis . com >
* Copyright ( C ) 2005 Phil Chang < pchang23 @ sbcglobal . net >
* Copyright ( C ) 2006 James Painter < jamie . painter @ iname . com >
* Copyright ( c ) 2002 - 2003 TiVo Inc .
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation ; either version 2 of the License , or
* ( at your option ) any later version .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public License
* along with this program ; if not , write to the Free Software
* Foundation , Inc . , 59 Temple Place , Suite 330 , Boston , MA 02111 - 1307 USA
*/
# include "asix.h"
# include <linux/phy.h>
struct ax88172a_private {
struct mii_bus * mdio ;
struct phy_device * phydev ;
char phy_name [ 20 ] ;
u16 phy_addr ;
u16 oldmode ;
int use_embdphy ;
2013-01-16 08:24:07 +04:00
struct asix_rx_fixup_info rx_fixup_info ;
2012-07-13 09:26:31 +04:00
} ;
/* MDIO read and write wrappers for phylib */
static int asix_mdio_bus_read ( struct mii_bus * bus , int phy_id , int regnum )
{
return asix_mdio_read ( ( ( struct usbnet * ) bus - > priv ) - > net , phy_id ,
regnum ) ;
}
static int asix_mdio_bus_write ( struct mii_bus * bus , int phy_id , int regnum ,
u16 val )
{
asix_mdio_write ( ( ( struct usbnet * ) bus - > priv ) - > net , phy_id , regnum , val ) ;
return 0 ;
}
static int ax88172a_ioctl ( struct net_device * net , struct ifreq * rq , int cmd )
{
if ( ! netif_running ( net ) )
return - EINVAL ;
if ( ! net - > phydev )
return - ENODEV ;
return phy_mii_ioctl ( net - > phydev , rq , cmd ) ;
}
/* set MAC link settings according to information from phylib */
static void ax88172a_adjust_link ( struct net_device * netdev )
{
struct phy_device * phydev = netdev - > phydev ;
struct usbnet * dev = netdev_priv ( netdev ) ;
struct ax88172a_private * priv = dev - > driver_priv ;
u16 mode = 0 ;
if ( phydev - > link ) {
mode = AX88772_MEDIUM_DEFAULT ;
if ( phydev - > duplex = = DUPLEX_HALF )
mode & = ~ AX_MEDIUM_FD ;
if ( phydev - > speed ! = SPEED_100 )
mode & = ~ AX_MEDIUM_PS ;
}
if ( mode ! = priv - > oldmode ) {
asix_write_medium_mode ( dev , mode ) ;
priv - > oldmode = mode ;
netdev_dbg ( netdev , " speed %u duplex %d, setting mode to 0x%04x \n " ,
phydev - > speed , phydev - > duplex , mode ) ;
phy_print_status ( phydev ) ;
}
}
static void ax88172a_status ( struct usbnet * dev , struct urb * urb )
{
/* link changes are detected by polling the phy */
}
/* use phylib infrastructure */
static int ax88172a_init_mdio ( struct usbnet * dev )
{
struct ax88172a_private * priv = dev - > driver_priv ;
int ret , i ;
priv - > mdio = mdiobus_alloc ( ) ;
if ( ! priv - > mdio ) {
netdev_err ( dev - > net , " Could not allocate MDIO bus \n " ) ;
return - ENOMEM ;
}
priv - > mdio - > priv = ( void * ) dev ;
priv - > mdio - > read = & asix_mdio_bus_read ;
priv - > mdio - > write = & asix_mdio_bus_write ;
priv - > mdio - > name = " Asix MDIO Bus " ;
/* mii bus name is usb-<usb bus number>-<usb device number> */
snprintf ( priv - > mdio - > id , MII_BUS_ID_SIZE , " usb-%03d:%03d " ,
dev - > udev - > bus - > busnum , dev - > udev - > devnum ) ;
priv - > mdio - > irq = kzalloc ( sizeof ( int ) * PHY_MAX_ADDR , GFP_KERNEL ) ;
if ( ! priv - > mdio - > irq ) {
ret = - ENOMEM ;
goto mfree ;
}
for ( i = 0 ; i < PHY_MAX_ADDR ; i + + )
priv - > mdio - > irq [ i ] = PHY_POLL ;
ret = mdiobus_register ( priv - > mdio ) ;
if ( ret ) {
netdev_err ( dev - > net , " Could not register MDIO bus \n " ) ;
goto ifree ;
}
netdev_info ( dev - > net , " registered mdio bus %s \n " , priv - > mdio - > id ) ;
return 0 ;
ifree :
kfree ( priv - > mdio - > irq ) ;
mfree :
mdiobus_free ( priv - > mdio ) ;
return ret ;
}
static void ax88172a_remove_mdio ( struct usbnet * dev )
{
struct ax88172a_private * priv = dev - > driver_priv ;
netdev_info ( dev - > net , " deregistering mdio bus %s \n " , priv - > mdio - > id ) ;
mdiobus_unregister ( priv - > mdio ) ;
kfree ( priv - > mdio - > irq ) ;
mdiobus_free ( priv - > mdio ) ;
}
static const struct net_device_ops ax88172a_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 = asix_set_mac_address ,
. ndo_validate_addr = eth_validate_addr ,
. ndo_do_ioctl = ax88172a_ioctl ,
. ndo_set_rx_mode = asix_set_multicast ,
} ;
2013-08-09 21:31:21 +04:00
static int ax88172a_get_settings ( struct net_device * net ,
struct ethtool_cmd * cmd )
2012-07-13 09:26:31 +04:00
{
if ( ! net - > phydev )
return - ENODEV ;
return phy_ethtool_gset ( net - > phydev , cmd ) ;
}
2013-08-09 21:31:21 +04:00
static int ax88172a_set_settings ( struct net_device * net ,
struct ethtool_cmd * cmd )
2012-07-13 09:26:31 +04:00
{
if ( ! net - > phydev )
return - ENODEV ;
return phy_ethtool_sset ( net - > phydev , cmd ) ;
}
2013-08-09 21:31:21 +04:00
static int ax88172a_nway_reset ( struct net_device * net )
2012-07-13 09:26:31 +04:00
{
if ( ! net - > phydev )
return - ENODEV ;
return phy_start_aneg ( net - > phydev ) ;
}
static const struct ethtool_ops ax88172a_ethtool_ops = {
. get_drvinfo = asix_get_drvinfo ,
. get_link = usbnet_get_link ,
. get_msglevel = usbnet_get_msglevel ,
. set_msglevel = usbnet_set_msglevel ,
. get_wol = asix_get_wol ,
. set_wol = asix_set_wol ,
. get_eeprom_len = asix_get_eeprom_len ,
. get_eeprom = asix_get_eeprom ,
2012-07-19 04:23:07 +04:00
. set_eeprom = asix_set_eeprom ,
2012-07-13 09:26:31 +04:00
. get_settings = ax88172a_get_settings ,
. set_settings = ax88172a_set_settings ,
. nway_reset = ax88172a_nway_reset ,
} ;
static int ax88172a_reset_phy ( struct usbnet * dev , int embd_phy )
{
int ret ;
ret = asix_sw_reset ( dev , AX_SWRESET_IPPD ) ;
if ( ret < 0 )
goto err ;
msleep ( 150 ) ;
ret = asix_sw_reset ( dev , AX_SWRESET_CLEAR ) ;
if ( ret < 0 )
goto err ;
msleep ( 150 ) ;
ret = asix_sw_reset ( dev , embd_phy ? AX_SWRESET_IPRL : AX_SWRESET_IPPD ) ;
if ( ret < 0 )
goto err ;
return 0 ;
err :
return ret ;
}
static int ax88172a_bind ( struct usbnet * dev , struct usb_interface * intf )
{
int ret ;
u8 buf [ ETH_ALEN ] ;
struct ax88172a_private * priv ;
usbnet_get_endpoints ( dev , intf ) ;
priv = kzalloc ( sizeof ( * priv ) , GFP_KERNEL ) ;
2013-02-03 21:28:11 +04:00
if ( ! priv )
2012-07-13 09:26:31 +04:00
return - ENOMEM ;
2013-02-03 21:28:11 +04:00
2012-07-13 09:26:31 +04:00
dev - > driver_priv = priv ;
/* Get the MAC address */
ret = asix_read_cmd ( dev , AX_CMD_READ_NODE_ID , 0 , 0 , ETH_ALEN , buf ) ;
if ( ret < 0 ) {
netdev_err ( dev - > net , " Failed to read MAC address: %d \n " , ret ) ;
goto free ;
}
memcpy ( dev - > net - > dev_addr , buf , ETH_ALEN ) ;
dev - > net - > netdev_ops = & ax88172a_netdev_ops ;
dev - > net - > ethtool_ops = & ax88172a_ethtool_ops ;
/* are we using the internal or the external phy? */
ret = asix_read_cmd ( dev , AX_CMD_SW_PHY_STATUS , 0 , 0 , 1 , buf ) ;
if ( ret < 0 ) {
netdev_err ( dev - > net , " Failed to read software interface selection register: %d \n " ,
ret ) ;
goto free ;
}
netdev_dbg ( dev - > net , " AX_CMD_SW_PHY_STATUS = 0x%02x \n " , buf [ 0 ] ) ;
switch ( buf [ 0 ] & AX_PHY_SELECT_MASK ) {
case AX_PHY_SELECT_INTERNAL :
netdev_dbg ( dev - > net , " use internal phy \n " ) ;
priv - > use_embdphy = 1 ;
break ;
case AX_PHY_SELECT_EXTERNAL :
netdev_dbg ( dev - > net , " use external phy \n " ) ;
priv - > use_embdphy = 0 ;
break ;
default :
netdev_err ( dev - > net , " Interface mode not supported by driver \n " ) ;
2012-07-18 14:56:52 +04:00
ret = - ENOTSUPP ;
2012-07-13 09:26:31 +04:00
goto free ;
}
priv - > phy_addr = asix_read_phy_addr ( dev , priv - > use_embdphy ) ;
ax88172a_reset_phy ( dev , priv - > use_embdphy ) ;
/* Asix framing packs multiple eth frames into a 2K usb bulk transfer */
if ( dev - > driver_info - > flags & FLAG_FRAMING_AX ) {
/* hard_mtu is still the default - the device does not support
jumbo eth frames */
dev - > rx_urb_size = 2048 ;
}
/* init MDIO bus */
ret = ax88172a_init_mdio ( dev ) ;
if ( ret )
goto free ;
return 0 ;
free :
kfree ( priv ) ;
return ret ;
}
static int ax88172a_stop ( struct usbnet * dev )
{
struct ax88172a_private * priv = dev - > driver_priv ;
netdev_dbg ( dev - > net , " Stopping interface \n " ) ;
if ( priv - > phydev ) {
netdev_info ( dev - > net , " Disconnecting from phy %s \n " ,
priv - > phy_name ) ;
phy_stop ( priv - > phydev ) ;
phy_disconnect ( priv - > phydev ) ;
}
return 0 ;
}
static void ax88172a_unbind ( struct usbnet * dev , struct usb_interface * intf )
{
struct ax88172a_private * priv = dev - > driver_priv ;
ax88172a_remove_mdio ( dev ) ;
kfree ( priv ) ;
}
static int ax88172a_reset ( struct usbnet * dev )
{
struct asix_data * data = ( struct asix_data * ) & dev - > data ;
struct ax88172a_private * priv = dev - > driver_priv ;
int ret ;
u16 rx_ctl ;
ax88172a_reset_phy ( dev , priv - > use_embdphy ) ;
msleep ( 150 ) ;
rx_ctl = asix_read_rx_ctl ( dev ) ;
netdev_dbg ( dev - > net , " RX_CTL is 0x%04x after software reset \n " , rx_ctl ) ;
ret = asix_write_rx_ctl ( dev , 0x0000 ) ;
if ( ret < 0 )
goto out ;
rx_ctl = asix_read_rx_ctl ( dev ) ;
netdev_dbg ( dev - > net , " RX_CTL is 0x%04x setting to 0x0000 \n " , rx_ctl ) ;
msleep ( 150 ) ;
ret = asix_write_cmd ( dev , AX_CMD_WRITE_IPG0 ,
AX88772_IPG0_DEFAULT | AX88772_IPG1_DEFAULT ,
AX88772_IPG2_DEFAULT , 0 , NULL ) ;
if ( ret < 0 ) {
netdev_err ( dev - > net , " Write IPG,IPG1,IPG2 failed: %d \n " , ret ) ;
goto out ;
}
/* Rewrite MAC address */
memcpy ( data - > mac_addr , dev - > net - > dev_addr , ETH_ALEN ) ;
ret = asix_write_cmd ( dev , AX_CMD_WRITE_NODE_ID , 0 , 0 , ETH_ALEN ,
data - > mac_addr ) ;
if ( ret < 0 )
goto out ;
/* Set RX_CTL to default values with 2k buffer, and enable cactus */
ret = asix_write_rx_ctl ( dev , AX_DEFAULT_RX_CTL ) ;
if ( ret < 0 )
goto out ;
rx_ctl = asix_read_rx_ctl ( dev ) ;
netdev_dbg ( dev - > net , " RX_CTL is 0x%04x after all initializations \n " ,
rx_ctl ) ;
rx_ctl = asix_read_medium_status ( dev ) ;
netdev_dbg ( dev - > net , " Medium Status is 0x%04x after all initializations \n " ,
rx_ctl ) ;
/* Connect to PHY */
snprintf ( priv - > phy_name , 20 , PHY_ID_FMT ,
priv - > mdio - > id , priv - > phy_addr ) ;
priv - > phydev = phy_connect ( dev - > net , priv - > phy_name ,
& ax88172a_adjust_link ,
2013-01-14 04:52:52 +04:00
PHY_INTERFACE_MODE_MII ) ;
2012-07-13 09:26:31 +04:00
if ( IS_ERR ( priv - > phydev ) ) {
netdev_err ( dev - > net , " Could not connect to PHY device %s \n " ,
priv - > phy_name ) ;
ret = PTR_ERR ( priv - > phydev ) ;
goto out ;
}
netdev_info ( dev - > net , " Connected to phy %s \n " , priv - > phy_name ) ;
/* During power-up, the AX88172A set the power down (BMCR_PDOWN)
* bit of the PHY . Bring the PHY up again .
*/
genphy_resume ( priv - > phydev ) ;
phy_start ( priv - > phydev ) ;
return 0 ;
out :
return ret ;
}
2013-01-16 08:24:07 +04:00
static int ax88172a_rx_fixup ( struct usbnet * dev , struct sk_buff * skb )
{
struct ax88172a_private * dp = dev - > driver_priv ;
struct asix_rx_fixup_info * rx = & dp - > rx_fixup_info ;
return asix_rx_fixup_internal ( dev , skb , rx ) ;
}
2012-07-13 09:26:31 +04:00
const struct driver_info ax88172a_info = {
. description = " ASIX AX88172A USB 2.0 Ethernet " ,
. bind = ax88172a_bind ,
. reset = ax88172a_reset ,
. stop = ax88172a_stop ,
. unbind = ax88172a_unbind ,
. status = ax88172a_status ,
. flags = FLAG_ETHER | FLAG_FRAMING_AX | FLAG_LINK_INTR |
FLAG_MULTI_PACKET ,
2013-01-16 08:24:07 +04:00
. rx_fixup = ax88172a_rx_fixup ,
2012-07-13 09:26:31 +04:00
. tx_fixup = asix_tx_fixup ,
} ;