2010-04-29 10:12:41 +04:00
/*
* drivers / net / phy / micrel . c
*
* Driver for Micrel PHYs
*
* Author : David J . Choi
*
* Copyright ( c ) 2010 Micrel , 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 .
*
2010-06-28 19:23:41 +04:00
* Support : ksz9021 1000 / 100 / 10 phy from Micrel
* ks8001 , ks8737 , ks8721 , ks8041 , ks8051 100 / 10 phy
2010-04-29 10:12:41 +04:00
*/
# include <linux/kernel.h>
# include <linux/module.h>
# include <linux/phy.h>
# define PHY_ID_KSZ9021 0x00221611
2010-06-28 19:23:41 +04:00
# define PHY_ID_KS8737 0x00221720
# define PHY_ID_KS8041 0x00221510
# define PHY_ID_KS8051 0x00221550
/* both for ks8001 Rev. A/B, and for ks8721 Rev 3. */
2010-04-29 10:12:41 +04:00
# define PHY_ID_KS8001 0x0022161A
2010-06-28 19:23:41 +04:00
/* general Interrupt control/status reg in vendor specific block. */
# define MII_KSZPHY_INTCS 0x1B
# define KSZPHY_INTCS_JABBER (1 << 15)
# define KSZPHY_INTCS_RECEIVE_ERR (1 << 14)
# define KSZPHY_INTCS_PAGE_RECEIVE (1 << 13)
# define KSZPHY_INTCS_PARELLEL (1 << 12)
# define KSZPHY_INTCS_LINK_PARTNER_ACK (1 << 11)
# define KSZPHY_INTCS_LINK_DOWN (1 << 10)
# define KSZPHY_INTCS_REMOTE_FAULT (1 << 9)
# define KSZPHY_INTCS_LINK_UP (1 << 8)
# define KSZPHY_INTCS_ALL (KSZPHY_INTCS_LINK_UP |\
KSZPHY_INTCS_LINK_DOWN )
/* general PHY control reg in vendor specific block. */
# define MII_KSZPHY_CTRL 0x1F
/* bitmap of PHY register to set interrupt mode */
# define KSZPHY_CTRL_INT_ACTIVE_HIGH (1 << 9)
# define KSZ9021_CTRL_INT_ACTIVE_HIGH (1 << 14)
# define KS8737_CTRL_INT_ACTIVE_HIGH (1 << 14)
static int kszphy_ack_interrupt ( struct phy_device * phydev )
{
/* bit[7..0] int status, which is a read and clear register. */
int rc ;
rc = phy_read ( phydev , MII_KSZPHY_INTCS ) ;
return ( rc < 0 ) ? rc : 0 ;
}
static int kszphy_set_interrupt ( struct phy_device * phydev )
{
int temp ;
temp = ( PHY_INTERRUPT_ENABLED = = phydev - > interrupts ) ?
KSZPHY_INTCS_ALL : 0 ;
return phy_write ( phydev , MII_KSZPHY_INTCS , temp ) ;
}
static int kszphy_config_intr ( struct phy_device * phydev )
{
int temp , rc ;
/* set the interrupt pin active low */
temp = phy_read ( phydev , MII_KSZPHY_CTRL ) ;
temp & = ~ KSZPHY_CTRL_INT_ACTIVE_HIGH ;
phy_write ( phydev , MII_KSZPHY_CTRL , temp ) ;
rc = kszphy_set_interrupt ( phydev ) ;
return rc < 0 ? rc : 0 ;
}
static int ksz9021_config_intr ( struct phy_device * phydev )
{
int temp , rc ;
/* set the interrupt pin active low */
temp = phy_read ( phydev , MII_KSZPHY_CTRL ) ;
temp & = ~ KSZ9021_CTRL_INT_ACTIVE_HIGH ;
phy_write ( phydev , MII_KSZPHY_CTRL , temp ) ;
rc = kszphy_set_interrupt ( phydev ) ;
return rc < 0 ? rc : 0 ;
}
static int ks8737_config_intr ( struct phy_device * phydev )
{
int temp , rc ;
/* set the interrupt pin active low */
temp = phy_read ( phydev , MII_KSZPHY_CTRL ) ;
temp & = ~ KS8737_CTRL_INT_ACTIVE_HIGH ;
phy_write ( phydev , MII_KSZPHY_CTRL , temp ) ;
rc = kszphy_set_interrupt ( phydev ) ;
return rc < 0 ? rc : 0 ;
}
2010-04-29 10:12:41 +04:00
static int kszphy_config_init ( struct phy_device * phydev )
{
return 0 ;
}
2010-06-28 19:23:41 +04:00
static struct phy_driver ks8737_driver = {
. phy_id = PHY_ID_KS8737 ,
. phy_id_mask = 0x00fffff0 ,
. name = " Micrel KS8737 " ,
. features = ( PHY_BASIC_FEATURES | SUPPORTED_Pause ) ,
. flags = PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT ,
. config_init = kszphy_config_init ,
. config_aneg = genphy_config_aneg ,
. read_status = genphy_read_status ,
. ack_interrupt = kszphy_ack_interrupt ,
. config_intr = ks8737_config_intr ,
. driver = { . owner = THIS_MODULE , } ,
} ;
static struct phy_driver ks8041_driver = {
. phy_id = PHY_ID_KS8041 ,
. phy_id_mask = 0x00fffff0 ,
. name = " Micrel KS8041 " ,
. features = ( PHY_BASIC_FEATURES | SUPPORTED_Pause
| SUPPORTED_Asym_Pause ) ,
. flags = PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT ,
. config_init = kszphy_config_init ,
. config_aneg = genphy_config_aneg ,
. read_status = genphy_read_status ,
. ack_interrupt = kszphy_ack_interrupt ,
. config_intr = kszphy_config_intr ,
. driver = { . owner = THIS_MODULE , } ,
} ;
2010-04-29 10:12:41 +04:00
2010-06-28 19:23:41 +04:00
static struct phy_driver ks8051_driver = {
. phy_id = PHY_ID_KS8051 ,
2010-04-29 10:12:41 +04:00
. phy_id_mask = 0x00fffff0 ,
2010-06-28 19:23:41 +04:00
. name = " Micrel KS8051 " ,
. features = ( PHY_BASIC_FEATURES | SUPPORTED_Pause
| SUPPORTED_Asym_Pause ) ,
. flags = PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT ,
2010-04-29 10:12:41 +04:00
. config_init = kszphy_config_init ,
. config_aneg = genphy_config_aneg ,
. read_status = genphy_read_status ,
2010-06-28 19:23:41 +04:00
. ack_interrupt = kszphy_ack_interrupt ,
. config_intr = kszphy_config_intr ,
2010-04-29 10:12:41 +04:00
. driver = { . owner = THIS_MODULE , } ,
} ;
2010-06-28 19:23:41 +04:00
static struct phy_driver ks8001_driver = {
. phy_id = PHY_ID_KS8001 ,
. name = " Micrel KS8001 or KS8721 " ,
2010-04-29 10:12:41 +04:00
. phy_id_mask = 0x00fffff0 ,
2010-06-28 19:23:41 +04:00
. features = ( PHY_BASIC_FEATURES | SUPPORTED_Pause ) ,
. flags = PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT ,
2010-04-29 10:12:41 +04:00
. config_init = kszphy_config_init ,
. config_aneg = genphy_config_aneg ,
. read_status = genphy_read_status ,
2010-06-28 19:23:41 +04:00
. ack_interrupt = kszphy_ack_interrupt ,
. config_intr = kszphy_config_intr ,
2010-04-29 10:12:41 +04:00
. driver = { . owner = THIS_MODULE , } ,
} ;
static struct phy_driver ksz9021_driver = {
. phy_id = PHY_ID_KSZ9021 ,
. phy_id_mask = 0x000fff10 ,
. name = " Micrel KSZ9021 Gigabit PHY " ,
2010-06-28 19:23:41 +04:00
. features = ( PHY_GBIT_FEATURES | SUPPORTED_Pause
| SUPPORTED_Asym_Pause ) ,
. flags = PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT ,
2010-04-29 10:12:41 +04:00
. config_init = kszphy_config_init ,
. config_aneg = genphy_config_aneg ,
. read_status = genphy_read_status ,
2010-06-28 19:23:41 +04:00
. ack_interrupt = kszphy_ack_interrupt ,
. config_intr = ksz9021_config_intr ,
2010-04-29 10:12:41 +04:00
. driver = { . owner = THIS_MODULE , } ,
} ;
static int __init ksphy_init ( void )
{
int ret ;
ret = phy_driver_register ( & ks8001_driver ) ;
if ( ret )
goto err1 ;
2010-06-28 19:23:41 +04:00
ret = phy_driver_register ( & ksz9021_driver ) ;
2010-04-29 10:12:41 +04:00
if ( ret )
goto err2 ;
2010-06-28 19:23:41 +04:00
ret = phy_driver_register ( & ks8737_driver ) ;
2010-04-29 10:12:41 +04:00
if ( ret )
goto err3 ;
2010-06-28 19:23:41 +04:00
ret = phy_driver_register ( & ks8041_driver ) ;
if ( ret )
goto err4 ;
ret = phy_driver_register ( & ks8051_driver ) ;
if ( ret )
goto err5 ;
2010-04-29 10:12:41 +04:00
return 0 ;
2010-06-28 19:23:41 +04:00
err5 :
phy_driver_unregister ( & ks8041_driver ) ;
err4 :
phy_driver_unregister ( & ks8737_driver ) ;
2010-04-29 10:12:41 +04:00
err3 :
2010-06-28 19:23:41 +04:00
phy_driver_unregister ( & ksz9021_driver ) ;
2010-04-29 10:12:41 +04:00
err2 :
phy_driver_unregister ( & ks8001_driver ) ;
err1 :
return ret ;
}
static void __exit ksphy_exit ( void )
{
phy_driver_unregister ( & ks8001_driver ) ;
2010-06-28 19:23:41 +04:00
phy_driver_unregister ( & ks8737_driver ) ;
2010-04-29 10:12:41 +04:00
phy_driver_unregister ( & ksz9021_driver ) ;
2010-06-28 19:23:41 +04:00
phy_driver_unregister ( & ks8041_driver ) ;
phy_driver_unregister ( & ks8051_driver ) ;
2010-04-29 10:12:41 +04:00
}
module_init ( ksphy_init ) ;
module_exit ( ksphy_exit ) ;
MODULE_DESCRIPTION ( " Micrel PHY driver " ) ;
MODULE_AUTHOR ( " David J. Choi " ) ;
MODULE_LICENSE ( " GPL " ) ;
2010-05-04 02:48:29 +04:00
2010-10-04 03:43:32 +04:00
static struct mdio_device_id __maybe_unused micrel_tbl [ ] = {
2010-05-04 02:48:29 +04:00
{ PHY_ID_KSZ9021 , 0x000fff10 } ,
{ PHY_ID_KS8001 , 0x00fffff0 } ,
2010-06-28 19:23:41 +04:00
{ PHY_ID_KS8737 , 0x00fffff0 } ,
{ PHY_ID_KS8041 , 0x00fffff0 } ,
{ PHY_ID_KS8051 , 0x00fffff0 } ,
2010-05-04 02:48:29 +04:00
{ }
} ;
MODULE_DEVICE_TABLE ( mdio , micrel_tbl ) ;