2009-04-25 16:52:56 +04:00
/*
* OF helpers for the MDIO ( Ethernet PHY ) API
*
* Copyright ( c ) 2009 Secret Lab Technologies , Ltd .
*
* This file is released under the GPLv2
*
* This file provides helper functions for extracting PHY device information
* out of the OpenFirmware device tree and using it to populate an mii_bus .
*/
2009-07-17 01:31:31 +04:00
# include <linux/kernel.h>
# include <linux/device.h>
# include <linux/netdevice.h>
# include <linux/err.h>
2009-04-25 16:52:56 +04:00
# include <linux/phy.h>
# include <linux/of.h>
2010-06-18 21:09:59 +04:00
# include <linux/of_irq.h>
2009-04-25 16:52:56 +04:00
# include <linux/of_mdio.h>
# include <linux/module.h>
MODULE_AUTHOR ( " Grant Likely <grant.likely@secretlab.ca> " ) ;
MODULE_LICENSE ( " GPL " ) ;
/**
* of_mdiobus_register - Register mii_bus and create PHYs from the device tree
* @ mdio : pointer to mii_bus structure
* @ np : pointer to device_node of MDIO bus .
*
* This function registers the mii_bus structure and registers a phy_device
* for each child node of @ np .
*/
int of_mdiobus_register ( struct mii_bus * mdio , struct device_node * np )
{
struct phy_device * phy ;
struct device_node * child ;
int rc , i ;
/* Mask out all PHYs from auto probing. Instead the PHYs listed in
* the device tree are populated after the bus has been registered */
mdio - > phy_mask = ~ 0 ;
/* Clear all the IRQ properties */
if ( mdio - > irq )
for ( i = 0 ; i < PHY_MAX_ADDR ; i + + )
mdio - > irq [ i ] = PHY_POLL ;
/* Register the MDIO bus */
rc = mdiobus_register ( mdio ) ;
if ( rc )
return rc ;
/* Loop over the child nodes and register a phy_device for each one */
for_each_child_of_node ( np , child ) {
2010-10-28 05:03:47 +04:00
const __be32 * paddr ;
u32 addr ;
2009-04-25 16:52:56 +04:00
int len ;
/* A PHY must have a reg property in the range [0-31] */
2010-10-28 05:03:47 +04:00
paddr = of_get_property ( child , " reg " , & len ) ;
if ( ! paddr | | len < sizeof ( * paddr ) ) {
2009-04-25 16:52:56 +04:00
dev_err ( & mdio - > dev , " %s has invalid PHY address \n " ,
child - > full_name ) ;
continue ;
}
2010-10-28 05:03:47 +04:00
addr = be32_to_cpup ( paddr ) ;
if ( addr > = 32 ) {
dev_err ( & mdio - > dev , " %s PHY address %i is too large \n " ,
child - > full_name , addr ) ;
continue ;
}
2009-04-25 16:52:56 +04:00
if ( mdio - > irq ) {
2010-10-28 05:03:47 +04:00
mdio - > irq [ addr ] = irq_of_parse_and_map ( child , 0 ) ;
if ( ! mdio - > irq [ addr ] )
mdio - > irq [ addr ] = PHY_POLL ;
2009-04-25 16:52:56 +04:00
}
2010-10-28 05:03:47 +04:00
phy = get_phy_device ( mdio , addr ) ;
2010-04-28 11:07:29 +04:00
if ( ! phy | | IS_ERR ( phy ) ) {
2009-04-25 16:52:56 +04:00
dev_err ( & mdio - > dev , " error probing PHY at address %i \n " ,
2010-10-28 05:03:47 +04:00
addr ) ;
2009-04-25 16:52:56 +04:00
continue ;
}
/* Associate the OF node with the device structure so it
* can be looked up later */
of_node_get ( child ) ;
2010-04-14 03:12:28 +04:00
phy - > dev . of_node = child ;
2009-04-25 16:52:56 +04:00
/* All data is now stored in the phy struct; register it */
rc = phy_device_register ( phy ) ;
if ( rc ) {
phy_device_free ( phy ) ;
of_node_put ( child ) ;
continue ;
}
dev_dbg ( & mdio - > dev , " registered phy %s at address %i \n " ,
2010-10-28 05:03:47 +04:00
child - > name , addr ) ;
2009-04-25 16:52:56 +04:00
}
return 0 ;
}
EXPORT_SYMBOL ( of_mdiobus_register ) ;
2009-10-15 19:58:27 +04:00
/* Helper function for of_phy_find_device */
static int of_phy_match ( struct device * dev , void * phy_np )
{
2010-04-14 03:12:29 +04:00
return dev - > of_node = = phy_np ;
2009-10-15 19:58:27 +04:00
}
2009-04-25 16:52:56 +04:00
/**
* of_phy_find_device - Give a PHY node , find the phy_device
* @ phy_np : Pointer to the phy ' s device tree node
*
* Returns a pointer to the phy_device .
*/
struct phy_device * of_phy_find_device ( struct device_node * phy_np )
{
struct device * d ;
if ( ! phy_np )
return NULL ;
2009-10-15 19:58:27 +04:00
d = bus_find_device ( & mdio_bus_type , NULL , phy_np , of_phy_match ) ;
2009-04-25 16:52:56 +04:00
return d ? to_phy_device ( d ) : NULL ;
}
EXPORT_SYMBOL ( of_phy_find_device ) ;
/**
* of_phy_connect - Connect to the phy described in the device tree
* @ dev : pointer to net_device claiming the phy
* @ phy_np : Pointer to device tree node for the PHY
* @ hndlr : Link state callback for the network device
* @ iface : PHY data interface type
*
2011-03-31 05:57:33 +04:00
* Returns a pointer to the phy_device if successful . NULL otherwise
2009-04-25 16:52:56 +04:00
*/
struct phy_device * of_phy_connect ( struct net_device * dev ,
struct device_node * phy_np ,
void ( * hndlr ) ( struct net_device * ) , u32 flags ,
phy_interface_t iface )
{
struct phy_device * phy = of_phy_find_device ( phy_np ) ;
if ( ! phy )
return NULL ;
return phy_connect_direct ( dev , phy , hndlr , flags , iface ) ? NULL : phy ;
}
EXPORT_SYMBOL ( of_phy_connect ) ;
2009-07-17 01:31:31 +04:00
/**
* of_phy_connect_fixed_link - Parse fixed - link property and return a dummy phy
* @ dev : pointer to net_device claiming the phy
* @ hndlr : Link state callback for the network device
* @ iface : PHY data interface type
*
* This function is a temporary stop - gap and will be removed soon . It is
* only to support the fs_enet , ucc_geth and gianfar Ethernet drivers . Do
* not call this function from new drivers .
*/
struct phy_device * of_phy_connect_fixed_link ( struct net_device * dev ,
void ( * hndlr ) ( struct net_device * ) ,
phy_interface_t iface )
{
struct device_node * net_np ;
char bus_id [ MII_BUS_ID_SIZE + 3 ] ;
struct phy_device * phy ;
2010-01-30 11:45:26 +03:00
const __be32 * phy_id ;
2009-07-17 01:31:31 +04:00
int sz ;
if ( ! dev - > dev . parent )
return NULL ;
2010-04-14 03:12:29 +04:00
net_np = dev - > dev . parent - > of_node ;
2009-07-17 01:31:31 +04:00
if ( ! net_np )
return NULL ;
phy_id = of_get_property ( net_np , " fixed-link " , & sz ) ;
if ( ! phy_id | | sz < sizeof ( * phy_id ) )
return NULL ;
2012-02-27 16:48:46 +04:00
sprintf ( bus_id , PHY_ID_FMT , " fixed-0 " , be32_to_cpu ( phy_id [ 0 ] ) ) ;
2009-07-17 01:31:31 +04:00
phy = phy_connect ( dev , bus_id , hndlr , 0 , iface ) ;
return IS_ERR ( phy ) ? NULL : phy ;
}
EXPORT_SYMBOL ( of_phy_connect_fixed_link ) ;