@ -12,16 +12,39 @@
# include <dm/lists.h>
# include <dt-bindings/clock/ast2500-scu.h>
/*
* MAC Clock Delay settings , taken from Aspeed SDK
*/
# define RGMII_TXCLK_ODLY 8
# define RMII_RXCLK_IDLY 2
/*
* TGMII Clock Duty constants , taken from Aspeed SDK
*/
# define RGMII2_TXCK_DUTY 0x66
# define RGMII1_TXCK_DUTY 0x64
# define D2PLL_DEFAULT_RATE (250 * 1000 * 1000)
DECLARE_GLOBAL_DATA_PTR ;
/*
* Clock divider / multiplier configuration struct .
* For H - PLL and M - PLL the formula is
* ( Output Frequency ) = CLKIN * ( ( M + 1 ) / ( N + 1 ) ) / ( P + 1 )
* M - Numerator
* N - Denumerator
* P - Post Divider
* They have the same layout in their control register .
*
* D - PLL and D2 - PLL have extra divider ( OD + 1 ) , which is not
* yet needed and ignored by clock configurations .
*/
struct ast2500_div_config {
unsigned int num ;
unsigned int denum ;
unsigned int post_div ;
} ;
/*
* Get the rate of the M - PLL clock from input clock frequency and
@ -143,30 +166,41 @@ static ulong ast2500_clk_get_rate(struct clk *clk)
return rate ;
}
static ulong ast2500_configure_ddr ( struct ast2500_scu * scu , ulong rate )
/*
* @ input_rate - the rate of input clock in Hz
* @ requested_rate - desired output rate in Hz
* @ div - this is an IN / OUT parameter , at input all fields of the config
* need to be set to their maximum allowed values .
* The result ( the best config we could find ) , would also be returned
* in this structure .
*
* @ return The clock rate , when the resulting div_config is used .
*/
static ulong ast2500_calc_clock_config ( ulong input_rate , ulong requested_rate ,
struct ast2500_div_config * cfg )
{
ulong clkin = ast2500_get_clkin ( scu ) ;
u32 mpll_reg ;
/*
* There are not that many combinations of numerator , denumerator
* and post divider , so just brute force the best combination .
* However , to avoid overflow when multiplying , use kHz .
* The assumption is that kHz precision is good enough and
* also enough to avoid overflow when multiplying .
*/
const ulong clkin_khz = clkin / 1000 ;
const ulong rate_khz = rate / 1000 ;
ulong best_num = 0 ;
ulong best_denum = 0 ;
ulong best_post = 0 ;
ulong delta = rate ;
ulong num , denum , post ;
for ( denum = 0 ; denum < = SCU_MPLL_DENUM_MASK ; + + denum ) {
for ( post = 0 ; post < = SCU_MPLL_POST_MASK ; + + post ) {
num = ( rate_khz * ( post + 1 ) / clkin_khz ) * ( denum + 1 ) ;
ulong new_rate_khz = ( clkin_khz
* ( ( num + 1 ) / ( denum + 1 ) ) )
/ ( post + 1 ) ;
const ulong input_rate_khz = input_rate / 1000 ;
const ulong rate_khz = requested_rate / 1000 ;
const struct ast2500_div_config max_vals = * cfg ;
struct ast2500_div_config it = { 0 , 0 , 0 } ;
ulong delta = rate_khz ;
ulong new_rate_khz = 0 ;
for ( ; it . denum < = max_vals . denum ; + + it . denum ) {
for ( it . post_div = 0 ; it . post_div < = max_vals . post_div ;
+ + it . post_div ) {
it . num = ( rate_khz * ( it . post_div + 1 ) / input_rate_khz )
* ( it . denum + 1 ) ;
if ( it . num > max_vals . num )
continue ;
new_rate_khz = ( input_rate_khz
* ( ( it . num + 1 ) / ( it . denum + 1 ) ) )
/ ( it . post_div + 1 ) ;
/* Keep the rate below requested one. */
if ( new_rate_khz > rate_khz )
@ -174,25 +208,35 @@ static ulong ast2500_configure_ddr(struct ast2500_scu *scu, ulong rate)
if ( new_rate_khz - rate_khz < delta ) {
delta = new_rate_khz - rate_khz ;
best_num = num ;
best_denum = denum ;
best_post = post ;
* cfg = it ;
if ( delta = = 0 )
goto rate_calc_done ;
return new_rate_khz * 1000 ;
}
}
}
rate_calc_done :
return new_rate_khz * 1000 ;
}
static ulong ast2500_configure_ddr ( struct ast2500_scu * scu , ulong rate )
{
ulong clkin = ast2500_get_clkin ( scu ) ;
u32 mpll_reg ;
struct ast2500_div_config div_cfg = {
. num = SCU_MPLL_NUM_MASK ,
. denum = SCU_MPLL_DENUM_MASK ,
. post_div = SCU_MPLL_POST_MASK
} ;
ast2500_calc_clock_config ( clkin , rate , & div_cfg ) ;
mpll_reg = readl ( & scu - > m_pll_param ) ;
mpll_reg & = ~ ( ( SCU_MPLL_POST_MASK < < SCU_MPLL_POST_SHIFT )
| ( SCU_MPLL_NUM_MASK < < SCU_MPLL_NUM_SHIFT )
| ( SCU_MPLL_DENUM_MASK < < SCU_MPLL_DENUM_SHIFT ) ) ;
mpll_reg | = ( best_post < < SCU_MPLL_POST_SHIFT )
| ( best_num < < SCU_MPLL_NUM_SHIFT )
| ( best_denum < < SCU_MPLL_DENUM_SHIFT ) ;
mpll_reg | = ( div_cfg . post_div < < SCU_MPLL_POST_SHIFT )
| ( div_cfg . num < < SCU_MPLL_NUM_SHIFT )
| ( div_cfg . denum < < SCU_MPLL_DENUM_SHIFT ) ;
ast_scu_unlock ( scu ) ;
writel ( mpll_reg , & scu - > m_pll_param ) ;
@ -201,6 +245,136 @@ static ulong ast2500_configure_ddr(struct ast2500_scu *scu, ulong rate)
return ast2500_get_mpll_rate ( clkin , mpll_reg ) ;
}
static ulong ast2500_configure_mac ( struct ast2500_scu * scu , int index )
{
ulong clkin = ast2500_get_clkin ( scu ) ;
ulong hpll_rate = ast2500_get_hpll_rate ( clkin ,
readl ( & scu - > h_pll_param ) ) ;
ulong required_rate ;
u32 hwstrap ;
u32 divisor ;
u32 reset_bit ;
u32 clkstop_bit ;
/*
* According to data sheet , for 10 / 100 mode the MAC clock frequency
* should be at least 25 MHz and for 1000 mode at least 100 MHz
*/
hwstrap = readl ( & scu - > hwstrap ) ;
if ( hwstrap & ( SCU_HWSTRAP_MAC1_RGMII | SCU_HWSTRAP_MAC2_RGMII ) )
required_rate = 100 * 1000 * 1000 ;
else
required_rate = 25 * 1000 * 1000 ;
divisor = hpll_rate / required_rate ;
if ( divisor < 4 ) {
/* Clock can't run fast enough, but let's try anyway */
debug ( " MAC clock too slow \n " ) ;
divisor = 4 ;
} else if ( divisor > 16 ) {
/* Can't slow down the clock enough, but let's try anyway */
debug ( " MAC clock too fast \n " ) ;
divisor = 16 ;
}
switch ( index ) {
case 1 :
reset_bit = SCU_SYSRESET_MAC1 ;
clkstop_bit = SCU_CLKSTOP_MAC1 ;
break ;
case 2 :
reset_bit = SCU_SYSRESET_MAC2 ;
clkstop_bit = SCU_CLKSTOP_MAC2 ;
break ;
default :
return - EINVAL ;
}
ast_scu_unlock ( scu ) ;
clrsetbits_le32 ( & scu - > clk_sel1 , SCU_MACCLK_MASK ,
( ( divisor - 2 ) / 2 ) < < SCU_MACCLK_SHIFT ) ;
/*
* Disable MAC , start its clock and re - enable it .
* The procedure and the delays ( 100u s & 10 ms ) are
* specified in the datasheet .
*/
setbits_le32 ( & scu - > sysreset_ctrl1 , reset_bit ) ;
udelay ( 100 ) ;
clrbits_le32 ( & scu - > clk_stop_ctrl1 , clkstop_bit ) ;
mdelay ( 10 ) ;
clrbits_le32 ( & scu - > sysreset_ctrl1 , reset_bit ) ;
writel ( ( RGMII2_TXCK_DUTY < < SCU_CLKDUTY_RGMII2TXCK_SHIFT )
| ( RGMII1_TXCK_DUTY < < SCU_CLKDUTY_RGMII1TXCK_SHIFT ) ,
& scu - > clk_duty_sel ) ;
ast_scu_lock ( scu ) ;
return required_rate ;
}
static ulong ast2500_configure_d2pll ( struct ast2500_scu * scu , ulong rate )
{
/*
* The values and the meaning of the next three
* parameters are undocumented . Taken from Aspeed SDK .
*/
const u32 d2_pll_ext_param = 0x2c ;
const u32 d2_pll_sip = 0x11 ;
const u32 d2_pll_sic = 0x18 ;
u32 clk_delay_settings =
( RMII_RXCLK_IDLY < < SCU_MICDS_MAC1RMII_RDLY_SHIFT )
| ( RMII_RXCLK_IDLY < < SCU_MICDS_MAC2RMII_RDLY_SHIFT )
| ( RGMII_TXCLK_ODLY < < SCU_MICDS_MAC1RGMII_TXDLY_SHIFT )
| ( RGMII_TXCLK_ODLY < < SCU_MICDS_MAC2RGMII_TXDLY_SHIFT ) ;
struct ast2500_div_config div_cfg = {
. num = SCU_D2PLL_NUM_MASK > > SCU_D2PLL_NUM_SHIFT ,
. denum = SCU_D2PLL_DENUM_MASK > > SCU_D2PLL_DENUM_SHIFT ,
. post_div = SCU_D2PLL_POST_MASK > > SCU_D2PLL_POST_SHIFT ,
} ;
ulong clkin = ast2500_get_clkin ( scu ) ;
ulong new_rate ;
ast_scu_unlock ( scu ) ;
writel ( ( d2_pll_ext_param < < SCU_D2PLL_EXT1_PARAM_SHIFT )
| SCU_D2PLL_EXT1_OFF
| SCU_D2PLL_EXT1_RESET , & scu - > d2_pll_ext_param [ 0 ] ) ;
/*
* Select USB2 .0 port1 PHY clock as a clock source for GCRT .
* This would disconnect it from D2 - PLL .
*/
clrsetbits_le32 ( & scu - > misc_ctrl1 , SCU_MISC_D2PLL_OFF ,
SCU_MISC_GCRT_USB20CLK ) ;
new_rate = ast2500_calc_clock_config ( clkin , rate , & div_cfg ) ;
writel ( ( d2_pll_sip < < SCU_D2PLL_SIP_SHIFT )
| ( d2_pll_sic < < SCU_D2PLL_SIC_SHIFT )
| ( div_cfg . num < < SCU_D2PLL_NUM_SHIFT )
| ( div_cfg . denum < < SCU_D2PLL_DENUM_SHIFT )
| ( div_cfg . post_div < < SCU_D2PLL_POST_SHIFT ) ,
& scu - > d2_pll_param ) ;
clrbits_le32 ( & scu - > d2_pll_ext_param [ 0 ] ,
SCU_D2PLL_EXT1_OFF | SCU_D2PLL_EXT1_RESET ) ;
clrsetbits_le32 ( & scu - > misc_ctrl2 ,
SCU_MISC2_RGMII_HPLL | SCU_MISC2_RMII_MPLL
| SCU_MISC2_RGMII_CLKDIV_MASK |
SCU_MISC2_RMII_CLKDIV_MASK ,
( 4 < < SCU_MISC2_RMII_CLKDIV_SHIFT ) ) ;
writel ( clk_delay_settings | SCU_MICDS_RGMIIPLL , & scu - > mac_clk_delay ) ;
writel ( clk_delay_settings , & scu - > mac_clk_delay_100M ) ;
writel ( clk_delay_settings , & scu - > mac_clk_delay_10M ) ;
ast_scu_lock ( scu ) ;
return new_rate ;
}
static ulong ast2500_clk_set_rate ( struct clk * clk , ulong rate )
{
struct ast2500_clk_priv * priv = dev_get_priv ( clk - > dev ) ;
@ -211,6 +385,9 @@ static ulong ast2500_clk_set_rate(struct clk *clk, ulong rate)
case MCLK_DDR :
new_rate = ast2500_configure_ddr ( priv - > scu , rate ) ;
break ;
case PLL_D2PLL :
new_rate = ast2500_configure_d2pll ( priv - > scu , rate ) ;
break ;
default :
return - ENOENT ;
}
@ -218,9 +395,35 @@ static ulong ast2500_clk_set_rate(struct clk *clk, ulong rate)
return new_rate ;
}
static int ast2500_clk_enable ( struct clk * clk )
{
struct ast2500_clk_priv * priv = dev_get_priv ( clk - > dev ) ;
switch ( clk - > id ) {
/*
* For MAC clocks the clock rate is
* configured based on whether RGMII or RMII mode has been selected
* through hardware strapping .
*/
case PCLK_MAC1 :
ast2500_configure_mac ( priv - > scu , 1 ) ;
break ;
case PCLK_MAC2 :
ast2500_configure_mac ( priv - > scu , 2 ) ;
break ;
case PLL_D2PLL :
ast2500_configure_d2pll ( priv - > scu , D2PLL_DEFAULT_RATE ) ;
default :
return - ENOENT ;
}
return 0 ;
}
struct clk_ops ast2500_clk_ops = {
. get_rate = ast2500_clk_get_rate ,
. set_rate = ast2500_clk_set_rate ,
. enable = ast2500_clk_enable ,
} ;
static int ast2500_clk_probe ( struct udevice * dev )