2019-04-04 16:11:45 +03:00
// SPDX-License-Identifier: GPL-2.0
/* Copyright (c) 2019 Baylibre, SAS.
* Author : Jerome Brunet < jbrunet @ baylibre . com >
*/
# include <linux/bitfield.h>
# include <linux/clk.h>
# include <linux/clk-provider.h>
# include <linux/device.h>
# include <linux/io.h>
# include <linux/iopoll.h>
# include <linux/mdio-mux.h>
# include <linux/module.h>
# include <linux/phy.h>
# include <linux/platform_device.h>
# define ETH_PLL_STS 0x40
# define ETH_PLL_CTL0 0x44
# define PLL_CTL0_LOCK_DIG BIT(30)
# define PLL_CTL0_RST BIT(29)
# define PLL_CTL0_EN BIT(28)
# define PLL_CTL0_SEL BIT(23)
# define PLL_CTL0_N GENMASK(14, 10)
# define PLL_CTL0_M GENMASK(8, 0)
# define PLL_LOCK_TIMEOUT 1000000
# define PLL_MUX_NUM_PARENT 2
# define ETH_PLL_CTL1 0x48
# define ETH_PLL_CTL2 0x4c
# define ETH_PLL_CTL3 0x50
# define ETH_PLL_CTL4 0x54
# define ETH_PLL_CTL5 0x58
# define ETH_PLL_CTL6 0x5c
# define ETH_PLL_CTL7 0x60
# define ETH_PHY_CNTL0 0x80
2019-05-13 00:12:37 +03:00
# define EPHY_G12A_ID 0x33010180
2019-04-04 16:11:45 +03:00
# define ETH_PHY_CNTL1 0x84
# define PHY_CNTL1_ST_MODE GENMASK(2, 0)
# define PHY_CNTL1_ST_PHYADD GENMASK(7, 3)
# define EPHY_DFLT_ADD 8
# define PHY_CNTL1_MII_MODE GENMASK(15, 14)
# define EPHY_MODE_RMII 0x1
# define PHY_CNTL1_CLK_EN BIT(16)
# define PHY_CNTL1_CLKFREQ BIT(17)
# define PHY_CNTL1_PHY_ENB BIT(18)
# define ETH_PHY_CNTL2 0x88
# define PHY_CNTL2_USE_INTERNAL BIT(5)
# define PHY_CNTL2_SMI_SRC_MAC BIT(6)
# define PHY_CNTL2_RX_CLK_EPHY BIT(9)
# define MESON_G12A_MDIO_EXTERNAL_ID 0
# define MESON_G12A_MDIO_INTERNAL_ID 1
struct g12a_mdio_mux {
bool pll_is_enabled ;
void __iomem * regs ;
void * mux_handle ;
struct clk * pclk ;
struct clk * pll ;
} ;
struct g12a_ephy_pll {
void __iomem * base ;
struct clk_hw hw ;
} ;
# define g12a_ephy_pll_to_dev(_hw) \
container_of ( _hw , struct g12a_ephy_pll , hw )
static unsigned long g12a_ephy_pll_recalc_rate ( struct clk_hw * hw ,
unsigned long parent_rate )
{
struct g12a_ephy_pll * pll = g12a_ephy_pll_to_dev ( hw ) ;
u32 val , m , n ;
val = readl ( pll - > base + ETH_PLL_CTL0 ) ;
m = FIELD_GET ( PLL_CTL0_M , val ) ;
n = FIELD_GET ( PLL_CTL0_N , val ) ;
return parent_rate * m / n ;
}
static int g12a_ephy_pll_enable ( struct clk_hw * hw )
{
struct g12a_ephy_pll * pll = g12a_ephy_pll_to_dev ( hw ) ;
u32 val = readl ( pll - > base + ETH_PLL_CTL0 ) ;
/* Apply both enable an reset */
val | = PLL_CTL0_RST | PLL_CTL0_EN ;
writel ( val , pll - > base + ETH_PLL_CTL0 ) ;
/* Clear the reset to let PLL lock */
val & = ~ PLL_CTL0_RST ;
writel ( val , pll - > base + ETH_PLL_CTL0 ) ;
/* Poll on the digital lock instead of the usual analog lock
* This is done because bit 31 is unreliable on some SoC . Bit
2021-06-01 17:18:59 +03:00
* 31 may indicate that the PLL is not lock even though the clock
2019-04-04 16:11:45 +03:00
* is actually running
*/
return readl_poll_timeout ( pll - > base + ETH_PLL_CTL0 , val ,
val & PLL_CTL0_LOCK_DIG , 0 , PLL_LOCK_TIMEOUT ) ;
}
static void g12a_ephy_pll_disable ( struct clk_hw * hw )
{
struct g12a_ephy_pll * pll = g12a_ephy_pll_to_dev ( hw ) ;
u32 val ;
val = readl ( pll - > base + ETH_PLL_CTL0 ) ;
val & = ~ PLL_CTL0_EN ;
val | = PLL_CTL0_RST ;
writel ( val , pll - > base + ETH_PLL_CTL0 ) ;
}
static int g12a_ephy_pll_is_enabled ( struct clk_hw * hw )
{
struct g12a_ephy_pll * pll = g12a_ephy_pll_to_dev ( hw ) ;
unsigned int val ;
val = readl ( pll - > base + ETH_PLL_CTL0 ) ;
return ( val & PLL_CTL0_LOCK_DIG ) ? 1 : 0 ;
}
2019-09-24 15:39:53 +03:00
static int g12a_ephy_pll_init ( struct clk_hw * hw )
2019-04-04 16:11:45 +03:00
{
struct g12a_ephy_pll * pll = g12a_ephy_pll_to_dev ( hw ) ;
/* Apply PLL HW settings */
writel ( 0x29c0040a , pll - > base + ETH_PLL_CTL0 ) ;
writel ( 0x927e0000 , pll - > base + ETH_PLL_CTL1 ) ;
writel ( 0xac5f49e5 , pll - > base + ETH_PLL_CTL2 ) ;
writel ( 0x00000000 , pll - > base + ETH_PLL_CTL3 ) ;
writel ( 0x00000000 , pll - > base + ETH_PLL_CTL4 ) ;
writel ( 0x20200000 , pll - > base + ETH_PLL_CTL5 ) ;
writel ( 0x0000c002 , pll - > base + ETH_PLL_CTL6 ) ;
writel ( 0x00000023 , pll - > base + ETH_PLL_CTL7 ) ;
2019-09-24 15:39:53 +03:00
return 0 ;
2019-04-04 16:11:45 +03:00
}
static const struct clk_ops g12a_ephy_pll_ops = {
. recalc_rate = g12a_ephy_pll_recalc_rate ,
. is_enabled = g12a_ephy_pll_is_enabled ,
. enable = g12a_ephy_pll_enable ,
. disable = g12a_ephy_pll_disable ,
. init = g12a_ephy_pll_init ,
} ;
static int g12a_enable_internal_mdio ( struct g12a_mdio_mux * priv )
{
int ret ;
/* Enable the phy clock */
if ( ! priv - > pll_is_enabled ) {
ret = clk_prepare_enable ( priv - > pll ) ;
if ( ret )
return ret ;
}
priv - > pll_is_enabled = true ;
/* Initialize ephy control */
writel ( EPHY_G12A_ID , priv - > regs + ETH_PHY_CNTL0 ) ;
writel ( FIELD_PREP ( PHY_CNTL1_ST_MODE , 3 ) |
FIELD_PREP ( PHY_CNTL1_ST_PHYADD , EPHY_DFLT_ADD ) |
FIELD_PREP ( PHY_CNTL1_MII_MODE , EPHY_MODE_RMII ) |
PHY_CNTL1_CLK_EN |
PHY_CNTL1_CLKFREQ |
PHY_CNTL1_PHY_ENB ,
priv - > regs + ETH_PHY_CNTL1 ) ;
writel ( PHY_CNTL2_USE_INTERNAL |
PHY_CNTL2_SMI_SRC_MAC |
PHY_CNTL2_RX_CLK_EPHY ,
priv - > regs + ETH_PHY_CNTL2 ) ;
return 0 ;
}
static int g12a_enable_external_mdio ( struct g12a_mdio_mux * priv )
{
/* Reset the mdio bus mux */
writel_relaxed ( 0x0 , priv - > regs + ETH_PHY_CNTL2 ) ;
/* Disable the phy clock if enabled */
if ( priv - > pll_is_enabled ) {
clk_disable_unprepare ( priv - > pll ) ;
priv - > pll_is_enabled = false ;
}
return 0 ;
}
static int g12a_mdio_switch_fn ( int current_child , int desired_child ,
void * data )
{
struct g12a_mdio_mux * priv = dev_get_drvdata ( data ) ;
if ( current_child = = desired_child )
return 0 ;
switch ( desired_child ) {
case MESON_G12A_MDIO_EXTERNAL_ID :
return g12a_enable_external_mdio ( priv ) ;
case MESON_G12A_MDIO_INTERNAL_ID :
return g12a_enable_internal_mdio ( priv ) ;
default :
return - EINVAL ;
}
}
static const struct of_device_id g12a_mdio_mux_match [ ] = {
{ . compatible = " amlogic,g12a-mdio-mux " , } ,
{ } ,
} ;
MODULE_DEVICE_TABLE ( of , g12a_mdio_mux_match ) ;
static int g12a_ephy_glue_clk_register ( struct device * dev )
{
struct g12a_mdio_mux * priv = dev_get_drvdata ( dev ) ;
const char * parent_names [ PLL_MUX_NUM_PARENT ] ;
struct clk_init_data init ;
struct g12a_ephy_pll * pll ;
struct clk_mux * mux ;
struct clk * clk ;
char * name ;
int i ;
/* get the mux parents */
for ( i = 0 ; i < PLL_MUX_NUM_PARENT ; i + + ) {
char in_name [ 8 ] ;
snprintf ( in_name , sizeof ( in_name ) , " clkin%d " , i ) ;
clk = devm_clk_get ( dev , in_name ) ;
if ( IS_ERR ( clk ) ) {
if ( PTR_ERR ( clk ) ! = - EPROBE_DEFER )
dev_err ( dev , " Missing clock %s \n " , in_name ) ;
return PTR_ERR ( clk ) ;
}
parent_names [ i ] = __clk_get_name ( clk ) ;
}
/* create the input mux */
mux = devm_kzalloc ( dev , sizeof ( * mux ) , GFP_KERNEL ) ;
if ( ! mux )
return - ENOMEM ;
name = kasprintf ( GFP_KERNEL , " %s#mux " , dev_name ( dev ) ) ;
if ( ! name )
return - ENOMEM ;
init . name = name ;
init . ops = & clk_mux_ro_ops ;
init . flags = 0 ;
init . parent_names = parent_names ;
init . num_parents = PLL_MUX_NUM_PARENT ;
mux - > reg = priv - > regs + ETH_PLL_CTL0 ;
mux - > shift = __ffs ( PLL_CTL0_SEL ) ;
mux - > mask = PLL_CTL0_SEL > > mux - > shift ;
mux - > hw . init = & init ;
clk = devm_clk_register ( dev , & mux - > hw ) ;
kfree ( name ) ;
if ( IS_ERR ( clk ) ) {
dev_err ( dev , " failed to register input mux \n " ) ;
return PTR_ERR ( clk ) ;
}
/* create the pll */
pll = devm_kzalloc ( dev , sizeof ( * pll ) , GFP_KERNEL ) ;
if ( ! pll )
return - ENOMEM ;
name = kasprintf ( GFP_KERNEL , " %s#pll " , dev_name ( dev ) ) ;
if ( ! name )
return - ENOMEM ;
init . name = name ;
init . ops = & g12a_ephy_pll_ops ;
init . flags = 0 ;
parent_names [ 0 ] = __clk_get_name ( clk ) ;
init . parent_names = parent_names ;
init . num_parents = 1 ;
pll - > base = priv - > regs ;
pll - > hw . init = & init ;
clk = devm_clk_register ( dev , & pll - > hw ) ;
kfree ( name ) ;
if ( IS_ERR ( clk ) ) {
dev_err ( dev , " failed to register input mux \n " ) ;
return PTR_ERR ( clk ) ;
}
priv - > pll = clk ;
return 0 ;
}
static int g12a_mdio_mux_probe ( struct platform_device * pdev )
{
struct device * dev = & pdev - > dev ;
struct g12a_mdio_mux * priv ;
int ret ;
priv = devm_kzalloc ( dev , sizeof ( * priv ) , GFP_KERNEL ) ;
if ( ! priv )
return - ENOMEM ;
platform_set_drvdata ( pdev , priv ) ;
2019-08-27 16:49:40 +03:00
priv - > regs = devm_platform_ioremap_resource ( pdev , 0 ) ;
2019-04-04 16:11:45 +03:00
if ( IS_ERR ( priv - > regs ) )
return PTR_ERR ( priv - > regs ) ;
priv - > pclk = devm_clk_get ( dev , " pclk " ) ;
if ( IS_ERR ( priv - > pclk ) ) {
ret = PTR_ERR ( priv - > pclk ) ;
if ( ret ! = - EPROBE_DEFER )
dev_err ( dev , " failed to get peripheral clock \n " ) ;
return ret ;
}
/* Make sure the device registers are clocked */
ret = clk_prepare_enable ( priv - > pclk ) ;
if ( ret ) {
dev_err ( dev , " failed to enable peripheral clock " ) ;
return ret ;
}
/* Register PLL in CCF */
ret = g12a_ephy_glue_clk_register ( dev ) ;
if ( ret )
goto err ;
ret = mdio_mux_init ( dev , dev - > of_node , g12a_mdio_switch_fn ,
& priv - > mux_handle , dev , NULL ) ;
if ( ret ) {
if ( ret ! = - EPROBE_DEFER )
dev_err ( dev , " mdio multiplexer init failed: %d " , ret ) ;
goto err ;
}
return 0 ;
err :
clk_disable_unprepare ( priv - > pclk ) ;
return ret ;
}
static int g12a_mdio_mux_remove ( struct platform_device * pdev )
{
struct g12a_mdio_mux * priv = platform_get_drvdata ( pdev ) ;
mdio_mux_uninit ( priv - > mux_handle ) ;
if ( priv - > pll_is_enabled )
clk_disable_unprepare ( priv - > pll ) ;
clk_disable_unprepare ( priv - > pclk ) ;
return 0 ;
}
static struct platform_driver g12a_mdio_mux_driver = {
. probe = g12a_mdio_mux_probe ,
. remove = g12a_mdio_mux_remove ,
. driver = {
. name = " g12a-mdio_mux " ,
. of_match_table = g12a_mdio_mux_match ,
} ,
} ;
module_platform_driver ( g12a_mdio_mux_driver ) ;
MODULE_DESCRIPTION ( " Amlogic G12a MDIO multiplexer driver " ) ;
MODULE_AUTHOR ( " Jerome Brunet <jbrunet@baylibre.com> " ) ;
MODULE_LICENSE ( " GPL v2 " ) ;