2012-08-20 09:26:39 +00:00
/*
* QorIQ 10 G MDIO Controller
*
* Copyright 2012 Freescale Semiconductor , Inc .
*
* Authors : Andy Fleming < afleming @ freescale . com >
* Timur Tabi < timur @ freescale . 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/kernel.h>
# include <linux/slab.h>
# include <linux/interrupt.h>
# include <linux/module.h>
# include <linux/phy.h>
# include <linux/mdio.h>
2013-09-17 14:28:33 -05:00
# include <linux/of_address.h>
2012-08-20 09:26:39 +00:00
# include <linux/of_platform.h>
# include <linux/of_mdio.h>
/* Number of microseconds to wait for a register to respond */
# define TIMEOUT 1000
struct tgec_mdio_controller {
__be32 reserved [ 12 ] ;
__be32 mdio_stat ; /* MDIO configuration and status */
__be32 mdio_ctl ; /* MDIO control */
__be32 mdio_data ; /* MDIO data */
__be32 mdio_addr ; /* MDIO address */
} __packed ;
2015-01-04 17:36:02 +08:00
# define MDIO_STAT_ENC BIT(6)
2012-08-20 09:26:39 +00:00
# define MDIO_STAT_CLKDIV(x) (((x>>1) & 0xff) << 8)
2015-01-13 10:30:59 +08:00
# define MDIO_STAT_BSY BIT(0)
# define MDIO_STAT_RD_ER BIT(1)
2012-08-20 09:26:39 +00:00
# define MDIO_CTL_DEV_ADDR(x) (x & 0x1f)
# define MDIO_CTL_PORT_ADDR(x) ((x & 0x1f) << 5)
2015-01-13 10:30:59 +08:00
# define MDIO_CTL_PRE_DIS BIT(10)
# define MDIO_CTL_SCAN_EN BIT(11)
# define MDIO_CTL_POST_INC BIT(14)
# define MDIO_CTL_READ BIT(15)
2012-08-20 09:26:39 +00:00
# define MDIO_DATA(x) (x & 0xffff)
2015-01-13 10:30:59 +08:00
# define MDIO_DATA_BSY BIT(31)
2012-08-20 09:26:39 +00:00
2015-03-16 18:56:29 +08:00
struct mdio_fsl_priv {
struct tgec_mdio_controller __iomem * mdio_base ;
bool is_little_endian ;
} ;
static u32 xgmac_read32 ( void __iomem * regs ,
bool is_little_endian )
{
if ( is_little_endian )
return ioread32 ( regs ) ;
else
return ioread32be ( regs ) ;
}
static void xgmac_write32 ( u32 value ,
void __iomem * regs ,
bool is_little_endian )
{
if ( is_little_endian )
iowrite32 ( value , regs ) ;
else
iowrite32be ( value , regs ) ;
}
2012-08-20 09:26:39 +00:00
/*
2014-07-29 14:47:25 -05:00
* Wait until the MDIO bus is free
2012-08-20 09:26:39 +00:00
*/
static int xgmac_wait_until_free ( struct device * dev ,
2015-03-16 18:56:29 +08:00
struct tgec_mdio_controller __iomem * regs ,
bool is_little_endian )
2012-08-20 09:26:39 +00:00
{
2015-01-21 19:08:32 +08:00
unsigned int timeout ;
2012-08-20 09:26:39 +00:00
/* Wait till the bus is free */
2015-01-21 19:08:32 +08:00
timeout = TIMEOUT ;
2015-03-16 18:56:29 +08:00
while ( ( xgmac_read32 ( & regs - > mdio_stat , is_little_endian ) &
MDIO_STAT_BSY ) & & timeout ) {
2015-01-21 19:08:32 +08:00
cpu_relax ( ) ;
timeout - - ;
}
if ( ! timeout ) {
2012-08-20 09:26:39 +00:00
dev_err ( dev , " timeout waiting for bus to be free \n " ) ;
return - ETIMEDOUT ;
}
return 0 ;
}
/*
* Wait till the MDIO read or write operation is complete
*/
static int xgmac_wait_until_done ( struct device * dev ,
2015-03-16 18:56:29 +08:00
struct tgec_mdio_controller __iomem * regs ,
bool is_little_endian )
2012-08-20 09:26:39 +00:00
{
2015-01-21 19:08:32 +08:00
unsigned int timeout ;
2012-08-20 09:26:39 +00:00
/* Wait till the MDIO write is complete */
2015-01-21 19:08:32 +08:00
timeout = TIMEOUT ;
2015-03-16 18:56:29 +08:00
while ( ( xgmac_read32 ( & regs - > mdio_stat , is_little_endian ) &
MDIO_STAT_BSY ) & & timeout ) {
2015-01-21 19:08:32 +08:00
cpu_relax ( ) ;
timeout - - ;
}
if ( ! timeout ) {
2012-08-20 09:26:39 +00:00
dev_err ( dev , " timeout waiting for operation to complete \n " ) ;
return - ETIMEDOUT ;
}
return 0 ;
}
/*
* 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 TSEC1 MIIM regs .
*/
static int xgmac_mdio_write ( struct mii_bus * bus , int phy_id , int regnum , u16 value )
{
2015-03-16 18:56:29 +08:00
struct mdio_fsl_priv * priv = ( struct mdio_fsl_priv * ) bus - > priv ;
struct tgec_mdio_controller __iomem * regs = priv - > mdio_base ;
2015-01-04 17:36:02 +08:00
uint16_t dev_addr ;
u32 mdio_ctl , mdio_stat ;
2012-08-20 09:26:39 +00:00
int ret ;
2015-03-16 18:56:29 +08:00
bool endian = priv - > is_little_endian ;
2012-08-20 09:26:39 +00:00
2015-03-16 18:56:29 +08:00
mdio_stat = xgmac_read32 ( & regs - > mdio_stat , endian ) ;
2015-01-04 17:36:02 +08:00
if ( regnum & MII_ADDR_C45 ) {
/* Clause 45 (ie 10G) */
dev_addr = ( regnum > > 16 ) & 0x1f ;
mdio_stat | = MDIO_STAT_ENC ;
} else {
/* Clause 22 (ie 1G) */
dev_addr = regnum & 0x1f ;
mdio_stat & = ~ MDIO_STAT_ENC ;
}
2012-08-20 09:26:39 +00:00
2015-03-16 18:56:29 +08:00
xgmac_write32 ( mdio_stat , & regs - > mdio_stat , endian ) ;
2012-08-20 09:26:39 +00:00
2015-03-16 18:56:29 +08:00
ret = xgmac_wait_until_free ( & bus - > dev , regs , endian ) ;
2012-08-20 09:26:39 +00:00
if ( ret )
return ret ;
2015-01-04 17:36:02 +08:00
/* Set the port and dev addr */
mdio_ctl = MDIO_CTL_PORT_ADDR ( phy_id ) | MDIO_CTL_DEV_ADDR ( dev_addr ) ;
2015-03-16 18:56:29 +08:00
xgmac_write32 ( mdio_ctl , & regs - > mdio_ctl , endian ) ;
2015-01-04 17:36:02 +08:00
/* Set the register address */
if ( regnum & MII_ADDR_C45 ) {
2015-03-16 18:56:29 +08:00
xgmac_write32 ( regnum & 0xffff , & regs - > mdio_addr , endian ) ;
2015-01-04 17:36:02 +08:00
2015-03-16 18:56:29 +08:00
ret = xgmac_wait_until_free ( & bus - > dev , regs , endian ) ;
2015-01-04 17:36:02 +08:00
if ( ret )
return ret ;
}
2012-08-20 09:26:39 +00:00
/* Write the value to the register */
2015-03-16 18:56:29 +08:00
xgmac_write32 ( MDIO_DATA ( value ) , & regs - > mdio_data , endian ) ;
2012-08-20 09:26:39 +00:00
2015-03-16 18:56:29 +08:00
ret = xgmac_wait_until_done ( & bus - > dev , regs , endian ) ;
2012-08-20 09:26:39 +00:00
if ( ret )
return ret ;
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 .
*/
static int xgmac_mdio_read ( struct mii_bus * bus , int phy_id , int regnum )
{
2015-03-16 18:56:29 +08:00
struct mdio_fsl_priv * priv = ( struct mdio_fsl_priv * ) bus - > priv ;
struct tgec_mdio_controller __iomem * regs = priv - > mdio_base ;
2015-01-04 17:36:02 +08:00
uint16_t dev_addr ;
uint32_t mdio_stat ;
2012-08-20 09:26:39 +00:00
uint32_t mdio_ctl ;
uint16_t value ;
int ret ;
2015-03-16 18:56:29 +08:00
bool endian = priv - > is_little_endian ;
2012-08-20 09:26:39 +00:00
2015-03-16 18:56:29 +08:00
mdio_stat = xgmac_read32 ( & regs - > mdio_stat , endian ) ;
2015-01-04 17:36:02 +08:00
if ( regnum & MII_ADDR_C45 ) {
dev_addr = ( regnum > > 16 ) & 0x1f ;
mdio_stat | = MDIO_STAT_ENC ;
} else {
dev_addr = regnum & 0x1f ;
2015-01-13 10:30:31 +08:00
mdio_stat & = ~ MDIO_STAT_ENC ;
2015-01-04 17:36:02 +08:00
}
2015-03-16 18:56:29 +08:00
xgmac_write32 ( mdio_stat , & regs - > mdio_stat , endian ) ;
2015-01-04 17:36:02 +08:00
2015-03-16 18:56:29 +08:00
ret = xgmac_wait_until_free ( & bus - > dev , regs , endian ) ;
2015-01-04 17:36:02 +08:00
if ( ret )
return ret ;
2012-08-20 09:26:39 +00:00
/* Set the Port and Device Addrs */
mdio_ctl = MDIO_CTL_PORT_ADDR ( phy_id ) | MDIO_CTL_DEV_ADDR ( dev_addr ) ;
2015-03-16 18:56:29 +08:00
xgmac_write32 ( mdio_ctl , & regs - > mdio_ctl , endian ) ;
2012-08-20 09:26:39 +00:00
/* Set the register address */
2015-01-04 17:36:02 +08:00
if ( regnum & MII_ADDR_C45 ) {
2015-03-16 18:56:29 +08:00
xgmac_write32 ( regnum & 0xffff , & regs - > mdio_addr , endian ) ;
2012-08-20 09:26:39 +00:00
2015-03-16 18:56:29 +08:00
ret = xgmac_wait_until_free ( & bus - > dev , regs , endian ) ;
2015-01-04 17:36:02 +08:00
if ( ret )
return ret ;
}
2012-08-20 09:26:39 +00:00
/* Initiate the read */
2015-03-16 18:56:29 +08:00
xgmac_write32 ( mdio_ctl | MDIO_CTL_READ , & regs - > mdio_ctl , endian ) ;
2012-08-20 09:26:39 +00:00
2015-03-16 18:56:29 +08:00
ret = xgmac_wait_until_done ( & bus - > dev , regs , endian ) ;
2012-08-20 09:26:39 +00:00
if ( ret )
return ret ;
/* Return all Fs if nothing was there */
2015-03-16 18:56:29 +08:00
if ( xgmac_read32 ( & regs - > mdio_stat , endian ) & MDIO_STAT_RD_ER ) {
2014-06-11 13:41:40 -05:00
dev_err ( & bus - > dev ,
2014-07-29 14:53:03 -05:00
" Error while reading PHY%d reg at %d.%hhu \n " ,
2014-06-11 13:41:40 -05:00
phy_id , dev_addr , regnum ) ;
2012-08-20 09:26:39 +00:00
return 0xffff ;
}
2015-03-16 18:56:29 +08:00
value = xgmac_read32 ( & regs - > mdio_data , endian ) & 0xffff ;
2012-08-20 09:26:39 +00:00
dev_dbg ( & bus - > dev , " read %04x \n " , value ) ;
return value ;
}
2012-12-03 09:23:58 -05:00
static int xgmac_mdio_probe ( struct platform_device * pdev )
2012-08-20 09:26:39 +00:00
{
struct device_node * np = pdev - > dev . of_node ;
struct mii_bus * bus ;
struct resource res ;
2015-03-16 18:56:29 +08:00
struct mdio_fsl_priv * priv ;
2012-08-20 09:26:39 +00:00
int ret ;
ret = of_address_to_resource ( np , 0 , & res ) ;
if ( ret ) {
dev_err ( & pdev - > dev , " could not obtain address \n " ) ;
return ret ;
}
2015-03-16 18:56:29 +08:00
bus = mdiobus_alloc_size ( sizeof ( struct mdio_fsl_priv ) ) ;
2012-08-20 09:26:39 +00:00
if ( ! bus )
return - ENOMEM ;
bus - > name = " Freescale XGMAC MDIO Bus " ;
bus - > read = xgmac_mdio_read ;
bus - > write = xgmac_mdio_write ;
bus - > parent = & pdev - > dev ;
snprintf ( bus - > id , MII_BUS_ID_SIZE , " %llx " , ( unsigned long long ) res . start ) ;
/* Set the PHY base address */
2015-03-16 18:56:29 +08:00
priv = bus - > priv ;
priv - > mdio_base = of_iomap ( np , 0 ) ;
if ( ! priv - > mdio_base ) {
2012-08-20 09:26:39 +00:00
ret = - ENOMEM ;
goto err_ioremap ;
}
2016-08-05 13:24:12 +02:00
priv - > is_little_endian = of_property_read_bool ( pdev - > dev . of_node ,
" little-endian " ) ;
2015-03-16 18:56:29 +08:00
2012-08-20 09:26:39 +00:00
ret = of_mdiobus_register ( bus , np ) ;
if ( ret ) {
dev_err ( & pdev - > dev , " cannot register MDIO bus \n " ) ;
goto err_registration ;
}
2013-05-23 00:52:31 +00:00
platform_set_drvdata ( pdev , bus ) ;
2012-08-20 09:26:39 +00:00
return 0 ;
err_registration :
2015-03-16 18:56:29 +08:00
iounmap ( priv - > mdio_base ) ;
2012-08-20 09:26:39 +00:00
err_ioremap :
mdiobus_free ( bus ) ;
return ret ;
}
2012-12-03 09:23:58 -05:00
static int xgmac_mdio_remove ( struct platform_device * pdev )
2012-08-20 09:26:39 +00:00
{
2013-05-23 00:52:31 +00:00
struct mii_bus * bus = platform_get_drvdata ( pdev ) ;
2012-08-20 09:26:39 +00:00
mdiobus_unregister ( bus ) ;
iounmap ( bus - > priv ) ;
mdiobus_free ( bus ) ;
return 0 ;
}
2015-03-17 19:37:34 +01:00
static const struct of_device_id xgmac_mdio_match [ ] = {
2012-08-20 09:26:39 +00:00
{
. compatible = " fsl,fman-xmdio " ,
} ,
2015-01-04 17:36:02 +08:00
{
. compatible = " fsl,fman-memac-mdio " ,
} ,
2012-08-20 09:26:39 +00:00
{ } ,
} ;
MODULE_DEVICE_TABLE ( of , xgmac_mdio_match ) ;
static struct platform_driver xgmac_mdio_driver = {
. driver = {
. name = " fsl-fman_xmdio " ,
. of_match_table = xgmac_mdio_match ,
} ,
. probe = xgmac_mdio_probe ,
. remove = xgmac_mdio_remove ,
} ;
module_platform_driver ( xgmac_mdio_driver ) ;
MODULE_DESCRIPTION ( " Freescale QorIQ 10G MDIO Controller " ) ;
MODULE_LICENSE ( " GPL v2 " ) ;