2019-01-21 21:05:50 +03:00
// SPDX-License-Identifier: GPL-2.0+
2016-07-26 03:12:40 +03:00
/* Applied Micro X-Gene SoC MDIO Driver
*
* Copyright ( c ) 2016 , Applied Micro Circuits Corporation
* Author : Iyappan Subramanian < isubramanian @ apm . com >
*/
# include <linux/acpi.h>
# include <linux/clk.h>
# include <linux/device.h>
# include <linux/efi.h>
# include <linux/if_vlan.h>
# include <linux/io.h>
2020-08-27 05:00:30 +03:00
# include <linux/mdio/mdio-xgene.h>
2016-07-26 03:12:40 +03:00
# include <linux/module.h>
# include <linux/of_mdio.h>
2021-03-15 13:49:05 +03:00
# include <linux/of_net.h>
# include <linux/of_platform.h>
2016-07-26 03:12:40 +03:00
# include <linux/phy.h>
2021-03-15 13:49:05 +03:00
# include <linux/prefetch.h>
2016-07-26 03:12:40 +03:00
# include <net/ip.h>
static bool xgene_mdio_status ;
2017-05-10 23:45:00 +03:00
u32 xgene_mdio_rd_mac ( struct xgene_mdio_pdata * pdata , u32 rd_addr )
2016-07-26 03:12:40 +03:00
{
void __iomem * addr , * rd , * cmd , * cmd_done ;
u32 done , rd_data = BUSY_MASK ;
u8 wait = 10 ;
2017-05-10 23:45:00 +03:00
addr = pdata - > mac_csr_addr + MAC_ADDR_REG_OFFSET ;
rd = pdata - > mac_csr_addr + MAC_READ_REG_OFFSET ;
cmd = pdata - > mac_csr_addr + MAC_COMMAND_REG_OFFSET ;
cmd_done = pdata - > mac_csr_addr + MAC_COMMAND_DONE_REG_OFFSET ;
2016-07-26 03:12:40 +03:00
2017-05-10 23:45:00 +03:00
spin_lock ( & pdata - > mac_lock ) ;
2016-07-26 03:12:40 +03:00
iowrite32 ( rd_addr , addr ) ;
iowrite32 ( XGENE_ENET_RD_CMD , cmd ) ;
2017-05-10 23:45:00 +03:00
while ( ! ( done = ioread32 ( cmd_done ) ) & & wait - - )
2016-07-26 03:12:40 +03:00
udelay ( 1 ) ;
2017-05-10 23:45:00 +03:00
if ( done )
rd_data = ioread32 ( rd ) ;
2016-07-26 03:12:40 +03:00
iowrite32 ( 0 , cmd ) ;
2017-05-10 23:45:00 +03:00
spin_unlock ( & pdata - > mac_lock ) ;
2016-07-26 03:12:40 +03:00
return rd_data ;
}
2017-05-10 23:45:00 +03:00
EXPORT_SYMBOL ( xgene_mdio_rd_mac ) ;
2016-07-26 03:12:40 +03:00
2017-05-10 23:45:00 +03:00
void xgene_mdio_wr_mac ( struct xgene_mdio_pdata * pdata , u32 wr_addr , u32 data )
2016-07-26 03:12:40 +03:00
{
void __iomem * addr , * wr , * cmd , * cmd_done ;
u8 wait = 10 ;
u32 done ;
2017-05-10 23:45:00 +03:00
addr = pdata - > mac_csr_addr + MAC_ADDR_REG_OFFSET ;
wr = pdata - > mac_csr_addr + MAC_WRITE_REG_OFFSET ;
cmd = pdata - > mac_csr_addr + MAC_COMMAND_REG_OFFSET ;
cmd_done = pdata - > mac_csr_addr + MAC_COMMAND_DONE_REG_OFFSET ;
2016-07-26 03:12:40 +03:00
2017-05-10 23:45:00 +03:00
spin_lock ( & pdata - > mac_lock ) ;
2016-07-26 03:12:40 +03:00
iowrite32 ( wr_addr , addr ) ;
2017-05-10 23:45:00 +03:00
iowrite32 ( data , wr ) ;
2016-07-26 03:12:40 +03:00
iowrite32 ( XGENE_ENET_WR_CMD , cmd ) ;
2017-05-10 23:45:00 +03:00
while ( ! ( done = ioread32 ( cmd_done ) ) & & wait - - )
2016-07-26 03:12:40 +03:00
udelay ( 1 ) ;
if ( ! done )
pr_err ( " MCX mac write failed, addr: 0x%04x \n " , wr_addr ) ;
iowrite32 ( 0 , cmd ) ;
2017-05-10 23:45:00 +03:00
spin_unlock ( & pdata - > mac_lock ) ;
2016-07-26 03:12:40 +03:00
}
2017-05-10 23:45:00 +03:00
EXPORT_SYMBOL ( xgene_mdio_wr_mac ) ;
2016-07-26 03:12:40 +03:00
int xgene_mdio_rgmii_read ( struct mii_bus * bus , int phy_id , int reg )
{
2017-05-10 23:45:00 +03:00
struct xgene_mdio_pdata * pdata = ( struct xgene_mdio_pdata * ) bus - > priv ;
2016-07-26 03:12:40 +03:00
u32 data , done ;
u8 wait = 10 ;
data = SET_VAL ( PHY_ADDR , phy_id ) | SET_VAL ( REG_ADDR , reg ) ;
2017-05-10 23:45:00 +03:00
xgene_mdio_wr_mac ( pdata , MII_MGMT_ADDRESS_ADDR , data ) ;
xgene_mdio_wr_mac ( pdata , MII_MGMT_COMMAND_ADDR , READ_CYCLE_MASK ) ;
2016-07-26 03:12:40 +03:00
do {
usleep_range ( 5 , 10 ) ;
2017-05-10 23:45:00 +03:00
done = xgene_mdio_rd_mac ( pdata , MII_MGMT_INDICATORS_ADDR ) ;
2016-07-26 03:12:40 +03:00
} while ( ( done & BUSY_MASK ) & & wait - - ) ;
if ( done & BUSY_MASK ) {
dev_err ( & bus - > dev , " MII_MGMT read failed \n " ) ;
return - EBUSY ;
}
2017-05-10 23:45:00 +03:00
data = xgene_mdio_rd_mac ( pdata , MII_MGMT_STATUS_ADDR ) ;
xgene_mdio_wr_mac ( pdata , MII_MGMT_COMMAND_ADDR , 0 ) ;
2016-07-26 03:12:40 +03:00
return data ;
}
EXPORT_SYMBOL ( xgene_mdio_rgmii_read ) ;
int xgene_mdio_rgmii_write ( struct mii_bus * bus , int phy_id , int reg , u16 data )
{
2017-05-10 23:45:00 +03:00
struct xgene_mdio_pdata * pdata = ( struct xgene_mdio_pdata * ) bus - > priv ;
2016-07-26 03:12:40 +03:00
u32 val , done ;
u8 wait = 10 ;
val = SET_VAL ( PHY_ADDR , phy_id ) | SET_VAL ( REG_ADDR , reg ) ;
2017-05-10 23:45:00 +03:00
xgene_mdio_wr_mac ( pdata , MII_MGMT_ADDRESS_ADDR , val ) ;
2016-07-26 03:12:40 +03:00
2017-05-10 23:45:00 +03:00
xgene_mdio_wr_mac ( pdata , MII_MGMT_CONTROL_ADDR , data ) ;
2016-07-26 03:12:40 +03:00
do {
usleep_range ( 5 , 10 ) ;
2017-05-10 23:45:00 +03:00
done = xgene_mdio_rd_mac ( pdata , MII_MGMT_INDICATORS_ADDR ) ;
2016-07-26 03:12:40 +03:00
} while ( ( done & BUSY_MASK ) & & wait - - ) ;
if ( done & BUSY_MASK ) {
dev_err ( & bus - > dev , " MII_MGMT write failed \n " ) ;
return - EBUSY ;
}
return 0 ;
}
EXPORT_SYMBOL ( xgene_mdio_rgmii_write ) ;
static u32 xgene_menet_rd_diag_csr ( struct xgene_mdio_pdata * pdata , u32 offset )
{
return ioread32 ( pdata - > diag_csr_addr + offset ) ;
}
static void xgene_menet_wr_diag_csr ( struct xgene_mdio_pdata * pdata ,
u32 offset , u32 val )
{
iowrite32 ( val , pdata - > diag_csr_addr + offset ) ;
}
static int xgene_enet_ecc_init ( struct xgene_mdio_pdata * pdata )
{
u32 data ;
u8 wait = 10 ;
xgene_menet_wr_diag_csr ( pdata , MENET_CFG_MEM_RAM_SHUTDOWN_ADDR , 0x0 ) ;
do {
usleep_range ( 100 , 110 ) ;
data = xgene_menet_rd_diag_csr ( pdata , MENET_BLOCK_MEM_RDY_ADDR ) ;
} while ( ( data ! = 0xffffffff ) & & wait - - ) ;
if ( data ! = 0xffffffff ) {
dev_err ( pdata - > dev , " Failed to release memory from shutdown \n " ) ;
return - ENODEV ;
}
return 0 ;
}
static void xgene_gmac_reset ( struct xgene_mdio_pdata * pdata )
{
2017-05-10 23:45:00 +03:00
xgene_mdio_wr_mac ( pdata , MAC_CONFIG_1_ADDR , SOFT_RESET ) ;
xgene_mdio_wr_mac ( pdata , MAC_CONFIG_1_ADDR , 0 ) ;
2016-07-26 03:12:40 +03:00
}
static int xgene_mdio_reset ( struct xgene_mdio_pdata * pdata )
{
int ret ;
if ( pdata - > dev - > of_node ) {
clk_prepare_enable ( pdata - > clk ) ;
udelay ( 5 ) ;
clk_disable_unprepare ( pdata - > clk ) ;
udelay ( 5 ) ;
clk_prepare_enable ( pdata - > clk ) ;
udelay ( 5 ) ;
} else {
# ifdef CONFIG_ACPI
acpi_evaluate_object ( ACPI_HANDLE ( pdata - > dev ) ,
" _RST " , NULL , NULL ) ;
# endif
}
ret = xgene_enet_ecc_init ( pdata ) ;
2017-12-16 00:52:39 +03:00
if ( ret ) {
if ( pdata - > dev - > of_node )
clk_disable_unprepare ( pdata - > clk ) ;
2016-07-26 03:12:40 +03:00
return ret ;
2017-12-16 00:52:39 +03:00
}
2016-07-26 03:12:40 +03:00
xgene_gmac_reset ( pdata ) ;
return 0 ;
}
static void xgene_enet_rd_mdio_csr ( void __iomem * base_addr ,
u32 offset , u32 * val )
{
void __iomem * addr = base_addr + offset ;
* val = ioread32 ( addr ) ;
}
static void xgene_enet_wr_mdio_csr ( void __iomem * base_addr ,
u32 offset , u32 val )
{
void __iomem * addr = base_addr + offset ;
iowrite32 ( val , addr ) ;
}
static int xgene_xfi_mdio_write ( struct mii_bus * bus , int phy_id ,
int reg , u16 data )
{
void __iomem * addr = ( void __iomem * ) bus - > priv ;
int timeout = 100 ;
u32 status , val ;
val = SET_VAL ( HSTPHYADX , phy_id ) | SET_VAL ( HSTREGADX , reg ) |
SET_VAL ( HSTMIIMWRDAT , data ) ;
2017-03-15 23:27:15 +03:00
xgene_enet_wr_mdio_csr ( addr , MIIM_FIELD_ADDR , val ) ;
2016-07-26 03:12:40 +03:00
val = HSTLDCMD | SET_VAL ( HSTMIIMCMD , MIIM_CMD_LEGACY_WRITE ) ;
xgene_enet_wr_mdio_csr ( addr , MIIM_COMMAND_ADDR , val ) ;
do {
usleep_range ( 5 , 10 ) ;
xgene_enet_rd_mdio_csr ( addr , MIIM_INDICATOR_ADDR , & status ) ;
} while ( ( status & BUSY_MASK ) & & timeout - - ) ;
xgene_enet_wr_mdio_csr ( addr , MIIM_COMMAND_ADDR , 0 ) ;
return 0 ;
}
static int xgene_xfi_mdio_read ( struct mii_bus * bus , int phy_id , int reg )
{
void __iomem * addr = ( void __iomem * ) bus - > priv ;
u32 data , status , val ;
int timeout = 100 ;
val = SET_VAL ( HSTPHYADX , phy_id ) | SET_VAL ( HSTREGADX , reg ) ;
xgene_enet_wr_mdio_csr ( addr , MIIM_FIELD_ADDR , val ) ;
val = HSTLDCMD | SET_VAL ( HSTMIIMCMD , MIIM_CMD_LEGACY_READ ) ;
xgene_enet_wr_mdio_csr ( addr , MIIM_COMMAND_ADDR , val ) ;
do {
usleep_range ( 5 , 10 ) ;
xgene_enet_rd_mdio_csr ( addr , MIIM_INDICATOR_ADDR , & status ) ;
} while ( ( status & BUSY_MASK ) & & timeout - - ) ;
if ( status & BUSY_MASK ) {
pr_err ( " XGENET_MII_MGMT write failed \n " ) ;
return - EBUSY ;
}
xgene_enet_rd_mdio_csr ( addr , MIIMRD_FIELD_ADDR , & data ) ;
xgene_enet_wr_mdio_csr ( addr , MIIM_COMMAND_ADDR , 0 ) ;
return data ;
}
struct phy_device * xgene_enet_phy_register ( struct mii_bus * bus , int phy_addr )
{
struct phy_device * phy_dev ;
phy_dev = get_phy_device ( bus , phy_addr , false ) ;
if ( ! phy_dev | | IS_ERR ( phy_dev ) )
return NULL ;
if ( phy_device_register ( phy_dev ) )
phy_device_free ( phy_dev ) ;
return phy_dev ;
}
EXPORT_SYMBOL ( xgene_enet_phy_register ) ;
# ifdef CONFIG_ACPI
static acpi_status acpi_register_phy ( acpi_handle handle , u32 lvl ,
void * context , void * * ret )
{
struct mii_bus * mdio = context ;
struct acpi_device * adev ;
struct phy_device * phy_dev ;
const union acpi_object * obj ;
u32 phy_addr ;
2022-02-02 17:19:20 +03:00
adev = acpi_fetch_acpi_dev ( handle ) ;
if ( ! adev )
2016-07-26 03:12:40 +03:00
return AE_OK ;
if ( acpi_dev_get_property ( adev , " phy-channel " , ACPI_TYPE_INTEGER , & obj ) )
return AE_OK ;
phy_addr = obj - > integer . value ;
phy_dev = xgene_enet_phy_register ( mdio , phy_addr ) ;
adev - > driver_data = phy_dev ;
return AE_OK ;
}
# endif
2017-02-01 19:46:02 +03:00
static const struct of_device_id xgene_mdio_of_match [ ] = {
{
. compatible = " apm,xgene-mdio-rgmii " ,
. data = ( void * ) XGENE_MDIO_RGMII
} ,
{
. compatible = " apm,xgene-mdio-xfi " ,
. data = ( void * ) XGENE_MDIO_XFI
} ,
{ } ,
} ;
MODULE_DEVICE_TABLE ( of , xgene_mdio_of_match ) ;
# ifdef CONFIG_ACPI
static const struct acpi_device_id xgene_mdio_acpi_match [ ] = {
{ " APMC0D65 " , XGENE_MDIO_RGMII } ,
{ " APMC0D66 " , XGENE_MDIO_XFI } ,
{ }
} ;
MODULE_DEVICE_TABLE ( acpi , xgene_mdio_acpi_match ) ;
# endif
2016-07-26 03:12:40 +03:00
static int xgene_mdio_probe ( struct platform_device * pdev )
{
struct device * dev = & pdev - > dev ;
struct mii_bus * mdio_bus ;
const struct of_device_id * of_id ;
struct xgene_mdio_pdata * pdata ;
void __iomem * csr_base ;
int mdio_id = 0 , ret = 0 ;
of_id = of_match_device ( xgene_mdio_of_match , & pdev - > dev ) ;
if ( of_id ) {
mdio_id = ( enum xgene_mdio_id ) of_id - > data ;
} else {
# ifdef CONFIG_ACPI
const struct acpi_device_id * acpi_id ;
acpi_id = acpi_match_device ( xgene_mdio_acpi_match , & pdev - > dev ) ;
if ( acpi_id )
mdio_id = ( enum xgene_mdio_id ) acpi_id - > driver_data ;
# endif
}
if ( ! mdio_id )
return - ENODEV ;
pdata = devm_kzalloc ( dev , sizeof ( struct xgene_mdio_pdata ) , GFP_KERNEL ) ;
if ( ! pdata )
return - ENOMEM ;
pdata - > mdio_id = mdio_id ;
pdata - > dev = dev ;
2019-08-01 15:46:30 +03:00
csr_base = devm_platform_ioremap_resource ( pdev , 0 ) ;
2016-07-28 05:12:17 +03:00
if ( IS_ERR ( csr_base ) )
2016-07-26 03:12:40 +03:00
return PTR_ERR ( csr_base ) ;
pdata - > mac_csr_addr = csr_base ;
pdata - > mdio_csr_addr = csr_base + BLOCK_XG_MDIO_CSR_OFFSET ;
pdata - > diag_csr_addr = csr_base + BLOCK_DIAG_CSR_OFFSET ;
2017-05-10 23:45:00 +03:00
if ( mdio_id = = XGENE_MDIO_RGMII )
spin_lock_init ( & pdata - > mac_lock ) ;
2016-07-26 03:12:40 +03:00
if ( dev - > of_node ) {
pdata - > clk = devm_clk_get ( dev , NULL ) ;
if ( IS_ERR ( pdata - > clk ) ) {
dev_err ( dev , " Unable to retrieve clk \n " ) ;
return PTR_ERR ( pdata - > clk ) ;
}
}
ret = xgene_mdio_reset ( pdata ) ;
if ( ret )
return ret ;
mdio_bus = mdiobus_alloc ( ) ;
2017-12-16 00:52:39 +03:00
if ( ! mdio_bus ) {
ret = - ENOMEM ;
goto out_clk ;
}
2016-07-26 03:12:40 +03:00
mdio_bus - > name = " APM X-Gene MDIO bus " ;
if ( mdio_id = = XGENE_MDIO_RGMII ) {
mdio_bus - > read = xgene_mdio_rgmii_read ;
mdio_bus - > write = xgene_mdio_rgmii_write ;
2017-05-10 23:45:00 +03:00
mdio_bus - > priv = ( void __force * ) pdata ;
2016-07-26 03:12:40 +03:00
snprintf ( mdio_bus - > id , MII_BUS_ID_SIZE , " %s " ,
" xgene-mii-rgmii " ) ;
} else {
mdio_bus - > read = xgene_xfi_mdio_read ;
mdio_bus - > write = xgene_xfi_mdio_write ;
mdio_bus - > priv = ( void __force * ) pdata - > mdio_csr_addr ;
snprintf ( mdio_bus - > id , MII_BUS_ID_SIZE , " %s " ,
" xgene-mii-xfi " ) ;
}
mdio_bus - > parent = dev ;
platform_set_drvdata ( pdev , pdata ) ;
if ( dev - > of_node ) {
ret = of_mdiobus_register ( mdio_bus , dev - > of_node ) ;
} else {
# ifdef CONFIG_ACPI
/* Mask out all PHYs from auto probing. */
mdio_bus - > phy_mask = ~ 0 ;
ret = mdiobus_register ( mdio_bus ) ;
if ( ret )
2017-12-16 00:52:39 +03:00
goto out_mdiobus ;
2016-07-26 03:12:40 +03:00
acpi_walk_namespace ( ACPI_TYPE_DEVICE , ACPI_HANDLE ( dev ) , 1 ,
acpi_register_phy , NULL , mdio_bus , NULL ) ;
# endif
}
if ( ret )
2017-12-16 00:52:39 +03:00
goto out_mdiobus ;
2016-07-26 03:12:40 +03:00
pdata - > mdio_bus = mdio_bus ;
xgene_mdio_status = true ;
return 0 ;
2017-12-16 00:52:39 +03:00
out_mdiobus :
2016-07-26 03:12:40 +03:00
mdiobus_free ( mdio_bus ) ;
2017-12-16 00:52:39 +03:00
out_clk :
if ( dev - > of_node )
clk_disable_unprepare ( pdata - > clk ) ;
2016-07-26 03:12:40 +03:00
return ret ;
}
static int xgene_mdio_remove ( struct platform_device * pdev )
{
struct xgene_mdio_pdata * pdata = platform_get_drvdata ( pdev ) ;
struct mii_bus * mdio_bus = pdata - > mdio_bus ;
struct device * dev = & pdev - > dev ;
mdiobus_unregister ( mdio_bus ) ;
mdiobus_free ( mdio_bus ) ;
2016-09-11 22:43:34 +03:00
if ( dev - > of_node )
clk_disable_unprepare ( pdata - > clk ) ;
2016-07-26 03:12:40 +03:00
return 0 ;
}
static struct platform_driver xgene_mdio_driver = {
. driver = {
. name = " xgene-mdio " ,
. of_match_table = of_match_ptr ( xgene_mdio_of_match ) ,
. acpi_match_table = ACPI_PTR ( xgene_mdio_acpi_match ) ,
} ,
. probe = xgene_mdio_probe ,
. remove = xgene_mdio_remove ,
} ;
module_platform_driver ( xgene_mdio_driver ) ;
MODULE_DESCRIPTION ( " APM X-Gene SoC MDIO driver " ) ;
MODULE_AUTHOR ( " Iyappan Subramanian <isubramanian@apm.com> " ) ;
MODULE_LICENSE ( " GPL " ) ;