2007-04-13 10:26:03 +04:00
/*
* drivers / net / ucc_geth_mii . c
*
2007-05-12 03:25:07 +04:00
* QE UCC Gigabit Ethernet Driver - - MII Management Bus Implementation
* Provides Bus interface for MII Management regs in the UCC register space
2007-04-13 10:26:03 +04:00
*
2007-05-12 03:25:07 +04:00
* Copyright ( C ) 2007 Freescale Semiconductor , Inc .
2007-04-13 10:26:03 +04:00
*
2007-05-12 03:25:07 +04:00
* Authors : Li Yang < leoli @ freescale . com >
* Kim Phillips < kim . phillips @ freescale . com >
2007-04-13 10:26:03 +04: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 .
*
*/
# include <linux/kernel.h>
# include <linux/sched.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/spinlock.h>
# include <linux/mm.h>
# include <linux/module.h>
# include <linux/platform_device.h>
# include <linux/crc32.h>
# include <linux/mii.h>
# include <linux/phy.h>
# include <linux/fsl_devices.h>
# include <asm/of_platform.h>
# include <asm/io.h>
# include <asm/irq.h>
# include <asm/uaccess.h>
# include <asm/ucc.h>
# include "ucc_geth_mii.h"
# include "ucc_geth.h"
# define DEBUG
# ifdef DEBUG
# define vdbg(format, arg...) printk(KERN_DEBUG , format "\n" , ## arg)
# else
# define vdbg(format, arg...) do {} while(0)
# endif
2007-07-19 07:47:47 +04:00
# define MII_DRV_DESC "QE UCC Ethernet Controller MII Bus"
# define MII_DRV_NAME "fsl-uec_mdio"
2007-04-13 10:26:03 +04:00
/* Write value to the PHY for this device to the register at regnum, */
/* waiting until the write is done before it returns. All PHY */
/* configuration has to be done through the master UEC MIIM regs */
int uec_mdio_write ( struct mii_bus * bus , int mii_id , int regnum , u16 value )
{
struct ucc_mii_mng __iomem * regs = ( void __iomem * ) bus - > priv ;
/* Setting up the MII Mangement Address Register */
out_be32 ( & regs - > miimadd ,
( mii_id < < MIIMADD_PHY_ADDRESS_SHIFT ) | regnum ) ;
/* Setting up the MII Mangement Control Register with the value */
out_be32 ( & regs - > miimcon , value ) ;
/* Wait till MII management write is complete */
while ( ( in_be32 ( & regs - > miimind ) ) & MIIMIND_BUSY )
cpu_relax ( ) ;
return 0 ;
}
/* Reads from register regnum in the PHY for device dev, */
/* returning the value. Clears miimcom first. All PHY */
/* configuration has to be done through the TSEC1 MIIM regs */
int uec_mdio_read ( struct mii_bus * bus , int mii_id , int regnum )
{
struct ucc_mii_mng __iomem * regs = ( void __iomem * ) bus - > priv ;
u16 value ;
/* Setting up the MII Mangement Address Register */
out_be32 ( & regs - > miimadd ,
( mii_id < < MIIMADD_PHY_ADDRESS_SHIFT ) | regnum ) ;
/* Clear miimcom, perform an MII management read cycle */
out_be32 ( & regs - > miimcom , 0 ) ;
out_be32 ( & regs - > miimcom , MIIMCOM_READ_CYCLE ) ;
/* Wait till MII management write is complete */
while ( ( in_be32 ( & regs - > miimind ) ) & ( MIIMIND_BUSY | MIIMIND_NOT_VALID ) )
cpu_relax ( ) ;
/* Read MII management status */
value = in_be32 ( & regs - > miimstat ) ;
return value ;
}
/* Reset the MIIM registers, and wait for the bus to free */
int uec_mdio_reset ( struct mii_bus * bus )
{
struct ucc_mii_mng __iomem * regs = ( void __iomem * ) bus - > priv ;
unsigned int timeout = PHY_INIT_TIMEOUT ;
spin_lock_bh ( & bus - > mdio_lock ) ;
/* Reset the management interface */
out_be32 ( & regs - > miimcfg , MIIMCFG_RESET_MANAGEMENT ) ;
/* Setup the MII Mgmt clock speed */
out_be32 ( & regs - > miimcfg , MIIMCFG_MANAGEMENT_CLOCK_DIVIDE_BY_112 ) ;
/* Wait until the bus is free */
while ( ( in_be32 ( & regs - > miimind ) & MIIMIND_BUSY ) & & timeout - - )
cpu_relax ( ) ;
spin_unlock_bh ( & bus - > mdio_lock ) ;
if ( timeout < = 0 ) {
printk ( KERN_ERR " %s: The MII Bus is stuck! \n " , bus - > name ) ;
return - EBUSY ;
}
return 0 ;
}
static int uec_mdio_probe ( struct of_device * ofdev , const struct of_device_id * match )
{
struct device * device = & ofdev - > dev ;
struct device_node * np = ofdev - > node , * tempnp = NULL ;
struct device_node * child = NULL ;
struct ucc_mii_mng __iomem * regs ;
struct mii_bus * new_bus ;
struct resource res ;
int k , err = 0 ;
new_bus = kzalloc ( sizeof ( struct mii_bus ) , GFP_KERNEL ) ;
if ( NULL = = new_bus )
return - ENOMEM ;
new_bus - > name = " UCC Ethernet Controller MII Bus " ;
new_bus - > read = & uec_mdio_read ;
new_bus - > write = & uec_mdio_write ;
new_bus - > reset = & uec_mdio_reset ;
memset ( & res , 0 , sizeof ( res ) ) ;
err = of_address_to_resource ( np , 0 , & res ) ;
if ( err )
goto reg_map_fail ;
new_bus - > id = res . start ;
new_bus - > irq = kmalloc ( 32 * sizeof ( int ) , GFP_KERNEL ) ;
if ( NULL = = new_bus - > irq ) {
err = - ENOMEM ;
goto reg_map_fail ;
}
for ( k = 0 ; k < 32 ; k + + )
new_bus - > irq [ k ] = PHY_POLL ;
while ( ( child = of_get_next_child ( np , child ) ) ! = NULL ) {
int irq = irq_of_parse_and_map ( child , 0 ) ;
if ( irq ! = NO_IRQ ) {
2007-05-01 07:54:02 +04:00
const u32 * id = of_get_property ( child , " reg " , NULL ) ;
2007-04-13 10:26:03 +04:00
new_bus - > irq [ * id ] = irq ;
}
}
/* Set the base address */
regs = ioremap ( res . start , sizeof ( struct ucc_mii_mng ) ) ;
if ( NULL = = regs ) {
err = - ENOMEM ;
goto ioremap_fail ;
}
new_bus - > priv = ( void __force * ) regs ;
new_bus - > dev = device ;
dev_set_drvdata ( device , new_bus ) ;
/* Read MII management master from device tree */
while ( ( tempnp = of_find_compatible_node ( tempnp , " network " , " ucc_geth " ) )
! = NULL ) {
struct resource tempres ;
err = of_address_to_resource ( tempnp , 0 , & tempres ) ;
if ( err )
goto bus_register_fail ;
/* if our mdio regs fall within this UCC regs range */
if ( ( res . start > = tempres . start ) & &
( res . end < = tempres . end ) ) {
/* set this UCC to be the MII master */
2007-05-01 07:54:02 +04:00
const u32 * id = of_get_property ( tempnp , " device-id " , NULL ) ;
2007-04-13 10:26:03 +04:00
if ( id = = NULL )
goto bus_register_fail ;
ucc_set_qe_mux_mii_mng ( * id - 1 ) ;
/* assign the TBI an address which won't
* conflict with the PHYs */
out_be32 ( & regs - > utbipar , UTBIPAR_INIT_TBIPA ) ;
break ;
}
}
err = mdiobus_register ( new_bus ) ;
if ( 0 ! = err ) {
printk ( KERN_ERR " %s: Cannot register as MDIO bus \n " ,
new_bus - > name ) ;
goto bus_register_fail ;
}
return 0 ;
bus_register_fail :
iounmap ( regs ) ;
ioremap_fail :
kfree ( new_bus - > irq ) ;
reg_map_fail :
kfree ( new_bus ) ;
return err ;
}
int uec_mdio_remove ( struct of_device * ofdev )
{
struct device * device = & ofdev - > dev ;
struct mii_bus * bus = dev_get_drvdata ( device ) ;
mdiobus_unregister ( bus ) ;
dev_set_drvdata ( device , NULL ) ;
iounmap ( ( void __iomem * ) bus - > priv ) ;
bus - > priv = NULL ;
kfree ( bus ) ;
return 0 ;
}
static struct of_device_id uec_mdio_match [ ] = {
{
. type = " mdio " ,
. compatible = " ucc_geth_phy " ,
} ,
{ } ,
} ;
static struct of_platform_driver uec_mdio_driver = {
2007-07-19 07:47:47 +04:00
. name = MII_DRV_NAME ,
2007-04-13 10:26:03 +04:00
. probe = uec_mdio_probe ,
. remove = uec_mdio_remove ,
. match_table = uec_mdio_match ,
} ;
int __init uec_mdio_init ( void )
{
return of_register_platform_driver ( & uec_mdio_driver ) ;
}
2007-08-03 12:07:58 +04:00
/* called from __init ucc_geth_init, therefore can not be __exit */
void uec_mdio_exit ( void )
2007-04-13 10:26:03 +04:00
{
of_unregister_platform_driver ( & uec_mdio_driver ) ;
}