2005-10-28 16:25:58 -04:00
/*
* Combined Ethernet driver for Motorola MPC8xx and MPC82xx .
*
2007-09-18 20:05:35 +04:00
* Copyright ( c ) 2003 Intracom S . A .
2005-10-28 16:25:58 -04:00
* by Pantelis Antoniou < panto @ intracom . gr >
2007-09-18 20:05:35 +04:00
*
* 2005 ( c ) MontaVista Software , Inc .
2005-10-28 16:25:58 -04:00
* Vitaly Bordug < vbordug @ ru . mvista . com >
*
2007-09-18 20:05:35 +04:00
* This file is licensed under the terms of the GNU General Public License
* version 2. This program is licensed " as is " without any warranty of any
2005-10-28 16:25:58 -04:00
* kind , whether express or implied .
*/
# include <linux/module.h>
# include <linux/ioport.h>
# include <linux/slab.h>
# include <linux/init.h>
2007-10-01 14:20:57 -05:00
# include <linux/interrupt.h>
2005-10-28 16:25:58 -04:00
# include <linux/netdevice.h>
# include <linux/etherdevice.h>
# include <linux/mii.h>
2006-08-14 23:00:30 -07:00
# include <linux/platform_device.h>
2007-10-01 14:20:57 -05:00
# include <linux/mdio-bitbang.h>
2009-04-25 12:53:33 +00:00
# include <linux/of_mdio.h>
2007-10-02 10:55:58 -05:00
# include <linux/of_platform.h>
2005-10-28 16:25:58 -04:00
# include "fs_enet.h"
2007-10-02 10:55:58 -05:00
struct bb_info {
2007-10-01 14:20:57 -05:00
struct mdiobb_ctrl ctrl ;
2007-10-02 10:55:58 -05:00
__be32 __iomem * dir ;
__be32 __iomem * dat ;
u32 mdio_msk ;
u32 mdc_msk ;
} ;
/* FIXME: If any other users of GPIO crop up, then these will have to
* have some sort of global synchronization to avoid races with other
* pins on the same port . The ideal solution would probably be to
* bind the ports to a GPIO driver , and have this be a client of it .
*/
static inline void bb_set ( u32 __iomem * p , u32 m )
2005-10-28 16:25:58 -04:00
{
2007-10-02 10:55:58 -05:00
out_be32 ( p , in_be32 ( p ) | m ) ;
2005-10-28 16:25:58 -04:00
}
2007-10-02 10:55:58 -05:00
static inline void bb_clr ( u32 __iomem * p , u32 m )
2005-10-28 16:25:58 -04:00
{
2007-10-02 10:55:58 -05:00
out_be32 ( p , in_be32 ( p ) & ~ m ) ;
2005-10-28 16:25:58 -04:00
}
2007-10-02 10:55:58 -05:00
static inline int bb_read ( u32 __iomem * p , u32 m )
2005-10-28 16:25:58 -04:00
{
2007-10-02 10:55:58 -05:00
return ( in_be32 ( p ) & m ) ! = 0 ;
2005-10-28 16:25:58 -04:00
}
2007-10-01 14:20:57 -05:00
static inline void mdio_dir ( struct mdiobb_ctrl * ctrl , int dir )
2005-10-28 16:25:58 -04:00
{
2007-10-01 14:20:57 -05:00
struct bb_info * bitbang = container_of ( ctrl , struct bb_info , ctrl ) ;
2005-10-28 16:25:58 -04:00
2007-10-01 14:20:57 -05:00
if ( dir )
bb_set ( bitbang - > dir , bitbang - > mdio_msk ) ;
else
bb_clr ( bitbang - > dir , bitbang - > mdio_msk ) ;
/* Read back to flush the write. */
in_be32 ( bitbang - > dir ) ;
2005-10-28 16:25:58 -04:00
}
2007-10-01 14:20:57 -05:00
static inline int mdio_read ( struct mdiobb_ctrl * ctrl )
2005-10-28 16:25:58 -04:00
{
2007-10-01 14:20:57 -05:00
struct bb_info * bitbang = container_of ( ctrl , struct bb_info , ctrl ) ;
2007-10-02 10:55:58 -05:00
return bb_read ( bitbang - > dat , bitbang - > mdio_msk ) ;
2005-10-28 16:25:58 -04:00
}
2007-10-01 14:20:57 -05:00
static inline void mdio ( struct mdiobb_ctrl * ctrl , int what )
2005-10-28 16:25:58 -04:00
{
2007-10-01 14:20:57 -05:00
struct bb_info * bitbang = container_of ( ctrl , struct bb_info , ctrl ) ;
2005-10-28 16:25:58 -04:00
if ( what )
2007-10-02 10:55:58 -05:00
bb_set ( bitbang - > dat , bitbang - > mdio_msk ) ;
2005-10-28 16:25:58 -04:00
else
2007-10-02 10:55:58 -05:00
bb_clr ( bitbang - > dat , bitbang - > mdio_msk ) ;
2007-10-01 14:20:57 -05:00
/* Read back to flush the write. */
in_be32 ( bitbang - > dat ) ;
2005-10-28 16:25:58 -04:00
}
2007-10-01 14:20:57 -05:00
static inline void mdc ( struct mdiobb_ctrl * ctrl , int what )
2005-10-28 16:25:58 -04:00
{
2007-10-01 14:20:57 -05:00
struct bb_info * bitbang = container_of ( ctrl , struct bb_info , ctrl ) ;
2005-10-28 16:25:58 -04:00
if ( what )
2007-10-02 10:55:58 -05:00
bb_set ( bitbang - > dat , bitbang - > mdc_msk ) ;
2005-10-28 16:25:58 -04:00
else
2007-10-02 10:55:58 -05:00
bb_clr ( bitbang - > dat , bitbang - > mdc_msk ) ;
2005-10-28 16:25:58 -04:00
2007-10-01 14:20:57 -05:00
/* Read back to flush the write. */
in_be32 ( bitbang - > dat ) ;
2005-10-28 16:25:58 -04:00
}
2007-10-01 14:20:57 -05:00
static struct mdiobb_ops bb_ops = {
. owner = THIS_MODULE ,
. set_mdc = mdc ,
. set_mdio_dir = mdio_dir ,
. set_mdio_data = mdio ,
. get_mdio_data = mdio_read ,
} ;
2006-08-14 23:00:30 -07:00
2007-10-02 10:55:58 -05:00
static int __devinit fs_mii_bitbang_init ( struct mii_bus * bus ,
struct device_node * np )
2005-10-28 16:25:58 -04:00
{
2007-10-02 10:55:58 -05:00
struct resource res ;
const u32 * data ;
int mdio_pin , mdc_pin , len ;
struct bb_info * bitbang = bus - > priv ;
2005-10-28 16:25:58 -04:00
2007-10-02 10:55:58 -05:00
int ret = of_address_to_resource ( np , 0 , & res ) ;
if ( ret )
return ret ;
if ( res . end - res . start < 13 )
return - ENODEV ;
/* This should really encode the pin number as well, but all
* we get is an int , and the odds of multiple bitbang mdio buses
* is low enough that it ' s not worth going too crazy .
*/
2008-04-09 19:38:13 -05:00
snprintf ( bus - > id , MII_BUS_ID_SIZE , " %x " , res . start ) ;
2007-10-02 10:55:58 -05:00
data = of_get_property ( np , " fsl,mdio-pin " , & len ) ;
if ( ! data | | len ! = 4 )
return - ENODEV ;
mdio_pin = * data ;
data = of_get_property ( np , " fsl,mdc-pin " , & len ) ;
if ( ! data | | len ! = 4 )
return - ENODEV ;
mdc_pin = * data ;
bitbang - > dir = ioremap ( res . start , res . end - res . start + 1 ) ;
if ( ! bitbang - > dir )
return - ENOMEM ;
bitbang - > dat = bitbang - > dir + 4 ;
bitbang - > mdio_msk = 1 < < ( 31 - mdio_pin ) ;
bitbang - > mdc_msk = 1 < < ( 31 - mdc_pin ) ;
return 0 ;
}
static int __devinit fs_enet_mdio_probe ( struct of_device * ofdev ,
const struct of_device_id * match )
{
struct mii_bus * new_bus ;
struct bb_info * bitbang ;
int ret = - ENOMEM ;
bitbang = kzalloc ( sizeof ( struct bb_info ) , GFP_KERNEL ) ;
if ( ! bitbang )
2007-10-01 14:20:57 -05:00
goto out ;
bitbang - > ctrl . ops = & bb_ops ;
new_bus = alloc_mdio_bitbang ( & bitbang - > ctrl ) ;
if ( ! new_bus )
goto out_free_priv ;
2007-10-02 10:55:58 -05:00
new_bus - > name = " CPM2 Bitbanged MII " ,
ret = fs_mii_bitbang_init ( new_bus , ofdev - > node ) ;
if ( ret )
2007-10-01 14:20:57 -05:00
goto out_free_bus ;
2007-10-02 10:55:58 -05:00
new_bus - > phy_mask = ~ 0 ;
new_bus - > irq = kmalloc ( sizeof ( int ) * PHY_MAX_ADDR , GFP_KERNEL ) ;
if ( ! new_bus - > irq )
goto out_unmap_regs ;
2008-10-01 15:41:33 +00:00
new_bus - > parent = & ofdev - > dev ;
2007-10-02 10:55:58 -05:00
dev_set_drvdata ( & ofdev - > dev , new_bus ) ;
2009-04-25 12:53:33 +00:00
ret = of_mdiobus_register ( new_bus , ofdev - > node ) ;
2007-10-02 10:55:58 -05:00
if ( ret )
goto out_free_irqs ;
return 0 ;
out_free_irqs :
dev_set_drvdata ( & ofdev - > dev , NULL ) ;
kfree ( new_bus - > irq ) ;
out_unmap_regs :
iounmap ( bitbang - > dir ) ;
out_free_bus :
2007-10-01 14:20:57 -05:00
free_mdio_bitbang ( new_bus ) ;
2008-10-08 16:29:57 -07:00
out_free_priv :
kfree ( bitbang ) ;
2007-10-02 10:55:58 -05:00
out :
return ret ;
}
static int fs_enet_mdio_remove ( struct of_device * ofdev )
{
struct mii_bus * bus = dev_get_drvdata ( & ofdev - > dev ) ;
struct bb_info * bitbang = bus - > priv ;
mdiobus_unregister ( bus ) ;
dev_set_drvdata ( & ofdev - > dev , NULL ) ;
kfree ( bus - > irq ) ;
2008-10-08 16:29:57 -07:00
free_mdio_bitbang ( bus ) ;
2007-10-02 10:55:58 -05:00
iounmap ( bitbang - > dir ) ;
kfree ( bitbang ) ;
return 0 ;
}
static struct of_device_id fs_enet_mdio_bb_match [ ] = {
{
. compatible = " fsl,cpm2-mdio-bitbang " ,
} ,
{ } ,
} ;
static struct of_platform_driver fs_enet_bb_mdio_driver = {
. name = " fsl-bb-mdio " ,
. match_table = fs_enet_mdio_bb_match ,
. probe = fs_enet_mdio_probe ,
. remove = fs_enet_mdio_remove ,
} ;
2007-10-01 14:20:57 -05:00
static int fs_enet_mdio_bb_init ( void )
2007-10-02 10:55:58 -05:00
{
return of_register_platform_driver ( & fs_enet_bb_mdio_driver ) ;
}
2007-10-01 14:20:57 -05:00
static void fs_enet_mdio_bb_exit ( void )
2007-10-02 10:55:58 -05:00
{
of_unregister_platform_driver ( & fs_enet_bb_mdio_driver ) ;
}
module_init ( fs_enet_mdio_bb_init ) ;
module_exit ( fs_enet_mdio_bb_exit ) ;