2010-04-29 06:12:41 +00:00
/*
* drivers / net / phy / micrel . c
*
* Driver for Micrel PHYs
*
* Author : David J . Choi
*
2013-01-23 14:05:15 +00:00
* Copyright ( c ) 2010 - 2013 Micrel , Inc .
2010-04-29 06:12:41 +00:00
*
* 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 .
*
2013-01-23 14:05:15 +00:00
* Support : Micrel Phys :
* Giga phys : ksz9021 , ksz9031
* 100 / 10 Phys : ksz8001 , ksz8721 , ksz8737 , ksz8041
* ksz8021 , ksz8031 , ksz8051 ,
* ksz8081 , ksz8091 ,
* ksz8061 ,
* Switch : ksz8873 , ksz886x
2010-04-29 06:12:41 +00:00
*/
# include <linux/kernel.h>
# include <linux/module.h>
# include <linux/phy.h>
2011-02-14 02:05:33 +00:00
# include <linux/micrel_phy.h>
2010-04-29 06:12:41 +00:00
2012-09-23 16:58:49 +00:00
/* Operation Mode Strap Override */
# define MII_KSZPHY_OMSO 0x16
# define KSZPHY_OMSO_B_CAST_OFF (1 << 9)
# define KSZPHY_OMSO_RMII_OVERRIDE (1 << 1)
# define KSZPHY_OMSO_MII_OVERRIDE (1 << 0)
2010-06-28 15:23:41 +00: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)
2011-02-14 02:05:33 +00:00
# define KSZ8051_RMII_50MHZ_CLK (1 << 7)
2010-06-28 15:23:41 +00:00
2013-03-10 22:50:03 +00:00
static int ksz_config_flags ( struct phy_device * phydev )
{
int regval ;
if ( phydev - > dev_flags & MICREL_PHY_50MHZ_CLK ) {
regval = phy_read ( phydev , MII_KSZPHY_CTRL ) ;
regval | = KSZ8051_RMII_50MHZ_CLK ;
return phy_write ( phydev , MII_KSZPHY_CTRL , regval ) ;
}
return 0 ;
}
2010-06-28 15:23:41 +00:00
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 06:12:41 +00:00
static int kszphy_config_init ( struct phy_device * phydev )
{
return 0 ;
}
2012-09-23 16:58:49 +00:00
static int ksz8021_config_init ( struct phy_device * phydev )
{
2013-03-10 22:50:03 +00:00
int rc ;
2012-09-23 16:58:49 +00:00
const u16 val = KSZPHY_OMSO_B_CAST_OFF | KSZPHY_OMSO_RMII_OVERRIDE ;
phy_write ( phydev , MII_KSZPHY_OMSO , val ) ;
2013-03-10 22:50:03 +00:00
rc = ksz_config_flags ( phydev ) ;
return rc < 0 ? rc : 0 ;
2012-09-23 16:58:49 +00:00
}
2011-02-14 02:05:33 +00:00
static int ks8051_config_init ( struct phy_device * phydev )
{
2013-03-10 22:50:03 +00:00
int rc ;
2011-02-14 02:05:33 +00:00
2013-03-10 22:50:03 +00:00
rc = ksz_config_flags ( phydev ) ;
return rc < 0 ? rc : 0 ;
2011-02-14 02:05:33 +00:00
}
2012-11-21 05:38:07 +00:00
# define KSZ8873MLL_GLOBAL_CONTROL_4 0x06
# define KSZ8873MLL_GLOBAL_CONTROL_4_DUPLEX (1 << 6)
# define KSZ8873MLL_GLOBAL_CONTROL_4_SPEED (1 << 4)
int ksz8873mll_read_status ( struct phy_device * phydev )
{
int regval ;
/* dummy read */
regval = phy_read ( phydev , KSZ8873MLL_GLOBAL_CONTROL_4 ) ;
regval = phy_read ( phydev , KSZ8873MLL_GLOBAL_CONTROL_4 ) ;
if ( regval & KSZ8873MLL_GLOBAL_CONTROL_4_DUPLEX )
phydev - > duplex = DUPLEX_HALF ;
else
phydev - > duplex = DUPLEX_FULL ;
if ( regval & KSZ8873MLL_GLOBAL_CONTROL_4_SPEED )
phydev - > speed = SPEED_10 ;
else
phydev - > speed = SPEED_100 ;
phydev - > link = 1 ;
phydev - > pause = phydev - > asym_pause = 0 ;
return 0 ;
}
static int ksz8873mll_config_aneg ( struct phy_device * phydev )
{
return 0 ;
}
2012-07-04 05:44:34 +00:00
static struct phy_driver ksphy_driver [ ] = {
{
2010-06-28 15:23:41 +00:00
. 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 , } ,
2012-09-23 16:58:49 +00:00
} , {
. phy_id = PHY_ID_KSZ8021 ,
. phy_id_mask = 0x00ffffff ,
2013-01-23 14:05:15 +00:00
. name = " Micrel KSZ8021 or KSZ8031 " ,
2012-09-23 16:58:49 +00:00
. features = ( PHY_BASIC_FEATURES | SUPPORTED_Pause |
SUPPORTED_Asym_Pause ) ,
. flags = PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT ,
. config_init = ksz8021_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 , } ,
2013-03-10 22:50:02 +00:00
} , {
. phy_id = PHY_ID_KSZ8031 ,
. phy_id_mask = 0x00ffffff ,
. name = " Micrel KSZ8031 " ,
. features = ( PHY_BASIC_FEATURES | SUPPORTED_Pause |
SUPPORTED_Asym_Pause ) ,
. flags = PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT ,
. config_init = ksz8021_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 , } ,
2012-07-04 05:44:34 +00:00
} , {
2012-09-23 16:58:50 +00:00
. phy_id = PHY_ID_KSZ8041 ,
2010-06-28 15:23:41 +00:00
. phy_id_mask = 0x00fffff0 ,
2012-09-23 16:58:50 +00:00
. name = " Micrel KSZ8041 " ,
2010-06-28 15:23:41 +00:00
. 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 , } ,
2012-07-04 05:44:34 +00:00
} , {
2012-09-23 16:58:50 +00:00
. phy_id = PHY_ID_KSZ8051 ,
2010-04-29 06:12:41 +00:00
. phy_id_mask = 0x00fffff0 ,
2012-09-23 16:58:50 +00:00
. name = " Micrel KSZ8051 " ,
2010-06-28 15:23:41 +00:00
. features = ( PHY_BASIC_FEATURES | SUPPORTED_Pause
| SUPPORTED_Asym_Pause ) ,
. flags = PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT ,
2011-02-14 02:05:33 +00:00
. config_init = ks8051_config_init ,
2010-04-29 06:12:41 +00:00
. config_aneg = genphy_config_aneg ,
. read_status = genphy_read_status ,
2010-06-28 15:23:41 +00:00
. ack_interrupt = kszphy_ack_interrupt ,
. config_intr = kszphy_config_intr ,
2010-04-29 06:12:41 +00:00
. driver = { . owner = THIS_MODULE , } ,
2012-07-04 05:44:34 +00:00
} , {
2012-09-23 16:58:50 +00:00
. phy_id = PHY_ID_KSZ8001 ,
. name = " Micrel KSZ8001 or KS8721 " ,
2012-06-17 22:52:09 +00:00
. phy_id_mask = 0x00ffffff ,
2010-06-28 15:23:41 +00:00
. features = ( PHY_BASIC_FEATURES | SUPPORTED_Pause ) ,
. flags = PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT ,
2010-04-29 06:12:41 +00:00
. config_init = kszphy_config_init ,
. config_aneg = genphy_config_aneg ,
. read_status = genphy_read_status ,
2010-06-28 15:23:41 +00:00
. ack_interrupt = kszphy_ack_interrupt ,
. config_intr = kszphy_config_intr ,
2010-04-29 06:12:41 +00:00
. driver = { . owner = THIS_MODULE , } ,
2013-01-23 14:05:15 +00:00
} , {
. phy_id = PHY_ID_KSZ8081 ,
. name = " Micrel KSZ8081 or KSZ8091 " ,
. phy_id_mask = 0x00fffff0 ,
. 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 = kszphy_config_intr ,
. driver = { . owner = THIS_MODULE , } ,
} , {
. phy_id = PHY_ID_KSZ8061 ,
. name = " Micrel KSZ8061 " ,
. phy_id_mask = 0x00fffff0 ,
. 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 = kszphy_config_intr ,
. driver = { . owner = THIS_MODULE , } ,
2012-07-04 05:44:34 +00:00
} , {
2010-04-29 06:12:41 +00:00
. phy_id = PHY_ID_KSZ9021 ,
2012-06-17 22:52:09 +00:00
. phy_id_mask = 0x000ffffe ,
2010-04-29 06:12:41 +00:00
. name = " Micrel KSZ9021 Gigabit PHY " ,
2013-02-28 08:45:22 +00:00
. features = ( PHY_GBIT_FEATURES | SUPPORTED_Pause ) ,
2010-06-28 15:23:41 +00:00
. flags = PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT ,
2010-04-29 06:12:41 +00:00
. config_init = kszphy_config_init ,
. config_aneg = genphy_config_aneg ,
. read_status = genphy_read_status ,
2010-06-28 15:23:41 +00:00
. ack_interrupt = kszphy_ack_interrupt ,
. config_intr = ksz9021_config_intr ,
2010-04-29 06:12:41 +00:00
. driver = { . owner = THIS_MODULE , } ,
2013-01-23 14:05:15 +00:00
} , {
. phy_id = PHY_ID_KSZ9031 ,
. phy_id_mask = 0x00fffff0 ,
. name = " Micrel KSZ9031 Gigabit PHY " ,
. features = ( PHY_GBIT_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 = ksz9021_config_intr ,
. driver = { . owner = THIS_MODULE , } ,
2012-11-21 05:38:07 +00:00
} , {
. phy_id = PHY_ID_KSZ8873MLL ,
. phy_id_mask = 0x00fffff0 ,
. name = " Micrel KSZ8873MLL Switch " ,
. features = ( SUPPORTED_Pause | SUPPORTED_Asym_Pause ) ,
. flags = PHY_HAS_MAGICANEG ,
. config_init = kszphy_config_init ,
. config_aneg = ksz8873mll_config_aneg ,
. read_status = ksz8873mll_read_status ,
. driver = { . owner = THIS_MODULE , } ,
2013-01-23 14:05:15 +00:00
} , {
. phy_id = PHY_ID_KSZ886X ,
. phy_id_mask = 0x00fffff0 ,
. name = " Micrel KSZ886X Switch " ,
. 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 ,
. driver = { . owner = THIS_MODULE , } ,
2012-07-04 05:44:34 +00:00
} } ;
2010-04-29 06:12:41 +00:00
static int __init ksphy_init ( void )
{
2012-07-04 05:44:34 +00:00
return phy_drivers_register ( ksphy_driver ,
ARRAY_SIZE ( ksphy_driver ) ) ;
2010-04-29 06:12:41 +00:00
}
static void __exit ksphy_exit ( void )
{
2012-07-04 05:44:34 +00:00
phy_drivers_unregister ( ksphy_driver ,
ARRAY_SIZE ( ksphy_driver ) ) ;
2010-04-29 06:12:41 +00:00
}
module_init ( ksphy_init ) ;
module_exit ( ksphy_exit ) ;
MODULE_DESCRIPTION ( " Micrel PHY driver " ) ;
MODULE_AUTHOR ( " David J. Choi " ) ;
MODULE_LICENSE ( " GPL " ) ;
2010-05-03 15:48:29 -07:00
2010-10-03 23:43:32 +00:00
static struct mdio_device_id __maybe_unused micrel_tbl [ ] = {
2012-06-17 22:52:09 +00:00
{ PHY_ID_KSZ9021 , 0x000ffffe } ,
2013-01-23 14:05:15 +00:00
{ PHY_ID_KSZ9031 , 0x00fffff0 } ,
2012-09-23 16:58:50 +00:00
{ PHY_ID_KSZ8001 , 0x00ffffff } ,
2010-06-28 15:23:41 +00:00
{ PHY_ID_KS8737 , 0x00fffff0 } ,
2012-09-23 16:58:49 +00:00
{ PHY_ID_KSZ8021 , 0x00ffffff } ,
2013-03-10 22:50:02 +00:00
{ PHY_ID_KSZ8031 , 0x00ffffff } ,
2012-09-23 16:58:50 +00:00
{ PHY_ID_KSZ8041 , 0x00fffff0 } ,
{ PHY_ID_KSZ8051 , 0x00fffff0 } ,
2013-01-23 14:05:15 +00:00
{ PHY_ID_KSZ8061 , 0x00fffff0 } ,
{ PHY_ID_KSZ8081 , 0x00fffff0 } ,
2012-11-21 05:38:07 +00:00
{ PHY_ID_KSZ8873MLL , 0x00fffff0 } ,
2013-01-23 14:05:15 +00:00
{ PHY_ID_KSZ886X , 0x00fffff0 } ,
2010-05-03 15:48:29 -07:00
{ }
} ;
MODULE_DEVICE_TABLE ( mdio , micrel_tbl ) ;