2006-05-08 01:22:53 +04:00
/*
* drivers / net / phy / smsc . c
*
* Driver for SMSC PHYs
*
* Author : Herbert Valerio Riedel
*
* Copyright ( c ) 2006 Herbert Valerio Riedel < hvr @ gnu . org >
*
* 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 .
*
2008-04-28 21:37:29 +04:00
* Support added for SMSC LAN8187 and LAN8700 by steve . glendinning @ smsc . com
*
2006-05-08 01:22:53 +04:00
*/
# include <linux/kernel.h>
# include <linux/module.h>
# include <linux/mii.h>
# include <linux/ethtool.h>
# include <linux/phy.h>
# include <linux/netdevice.h>
# define MII_LAN83C185_ISF 29 /* Interrupt Source Flags */
# define MII_LAN83C185_IM 30 /* Interrupt Mask */
# define MII_LAN83C185_ISF_INT1 (1<<1) /* Auto-Negotiation Page Received */
# define MII_LAN83C185_ISF_INT2 (1<<2) /* Parallel Detection Fault */
# define MII_LAN83C185_ISF_INT3 (1<<3) /* Auto-Negotiation LP Ack */
# define MII_LAN83C185_ISF_INT4 (1<<4) /* Link Down */
# define MII_LAN83C185_ISF_INT5 (1<<5) /* Remote Fault Detected */
# define MII_LAN83C185_ISF_INT6 (1<<6) /* Auto-Negotiation complete */
# define MII_LAN83C185_ISF_INT7 (1<<7) /* ENERGYON */
# define MII_LAN83C185_ISF_INT_ALL (0x0e)
# define MII_LAN83C185_ISF_INT_PHYLIB_EVENTS \
( MII_LAN83C185_ISF_INT6 | MII_LAN83C185_ISF_INT4 )
2008-04-28 21:36:46 +04:00
static int smsc_phy_config_intr ( struct phy_device * phydev )
2006-05-08 01:22:53 +04:00
{
int rc = phy_write ( phydev , MII_LAN83C185_IM ,
( ( PHY_INTERRUPT_ENABLED = = phydev - > interrupts )
? MII_LAN83C185_ISF_INT_PHYLIB_EVENTS
: 0 ) ) ;
return rc < 0 ? rc : 0 ;
}
2008-04-28 21:36:46 +04:00
static int smsc_phy_ack_interrupt ( struct phy_device * phydev )
2006-05-08 01:22:53 +04:00
{
int rc = phy_read ( phydev , MII_LAN83C185_ISF ) ;
return rc < 0 ? rc : 0 ;
}
2008-04-28 21:36:46 +04:00
static int smsc_phy_config_init ( struct phy_device * phydev )
2006-05-08 01:22:53 +04:00
{
2008-04-28 21:36:46 +04:00
return smsc_phy_ack_interrupt ( phydev ) ;
2006-05-08 01:22:53 +04:00
}
static struct phy_driver lan83c185_driver = {
. phy_id = 0x0007c0a0 , /* OUI=0x00800f, Model#=0x0a */
. phy_id_mask = 0xfffffff0 ,
. name = " SMSC LAN83C185 " ,
. features = ( PHY_BASIC_FEATURES | SUPPORTED_Pause
| SUPPORTED_Asym_Pause ) ,
. flags = PHY_HAS_INTERRUPT | PHY_HAS_MAGICANEG ,
/* basic functions */
. config_aneg = genphy_config_aneg ,
. read_status = genphy_read_status ,
2008-04-28 21:36:46 +04:00
. config_init = smsc_phy_config_init ,
2006-05-08 01:22:53 +04:00
/* IRQ related */
2008-04-28 21:36:46 +04:00
. ack_interrupt = smsc_phy_ack_interrupt ,
. config_intr = smsc_phy_config_intr ,
2006-05-08 01:22:53 +04:00
. driver = { . owner = THIS_MODULE , }
} ;
2008-04-28 21:37:29 +04:00
static struct phy_driver lan8187_driver = {
. phy_id = 0x0007c0b0 , /* OUI=0x00800f, Model#=0x0b */
. phy_id_mask = 0xfffffff0 ,
. name = " SMSC LAN8187 " ,
. features = ( PHY_BASIC_FEATURES | SUPPORTED_Pause
| SUPPORTED_Asym_Pause ) ,
. flags = PHY_HAS_INTERRUPT | PHY_HAS_MAGICANEG ,
/* basic functions */
. config_aneg = genphy_config_aneg ,
. read_status = genphy_read_status ,
. config_init = smsc_phy_config_init ,
/* IRQ related */
. ack_interrupt = smsc_phy_ack_interrupt ,
. config_intr = smsc_phy_config_intr ,
. driver = { . owner = THIS_MODULE , }
} ;
static struct phy_driver lan8700_driver = {
. phy_id = 0x0007c0c0 , /* OUI=0x00800f, Model#=0x0c */
. phy_id_mask = 0xfffffff0 ,
. name = " SMSC LAN8700 " ,
. features = ( PHY_BASIC_FEATURES | SUPPORTED_Pause
| SUPPORTED_Asym_Pause ) ,
. flags = PHY_HAS_INTERRUPT | PHY_HAS_MAGICANEG ,
/* basic functions */
. config_aneg = genphy_config_aneg ,
. read_status = genphy_read_status ,
. config_init = smsc_phy_config_init ,
/* IRQ related */
. ack_interrupt = smsc_phy_ack_interrupt ,
. config_intr = smsc_phy_config_intr ,
. driver = { . owner = THIS_MODULE , }
} ;
2008-11-05 03:35:37 +03:00
static struct phy_driver lan911x_int_driver = {
. phy_id = 0x0007c0d0 , /* OUI=0x00800f, Model#=0x0d */
. phy_id_mask = 0xfffffff0 ,
. name = " SMSC LAN911x Internal PHY " ,
. features = ( PHY_BASIC_FEATURES | SUPPORTED_Pause
| SUPPORTED_Asym_Pause ) ,
. flags = PHY_HAS_INTERRUPT | PHY_HAS_MAGICANEG ,
/* basic functions */
. config_aneg = genphy_config_aneg ,
. read_status = genphy_read_status ,
. config_init = smsc_phy_config_init ,
/* IRQ related */
. ack_interrupt = smsc_phy_ack_interrupt ,
. config_intr = smsc_phy_config_intr ,
. driver = { . owner = THIS_MODULE , }
} ;
2006-05-08 01:22:53 +04:00
static int __init smsc_init ( void )
{
2008-04-28 21:37:29 +04:00
int ret ;
ret = phy_driver_register ( & lan83c185_driver ) ;
if ( ret )
goto err1 ;
ret = phy_driver_register ( & lan8187_driver ) ;
if ( ret )
goto err2 ;
ret = phy_driver_register ( & lan8700_driver ) ;
if ( ret )
goto err3 ;
2008-11-05 03:35:37 +03:00
ret = phy_driver_register ( & lan911x_int_driver ) ;
if ( ret )
goto err4 ;
2008-04-28 21:37:29 +04:00
return 0 ;
2008-11-05 03:35:37 +03:00
err4 :
phy_driver_unregister ( & lan8700_driver ) ;
2008-04-28 21:37:29 +04:00
err3 :
phy_driver_unregister ( & lan8187_driver ) ;
err2 :
phy_driver_unregister ( & lan83c185_driver ) ;
err1 :
return ret ;
2006-05-08 01:22:53 +04:00
}
static void __exit smsc_exit ( void )
{
2008-11-05 03:35:37 +03:00
phy_driver_unregister ( & lan911x_int_driver ) ;
2008-04-28 21:37:29 +04:00
phy_driver_unregister ( & lan8700_driver ) ;
phy_driver_unregister ( & lan8187_driver ) ;
2006-05-08 01:22:53 +04:00
phy_driver_unregister ( & lan83c185_driver ) ;
}
MODULE_DESCRIPTION ( " SMSC PHY driver " ) ;
MODULE_AUTHOR ( " Herbert Valerio Riedel " ) ;
MODULE_LICENSE ( " GPL " ) ;
module_init ( smsc_init ) ;
module_exit ( smsc_exit ) ;