2005-07-31 03:31:23 +04:00
/*
* drivers / net / phy / phy_device . c
*
* Framework for finding and configuring PHYs .
* Also contains generic PHY driver
*
* 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>
# include <linux/netdevice.h>
# include <linux/etherdevice.h>
# include <linux/skbuff.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>
2005-12-14 02:33:49 +03:00
MODULE_DESCRIPTION ( " PHY library " ) ;
MODULE_AUTHOR ( " Andy Fleming " ) ;
MODULE_LICENSE ( " GPL " ) ;
2007-12-04 16:17:33 +03:00
void phy_device_free ( struct phy_device * phydev )
{
2013-02-28 05:01:52 +04:00
put_device ( & phydev - > dev ) ;
2007-12-04 16:17:33 +03:00
}
2009-04-25 16:52:46 +04:00
EXPORT_SYMBOL ( phy_device_free ) ;
2007-12-04 16:17:33 +03:00
static void phy_device_release ( struct device * dev )
{
2013-02-28 05:01:52 +04:00
kfree ( to_phy_device ( dev ) ) ;
2007-12-04 16:17:33 +03:00
}
2009-04-25 16:52:46 +04:00
static struct phy_driver genphy_driver ;
extern int mdio_bus_init ( void ) ;
extern void mdio_bus_exit ( void ) ;
2008-04-19 02:29:54 +04:00
static LIST_HEAD ( phy_fixup_list ) ;
static DEFINE_MUTEX ( phy_fixup_lock ) ;
2010-10-21 12:37:41 +04:00
static int phy_attach_direct ( struct net_device * dev , struct phy_device * phydev ,
u32 flags , phy_interface_t interface ) ;
2008-04-19 02:29:54 +04:00
/*
* Creates a new phy_fixup and adds it to the list
* @ bus_id : A string which matches phydev - > dev . bus_id ( or PHY_ANY_ID )
* @ phy_uid : Used to match against phydev - > phy_id ( the UID of the PHY )
* It can also be PHY_ANY_UID
* @ phy_uid_mask : Applied to phydev - > phy_id and fixup - > phy_uid before
* comparison
* @ run : The actual code to be run when a matching PHY is found
*/
int phy_register_fixup ( const char * bus_id , u32 phy_uid , u32 phy_uid_mask ,
int ( * run ) ( struct phy_device * ) )
{
struct phy_fixup * fixup ;
fixup = kzalloc ( sizeof ( struct phy_fixup ) , GFP_KERNEL ) ;
if ( ! fixup )
return - ENOMEM ;
2008-11-11 00:55:14 +03:00
strlcpy ( fixup - > bus_id , bus_id , sizeof ( fixup - > bus_id ) ) ;
2008-04-19 02:29:54 +04:00
fixup - > phy_uid = phy_uid ;
fixup - > phy_uid_mask = phy_uid_mask ;
fixup - > run = run ;
mutex_lock ( & phy_fixup_lock ) ;
list_add_tail ( & fixup - > list , & phy_fixup_list ) ;
mutex_unlock ( & phy_fixup_lock ) ;
return 0 ;
}
EXPORT_SYMBOL ( phy_register_fixup ) ;
/* Registers a fixup to be run on any PHY with the UID in phy_uid */
int phy_register_fixup_for_uid ( u32 phy_uid , u32 phy_uid_mask ,
int ( * run ) ( struct phy_device * ) )
{
return phy_register_fixup ( PHY_ANY_ID , phy_uid , phy_uid_mask , run ) ;
}
EXPORT_SYMBOL ( phy_register_fixup_for_uid ) ;
/* Registers a fixup to be run on the PHY with id string bus_id */
int phy_register_fixup_for_id ( const char * bus_id ,
int ( * run ) ( struct phy_device * ) )
{
return phy_register_fixup ( bus_id , PHY_ANY_UID , 0xffffffff , run ) ;
}
EXPORT_SYMBOL ( phy_register_fixup_for_id ) ;
/*
* Returns 1 if fixup matches phydev in bus_id and phy_uid .
* Fixups can be set to match any in one or more fields .
*/
static int phy_needs_fixup ( struct phy_device * phydev , struct phy_fixup * fixup )
{
2008-11-11 00:55:14 +03:00
if ( strcmp ( fixup - > bus_id , dev_name ( & phydev - > dev ) ) ! = 0 )
2008-04-19 02:29:54 +04:00
if ( strcmp ( fixup - > bus_id , PHY_ANY_ID ) ! = 0 )
return 0 ;
if ( ( fixup - > phy_uid & fixup - > phy_uid_mask ) ! =
( phydev - > phy_id & fixup - > phy_uid_mask ) )
if ( fixup - > phy_uid ! = PHY_ANY_UID )
return 0 ;
return 1 ;
}
/* Runs any matching fixups for this phydev */
int phy_scan_fixups ( struct phy_device * phydev )
{
struct phy_fixup * fixup ;
mutex_lock ( & phy_fixup_lock ) ;
list_for_each_entry ( fixup , & phy_fixup_list , list ) {
if ( phy_needs_fixup ( phydev , fixup ) ) {
int err ;
err = fixup - > run ( phydev ) ;
2009-07-13 15:23:39 +04:00
if ( err < 0 ) {
mutex_unlock ( & phy_fixup_lock ) ;
2008-04-19 02:29:54 +04:00
return err ;
2009-07-13 15:23:39 +04:00
}
2008-04-19 02:29:54 +04:00
}
}
mutex_unlock ( & phy_fixup_lock ) ;
return 0 ;
}
EXPORT_SYMBOL ( phy_scan_fixups ) ;
2012-06-27 11:33:35 +04:00
struct phy_device * phy_device_create ( struct mii_bus * bus , int addr , int phy_id ,
bool is_c45 , struct phy_c45_device_ids * c45_ids )
2006-08-15 10:00:29 +04:00
{
struct phy_device * dev ;
2010-04-02 05:05:27 +04:00
2006-08-15 10:00:29 +04:00
/* We allocate the device, and initialize the
* default values */
2006-12-13 11:34:52 +03:00
dev = kzalloc ( sizeof ( * dev ) , GFP_KERNEL ) ;
2006-08-15 10:00:29 +04:00
if ( NULL = = dev )
return ( struct phy_device * ) PTR_ERR ( ( void * ) - ENOMEM ) ;
2007-12-04 16:17:33 +03:00
dev - > dev . release = phy_device_release ;
2006-08-15 10:00:29 +04:00
dev - > speed = 0 ;
dev - > duplex = - 1 ;
dev - > pause = dev - > asym_pause = 0 ;
dev - > link = 1 ;
2006-12-01 21:01:06 +03:00
dev - > interface = PHY_INTERFACE_MODE_GMII ;
2006-08-15 10:00:29 +04:00
dev - > autoneg = AUTONEG_ENABLE ;
2012-06-27 11:33:35 +04:00
dev - > is_c45 = is_c45 ;
2006-08-15 10:00:29 +04:00
dev - > addr = addr ;
dev - > phy_id = phy_id ;
2012-06-27 11:33:35 +04:00
if ( c45_ids )
dev - > c45_ids = * c45_ids ;
2006-08-15 10:00:29 +04:00
dev - > bus = bus ;
2009-04-25 16:52:46 +04:00
dev - > dev . parent = bus - > parent ;
dev - > dev . bus = & mdio_bus_type ;
dev - > irq = bus - > irq ! = NULL ? bus - > irq [ addr ] : PHY_POLL ;
dev_set_name ( & dev - > dev , PHY_ID_FMT , bus - > id , addr ) ;
2006-08-15 10:00:29 +04:00
dev - > state = PHY_DOWN ;
2008-01-29 19:05:09 +03:00
mutex_init ( & dev - > lock ) ;
2010-01-18 08:37:16 +03:00
INIT_DELAYED_WORK ( & dev - > state_queue , phy_state_machine ) ;
2013-05-20 02:53:43 +04:00
INIT_WORK ( & dev - > phy_queue , phy_change ) ;
2006-08-15 10:00:29 +04:00
2010-04-02 05:05:27 +04:00
/* Request the appropriate module unconditionally; don't
bother trying to do so only if it isn ' t already loaded ,
because that gets complicated . A hotplug event would have
done an unconditional modprobe anyway .
We don ' t do normal hotplug because it won ' t work for MDIO
- - because it relies on the device staying around for long
enough for the driver to get loaded . With MDIO , the NIC
driver will get bored and give up as soon as it finds that
there ' s no driver _already_ loaded . */
request_module ( MDIO_MODULE_PREFIX MDIO_ID_FMT , MDIO_ID_ARGS ( phy_id ) ) ;
2013-02-28 05:01:52 +04:00
device_initialize ( & dev - > dev ) ;
2006-08-15 10:00:29 +04:00
return dev ;
}
2012-06-27 11:33:35 +04:00
EXPORT_SYMBOL ( phy_device_create ) ;
/**
* get_phy_c45_ids - reads the specified addr for its 802.3 - c45 IDs .
* @ bus : the target MII bus
* @ addr : PHY address on the MII bus
* @ phy_id : where to store the ID retrieved .
* @ c45_ids : where to store the c45 ID information .
*
* If the PHY devices - in - package appears to be valid , it and the
* corresponding identifiers are stored in @ c45_ids , zero is stored
* in @ phy_id . Otherwise 0xffffffff is stored in @ phy_id . Returns
* zero on success .
*
*/
static int get_phy_c45_ids ( struct mii_bus * bus , int addr , u32 * phy_id ,
struct phy_c45_device_ids * c45_ids ) {
int phy_reg ;
int i , reg_addr ;
const int num_ids = ARRAY_SIZE ( c45_ids - > device_ids ) ;
/* Find first non-zero Devices In package. Device
* zero is reserved , so don ' t probe it .
*/
for ( i = 1 ;
i < num_ids & & c45_ids - > devices_in_package = = 0 ;
i + + ) {
reg_addr = MII_ADDR_C45 | i < < 16 | 6 ;
phy_reg = mdiobus_read ( bus , addr , reg_addr ) ;
if ( phy_reg < 0 )
return - EIO ;
c45_ids - > devices_in_package = ( phy_reg & 0xffff ) < < 16 ;
reg_addr = MII_ADDR_C45 | i < < 16 | 5 ;
phy_reg = mdiobus_read ( bus , addr , reg_addr ) ;
if ( phy_reg < 0 )
return - EIO ;
c45_ids - > devices_in_package | = ( phy_reg & 0xffff ) ;
/* If mostly Fs, there is no device there,
* let ' s get out of here .
*/
if ( ( c45_ids - > devices_in_package & 0x1fffffff ) = = 0x1fffffff ) {
* phy_id = 0xffffffff ;
return 0 ;
}
}
/* Now probe Device Identifiers for each device present. */
for ( i = 1 ; i < num_ids ; i + + ) {
if ( ! ( c45_ids - > devices_in_package & ( 1 < < i ) ) )
continue ;
reg_addr = MII_ADDR_C45 | i < < 16 | MII_PHYSID1 ;
phy_reg = mdiobus_read ( bus , addr , reg_addr ) ;
if ( phy_reg < 0 )
return - EIO ;
c45_ids - > device_ids [ i ] = ( phy_reg & 0xffff ) < < 16 ;
reg_addr = MII_ADDR_C45 | i < < 16 | MII_PHYSID2 ;
phy_reg = mdiobus_read ( bus , addr , reg_addr ) ;
if ( phy_reg < 0 )
return - EIO ;
c45_ids - > device_ids [ i ] | = ( phy_reg & 0xffff ) ;
}
* phy_id = 0 ;
return 0 ;
}
2006-08-15 10:00:29 +04:00
2007-03-06 13:41:48 +03:00
/**
2008-04-15 20:49:21 +04:00
* get_phy_id - reads the specified addr for its ID .
2007-03-06 13:41:48 +03:00
* @ bus : the target MII bus
* @ addr : PHY address on the MII bus
2008-04-15 20:49:21 +04:00
* @ phy_id : where to store the ID retrieved .
2012-06-27 11:33:35 +04:00
* @ is_c45 : If true the PHY uses the 802.3 clause 45 protocol
* @ c45_ids : where to store the c45 ID information .
*
* Description : In the case of a 802.3 - c22 PHY , reads the ID registers
* of the PHY at @ addr on the @ bus , stores it in @ phy_id and returns
* zero on success .
*
* In the case of a 802.3 - c45 PHY , get_phy_c45_ids ( ) is invoked , and
* its return value is in turn returned .
2005-07-31 03:31:23 +04:00
*
*/
2012-06-27 11:33:35 +04:00
static int get_phy_id ( struct mii_bus * bus , int addr , u32 * phy_id ,
bool is_c45 , struct phy_c45_device_ids * c45_ids )
2005-07-31 03:31:23 +04:00
{
int phy_reg ;
2012-06-27 11:33:35 +04:00
if ( is_c45 )
return get_phy_c45_ids ( bus , addr , phy_id , c45_ids ) ;
2005-07-31 03:31:23 +04:00
/* Grab the bits from PHYIR1, and put them
* in the upper half */
2011-09-30 15:51:22 +04:00
phy_reg = mdiobus_read ( bus , addr , MII_PHYSID1 ) ;
2005-07-31 03:31:23 +04:00
if ( phy_reg < 0 )
2008-04-15 20:49:21 +04:00
return - EIO ;
2005-07-31 03:31:23 +04:00
2008-04-15 20:49:21 +04:00
* phy_id = ( phy_reg & 0xffff ) < < 16 ;
2005-07-31 03:31:23 +04:00
/* Grab the bits from PHYIR2, and put them in the lower half */
2011-09-30 15:51:22 +04:00
phy_reg = mdiobus_read ( bus , addr , MII_PHYSID2 ) ;
2005-07-31 03:31:23 +04:00
if ( phy_reg < 0 )
2008-04-15 20:49:21 +04:00
return - EIO ;
* phy_id | = ( phy_reg & 0xffff ) ;
return 0 ;
}
/**
* get_phy_device - reads the specified PHY device and returns its @ phy_device struct
* @ bus : the target MII bus
* @ addr : PHY address on the MII bus
2012-06-27 11:33:35 +04:00
* @ is_c45 : If true the PHY uses the 802.3 clause 45 protocol
2008-04-15 20:49:21 +04:00
*
* Description : Reads the ID registers of the PHY at @ addr on the
* @ bus , then allocates and returns the phy_device to represent it .
*/
2012-06-27 11:33:35 +04:00
struct phy_device * get_phy_device ( struct mii_bus * bus , int addr , bool is_c45 )
2008-04-15 20:49:21 +04:00
{
2012-06-27 11:33:35 +04:00
struct phy_c45_device_ids c45_ids = { 0 } ;
2012-06-28 08:28:14 +04:00
struct phy_device * dev = NULL ;
u32 phy_id = 0 ;
2008-04-15 20:49:21 +04:00
int r ;
2005-07-31 03:31:23 +04:00
2012-06-27 11:33:35 +04:00
r = get_phy_id ( bus , addr , & phy_id , is_c45 , & c45_ids ) ;
2008-04-15 20:49:21 +04:00
if ( r )
return ERR_PTR ( r ) ;
2005-07-31 03:31:23 +04:00
2008-11-21 07:43:18 +03:00
/* If the phy_id is mostly Fs, there is no device there */
if ( ( phy_id & 0x1fffffff ) = = 0x1fffffff )
return NULL ;
2012-06-27 11:33:35 +04:00
dev = phy_device_create ( bus , addr , phy_id , is_c45 , & c45_ids ) ;
2005-07-31 03:31:23 +04:00
return dev ;
}
2009-04-25 16:52:46 +04:00
EXPORT_SYMBOL ( get_phy_device ) ;
/**
* phy_device_register - Register the phy device on the MDIO bus
2009-06-16 20:56:33 +04:00
* @ phydev : phy_device structure to be added to the MDIO bus
2009-04-25 16:52:46 +04:00
*/
int phy_device_register ( struct phy_device * phydev )
{
int err ;
/* Don't register a phy if one is already registered at this
* address */
if ( phydev - > bus - > phy_map [ phydev - > addr ] )
return - EINVAL ;
phydev - > bus - > phy_map [ phydev - > addr ] = phydev ;
/* Run all of the fixups for this PHY */
phy_scan_fixups ( phydev ) ;
2013-02-28 05:01:52 +04:00
err = device_add ( & phydev - > dev ) ;
2009-04-25 16:52:46 +04:00
if ( err ) {
2013-02-28 05:01:52 +04:00
pr_err ( " PHY %d failed to add \n " , phydev - > addr ) ;
2009-04-25 16:52:46 +04:00
goto out ;
}
return 0 ;
out :
phydev - > bus - > phy_map [ phydev - > addr ] = NULL ;
return err ;
}
EXPORT_SYMBOL ( phy_device_register ) ;
2005-07-31 03:31:23 +04:00
2010-02-04 21:23:02 +03:00
/**
* phy_find_first - finds the first PHY device on the bus
* @ bus : the target MII bus
*/
struct phy_device * phy_find_first ( struct mii_bus * bus )
{
int addr ;
for ( addr = 0 ; addr < PHY_MAX_ADDR ; addr + + ) {
if ( bus - > phy_map [ addr ] )
return bus - > phy_map [ addr ] ;
}
return NULL ;
}
EXPORT_SYMBOL ( phy_find_first ) ;
2007-03-06 13:41:48 +03:00
/**
* phy_prepare_link - prepares the PHY layer to monitor link status
* @ phydev : target phy_device struct
* @ handler : callback function for link status change notifications
2005-07-31 03:31:23 +04:00
*
2007-03-06 13:41:48 +03:00
* Description : Tells the PHY infrastructure to handle the
2005-07-31 03:31:23 +04:00
* gory details on monitoring link status ( whether through
* polling or an interrupt ) , and to call back to the
* connected device driver when the link status changes .
* If you want to monitor your own link state , don ' t call
2007-03-06 13:41:48 +03:00
* this function .
*/
2010-10-21 12:37:41 +04:00
static void phy_prepare_link ( struct phy_device * phydev ,
2005-07-31 03:31:23 +04:00
void ( * handler ) ( struct net_device * ) )
{
phydev - > adjust_link = handler ;
}
2009-04-25 16:52:51 +04:00
/**
* phy_connect_direct - connect an ethernet device to a specific phy_device
* @ dev : the network device to connect
* @ phydev : the pointer to the phy device
* @ handler : callback function for state change notifications
* @ interface : PHY device ' s interface
*/
int phy_connect_direct ( struct net_device * dev , struct phy_device * phydev ,
2013-01-14 04:52:52 +04:00
void ( * handler ) ( struct net_device * ) ,
2009-04-25 16:52:51 +04:00
phy_interface_t interface )
{
int rc ;
2013-01-14 04:52:52 +04:00
rc = phy_attach_direct ( dev , phydev , phydev - > dev_flags , interface ) ;
2009-04-25 16:52:51 +04:00
if ( rc )
return rc ;
phy_prepare_link ( phydev , handler ) ;
phy_start_machine ( phydev , NULL ) ;
if ( phydev - > irq > 0 )
phy_start_interrupts ( phydev ) ;
return 0 ;
}
EXPORT_SYMBOL ( phy_connect_direct ) ;
2007-03-06 13:41:48 +03:00
/**
* phy_connect - connect an ethernet device to a PHY device
* @ dev : the network device to connect
2008-04-28 21:58:22 +04:00
* @ bus_id : the id string of the PHY device to connect
2007-03-06 13:41:48 +03:00
* @ handler : callback function for state change notifications
* @ interface : PHY device ' s interface
2005-08-25 03:46:21 +04:00
*
2007-03-06 13:41:48 +03:00
* Description : Convenience function for connecting ethernet
2005-08-25 03:46:21 +04:00
* devices to PHY devices . The default behavior is for
* the PHY infrastructure to handle everything , and only notify
* the connected driver when the link status changes . If you
* don ' t want , or can ' t use the provided functionality , you may
* choose to call only the subset of functions which provide
* the desired functionality .
*/
2008-04-19 02:29:54 +04:00
struct phy_device * phy_connect ( struct net_device * dev , const char * bus_id ,
2013-01-14 04:52:52 +04:00
void ( * handler ) ( struct net_device * ) ,
2007-02-05 21:44:20 +03:00
phy_interface_t interface )
2005-08-25 03:46:21 +04:00
{
struct phy_device * phydev ;
2009-04-25 16:52:51 +04:00
struct device * d ;
int rc ;
2005-08-25 03:46:21 +04:00
2009-04-25 16:52:51 +04:00
/* Search the list of PHY devices on the mdio bus for the
* PHY with the requested name */
d = bus_find_device_by_name ( & mdio_bus_type , NULL , bus_id ) ;
if ( ! d ) {
pr_err ( " PHY %s not found \n " , bus_id ) ;
return ERR_PTR ( - ENODEV ) ;
}
phydev = to_phy_device ( d ) ;
2005-08-25 03:46:21 +04:00
2013-01-14 04:52:52 +04:00
rc = phy_connect_direct ( dev , phydev , handler , interface ) ;
2009-04-25 16:52:51 +04:00
if ( rc )
return ERR_PTR ( rc ) ;
2005-08-25 03:46:21 +04:00
return phydev ;
}
EXPORT_SYMBOL ( phy_connect ) ;
2007-03-06 13:41:48 +03:00
/**
* phy_disconnect - disable interrupts , stop state machine , and detach a PHY device
* @ phydev : target phy_device struct
*/
2005-08-25 03:46:21 +04:00
void phy_disconnect ( struct phy_device * phydev )
{
if ( phydev - > irq > 0 )
phy_stop_interrupts ( phydev ) ;
phy_stop_machine ( phydev ) ;
phydev - > adjust_link = NULL ;
phy_detach ( phydev ) ;
}
EXPORT_SYMBOL ( phy_disconnect ) ;
2009-12-30 11:23:30 +03:00
int phy_init_hw ( struct phy_device * phydev )
{
int ret ;
if ( ! phydev - > drv | | ! phydev - > drv - > config_init )
return 0 ;
ret = phy_scan_fixups ( phydev ) ;
if ( ret < 0 )
return ret ;
return phydev - > drv - > config_init ( phydev ) ;
}
2007-03-06 13:41:48 +03:00
/**
2009-04-25 16:52:51 +04:00
* phy_attach_direct - attach a network device to a given PHY device pointer
2007-03-06 13:41:48 +03:00
* @ dev : network device to attach
2009-04-25 16:52:51 +04:00
* @ phydev : Pointer to phy_device to attach
2007-03-06 13:41:48 +03:00
* @ flags : PHY device ' s dev_flags
* @ interface : PHY device ' s interface
2005-08-25 03:46:21 +04:00
*
2007-03-06 13:41:48 +03:00
* Description : Called by drivers to attach to a particular PHY
2005-08-25 03:46:21 +04:00
* device . The phy_device is found , and properly hooked up
* to the phy_driver . If no driver is attached , then the
* genphy_driver is used . The phy_device is given a ptr to
* the attaching device , and given a callback for link status
2007-03-06 13:41:48 +03:00
* change . The phy_device is returned to the attaching driver .
2005-08-25 03:46:21 +04:00
*/
2010-10-21 12:37:41 +04:00
static int phy_attach_direct ( struct net_device * dev , struct phy_device * phydev ,
u32 flags , phy_interface_t interface )
2005-08-25 03:46:21 +04:00
{
2009-04-25 16:52:51 +04:00
struct device * d = & phydev - > dev ;
2011-03-28 18:54:08 +04:00
int err ;
2005-08-25 03:46:21 +04:00
/* Assume that if there is no driver, that it doesn't
* exist , and we should use the genphy driver . */
if ( NULL = = d - > driver ) {
2012-06-27 11:33:35 +04:00
if ( phydev - > is_c45 ) {
pr_err ( " No driver for phy %x \n " , phydev - > phy_id ) ;
return - ENODEV ;
}
2005-08-25 03:46:21 +04:00
d - > driver = & genphy_driver . driver ;
err = d - > driver - > probe ( d ) ;
2006-10-01 15:27:46 +04:00
if ( err > = 0 )
err = device_bind_driver ( d ) ;
2005-08-25 03:46:21 +04:00
2006-10-01 15:27:46 +04:00
if ( err )
2009-04-25 16:52:51 +04:00
return err ;
2005-08-25 03:46:21 +04:00
}
if ( phydev - > attached_dev ) {
2009-04-25 16:52:51 +04:00
dev_err ( & dev - > dev , " PHY already attached \n " ) ;
return - EBUSY ;
2005-08-25 03:46:21 +04:00
}
phydev - > attached_dev = dev ;
2010-07-17 12:49:36 +04:00
dev - > phydev = phydev ;
2005-08-25 03:46:21 +04:00
phydev - > dev_flags = flags ;
2006-12-01 21:01:06 +03:00
phydev - > interface = interface ;
phylib: Fix race between returning phydev and calling adjust_link
It is possible that phylib will call adjust_link before returning
from {,of_}phy_connect(), which may cause the following [very rare,
though] oops upon reopening the device:
Unable to handle kernel paging request for data at address 0x0000024c
Oops: Kernel access of bad area, sig: 11 [#1]
PREEMPT SMP NR_CPUS=2 LTT NESTING LEVEL : 0
P1021 RDB
Modules linked in:
NIP: c0345dac LR: c0345dac CTR: c0345d84
TASK = dffab6b0[30] 'events/0' THREAD: c0d24000 CPU: 0
[...]
NIP [c0345dac] adjust_link+0x28/0x19c
LR [c0345dac] adjust_link+0x28/0x19c
Call Trace:
[c0d25f00] [000045e1] 0x45e1 (unreliable)
[c0d25f30] [c036c158] phy_state_machine+0x3ac/0x554
[...]
Here is why. Drivers store phydev in their private structures, e.g.
gianfar driver:
static int init_phy(struct net_device *dev)
{
...
priv->phydev = of_phy_connect(...);
...
}
So that adjust_link could retrieve it back:
static void adjust_link(struct net_device *dev)
{
...
struct phy_device *phydev = priv->phydev;
...
}
If the device has been opened before, then phydev->state is set to
PHY_HALTED (or undefined if the driver didn't call phy_stop()).
Now, phy_connect starts the PHY state machine before returning phydev to
the driver:
phy_start_machine(phydev, NULL);
if (phydev->irq > 0)
phy_start_interrupts(phydev);
return phydev;
The time between 'phy_start_machine()' and 'return phydev' is undefined.
The start machine routine delays execution for 1 second, which is enough
for most cases. But under heavy load, or if you're unlucky, it is quite
possible that PHY state machine will execute before phy_connect()
returns, and so adjust_link callback will try to dereference phydev,
which is not yet ready.
To fix the issue, simply initialize the PHY's state to PHY_READY during
phy_attach(). This will ensure that phylib won't call adjust_link before
phy_start().
Signed-off-by: Anton Vorontsov <avorontsov@mvista.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2010-08-25 01:46:12 +04:00
phydev - > state = PHY_READY ;
2006-12-01 21:01:06 +03:00
/* Do initial configuration here, now that
* we have certain key parameters
* ( dev_flags and interface ) */
2011-03-28 18:54:08 +04:00
err = phy_init_hw ( phydev ) ;
if ( err )
phy_detach ( phydev ) ;
return err ;
2009-04-25 16:52:51 +04:00
}
/**
* phy_attach - attach a network device to a particular PHY device
* @ dev : network device to attach
* @ bus_id : Bus ID of PHY device to attach
* @ interface : PHY device ' s interface
*
* Description : Same as phy_attach_direct ( ) except that a PHY bus_id
* string is passed instead of a pointer to a struct phy_device .
*/
struct phy_device * phy_attach ( struct net_device * dev ,
2013-01-14 04:52:52 +04:00
const char * bus_id , phy_interface_t interface )
2009-04-25 16:52:51 +04:00
{
struct bus_type * bus = & mdio_bus_type ;
struct phy_device * phydev ;
struct device * d ;
int rc ;
/* Search the list of PHY devices on the mdio bus for the
* PHY with the requested name */
d = bus_find_device_by_name ( bus , NULL , bus_id ) ;
if ( ! d ) {
pr_err ( " PHY %s not found \n " , bus_id ) ;
return ERR_PTR ( - ENODEV ) ;
2006-12-01 21:01:06 +03:00
}
2009-04-25 16:52:51 +04:00
phydev = to_phy_device ( d ) ;
2013-01-14 04:52:52 +04:00
rc = phy_attach_direct ( dev , phydev , phydev - > dev_flags , interface ) ;
2009-04-25 16:52:51 +04:00
if ( rc )
return ERR_PTR ( rc ) ;
2006-12-01 21:01:06 +03:00
2005-08-25 03:46:21 +04:00
return phydev ;
}
EXPORT_SYMBOL ( phy_attach ) ;
2007-03-06 13:41:48 +03:00
/**
* phy_detach - detach a PHY device from its network device
* @ phydev : target phy_device struct
*/
2005-08-25 03:46:21 +04:00
void phy_detach ( struct phy_device * phydev )
{
2010-07-17 12:49:36 +04:00
phydev - > attached_dev - > phydev = NULL ;
2005-08-25 03:46:21 +04:00
phydev - > attached_dev = NULL ;
/* If the device had no specific driver before (i.e. - it
* was using the generic driver ) , we unbind the device
* from the generic driver so that there ' s a chance a
* real driver could be loaded */
2007-04-09 19:52:31 +04:00
if ( phydev - > dev . driver = = & genphy_driver . driver )
2005-08-25 03:46:21 +04:00
device_release_driver ( & phydev - > dev ) ;
}
EXPORT_SYMBOL ( phy_detach ) ;
2005-07-31 03:31:23 +04:00
/* Generic PHY support and helper functions */
2007-03-06 13:41:48 +03:00
/**
2011-03-31 05:57:33 +04:00
* genphy_config_advert - sanitize and advertise auto - negotiation parameters
2007-03-06 13:41:48 +03:00
* @ phydev : target phy_device struct
2005-07-31 03:31:23 +04:00
*
2007-03-06 13:41:48 +03:00
* Description : Writes MII_ADVERTISE with the appropriate values ,
2005-07-31 03:31:23 +04:00
* after sanitizing the values to make sure we only advertise
2008-09-24 14:55:46 +04:00
* what is supported . Returns < 0 on error , 0 if the PHY ' s advertisement
* hasn ' t changed , and > 0 if it has changed .
2005-07-31 03:31:23 +04:00
*/
2010-10-21 12:37:41 +04:00
static int genphy_config_advert ( struct phy_device * phydev )
2005-07-31 03:31:23 +04:00
{
u32 advertise ;
2008-09-24 14:55:46 +04:00
int oldadv , adv ;
int err , changed = 0 ;
2005-07-31 03:31:23 +04:00
/* Only allow advertising what
* this PHY supports */
phydev - > advertising & = phydev - > supported ;
advertise = phydev - > advertising ;
/* Setup standard advertisement */
2008-09-24 14:55:46 +04:00
oldadv = adv = phy_read ( phydev , MII_ADVERTISE ) ;
2005-07-31 03:31:23 +04:00
if ( adv < 0 )
return adv ;
2011-11-17 03:36:59 +04:00
adv & = ~ ( ADVERTISE_ALL | ADVERTISE_100BASE4 | ADVERTISE_PAUSE_CAP |
2005-07-31 03:31:23 +04:00
ADVERTISE_PAUSE_ASYM ) ;
2011-11-17 18:30:55 +04:00
adv | = ethtool_adv_to_mii_adv_t ( advertise ) ;
2005-07-31 03:31:23 +04:00
2008-09-24 14:55:46 +04:00
if ( adv ! = oldadv ) {
err = phy_write ( phydev , MII_ADVERTISE , adv ) ;
2005-07-31 03:31:23 +04:00
2008-09-24 14:55:46 +04:00
if ( err < 0 )
return err ;
changed = 1 ;
}
2005-07-31 03:31:23 +04:00
/* Configure gigabit if it's supported */
if ( phydev - > supported & ( SUPPORTED_1000baseT_Half |
SUPPORTED_1000baseT_Full ) ) {
2008-09-24 14:55:46 +04:00
oldadv = adv = phy_read ( phydev , MII_CTRL1000 ) ;
2005-07-31 03:31:23 +04:00
if ( adv < 0 )
return adv ;
adv & = ~ ( ADVERTISE_1000FULL | ADVERTISE_1000HALF ) ;
2011-11-17 18:30:55 +04:00
adv | = ethtool_adv_to_mii_ctrl1000_t ( advertise ) ;
2005-07-31 03:31:23 +04:00
2008-09-24 14:55:46 +04:00
if ( adv ! = oldadv ) {
err = phy_write ( phydev , MII_CTRL1000 , adv ) ;
if ( err < 0 )
return err ;
changed = 1 ;
}
2005-07-31 03:31:23 +04:00
}
2008-09-24 14:55:46 +04:00
return changed ;
2005-07-31 03:31:23 +04:00
}
2007-03-06 13:41:48 +03:00
/**
* genphy_setup_forced - configures / forces speed / duplex from @ phydev
* @ phydev : target phy_device struct
2005-07-31 03:31:23 +04:00
*
2007-03-06 13:41:48 +03:00
* Description : Configures MII_BMCR to force speed / duplex
2005-07-31 03:31:23 +04:00
* to the values in phydev . Assumes that the values are valid .
2007-03-06 13:41:48 +03:00
* Please see phy_sanitize_settings ( ) .
*/
2010-10-21 12:37:41 +04:00
static int genphy_setup_forced ( struct phy_device * phydev )
2005-07-31 03:31:23 +04:00
{
2008-04-19 02:29:54 +04:00
int err ;
2007-08-17 10:54:45 +04:00
int ctl = 0 ;
2005-07-31 03:31:23 +04:00
phydev - > pause = phydev - > asym_pause = 0 ;
if ( SPEED_1000 = = phydev - > speed )
ctl | = BMCR_SPEED1000 ;
else if ( SPEED_100 = = phydev - > speed )
ctl | = BMCR_SPEED100 ;
if ( DUPLEX_FULL = = phydev - > duplex )
ctl | = BMCR_FULLDPLX ;
2008-04-19 02:29:54 +04:00
err = phy_write ( phydev , MII_BMCR , ctl ) ;
2005-07-31 03:31:23 +04:00
2008-04-19 02:29:54 +04:00
return err ;
2005-07-31 03:31:23 +04:00
}
2007-03-06 13:41:48 +03:00
/**
* genphy_restart_aneg - Enable and Restart Autonegotiation
* @ phydev : target phy_device struct
*/
2005-07-31 03:31:23 +04:00
int genphy_restart_aneg ( struct phy_device * phydev )
{
int ctl ;
ctl = phy_read ( phydev , MII_BMCR ) ;
if ( ctl < 0 )
return ctl ;
ctl | = ( BMCR_ANENABLE | BMCR_ANRESTART ) ;
/* Don't isolate the PHY if we're negotiating */
ctl & = ~ ( BMCR_ISOLATE ) ;
ctl = phy_write ( phydev , MII_BMCR , ctl ) ;
return ctl ;
}
2008-10-14 05:48:09 +04:00
EXPORT_SYMBOL ( genphy_restart_aneg ) ;
2005-07-31 03:31:23 +04:00
2007-03-06 13:41:48 +03:00
/**
* genphy_config_aneg - restart auto - negotiation or write BMCR
* @ phydev : target phy_device struct
2005-07-31 03:31:23 +04:00
*
2007-03-06 13:41:48 +03:00
* Description : If auto - negotiation is enabled , we configure the
2005-07-31 03:31:23 +04:00
* advertising , and then restart auto - negotiation . If it is not
2007-03-06 13:41:48 +03:00
* enabled , then we write the BMCR .
2005-07-31 03:31:23 +04:00
*/
int genphy_config_aneg ( struct phy_device * phydev )
{
2008-11-20 02:52:41 +03:00
int result ;
2005-07-31 03:31:23 +04:00
2008-11-20 02:52:41 +03:00
if ( AUTONEG_ENABLE ! = phydev - > autoneg )
return genphy_setup_forced ( phydev ) ;
2005-07-31 03:31:23 +04:00
2008-11-20 02:52:41 +03:00
result = genphy_config_advert ( phydev ) ;
2005-07-31 03:31:23 +04:00
2008-11-20 02:52:41 +03:00
if ( result < 0 ) /* error */
return result ;
if ( result = = 0 ) {
2011-03-31 05:57:33 +04:00
/* Advertisement hasn't changed, but maybe aneg was never on to
2008-11-20 02:52:41 +03:00
* begin with ? Or maybe phy was isolated ? */
int ctl = phy_read ( phydev , MII_BMCR ) ;
if ( ctl < 0 )
return ctl ;
if ( ! ( ctl & BMCR_ANENABLE ) | | ( ctl & BMCR_ISOLATE ) )
result = 1 ; /* do restart aneg */
}
/* Only restart aneg if we are advertising something different
* than we were before . */
if ( result > 0 )
result = genphy_restart_aneg ( phydev ) ;
2005-07-31 03:31:23 +04:00
2008-09-24 14:55:46 +04:00
return result ;
2005-07-31 03:31:23 +04:00
}
EXPORT_SYMBOL ( genphy_config_aneg ) ;
2007-03-06 13:41:48 +03:00
/**
* genphy_update_link - update link status in @ phydev
* @ phydev : target phy_device struct
2005-07-31 03:31:23 +04:00
*
2007-03-06 13:41:48 +03:00
* Description : Update the value in phydev - > link to reflect the
2005-07-31 03:31:23 +04:00
* current link value . In order to do this , we need to read
2007-03-06 13:41:48 +03:00
* the status register twice , keeping the second value .
2005-07-31 03:31:23 +04:00
*/
int genphy_update_link ( struct phy_device * phydev )
{
int status ;
/* Do a fake read */
status = phy_read ( phydev , MII_BMSR ) ;
if ( status < 0 )
return status ;
/* Read link and autonegotiation status */
status = phy_read ( phydev , MII_BMSR ) ;
if ( status < 0 )
return status ;
if ( ( status & BMSR_LSTATUS ) = = 0 )
phydev - > link = 0 ;
else
phydev - > link = 1 ;
return 0 ;
}
2006-10-17 01:19:17 +04:00
EXPORT_SYMBOL ( genphy_update_link ) ;
2005-07-31 03:31:23 +04:00
2007-03-06 13:41:48 +03:00
/**
* genphy_read_status - check the link status and update current link state
* @ phydev : target phy_device struct
2005-07-31 03:31:23 +04:00
*
2007-03-06 13:41:48 +03:00
* Description : Check the link , then figure out the current state
2005-07-31 03:31:23 +04:00
* by comparing what we advertise with what the link partner
* advertises . Start by checking the gigabit possibilities ,
* then move on to 10 / 100.
*/
int genphy_read_status ( struct phy_device * phydev )
{
int adv ;
int err ;
int lpa ;
int lpagb = 0 ;
/* Update the link, but return if there
* was an error */
err = genphy_update_link ( phydev ) ;
if ( err )
return err ;
if ( AUTONEG_ENABLE = = phydev - > autoneg ) {
if ( phydev - > supported & ( SUPPORTED_1000baseT_Half
| SUPPORTED_1000baseT_Full ) ) {
lpagb = phy_read ( phydev , MII_STAT1000 ) ;
if ( lpagb < 0 )
return lpagb ;
adv = phy_read ( phydev , MII_CTRL1000 ) ;
if ( adv < 0 )
return adv ;
lpagb & = adv < < 2 ;
}
lpa = phy_read ( phydev , MII_LPA ) ;
if ( lpa < 0 )
return lpa ;
adv = phy_read ( phydev , MII_ADVERTISE ) ;
if ( adv < 0 )
return adv ;
lpa & = adv ;
phydev - > speed = SPEED_10 ;
phydev - > duplex = DUPLEX_HALF ;
phydev - > pause = phydev - > asym_pause = 0 ;
if ( lpagb & ( LPA_1000FULL | LPA_1000HALF ) ) {
phydev - > speed = SPEED_1000 ;
if ( lpagb & LPA_1000FULL )
phydev - > duplex = DUPLEX_FULL ;
} else if ( lpa & ( LPA_100FULL | LPA_100HALF ) ) {
phydev - > speed = SPEED_100 ;
if ( lpa & LPA_100FULL )
phydev - > duplex = DUPLEX_FULL ;
} else
if ( lpa & LPA_10FULL )
phydev - > duplex = DUPLEX_FULL ;
if ( phydev - > duplex = = DUPLEX_FULL ) {
phydev - > pause = lpa & LPA_PAUSE_CAP ? 1 : 0 ;
phydev - > asym_pause = lpa & LPA_PAUSE_ASYM ? 1 : 0 ;
}
} else {
int bmcr = phy_read ( phydev , MII_BMCR ) ;
if ( bmcr < 0 )
return bmcr ;
if ( bmcr & BMCR_FULLDPLX )
phydev - > duplex = DUPLEX_FULL ;
else
phydev - > duplex = DUPLEX_HALF ;
if ( bmcr & BMCR_SPEED1000 )
phydev - > speed = SPEED_1000 ;
else if ( bmcr & BMCR_SPEED100 )
phydev - > speed = SPEED_100 ;
else
phydev - > speed = SPEED_10 ;
phydev - > pause = phydev - > asym_pause = 0 ;
}
return 0 ;
}
EXPORT_SYMBOL ( genphy_read_status ) ;
static int genphy_config_init ( struct phy_device * phydev )
{
2006-09-26 03:39:22 +04:00
int val ;
2005-07-31 03:31:23 +04:00
u32 features ;
/* For now, I'll claim that the generic driver supports
* all possible port types */
features = ( SUPPORTED_TP | SUPPORTED_MII
| SUPPORTED_AUI | SUPPORTED_FIBRE |
SUPPORTED_BNC ) ;
/* Do we support autonegotiation? */
val = phy_read ( phydev , MII_BMSR ) ;
if ( val < 0 )
return val ;
if ( val & BMSR_ANEGCAPABLE )
features | = SUPPORTED_Autoneg ;
if ( val & BMSR_100FULL )
features | = SUPPORTED_100baseT_Full ;
if ( val & BMSR_100HALF )
features | = SUPPORTED_100baseT_Half ;
if ( val & BMSR_10FULL )
features | = SUPPORTED_10baseT_Full ;
if ( val & BMSR_10HALF )
features | = SUPPORTED_10baseT_Half ;
if ( val & BMSR_ESTATEN ) {
val = phy_read ( phydev , MII_ESTATUS ) ;
if ( val < 0 )
return val ;
if ( val & ESTATUS_1000_TFULL )
features | = SUPPORTED_1000baseT_Full ;
if ( val & ESTATUS_1000_THALF )
features | = SUPPORTED_1000baseT_Half ;
}
phydev - > supported = features ;
phydev - > advertising = features ;
return 0 ;
}
2008-11-29 03:24:56 +03:00
int genphy_suspend ( struct phy_device * phydev )
{
int value ;
mutex_lock ( & phydev - > lock ) ;
value = phy_read ( phydev , MII_BMCR ) ;
phy_write ( phydev , MII_BMCR , ( value | BMCR_PDOWN ) ) ;
mutex_unlock ( & phydev - > lock ) ;
return 0 ;
}
EXPORT_SYMBOL ( genphy_suspend ) ;
2005-07-31 03:31:23 +04:00
2008-11-29 03:24:56 +03:00
int genphy_resume ( struct phy_device * phydev )
{
int value ;
mutex_lock ( & phydev - > lock ) ;
value = phy_read ( phydev , MII_BMCR ) ;
phy_write ( phydev , MII_BMCR , ( value & ~ BMCR_PDOWN ) ) ;
mutex_unlock ( & phydev - > lock ) ;
return 0 ;
}
EXPORT_SYMBOL ( genphy_resume ) ;
2005-07-31 03:31:23 +04:00
2007-03-06 13:41:48 +03:00
/**
* phy_probe - probe and init a PHY device
* @ dev : device to probe and init
2005-07-31 03:31:23 +04:00
*
2007-03-06 13:41:48 +03:00
* Description : Take care of setting up the phy_device structure ,
2005-07-31 03:31:23 +04:00
* set the state to READY ( the driver ' s init function should
* set it to STARTING if needed ) .
*/
static int phy_probe ( struct device * dev )
{
struct phy_device * phydev ;
struct phy_driver * phydrv ;
struct device_driver * drv ;
int err = 0 ;
phydev = to_phy_device ( dev ) ;
2012-01-24 22:35:24 +04:00
drv = phydev - > dev . driver ;
2005-07-31 03:31:23 +04:00
phydrv = to_phy_driver ( drv ) ;
phydev - > drv = phydrv ;
2013-05-20 02:53:42 +04:00
/* Disable the interrupt if the PHY doesn't support it
* but the interrupt is still a valid one
*/
if ( ! ( phydrv - > flags & PHY_HAS_INTERRUPT ) & &
phy_interrupt_is_valid ( phydev ) )
2005-07-31 03:31:23 +04:00
phydev - > irq = PHY_POLL ;
2013-05-23 05:11:12 +04:00
if ( phydrv - > flags & PHY_IS_INTERNAL )
phydev - > is_internal = true ;
2008-01-29 19:05:09 +03:00
mutex_lock ( & phydev - > lock ) ;
2005-07-31 03:31:23 +04:00
/* Start out supporting everything. Eventually,
* a controller will attach , and may modify one
* or both of these values */
phydev - > supported = phydrv - > features ;
phydev - > advertising = phydrv - > features ;
/* Set the state to READY by default */
phydev - > state = PHY_READY ;
if ( phydev - > drv - > probe )
err = phydev - > drv - > probe ( phydev ) ;
2008-01-29 19:05:09 +03:00
mutex_unlock ( & phydev - > lock ) ;
2005-07-31 03:31:23 +04:00
return err ;
2006-12-01 21:01:06 +03:00
2005-07-31 03:31:23 +04:00
}
static int phy_remove ( struct device * dev )
{
struct phy_device * phydev ;
phydev = to_phy_device ( dev ) ;
2008-01-29 19:05:09 +03:00
mutex_lock ( & phydev - > lock ) ;
2005-07-31 03:31:23 +04:00
phydev - > state = PHY_DOWN ;
2008-01-29 19:05:09 +03:00
mutex_unlock ( & phydev - > lock ) ;
2005-07-31 03:31:23 +04:00
if ( phydev - > drv - > remove )
phydev - > drv - > remove ( phydev ) ;
phydev - > drv = NULL ;
return 0 ;
}
2007-03-06 13:41:48 +03:00
/**
* phy_driver_register - register a phy_driver with the PHY layer
* @ new_driver : new phy_driver to register
*/
2005-07-31 03:31:23 +04:00
int phy_driver_register ( struct phy_driver * new_driver )
{
int retval ;
new_driver - > driver . name = new_driver - > name ;
new_driver - > driver . bus = & mdio_bus_type ;
new_driver - > driver . probe = phy_probe ;
new_driver - > driver . remove = phy_remove ;
retval = driver_register ( & new_driver - > driver ) ;
if ( retval ) {
2012-06-09 11:49:07 +04:00
pr_err ( " %s: Error %d in registering driver \n " ,
new_driver - > name , retval ) ;
2005-07-31 03:31:23 +04:00
return retval ;
}
2007-11-05 01:09:23 +03:00
pr_debug ( " %s: Registered new driver \n " , new_driver - > name ) ;
2005-07-31 03:31:23 +04:00
return 0 ;
}
EXPORT_SYMBOL ( phy_driver_register ) ;
2012-07-04 09:44:34 +04:00
int phy_drivers_register ( struct phy_driver * new_driver , int n )
{
int i , ret = 0 ;
for ( i = 0 ; i < n ; i + + ) {
ret = phy_driver_register ( new_driver + i ) ;
if ( ret ) {
while ( i - - > 0 )
phy_driver_unregister ( new_driver + i ) ;
break ;
}
}
return ret ;
}
EXPORT_SYMBOL ( phy_drivers_register ) ;
2005-07-31 03:31:23 +04:00
void phy_driver_unregister ( struct phy_driver * drv )
{
driver_unregister ( & drv - > driver ) ;
}
EXPORT_SYMBOL ( phy_driver_unregister ) ;
2012-07-04 09:44:34 +04:00
void phy_drivers_unregister ( struct phy_driver * drv , int n )
{
int i ;
for ( i = 0 ; i < n ; i + + ) {
phy_driver_unregister ( drv + i ) ;
}
}
EXPORT_SYMBOL ( phy_drivers_unregister ) ;
2005-08-25 03:46:21 +04:00
static struct phy_driver genphy_driver = {
. phy_id = 0xffffffff ,
. phy_id_mask = 0xffffffff ,
. name = " Generic PHY " ,
. config_init = genphy_config_init ,
. features = 0 ,
. config_aneg = genphy_config_aneg ,
. read_status = genphy_read_status ,
2008-11-29 03:24:56 +03:00
. suspend = genphy_suspend ,
. resume = genphy_resume ,
2005-08-25 03:46:21 +04:00
. driver = { . owner = THIS_MODULE , } ,
} ;
2005-07-31 03:31:23 +04:00
2005-08-11 10:07:25 +04:00
static int __init phy_init ( void )
2005-07-31 03:31:23 +04:00
{
2005-08-11 10:07:25 +04:00
int rc ;
rc = mdio_bus_init ( ) ;
if ( rc )
2005-08-25 03:46:21 +04:00
return rc ;
2005-07-31 03:31:23 +04:00
2005-08-25 03:46:21 +04:00
rc = phy_driver_register ( & genphy_driver ) ;
if ( rc )
mdio_bus_exit ( ) ;
2005-08-11 10:07:25 +04:00
return rc ;
2005-07-31 03:31:23 +04:00
}
2005-08-11 10:07:25 +04:00
static void __exit phy_exit ( void )
2005-07-31 03:31:23 +04:00
{
phy_driver_unregister ( & genphy_driver ) ;
2005-08-25 03:46:21 +04:00
mdio_bus_exit ( ) ;
2005-07-31 03:31:23 +04:00
}
2005-08-25 03:46:21 +04:00
subsys_initcall ( phy_init ) ;
2005-08-11 10:07:25 +04:00
module_exit ( phy_exit ) ;