2005-07-31 03:31:23 +04:00
/*
* drivers / net / phy / mdio_bus . c
*
* MDIO Bus interface
*
* Author : Andy Fleming
*
* Copyright ( c ) 2004 Freescale Semiconductor , 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 .
*
*/
2012-06-09 11:49:07 +04:00
# define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
2005-07-31 03:31:23 +04:00
# include <linux/kernel.h>
# include <linux/string.h>
# include <linux/errno.h>
# include <linux/unistd.h>
# include <linux/slab.h>
# include <linux/interrupt.h>
# include <linux/init.h>
# include <linux/delay.h>
2009-02-01 11:53:34 +03:00
# include <linux/device.h>
2005-07-31 03:31:23 +04:00
# include <linux/netdevice.h>
# include <linux/etherdevice.h>
# include <linux/skbuff.h>
# include <linux/spinlock.h>
# include <linux/mm.h>
# include <linux/module.h>
# include <linux/mii.h>
# include <linux/ethtool.h>
# include <linux/phy.h>
# include <asm/io.h>
# include <asm/irq.h>
# include <asm/uaccess.h>
2008-10-09 03:29:57 +04:00
/**
2012-01-13 03:23:04 +04:00
* mdiobus_alloc_size - allocate a mii_bus structure
2012-01-21 13:03:04 +04:00
* @ size : extra amount of memory to allocate for private storage .
* If non - zero , then bus - > priv is points to that memory .
2008-10-09 03:29:57 +04:00
*
* Description : called by a bus driver to allocate an mii_bus
* structure to fill in .
*/
2012-01-13 03:23:04 +04:00
struct mii_bus * mdiobus_alloc_size ( size_t size )
2008-10-09 03:29:57 +04:00
{
2008-10-09 03:33:40 +04:00
struct mii_bus * bus ;
2012-01-13 03:23:04 +04:00
size_t aligned_size = ALIGN ( sizeof ( * bus ) , NETDEV_ALIGN ) ;
size_t alloc_size ;
/* If we alloc extra space, it should be aligned */
if ( size )
alloc_size = aligned_size + size ;
else
alloc_size = sizeof ( * bus ) ;
2008-10-09 03:33:40 +04:00
2012-01-13 03:23:04 +04:00
bus = kzalloc ( alloc_size , GFP_KERNEL ) ;
if ( bus ) {
2008-10-09 03:33:40 +04:00
bus - > state = MDIOBUS_ALLOCATED ;
2012-01-13 03:23:04 +04:00
if ( size )
bus - > priv = ( void * ) bus + aligned_size ;
}
2008-10-09 03:33:40 +04:00
return bus ;
2008-10-09 03:29:57 +04:00
}
2012-01-13 03:23:04 +04:00
EXPORT_SYMBOL ( mdiobus_alloc_size ) ;
2008-10-09 03:29:57 +04:00
2008-10-09 03:33:40 +04:00
/**
* mdiobus_release - mii_bus device release callback
2008-10-14 05:46:22 +04:00
* @ d : the target struct device that contains the mii_bus
2008-10-09 03:33:40 +04:00
*
* Description : called when the last reference to an mii_bus is
* dropped , to free the underlying memory .
*/
static void mdiobus_release ( struct device * d )
{
struct mii_bus * bus = to_mii_bus ( d ) ;
2008-12-26 03:50:41 +03:00
BUG_ON ( bus - > state ! = MDIOBUS_RELEASED & &
/* for compatibility with error handling in drivers */
bus - > state ! = MDIOBUS_ALLOCATED ) ;
2008-10-09 03:33:40 +04:00
kfree ( bus ) ;
}
static struct class mdio_bus_class = {
. name = " mdio_bus " ,
. dev_release = mdiobus_release ,
} ;
2012-05-11 09:47:01 +04:00
# if IS_ENABLED(CONFIG_OF_MDIO)
2012-05-02 19:16:37 +04:00
/* Helper function for of_mdio_find_bus */
static int of_mdio_bus_match ( struct device * dev , void * mdio_bus_np )
{
return dev - > of_node = = mdio_bus_np ;
}
/**
* of_mdio_find_bus - Given an mii_bus node , find the mii_bus .
2012-06-08 18:07:19 +04:00
* @ mdio_bus_np : Pointer to the mii_bus .
2012-05-02 19:16:37 +04:00
*
* Returns a pointer to the mii_bus , or NULL if none found .
*
* Because the association of a device_node and mii_bus is made via
* of_mdiobus_register ( ) , the mii_bus cannot be found before it is
* registered with of_mdiobus_register ( ) .
*
*/
struct mii_bus * of_mdio_find_bus ( struct device_node * mdio_bus_np )
{
struct device * d ;
if ( ! mdio_bus_np )
return NULL ;
d = class_find_device ( & mdio_bus_class , NULL , mdio_bus_np ,
of_mdio_bus_match ) ;
return d ? to_mii_bus ( d ) : NULL ;
}
EXPORT_SYMBOL ( of_mdio_find_bus ) ;
# endif
2007-03-06 13:41:48 +03:00
/**
* mdiobus_register - bring up all the PHYs on a given bus and attach them to bus
* @ bus : target mii_bus
2005-08-25 03:46:21 +04:00
*
2007-03-06 13:41:48 +03:00
* Description : Called by a bus driver to bring up all the PHYs
* on a given bus , and attach them to the bus .
*
* Returns 0 on success or < 0 on error .
2005-08-25 03:46:21 +04:00
*/
int mdiobus_register ( struct mii_bus * bus )
{
2008-12-26 03:50:41 +03:00
int i , err ;
2005-08-25 03:46:21 +04:00
if ( NULL = = bus | | NULL = = bus - > name | |
NULL = = bus - > read | |
NULL = = bus - > write )
return - EINVAL ;
2008-10-09 03:33:40 +04:00
BUG_ON ( bus - > state ! = MDIOBUS_ALLOCATED & &
bus - > state ! = MDIOBUS_UNREGISTERED ) ;
bus - > dev . parent = bus - > parent ;
bus - > dev . class = & mdio_bus_class ;
bus - > dev . groups = NULL ;
2009-02-26 13:19:36 +03:00
dev_set_name ( & bus - > dev , " %s " , bus - > id ) ;
2008-10-09 03:33:40 +04:00
err = device_register ( & bus - > dev ) ;
if ( err ) {
2012-06-09 11:49:07 +04:00
pr_err ( " mii_bus %s failed to register \n " , bus - > id ) ;
2008-10-09 03:33:40 +04:00
return - EINVAL ;
}
2008-02-20 03:13:53 +03:00
mutex_init ( & bus - > mdio_lock ) ;
2005-08-25 03:46:21 +04:00
if ( bus - > reset )
bus - > reset ( bus ) ;
for ( i = 0 ; i < PHY_MAX_ADDR ; i + + ) {
2008-08-26 15:08:46 +04:00
if ( ( bus - > phy_mask & ( 1 < < i ) ) = = 0 ) {
struct phy_device * phydev ;
2005-08-25 03:46:21 +04:00
2008-08-26 15:08:46 +04:00
phydev = mdiobus_scan ( bus , i ) ;
2008-12-26 03:50:41 +03:00
if ( IS_ERR ( phydev ) ) {
2008-08-26 15:08:46 +04:00
err = PTR_ERR ( phydev ) ;
2008-12-26 03:50:41 +03:00
goto error ;
}
2006-05-10 20:12:57 +04:00
}
2005-08-25 03:46:21 +04:00
}
2008-12-26 03:50:41 +03:00
bus - > state = MDIOBUS_REGISTERED ;
2005-08-25 03:46:21 +04:00
pr_info ( " %s: probed \n " , bus - > name ) ;
2008-12-26 03:50:41 +03:00
return 0 ;
2005-08-25 03:46:21 +04:00
2008-12-26 03:50:41 +03:00
error :
while ( - - i > = 0 ) {
if ( bus - > phy_map [ i ] )
device_unregister ( & bus - > phy_map [ i ] - > dev ) ;
}
device_del ( & bus - > dev ) ;
2005-08-25 03:46:21 +04:00
return err ;
}
EXPORT_SYMBOL ( mdiobus_register ) ;
void mdiobus_unregister ( struct mii_bus * bus )
{
int i ;
2008-10-09 03:33:40 +04:00
BUG_ON ( bus - > state ! = MDIOBUS_REGISTERED ) ;
bus - > state = MDIOBUS_UNREGISTERED ;
2008-11-09 07:34:47 +03:00
device_del ( & bus - > dev ) ;
2005-08-25 03:46:21 +04:00
for ( i = 0 ; i < PHY_MAX_ADDR ; i + + ) {
2007-12-04 16:17:33 +03:00
if ( bus - > phy_map [ i ] )
2005-08-25 03:46:21 +04:00
device_unregister ( & bus - > phy_map [ i ] - > dev ) ;
2009-04-25 16:52:46 +04:00
bus - > phy_map [ i ] = NULL ;
2005-08-25 03:46:21 +04:00
}
}
EXPORT_SYMBOL ( mdiobus_unregister ) ;
2008-10-09 03:29:57 +04:00
/**
* mdiobus_free - free a struct mii_bus
* @ bus : mii_bus to free
*
2008-10-09 03:33:40 +04:00
* This function releases the reference to the underlying device
* object in the mii_bus . If this is the last reference , the mii_bus
* will be freed .
2008-10-09 03:29:57 +04:00
*/
void mdiobus_free ( struct mii_bus * bus )
{
2008-10-09 03:33:40 +04:00
/*
* For compatibility with error handling in drivers .
*/
if ( bus - > state = = MDIOBUS_ALLOCATED ) {
kfree ( bus ) ;
return ;
}
BUG_ON ( bus - > state ! = MDIOBUS_UNREGISTERED ) ;
bus - > state = MDIOBUS_RELEASED ;
put_device ( & bus - > dev ) ;
2008-10-09 03:29:57 +04:00
}
EXPORT_SYMBOL ( mdiobus_free ) ;
2008-08-26 15:08:46 +04:00
struct phy_device * mdiobus_scan ( struct mii_bus * bus , int addr )
{
struct phy_device * phydev ;
int err ;
2012-06-27 11:33:35 +04:00
phydev = get_phy_device ( bus , addr , false ) ;
2008-08-26 15:08:46 +04:00
if ( IS_ERR ( phydev ) | | phydev = = NULL )
return phydev ;
2009-04-25 16:52:46 +04:00
err = phy_device_register ( phydev ) ;
2008-08-26 15:08:46 +04:00
if ( err ) {
phy_device_free ( phydev ) ;
2009-04-25 16:52:46 +04:00
return NULL ;
2008-08-26 15:08:46 +04:00
}
return phydev ;
}
EXPORT_SYMBOL ( mdiobus_scan ) ;
2008-09-29 21:12:35 +04:00
/**
* mdiobus_read - Convenience function for reading a given MII mgmt register
* @ bus : the mii_bus struct
* @ addr : the phy address
* @ regnum : register number to read
*
* NOTE : MUST NOT be called from interrupt context ,
* because the bus read / write functions may wait for an interrupt
* to conclude the operation .
*/
2010-03-09 12:17:42 +03:00
int mdiobus_read ( struct mii_bus * bus , int addr , u32 regnum )
2008-09-29 21:12:35 +04:00
{
int retval ;
BUG_ON ( in_interrupt ( ) ) ;
mutex_lock ( & bus - > mdio_lock ) ;
retval = bus - > read ( bus , addr , regnum ) ;
mutex_unlock ( & bus - > mdio_lock ) ;
return retval ;
}
EXPORT_SYMBOL ( mdiobus_read ) ;
/**
* mdiobus_write - Convenience function for writing a given MII mgmt register
* @ bus : the mii_bus struct
* @ addr : the phy address
* @ regnum : register number to write
* @ val : value to write to @ regnum
*
* NOTE : MUST NOT be called from interrupt context ,
* because the bus read / write functions may wait for an interrupt
* to conclude the operation .
*/
2010-03-09 12:17:42 +03:00
int mdiobus_write ( struct mii_bus * bus , int addr , u32 regnum , u16 val )
2008-09-29 21:12:35 +04:00
{
int err ;
BUG_ON ( in_interrupt ( ) ) ;
mutex_lock ( & bus - > mdio_lock ) ;
err = bus - > write ( bus , addr , regnum , val ) ;
mutex_unlock ( & bus - > mdio_lock ) ;
return err ;
}
EXPORT_SYMBOL ( mdiobus_write ) ;
2007-03-06 13:41:48 +03:00
/**
* mdio_bus_match - determine if given PHY driver supports the given PHY device
* @ dev : target PHY device
* @ drv : given PHY driver
2005-07-31 03:31:23 +04:00
*
2007-03-06 13:41:48 +03:00
* Description : Given a PHY device , and a PHY driver , return 1 if
* the driver supports the device . Otherwise , return 0.
2005-07-31 03:31:23 +04:00
*/
static int mdio_bus_match ( struct device * dev , struct device_driver * drv )
{
struct phy_device * phydev = to_phy_device ( dev ) ;
struct phy_driver * phydrv = to_phy_driver ( drv ) ;
2007-06-28 22:26:06 +04:00
return ( ( phydrv - > phy_id & phydrv - > phy_id_mask ) = =
( phydev - > phy_id & phydrv - > phy_id_mask ) ) ;
2005-07-31 03:31:23 +04:00
}
2009-12-30 11:23:30 +03:00
# ifdef CONFIG_PM
2009-02-01 11:53:34 +03:00
static bool mdio_bus_phy_may_suspend ( struct phy_device * phydev )
{
struct device_driver * drv = phydev - > dev . driver ;
struct phy_driver * phydrv = to_phy_driver ( drv ) ;
struct net_device * netdev = phydev - > attached_dev ;
if ( ! drv | | ! phydrv - > suspend )
return false ;
/* PHY not attached? May suspend. */
if ( ! netdev )
return true ;
/*
* Don ' t suspend PHY if the attched netdev parent may wakeup .
* The parent may point to a PCI device , as in tg3 driver .
*/
if ( netdev - > dev . parent & & device_may_wakeup ( netdev - > dev . parent ) )
return false ;
/*
* Also don ' t suspend PHY if the netdev itself may wakeup . This
* is the case for devices w / o underlaying pwr . mgmt . aware bus ,
* e . g . SoC devices .
*/
if ( device_may_wakeup ( & netdev - > dev ) )
return false ;
return true ;
}
2009-12-30 11:23:30 +03:00
static int mdio_bus_suspend ( struct device * dev )
2005-07-31 03:31:23 +04:00
{
2009-02-01 11:53:34 +03:00
struct phy_driver * phydrv = to_phy_driver ( dev - > driver ) ;
2008-11-29 03:24:56 +03:00
struct phy_device * phydev = to_phy_device ( dev ) ;
2005-07-31 03:31:23 +04:00
2009-12-30 11:23:28 +03:00
/*
* We must stop the state machine manually , otherwise it stops out of
* control , possibly with the phydev - > lock held . Upon resume , netdev
* may call phy routines that try to grab the same lock , and that may
* lead to a deadlock .
*/
2010-09-14 02:12:01 +04:00
if ( phydev - > attached_dev & & phydev - > adjust_link )
2009-12-30 11:23:28 +03:00
phy_stop_machine ( phydev ) ;
2009-02-01 11:53:34 +03:00
if ( ! mdio_bus_phy_may_suspend ( phydev ) )
return 0 ;
2009-12-30 11:23:28 +03:00
2009-02-01 11:53:34 +03:00
return phydrv - > suspend ( phydev ) ;
2005-07-31 03:31:23 +04:00
}
2009-12-30 11:23:30 +03:00
static int mdio_bus_resume ( struct device * dev )
2005-07-31 03:31:23 +04:00
{
2009-02-01 11:53:34 +03:00
struct phy_driver * phydrv = to_phy_driver ( dev - > driver ) ;
2008-11-29 03:24:56 +03:00
struct phy_device * phydev = to_phy_device ( dev ) ;
2009-12-30 11:23:28 +03:00
int ret ;
2005-07-31 03:31:23 +04:00
2009-02-01 11:53:34 +03:00
if ( ! mdio_bus_phy_may_suspend ( phydev ) )
2009-12-30 11:23:28 +03:00
goto no_resume ;
ret = phydrv - > resume ( phydev ) ;
if ( ret < 0 )
return ret ;
no_resume :
2010-09-14 02:12:01 +04:00
if ( phydev - > attached_dev & & phydev - > adjust_link )
2009-12-30 11:23:28 +03:00
phy_start_machine ( phydev , NULL ) ;
return 0 ;
2005-07-31 03:31:23 +04:00
}
2009-12-30 11:23:30 +03:00
static int mdio_bus_restore ( struct device * dev )
{
struct phy_device * phydev = to_phy_device ( dev ) ;
struct net_device * netdev = phydev - > attached_dev ;
int ret ;
if ( ! netdev )
return 0 ;
ret = phy_init_hw ( phydev ) ;
if ( ret < 0 )
return ret ;
/* The PHY needs to renegotiate. */
phydev - > link = 0 ;
phydev - > state = PHY_UP ;
phy_start_machine ( phydev , NULL ) ;
return 0 ;
}
static struct dev_pm_ops mdio_bus_pm_ops = {
. suspend = mdio_bus_suspend ,
. resume = mdio_bus_resume ,
. freeze = mdio_bus_suspend ,
. thaw = mdio_bus_resume ,
. restore = mdio_bus_restore ,
} ;
# define MDIO_BUS_PM_OPS (&mdio_bus_pm_ops)
# else
# define MDIO_BUS_PM_OPS NULL
# endif /* CONFIG_PM */
2005-07-31 03:31:23 +04:00
struct bus_type mdio_bus_type = {
. name = " mdio_bus " ,
. match = mdio_bus_match ,
2009-12-30 11:23:30 +03:00
. pm = MDIO_BUS_PM_OPS ,
2005-07-31 03:31:23 +04:00
} ;
2006-08-15 10:00:29 +04:00
EXPORT_SYMBOL ( mdio_bus_type ) ;
2005-07-31 03:31:23 +04:00
2005-08-11 10:07:25 +04:00
int __init mdio_bus_init ( void )
2005-07-31 03:31:23 +04:00
{
2008-10-09 03:33:40 +04:00
int ret ;
ret = class_register ( & mdio_bus_class ) ;
if ( ! ret ) {
ret = bus_register ( & mdio_bus_type ) ;
if ( ret )
class_unregister ( & mdio_bus_class ) ;
}
return ret ;
2005-07-31 03:31:23 +04:00
}
2005-09-04 01:05:06 +04:00
void mdio_bus_exit ( void )
2005-08-25 03:46:21 +04:00
{
2008-10-09 03:33:40 +04:00
class_unregister ( & mdio_bus_class ) ;
2005-08-25 03:46:21 +04:00
bus_unregister ( & mdio_bus_type ) ;
}