2006-08-14 23:00:30 -07:00
/*
* Combined Ethernet driver for Motorola MPC8xx and MPC82xx .
*
* Copyright ( c ) 2003 Intracom S . A .
* by Pantelis Antoniou < panto @ intracom . gr >
*
* 2005 ( c ) MontaVista Software , Inc .
* Vitaly Bordug < vbordug @ ru . mvista . com >
*
* 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
* kind , whether express or implied .
*/
# include <linux/module.h>
# include <linux/types.h>
# include <linux/kernel.h>
# include <linux/string.h>
# include <linux/ptrace.h>
# include <linux/errno.h>
# include <linux/ioport.h>
# include <linux/slab.h>
# include <linux/interrupt.h>
# include <linux/delay.h>
# include <linux/netdevice.h>
# include <linux/etherdevice.h>
# include <linux/skbuff.h>
# include <linux/spinlock.h>
# include <linux/mii.h>
# include <linux/ethtool.h>
# include <linux/bitops.h>
# include <linux/platform_device.h>
2013-09-17 14:28:33 -05:00
# include <linux/of_address.h>
2008-06-12 08:32:13 -05:00
# include <linux/of_platform.h>
2006-08-14 23:00:30 -07:00
# include <asm/pgtable.h>
# include <asm/irq.h>
2016-12-24 11:46:01 -08:00
# include <linux/uaccess.h>
2009-07-17 02:27:07 +00:00
# include <asm/mpc5xxx.h>
2006-08-14 23:00:30 -07:00
# include "fs_enet.h"
# include "fec.h"
/* Make MII read/write commands for the FEC.
*/
# define mk_mii_read(REG) (0x60020000 | ((REG & 0x1f) << 18))
# define mk_mii_write(REG, VAL) (0x50020000 | ((REG & 0x1f) << 18) | (VAL & 0xffff))
# define mk_mii_end 0
# define FEC_MII_LOOPS 10000
static int fs_enet_fec_mii_read ( struct mii_bus * bus , int phy_id , int location )
{
struct fec_info * fec = bus - > priv ;
2010-02-26 12:00:48 +00:00
struct fec __iomem * fecp = fec - > fecp ;
2006-08-14 23:00:30 -07:00
int i , ret = - 1 ;
2009-04-11 14:50:23 +00:00
BUG_ON ( ( in_be32 ( & fecp - > fec_r_cntrl ) & FEC_RCNTRL_MII_MODE ) = = 0 ) ;
2006-08-14 23:00:30 -07:00
/* Add PHY address to register command. */
out_be32 ( & fecp - > fec_mii_data , ( phy_id < < 23 ) | mk_mii_read ( location ) ) ;
for ( i = 0 ; i < FEC_MII_LOOPS ; i + + )
if ( ( in_be32 ( & fecp - > fec_ievent ) & FEC_ENET_MII ) ! = 0 )
break ;
if ( i < FEC_MII_LOOPS ) {
out_be32 ( & fecp - > fec_ievent , FEC_ENET_MII ) ;
ret = in_be32 ( & fecp - > fec_mii_data ) & 0xffff ;
}
return ret ;
}
static int fs_enet_fec_mii_write ( struct mii_bus * bus , int phy_id , int location , u16 val )
{
struct fec_info * fec = bus - > priv ;
2010-02-26 12:00:48 +00:00
struct fec __iomem * fecp = fec - > fecp ;
2006-08-14 23:00:30 -07:00
int i ;
/* this must never happen */
2009-04-11 14:50:23 +00:00
BUG_ON ( ( in_be32 ( & fecp - > fec_r_cntrl ) & FEC_RCNTRL_MII_MODE ) = = 0 ) ;
2006-08-14 23:00:30 -07:00
/* Add PHY address to register command. */
out_be32 ( & fecp - > fec_mii_data , ( phy_id < < 23 ) | mk_mii_write ( location , val ) ) ;
for ( i = 0 ; i < FEC_MII_LOOPS ; i + + )
if ( ( in_be32 ( & fecp - > fec_ievent ) & FEC_ENET_MII ) ! = 0 )
break ;
if ( i < FEC_MII_LOOPS )
out_be32 ( & fecp - > fec_ievent , FEC_ENET_MII ) ;
return 0 ;
}
2015-03-17 19:37:34 +01:00
static const struct of_device_id fs_enet_mdio_fec_match [ ] ;
2012-12-03 09:23:08 -05:00
static int fs_enet_mdio_probe ( struct platform_device * ofdev )
2007-10-02 10:55:58 -05:00
{
2011-05-18 11:19:24 -06:00
const struct of_device_id * match ;
2007-10-02 10:55:58 -05:00
struct resource res ;
struct mii_bus * new_bus ;
struct fec_info * fec ;
2011-02-22 21:05:51 -07:00
int ( * get_bus_freq ) ( struct device_node * ) ;
2009-07-17 02:27:07 +00:00
int ret = - ENOMEM , clock , speed ;
2007-10-02 10:55:58 -05:00
2011-05-18 11:19:24 -06:00
match = of_match_device ( fs_enet_mdio_fec_match , & ofdev - > dev ) ;
if ( ! match )
2011-02-22 21:05:51 -07:00
return - EINVAL ;
2011-05-18 11:19:24 -06:00
get_bus_freq = match - > data ;
2011-02-22 21:05:51 -07:00
2008-10-08 16:29:57 -07:00
new_bus = mdiobus_alloc ( ) ;
2007-10-02 10:55:58 -05:00
if ( ! new_bus )
goto out ;
fec = kzalloc ( sizeof ( struct fec_info ) , GFP_KERNEL ) ;
if ( ! fec )
goto out_mii ;
new_bus - > priv = fec ;
new_bus - > name = " FEC MII Bus " ;
new_bus - > read = & fs_enet_fec_mii_read ;
new_bus - > write = & fs_enet_fec_mii_write ;
2010-04-13 16:12:29 -07:00
ret = of_address_to_resource ( ofdev - > dev . of_node , 0 , & res ) ;
2007-10-02 10:55:58 -05:00
if ( ret )
2008-05-02 13:42:41 -05:00
goto out_res ;
2007-10-02 10:55:58 -05:00
2008-04-09 19:38:13 -05:00
snprintf ( new_bus - > id , MII_BUS_ID_SIZE , " %x " , res . start ) ;
2007-10-02 10:55:58 -05:00
2011-06-09 09:13:32 -07:00
fec - > fecp = ioremap ( res . start , resource_size ( & res ) ) ;
2012-08-14 02:58:33 +00:00
if ( ! fec - > fecp ) {
ret = - ENOMEM ;
2007-10-02 10:55:58 -05:00
goto out_fec ;
2012-08-14 02:58:33 +00:00
}
2007-10-02 10:55:58 -05:00
2009-07-17 02:27:07 +00:00
if ( get_bus_freq ) {
2010-04-13 16:12:29 -07:00
clock = get_bus_freq ( ofdev - > dev . of_node ) ;
2009-07-17 02:27:07 +00:00
if ( ! clock ) {
/* Use maximum divider if clock is unknown */
dev_warn ( & ofdev - > dev , " could not determine IPS clock \n " ) ;
clock = 0x3F * 5000000 ;
}
} else
clock = ppc_proc_freq ;
/*
* Scale for a MII clock < = 2.5 MHz
* Note that only 6 bits ( 25 : 30 ) are available for MII speed .
*/
speed = ( clock + 4999999 ) / 5000000 ;
if ( speed > 0x3F ) {
speed = 0x3F ;
dev_err ( & ofdev - > dev ,
" MII clock (%d Hz) exceeds max (2.5 MHz) \n " ,
clock / speed ) ;
}
fec - > mii_speed = speed < < 1 ;
2007-10-02 10:55:58 -05:00
setbits32 ( & fec - > fecp - > fec_r_cntrl , FEC_RCNTRL_MII_MODE ) ;
setbits32 ( & fec - > fecp - > fec_ecntrl , FEC_ECNTRL_PINMUX |
FEC_ECNTRL_ETHER_EN ) ;
out_be32 ( & fec - > fecp - > fec_ievent , FEC_ENET_MII ) ;
2009-07-17 02:27:07 +00:00
clrsetbits_be32 ( & fec - > fecp - > fec_mii_speed , 0x7E , fec - > mii_speed ) ;
2007-10-02 10:55:58 -05:00
new_bus - > phy_mask = ~ 0 ;
2008-10-01 15:41:33 +00:00
new_bus - > parent = & ofdev - > dev ;
2013-05-23 00:52:31 +00:00
platform_set_drvdata ( ofdev , new_bus ) ;
2007-10-02 10:55:58 -05:00
2010-04-13 16:12:29 -07:00
ret = of_mdiobus_register ( new_bus , ofdev - > dev . of_node ) ;
2007-10-02 10:55:58 -05:00
if ( ret )
2016-01-06 20:11:15 +01:00
goto out_unmap_regs ;
2007-10-02 10:55:58 -05:00
return 0 ;
out_unmap_regs :
iounmap ( fec - > fecp ) ;
2008-05-02 13:42:41 -05:00
out_res :
2007-10-02 10:55:58 -05:00
out_fec :
kfree ( fec ) ;
out_mii :
2008-10-08 16:29:57 -07:00
mdiobus_free ( new_bus ) ;
2007-10-02 10:55:58 -05:00
out :
return ret ;
}
2010-08-06 09:25:50 -06:00
static int fs_enet_mdio_remove ( struct platform_device * ofdev )
2007-10-02 10:55:58 -05:00
{
2013-05-23 00:52:31 +00:00
struct mii_bus * bus = platform_get_drvdata ( ofdev ) ;
2007-10-02 10:55:58 -05:00
struct fec_info * fec = bus - > priv ;
mdiobus_unregister ( bus ) ;
iounmap ( fec - > fecp ) ;
kfree ( fec ) ;
2008-10-08 16:29:57 -07:00
mdiobus_free ( bus ) ;
2007-10-02 10:55:58 -05:00
return 0 ;
}
2015-03-17 19:37:34 +01:00
static const struct of_device_id fs_enet_mdio_fec_match [ ] = {
2007-10-02 10:55:58 -05:00
{
. compatible = " fsl,pq1-fec-mdio " ,
} ,
2009-07-17 02:27:07 +00:00
# if defined(CONFIG_PPC_MPC512x)
{
. compatible = " fsl,mpc5121-fec-mdio " ,
. data = mpc5xxx_get_bus_frequency ,
} ,
# endif
2007-10-02 10:55:58 -05:00
{ } ,
} ;
2009-10-14 14:54:52 -07:00
MODULE_DEVICE_TABLE ( of , fs_enet_mdio_fec_match ) ;
2007-10-02 10:55:58 -05:00
2011-02-22 21:05:51 -07:00
static struct platform_driver fs_enet_fec_mdio_driver = {
2010-04-13 16:13:02 -07:00
. driver = {
. name = " fsl-fec-mdio " ,
. of_match_table = fs_enet_mdio_fec_match ,
} ,
2007-10-02 10:55:58 -05:00
. probe = fs_enet_mdio_probe ,
. remove = fs_enet_mdio_remove ,
} ;
2011-11-27 16:44:17 +00:00
module_platform_driver ( fs_enet_fec_mdio_driver ) ;