2005-10-11 03:58:14 +04:00
/*
* drivers / net / ibm_emac / ibm_emac_zmii . c
*
* Driver for PowerPC 4 xx on - chip ethernet controller , ZMII bridge support .
*
* Copyright ( c ) 2004 , 2005 Zultys Technologies .
* Eugene Surovegin < eugene . surovegin @ zultys . com > or < ebs @ ebshome . net >
*
* Based on original work by
* Armin Kuster < akuster @ mvista . com >
* Copyright 2001 MontaVista Softare 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 .
*
*/
# include <linux/kernel.h>
# include <linux/ethtool.h>
# include <asm/io.h>
# include "ibm_emac_core.h"
# include "ibm_emac_debug.h"
/* ZMIIx_FER */
# define ZMII_FER_MDI(idx) (0x80000000 >> ((idx) * 4))
# define ZMII_FER_MDI_ALL (ZMII_FER_MDI(0) | ZMII_FER_MDI(1) | \
ZMII_FER_MDI ( 2 ) | ZMII_FER_MDI ( 3 ) )
# define ZMII_FER_SMII(idx) (0x40000000 >> ((idx) * 4))
# define ZMII_FER_RMII(idx) (0x20000000 >> ((idx) * 4))
# define ZMII_FER_MII(idx) (0x10000000 >> ((idx) * 4))
/* ZMIIx_SSR */
# define ZMII_SSR_SCI(idx) (0x40000000 >> ((idx) * 4))
# define ZMII_SSR_FSS(idx) (0x20000000 >> ((idx) * 4))
# define ZMII_SSR_SP(idx) (0x10000000 >> ((idx) * 4))
/* ZMII only supports MII, RMII and SMII
* we also support autodetection for backward compatibility
*/
static inline int zmii_valid_mode ( int mode )
{
return mode = = PHY_MODE_MII | |
mode = = PHY_MODE_RMII | |
mode = = PHY_MODE_SMII | |
mode = = PHY_MODE_NA ;
}
static inline const char * zmii_mode_name ( int mode )
{
switch ( mode ) {
case PHY_MODE_MII :
return " MII " ;
case PHY_MODE_RMII :
return " RMII " ;
case PHY_MODE_SMII :
return " SMII " ;
default :
BUG ( ) ;
}
}
static inline u32 zmii_mode_mask ( int mode , int input )
{
switch ( mode ) {
case PHY_MODE_MII :
return ZMII_FER_MII ( input ) ;
case PHY_MODE_RMII :
return ZMII_FER_RMII ( input ) ;
case PHY_MODE_SMII :
return ZMII_FER_SMII ( input ) ;
default :
return 0 ;
}
}
static int __init zmii_init ( struct ocp_device * ocpdev , int input , int * mode )
{
struct ibm_ocp_zmii * dev = ocp_get_drvdata ( ocpdev ) ;
2005-12-01 18:15:21 +03:00
struct zmii_regs __iomem * p ;
2005-10-11 03:58:14 +04:00
ZMII_DBG ( " %d: init(%d, %d) " NL , ocpdev - > def - > index , input , * mode ) ;
if ( ! dev ) {
dev = kzalloc ( sizeof ( struct ibm_ocp_zmii ) , GFP_KERNEL ) ;
if ( ! dev ) {
printk ( KERN_ERR
" zmii%d: couldn't allocate device structure! \n " ,
ocpdev - > def - > index ) ;
return - ENOMEM ;
}
dev - > mode = PHY_MODE_NA ;
2005-12-01 18:15:21 +03:00
p = ioremap ( ocpdev - > def - > paddr , sizeof ( struct zmii_regs ) ) ;
2005-10-11 03:58:14 +04:00
if ( ! p ) {
printk ( KERN_ERR
" zmii%d: could not ioremap device registers! \n " ,
ocpdev - > def - > index ) ;
kfree ( dev ) ;
return - ENOMEM ;
}
dev - > base = p ;
ocp_set_drvdata ( ocpdev , dev ) ;
/* We may need FER value for autodetection later */
dev - > fer_save = in_be32 ( & p - > fer ) ;
/* Disable all inputs by default */
out_be32 ( & p - > fer , 0 ) ;
} else
p = dev - > base ;
if ( ! zmii_valid_mode ( * mode ) ) {
/* Probably an EMAC connected to RGMII,
* but it still may need ZMII for MDIO
*/
goto out ;
}
/* Autodetect ZMII mode if not specified.
* This is only for backward compatibility with the old driver .
* Please , always specify PHY mode in your board port to avoid
* any surprises .
*/
if ( dev - > mode = = PHY_MODE_NA ) {
if ( * mode = = PHY_MODE_NA ) {
u32 r = dev - > fer_save ;
ZMII_DBG ( " %d: autodetecting mode, FER = 0x%08x " NL ,
ocpdev - > def - > index , r ) ;
if ( r & ( ZMII_FER_MII ( 0 ) | ZMII_FER_MII ( 1 ) ) )
dev - > mode = PHY_MODE_MII ;
else if ( r & ( ZMII_FER_RMII ( 0 ) | ZMII_FER_RMII ( 1 ) ) )
dev - > mode = PHY_MODE_RMII ;
else
dev - > mode = PHY_MODE_SMII ;
} else
dev - > mode = * mode ;
printk ( KERN_NOTICE " zmii%d: bridge in %s mode \n " ,
ocpdev - > def - > index , zmii_mode_name ( dev - > mode ) ) ;
} else {
/* All inputs must use the same mode */
if ( * mode ! = PHY_MODE_NA & & * mode ! = dev - > mode ) {
printk ( KERN_ERR
" zmii%d: invalid mode %d specified for input %d \n " ,
ocpdev - > def - > index , * mode , input ) ;
return - EINVAL ;
}
}
/* Report back correct PHY mode,
* it may be used during PHY initialization .
*/
* mode = dev - > mode ;
/* Enable this input */
out_be32 ( & p - > fer , in_be32 ( & p - > fer ) | zmii_mode_mask ( dev - > mode , input ) ) ;
out :
+ + dev - > users ;
return 0 ;
}
int __init zmii_attach ( void * emac )
{
struct ocp_enet_private * dev = emac ;
struct ocp_func_emac_data * emacdata = dev - > def - > additions ;
if ( emacdata - > zmii_idx > = 0 ) {
dev - > zmii_input = emacdata - > zmii_mux ;
dev - > zmii_dev =
ocp_find_device ( OCP_VENDOR_IBM , OCP_FUNC_ZMII ,
emacdata - > zmii_idx ) ;
if ( ! dev - > zmii_dev ) {
printk ( KERN_ERR " emac%d: unknown zmii%d! \n " ,
dev - > def - > index , emacdata - > zmii_idx ) ;
return - ENODEV ;
}
if ( zmii_init
( dev - > zmii_dev , dev - > zmii_input , & emacdata - > phy_mode ) ) {
printk ( KERN_ERR
" emac%d: zmii%d initialization failed! \n " ,
dev - > def - > index , emacdata - > zmii_idx ) ;
return - ENODEV ;
}
}
return 0 ;
}
void __zmii_enable_mdio ( struct ocp_device * ocpdev , int input )
{
struct ibm_ocp_zmii * dev = ocp_get_drvdata ( ocpdev ) ;
u32 fer = in_be32 ( & dev - > base - > fer ) & ~ ZMII_FER_MDI_ALL ;
ZMII_DBG2 ( " %d: mdio(%d) " NL , ocpdev - > def - > index , input ) ;
out_be32 ( & dev - > base - > fer , fer | ZMII_FER_MDI ( input ) ) ;
}
void __zmii_set_speed ( struct ocp_device * ocpdev , int input , int speed )
{
struct ibm_ocp_zmii * dev = ocp_get_drvdata ( ocpdev ) ;
u32 ssr = in_be32 ( & dev - > base - > ssr ) ;
ZMII_DBG ( " %d: speed(%d, %d) " NL , ocpdev - > def - > index , input , speed ) ;
if ( speed = = SPEED_100 )
ssr | = ZMII_SSR_SP ( input ) ;
else
ssr & = ~ ZMII_SSR_SP ( input ) ;
out_be32 ( & dev - > base - > ssr , ssr ) ;
}
2007-05-16 22:57:37 +04:00
void __zmii_fini ( struct ocp_device * ocpdev , int input )
2005-10-11 03:58:14 +04:00
{
struct ibm_ocp_zmii * dev = ocp_get_drvdata ( ocpdev ) ;
BUG_ON ( ! dev | | dev - > users = = 0 ) ;
ZMII_DBG ( " %d: fini(%d) " NL , ocpdev - > def - > index , input ) ;
/* Disable this input */
out_be32 ( & dev - > base - > fer ,
in_be32 ( & dev - > base - > fer ) & ~ zmii_mode_mask ( dev - > mode , input ) ) ;
if ( ! - - dev - > users ) {
/* Free everything if this is the last user */
ocp_set_drvdata ( ocpdev , NULL ) ;
2005-12-01 18:15:21 +03:00
iounmap ( dev - > base ) ;
2005-10-11 03:58:14 +04:00
kfree ( dev ) ;
}
}
int __zmii_get_regs_len ( struct ocp_device * ocpdev )
{
return sizeof ( struct emac_ethtool_regs_subhdr ) +
sizeof ( struct zmii_regs ) ;
}
void * zmii_dump_regs ( struct ocp_device * ocpdev , void * buf )
{
struct ibm_ocp_zmii * dev = ocp_get_drvdata ( ocpdev ) ;
struct emac_ethtool_regs_subhdr * hdr = buf ;
struct zmii_regs * regs = ( struct zmii_regs * ) ( hdr + 1 ) ;
hdr - > version = 0 ;
hdr - > index = ocpdev - > def - > index ;
memcpy_fromio ( regs , dev - > base , sizeof ( struct zmii_regs ) ) ;
return regs + 1 ;
}