@ -197,6 +197,17 @@
# define AR7240_PHY_ID1 0x004d
# define AR7240_PHY_ID2 0xd041
# define MII_ATH_REG_FUNCTION_CTRL 0x10
# define MII_ATH_FUNCTION_CTRL_MDIX BITS(5, 2)
# define MII_ATH_FUNCTION_CTRL_MDIX_OFFSET 5
# define MII_ATH_FUNCTION_CTRL_MDIX_MDI 0x0
# define MII_ATH_FUNCTION_CTRL_MDIX_MDIX 0x1
# define MII_ATH_FUNCTION_CTRL_MDIX_AUTO 0x3
# define MII_ATH_REG_PHY_SPECIFIC_STATUS 0x11
# define MII_ATH_PHY_SPECIFIC_STATUS_MDIX BIT(6)
# define MII_ATH_PHY_SPECIFIC_STATUS_MDIX_MDI 0x0
# define MII_ATH_PHY_SPECIFIC_STATUS_MDIX_MDIX 0x1
# define AR934X_PHY_ID1 0x004d
# define AR934X_PHY_ID2 0xd042
@ -303,6 +314,8 @@ struct ar7240sw {
rwlock_t stats_lock ;
struct ar7240sw_port_stat port_stats [ AR7240_NUM_PORTS ] ;
bool export_netdev ;
struct net_device * port_netdev [ AR7240_NUM_PORTS ] ;
} ;
struct ar7240sw_hw_stat {
@ -835,6 +848,260 @@ ar7240_set_ports(struct switch_dev *dev, struct switch_val *val)
return 0 ;
}
struct priv_netdev_port {
uint8_t port ;
struct net_device * netdev ;
} ;
int port_init ( struct net_device * dev ) {
return 0 ;
}
void port_dummy_cb ( struct net_device * dev ) {
}
int port_open ( struct net_device * dev ) {
phy_start ( dev - > phydev ) ;
netif_start_queue ( dev ) ;
phy_start_aneg ( dev - > phydev ) ;
return 0 ;
}
int port_stop ( struct net_device * dev ) {
netif_stop_queue ( dev ) ;
phy_stop ( dev - > phydev ) ;
return 0 ;
}
static int port_start_xmit ( struct sk_buff * skb , struct net_device * dev ) {
dev_kfree_skb ( skb ) ;
return NETDEV_TX_OK ;
}
static int port_get_settings ( struct net_device * dev , struct ethtool_cmd * cmd )
{
struct mii_bus * bus ;
struct phy_device * phydev ;
int reg ;
if ( ! dev - > phydev | | ! dev - > phydev - > bus )
return - EINVAL ;
phydev = dev - > phydev ;
bus = dev - > phydev - > bus ;
/* get mdix ctrl */
mutex_lock ( & bus - > mdio_lock ) ;
reg = bus - > read ( bus , phydev - > addr , MII_ATH_REG_FUNCTION_CTRL ) ;
mutex_unlock ( & bus - > mdio_lock ) ;
reg & = MII_ATH_FUNCTION_CTRL_MDIX ;
reg = reg > > MII_ATH_FUNCTION_CTRL_MDIX_OFFSET ;
switch ( reg ) {
case MII_ATH_FUNCTION_CTRL_MDIX_MDI :
cmd - > eth_tp_mdix_ctrl = ETH_TP_MDI ;
break ;
case MII_ATH_FUNCTION_CTRL_MDIX_MDIX :
cmd - > eth_tp_mdix_ctrl = ETH_TP_MDI_X ;
break ;
case MII_ATH_FUNCTION_CTRL_MDIX_AUTO :
cmd - > eth_tp_mdix_ctrl = ETH_TP_MDI_AUTO ;
break ;
default :
printk ( KERN_ERR " %s:%d Unknown state %x \n " , __FILE__ , __LINE__ , reg ) ;
}
/* get mdix status */
mutex_lock ( & bus - > mdio_lock ) ;
reg = bus - > read ( bus , phydev - > addr , MII_ATH_REG_PHY_SPECIFIC_STATUS ) ;
mutex_unlock ( & bus - > mdio_lock ) ;
cmd - > eth_tp_mdix = reg & MII_ATH_PHY_SPECIFIC_STATUS_MDIX ? ETH_TP_MDI_X : ETH_TP_MDI ;
/* phy_ethtool_gset sets everything correct beside the port */
phy_ethtool_gset ( dev - > phydev , cmd ) ;
cmd - > port = PORT_TP ;
return 0 ;
}
static int port_set_settings ( struct net_device * dev , struct ethtool_cmd * cmd )
{
int reg = 0 ;
int ret = 0 ;
struct mii_bus * bus ;
struct phy_device * phydev ;
if ( ! dev - > phydev | | ! dev - > phydev - > bus )
return - EINVAL ;
phydev = dev - > phydev ;
bus = dev - > phydev - > bus ;
/* mdix is vendor specific */
if ( cmd - > eth_tp_mdix_ctrl ) {
int mdi ;
switch ( cmd - > eth_tp_mdix_ctrl ) {
case ETH_TP_MDI :
mdi = MII_ATH_FUNCTION_CTRL_MDIX_MDI ;
break ;
case ETH_TP_MDI_X :
mdi = MII_ATH_FUNCTION_CTRL_MDIX_MDIX ;
break ;
case ETH_TP_MDI_AUTO :
mdi = MII_ATH_FUNCTION_CTRL_MDIX_AUTO ;
break ;
default :
return - EINVAL ;
}
mutex_lock ( & bus - > mdio_lock ) ;
reg = bus - > read ( bus , phydev - > addr , MII_ATH_REG_FUNCTION_CTRL ) ;
reg & = ~ MII_ATH_FUNCTION_CTRL_MDIX ;
reg | = mdi < < MII_ATH_FUNCTION_CTRL_MDIX_OFFSET ;
bus - > write ( bus , phydev - > addr , MII_ATH_REG_FUNCTION_CTRL , reg ) ;
mutex_unlock ( & bus - > mdio_lock ) ;
ret = genphy_soft_reset ( phydev ) ;
if ( ret )
return ret ;
}
return phy_ethtool_sset ( dev - > phydev , cmd ) ;
}
static int port_nway_reset ( struct net_device * dev )
{
if ( dev - > phydev )
return phy_start_aneg ( dev - > phydev ) ;
return - EINVAL ;
}
int port_ioctl ( struct net_device * dev , struct ifreq * rq , int cmd )
{
if ( ! dev - > phydev )
return - EINVAL ;
return phy_mii_ioctl ( dev - > phydev , rq , cmd ) ;
}
static const struct net_device_ops port_netdev_ops = {
. ndo_init = port_init ,
. ndo_uninit = port_dummy_cb ,
. ndo_open = port_open ,
. ndo_stop = port_stop ,
. ndo_start_xmit = port_start_xmit ,
. ndo_tx_timeout = port_dummy_cb ,
. ndo_set_mac_address = eth_mac_addr ,
. ndo_validate_addr = eth_validate_addr ,
. ndo_do_ioctl = port_ioctl ,
} ;
static const struct ethtool_ops port_ethtool_ops = {
. get_settings = port_get_settings ,
. set_settings = port_set_settings ,
. get_link = ethtool_op_get_link ,
. nway_reset = port_nway_reset ,
} ;
void port_dev_setup ( struct net_device * dev ) {
}
static int
ar7240_set_export_netdevs ( struct switch_dev * dev , const struct switch_attr * attr ,
struct switch_val * val )
{
struct ar7240sw * as = sw_to_ar7240 ( dev ) ;
bool create = ! ! val - > value . i ;
int i ;
int err = 0 ;
if ( as - > export_netdev = = create )
return 0 ;
if ( create = = as - > export_netdev )
return 0 ;
if ( create ) {
for ( i = 0 ; i < AR7240_NUM_PORTS ; i + + ) {
struct net_device * netdev ;
struct phy_device * phy ;
struct priv_netdev_port * priv ;
phy = as - > mii_bus - > phy_map [ i ] ;
if ( ! phy )
continue ;
netdev = alloc_netdev_mqs ( sizeof ( struct priv_netdev_port ) , " phy%d " , ether_setup , 1 , 1 ) ;
if ( ! netdev ) {
printk ( KERN_ERR " can not allow netdev \n " ) ;
/* TODO: free and set export = 0 */
return - 1 ;
}
SET_NETDEV_DEV ( netdev , & dev - > netdev - > dev ) ;
netdev - > netdev_ops = & port_netdev_ops ;
SET_ETHTOOL_OPS ( netdev , & port_ethtool_ops ) ;
eth_hw_addr_random ( netdev ) ;
priv = netdev_priv ( netdev ) ;
priv - > netdev = netdev ;
priv - > port = i ;
err = phy_connect_direct ( netdev , phy , & port_dummy_cb , PHY_INTERFACE_MODE_MII ) ;
if ( err ) {
printk ( KERN_ERR " phy attach failed %d with err %d \n " , i , err ) ;
continue ;
}
phy - > supported = ( PHY_BASIC_FEATURES & ~ SUPPORTED_MII ) ;
phy - > advertising = phy - > supported ;
err = register_netdev ( netdev ) ;
if ( err ) {
dev_err ( & dev - > netdev - > dev , " register netdevice failed %d \n " , err ) ;
/* TODO: exit */
return - 1 ;
}
as - > port_netdev [ i ] = netdev ;
/* after creating the port phys we put them up */
rtnl_lock ( ) ;
dev_open ( netdev ) ;
rtnl_unlock ( ) ;
}
as - > export_netdev = true ;
} else {
/* disable ports */
struct net_device * netdev ;
for ( i = 0 ; i < AR7240_NUM_PORTS ; i + + ) {
netdev = as - > port_netdev [ i ] ;
if ( ! netdev )
continue ;
unregister_netdev ( netdev ) ;
as - > port_netdev [ i ] = NULL ;
}
as - > export_netdev = false ;
}
return 0 ;
}
static int
ar7240_get_export_netdevs ( struct switch_dev * dev , const struct switch_attr * attr ,
struct switch_val * val )
{
struct ar7240sw * as = sw_to_ar7240 ( dev ) ;
val - > value . i = as - > export_netdev ;
return 0 ;
}
static int
ar7240_set_vlan ( struct switch_dev * dev , const struct switch_attr * attr ,
struct switch_val * val )
@ -995,6 +1262,14 @@ static struct switch_attr ar7240_globals[] = {
. get = ar7240_get_vlan ,
. max = 1
} ,
{
. type = SWITCH_TYPE_INT ,
. name = " export_netdevs " ,
. description = " Export ports a netdev (no traffic!) " ,
. set = ar7240_set_export_netdevs ,
. get = ar7240_get_export_netdevs ,
. max = 1
}
} ;
static struct switch_attr ar7240_port [ ] = {