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 .
*
*/
# 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
/**
* mdiobus_alloc - allocate a mii_bus structure
*
* Description : called by a bus driver to allocate an mii_bus
* structure to fill in .
*/
struct mii_bus * mdiobus_alloc ( void )
{
2008-10-09 03:33:40 +04:00
struct mii_bus * bus ;
bus = kzalloc ( sizeof ( * bus ) , GFP_KERNEL ) ;
if ( bus ! = NULL )
bus - > state = MDIOBUS_ALLOCATED ;
return bus ;
2008-10-09 03:29:57 +04:00
}
EXPORT_SYMBOL ( mdiobus_alloc ) ;
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 ,
} ;
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 ) {
printk ( KERN_ERR " mii_bus %s failed to register \n " , bus - > id ) ;
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
bus - > phy_map [ i ] = NULL ;
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 ) ;
}
}
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 ;
phydev = get_phy_device ( bus , addr ) ;
if ( IS_ERR ( phydev ) | | phydev = = NULL )
return phydev ;
/* There's a PHY at this address
* We need to set :
* 1 ) IRQ
* 2 ) bus_id
* 3 ) parent
* 4 ) bus
* 5 ) mii_bus
* And , we need to register it */
phydev - > irq = bus - > irq ! = NULL ? bus - > irq [ addr ] : PHY_POLL ;
2008-10-01 19:41:33 +04:00
phydev - > dev . parent = bus - > parent ;
2008-08-26 15:08:46 +04:00
phydev - > dev . bus = & mdio_bus_type ;
2008-11-11 00:55:14 +03:00
dev_set_name ( & phydev - > dev , PHY_ID_FMT , bus - > id , addr ) ;
2008-08-26 15:08:46 +04:00
phydev - > bus = bus ;
/* Run all of the fixups for this PHY */
phy_scan_fixups ( phydev ) ;
err = device_register ( & phydev - > dev ) ;
if ( err ) {
printk ( KERN_ERR " phy %d failed to register \n " , addr ) ;
phy_device_free ( phydev ) ;
phydev = NULL ;
}
bus - > phy_map [ addr ] = phydev ;
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 .
*/
int mdiobus_read ( struct mii_bus * bus , int addr , u16 regnum )
{
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 .
*/
int mdiobus_write ( struct mii_bus * bus , int addr , u16 regnum , u16 val )
{
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-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 ;
}
2005-07-31 03:31:23 +04:00
/* Suspend and resume. Copied from platform_suspend and
* platform_resume
*/
2005-09-04 02:56:56 +04:00
static int mdio_bus_suspend ( struct device * dev , pm_message_t state )
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-02-01 11:53:34 +03:00
if ( ! mdio_bus_phy_may_suspend ( phydev ) )
return 0 ;
return phydrv - > suspend ( phydev ) ;
2005-07-31 03:31:23 +04:00
}
static int mdio_bus_resume ( struct device * dev )
{
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-02-01 11:53:34 +03:00
if ( ! mdio_bus_phy_may_suspend ( phydev ) )
return 0 ;
return phydrv - > resume ( phydev ) ;
2005-07-31 03:31:23 +04:00
}
struct bus_type mdio_bus_type = {
. name = " mdio_bus " ,
. match = mdio_bus_match ,
. suspend = mdio_bus_suspend ,
. resume = mdio_bus_resume ,
} ;
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 ) ;
}