@ -18,6 +18,7 @@
# include <linux/clkdev.h>
# include <linux/of.h>
# include <linux/of_address.h>
# include <linux/reset-controller.h>
# include "clk-factors.h"
@ -51,6 +52,8 @@ static void __init sun4i_osc_clk_setup(struct device_node *node)
if ( ! gate )
goto err_free_fixed ;
of_property_read_string ( node , " clock-output-names " , & clk_name ) ;
/* set up gate and fixed rate properties */
gate - > reg = of_iomap ( node , 0 ) ;
gate - > bit_idx = SUNXI_OSC24M_GATE ;
@ -77,7 +80,7 @@ err_free_gate:
err_free_fixed :
kfree ( fixed ) ;
}
CLK_OF_DECLARE ( sun4i_osc , " allwinner,sun4i-osc-clk " , sun4i_osc_clk_setup ) ;
CLK_OF_DECLARE ( sun4i_osc , " allwinner,sun4i-a10- osc-clk " , sun4i_osc_clk_setup ) ;
@ -249,7 +252,38 @@ static void sun4i_get_pll5_factors(u32 *freq, u32 parent_rate,
* n = DIV_ROUND_UP ( div , ( * k + 1 ) ) ;
}
/**
* sun6i_a31_get_pll6_factors ( ) - calculates n , k factors for A31 PLL6
* PLL6 rate is calculated as follows
* rate = parent_rate * n * ( k + 1 ) / 2
* parent_rate is always 24 Mhz
*/
static void sun6i_a31_get_pll6_factors ( u32 * freq , u32 parent_rate ,
u8 * n , u8 * k , u8 * m , u8 * p )
{
u8 div ;
/*
* We always have 24 MHz / 2 , so we can just say that our
* parent clock is 12 MHz .
*/
parent_rate = parent_rate / 2 ;
/* Normalize value to a parent_rate multiple (24M / 2) */
div = * freq / parent_rate ;
* freq = parent_rate * div ;
/* we were called to round the frequency, we can now return */
if ( n = = NULL )
return ;
* k = div / 32 ;
if ( * k > 3 )
* k = 3 ;
* n = DIV_ROUND_UP ( div , ( * k + 1 ) ) ;
}
/**
* sun4i_get_apb1_factors ( ) - calculates m , p factors for APB1
@ -376,6 +410,102 @@ static void sun7i_a20_get_out_factors(u32 *freq, u32 parent_rate,
/**
* sun7i_a20_gmac_clk_setup - Setup function for A20 / A31 GMAC clock module
*
* This clock looks something like this
* ________________________
* MII TX clock from PHY > - - - - - | ___________ _________ | - - - - > to GMAC core
* GMAC Int . RGMII TX clk > - - - - | ___________ \ __ / __gate - - - | - - - - > to PHY
* Ext . 125 MHz RGMII TX clk > - - | __divider__ / |
* | ________________________ |
*
* The external 125 MHz reference is optional , i . e . GMAC can use its
* internal TX clock just fine . The A31 GMAC clock module does not have
* the divider controls for the external reference .
*
* To keep it simple , let the GMAC use either the MII TX clock for MII mode ,
* and its internal TX clock for GMII and RGMII modes . The GMAC driver should
* select the appropriate source and gate / ungate the output to the PHY .
*
* Only the GMAC should use this clock . Altering the clock so that it doesn ' t
* match the GMAC ' s operation parameters will result in the GMAC not being
* able to send traffic out . The GMAC driver should set the clock rate and
* enable / disable this clock to configure the required state . The clock
* driver then responds by auto - reparenting the clock .
*/
# define SUN7I_A20_GMAC_GPIT 2
# define SUN7I_A20_GMAC_MASK 0x3
# define SUN7I_A20_GMAC_PARENTS 2
static void __init sun7i_a20_gmac_clk_setup ( struct device_node * node )
{
struct clk * clk ;
struct clk_mux * mux ;
struct clk_gate * gate ;
const char * clk_name = node - > name ;
const char * parents [ SUN7I_A20_GMAC_PARENTS ] ;
void * reg ;
if ( of_property_read_string ( node , " clock-output-names " , & clk_name ) )
return ;
/* allocate mux and gate clock structs */
mux = kzalloc ( sizeof ( struct clk_mux ) , GFP_KERNEL ) ;
if ( ! mux )
return ;
gate = kzalloc ( sizeof ( struct clk_gate ) , GFP_KERNEL ) ;
if ( ! gate )
goto free_mux ;
/* gmac clock requires exactly 2 parents */
parents [ 0 ] = of_clk_get_parent_name ( node , 0 ) ;
parents [ 1 ] = of_clk_get_parent_name ( node , 1 ) ;
if ( ! parents [ 0 ] | | ! parents [ 1 ] )
goto free_gate ;
reg = of_iomap ( node , 0 ) ;
if ( ! reg )
goto free_gate ;
/* set up gate and fixed rate properties */
gate - > reg = reg ;
gate - > bit_idx = SUN7I_A20_GMAC_GPIT ;
gate - > lock = & clk_lock ;
mux - > reg = reg ;
mux - > mask = SUN7I_A20_GMAC_MASK ;
mux - > flags = CLK_MUX_INDEX_BIT ;
mux - > lock = & clk_lock ;
clk = clk_register_composite ( NULL , clk_name ,
parents , SUN7I_A20_GMAC_PARENTS ,
& mux - > hw , & clk_mux_ops ,
NULL , NULL ,
& gate - > hw , & clk_gate_ops ,
0 ) ;
if ( IS_ERR ( clk ) )
goto iounmap_reg ;
of_clk_add_provider ( node , of_clk_src_simple_get , clk ) ;
clk_register_clkdev ( clk , clk_name , NULL ) ;
return ;
iounmap_reg :
iounmap ( reg ) ;
free_gate :
kfree ( gate ) ;
free_mux :
kfree ( mux ) ;
}
CLK_OF_DECLARE ( sun7i_a20_gmac , " allwinner,sun7i-a20-gmac-clk " ,
sun7i_a20_gmac_clk_setup ) ;
/**
* sunxi_factors_clk_setup ( ) - Setup function for factor clocks
*/
@ -387,6 +517,7 @@ struct factors_data {
int mux ;
struct clk_factors_config * table ;
void ( * getter ) ( u32 * rate , u32 parent_rate , u8 * n , u8 * k , u8 * m , u8 * p ) ;
const char * name ;
} ;
static struct clk_factors_config sun4i_pll1_config = {
@ -416,6 +547,13 @@ static struct clk_factors_config sun4i_pll5_config = {
. kwidth = 2 ,
} ;
static struct clk_factors_config sun6i_a31_pll6_config = {
. nshift = 8 ,
. nwidth = 5 ,
. kshift = 4 ,
. kwidth = 2 ,
} ;
static struct clk_factors_config sun4i_apb1_config = {
. mshift = 0 ,
. mwidth = 5 ,
@ -455,6 +593,20 @@ static const struct factors_data sun4i_pll5_data __initconst = {
. enable = 31 ,
. table = & sun4i_pll5_config ,
. getter = sun4i_get_pll5_factors ,
. name = " pll5 " ,
} ;
static const struct factors_data sun4i_pll6_data __initconst = {
. enable = 31 ,
. table = & sun4i_pll5_config ,
. getter = sun4i_get_pll5_factors ,
. name = " pll6 " ,
} ;
static const struct factors_data sun6i_a31_pll6_data __initconst = {
. enable = 31 ,
. table = & sun6i_a31_pll6_config ,
. getter = sun6i_a31_get_pll6_factors ,
} ;
static const struct factors_data sun4i_apb1_data __initconst = {
@ -497,14 +649,14 @@ static struct clk * __init sunxi_factors_clk_setup(struct device_node *node,
( parents [ i ] = of_clk_get_parent_name ( node , i ) ) ! = NULL )
i + + ;
/* Nodes should be providing the name via clock-output-names
* but originally our dts didn ' t , and so we used node - > name .
* The new , better nodes look like clk @ deadbeef , so we pull the
* name just in this case */
if ( ! strcmp ( " clk " , clk_name ) ) {
of_property_read_string_index ( node , " clock-output-names " ,
0 , & clk_name ) ;
}
/*
* some factor clocks , such as pll5 and pll6 , may have multiple
* outputs , and have their name designated in factors_data
*/
if ( data - > name )
clk_name = data - > name ;
else
of_property_read_string ( node , " clock-output-names " , & clk_name ) ;
factors = kzalloc ( sizeof ( struct clk_factors ) , GFP_KERNEL ) ;
if ( ! factors )
@ -601,6 +753,8 @@ static void __init sunxi_mux_clk_setup(struct device_node *node,
( parents [ i ] = of_clk_get_parent_name ( node , i ) ) ! = NULL )
i + + ;
of_property_read_string ( node , " clock-output-names " , & clk_name ) ;
clk = clk_register_mux ( NULL , clk_name , parents , i ,
CLK_SET_RATE_NO_REPARENT , reg ,
data - > shift , SUNXI_MUX_GATE_WIDTH ,
@ -660,6 +814,8 @@ static void __init sunxi_divider_clk_setup(struct device_node *node,
clk_parent = of_clk_get_parent_name ( node , 0 ) ;
of_property_read_string ( node , " clock-output-names " , & clk_name ) ;
clk = clk_register_divider ( NULL , clk_name , clk_parent , 0 ,
reg , data - > shift , data - > width ,
data - > pow ? CLK_DIVIDER_POWER_OF_TWO : 0 ,
@ -672,6 +828,59 @@ static void __init sunxi_divider_clk_setup(struct device_node *node,
/**
* sunxi_gates_reset . . . - reset bits in leaf gate clk registers handling
*/
struct gates_reset_data {
void __iomem * reg ;
spinlock_t * lock ;
struct reset_controller_dev rcdev ;
} ;
static int sunxi_gates_reset_assert ( struct reset_controller_dev * rcdev ,
unsigned long id )
{
struct gates_reset_data * data = container_of ( rcdev ,
struct gates_reset_data ,
rcdev ) ;
unsigned long flags ;
u32 reg ;
spin_lock_irqsave ( data - > lock , flags ) ;
reg = readl ( data - > reg ) ;
writel ( reg & ~ BIT ( id ) , data - > reg ) ;
spin_unlock_irqrestore ( data - > lock , flags ) ;
return 0 ;
}
static int sunxi_gates_reset_deassert ( struct reset_controller_dev * rcdev ,
unsigned long id )
{
struct gates_reset_data * data = container_of ( rcdev ,
struct gates_reset_data ,
rcdev ) ;
unsigned long flags ;
u32 reg ;
spin_lock_irqsave ( data - > lock , flags ) ;
reg = readl ( data - > reg ) ;
writel ( reg | BIT ( id ) , data - > reg ) ;
spin_unlock_irqrestore ( data - > lock , flags ) ;
return 0 ;
}
static struct reset_control_ops sunxi_gates_reset_ops = {
. assert = sunxi_gates_reset_assert ,
. deassert = sunxi_gates_reset_deassert ,
} ;
/**
* sunxi_gates_clk_setup ( ) - Setup function for leaf gates on clocks
*/
@ -680,6 +889,7 @@ static void __init sunxi_divider_clk_setup(struct device_node *node,
struct gates_data {
DECLARE_BITMAP ( mask , SUNXI_GATES_MAX_SIZE ) ;
u32 reset_mask ;
} ;
static const struct gates_data sun4i_axi_gates_data __initconst = {
@ -746,10 +956,21 @@ static const struct gates_data sun7i_a20_apb1_gates_data __initconst = {
. mask = { 0xff80ff } ,
} ;
static const struct gates_data sun4i_a10_usb_gates_data __initconst = {
. mask = { 0x1C0 } ,
. reset_mask = 0x07 ,
} ;
static const struct gates_data sun5i_a13_usb_gates_data __initconst = {
. mask = { 0x140 } ,
. reset_mask = 0x03 ,
} ;
static void __init sunxi_gates_clk_setup ( struct device_node * node ,
struct gates_data * data )
{
struct clk_onecell_data * clk_data ;
struct gates_reset_data * reset_data ;
const char * clk_parent ;
const char * clk_name ;
void * reg ;
@ -793,6 +1014,21 @@ static void __init sunxi_gates_clk_setup(struct device_node *node,
clk_data - > clk_num = i ;
of_clk_add_provider ( node , of_clk_src_onecell_get , clk_data ) ;
/* Register a reset controler for gates with reset bits */
if ( data - > reset_mask = = 0 )
return ;
reset_data = kzalloc ( sizeof ( * reset_data ) , GFP_KERNEL ) ;
if ( ! reset_data )
return ;
reset_data - > reg = reg ;
reset_data - > lock = & clk_lock ;
reset_data - > rcdev . nr_resets = __fls ( data - > reset_mask ) + 1 ;
reset_data - > rcdev . ops = & sunxi_gates_reset_ops ;
reset_data - > rcdev . of_node = node ;
reset_controller_register ( & reset_data - > rcdev ) ;
}
@ -832,7 +1068,7 @@ static const struct divs_data pll5_divs_data __initconst = {
} ;
static const struct divs_data pll6_divs_data __initconst = {
. factors = & sun4i_pll5 _data ,
. factors = & sun4i_pll6 _data ,
. div = {
{ . shift = 0 , . table = pll6_sata_tbl , . gate = 14 } , /* M, SATA */
{ . fixed = 2 } , /* P, other */
@ -854,7 +1090,7 @@ static void __init sunxi_divs_clk_setup(struct device_node *node,
struct divs_data * data )
{
struct clk_onecell_data * clk_data ;
const char * parent = node - > name ;
const char * parent ;
const char * clk_name ;
struct clk * * clks , * pclk ;
struct clk_hw * gate_hw , * rate_hw ;
@ -868,6 +1104,7 @@ static void __init sunxi_divs_clk_setup(struct device_node *node,
/* Set up factor clock that we will be dividing */
pclk = sunxi_factors_clk_setup ( node , data - > factors ) ;
parent = __clk_get_name ( pclk ) ;
reg = of_iomap ( node , 0 ) ;
@ -970,56 +1207,59 @@ free_clkdata:
/* Matches for factors clocks */
static const struct of_device_id clk_factors_match [ ] __initconst = {
{ . compatible = " allwinner,sun4i-pll1-clk " , . data = & sun4i_pll1_data , } ,
{ . compatible = " allwinner,sun4i-a10- pll1-clk " , . data = & sun4i_pll1_data , } ,
{ . compatible = " allwinner,sun6i-a31-pll1-clk " , . data = & sun6i_a31_pll1_data , } ,
{ . compatible = " allwinner,sun4i-apb1-clk " , . data = & sun4i_apb1_data , } ,
{ . compatible = " allwinner,sun4i-mod0-clk " , . data = & sun4i_mod0_data , } ,
{ . compatible = " allwinner,sun6i-a31-pll6-clk " , . data = & sun6i_a31_pll6_data , } ,
{ . compatible = " allwinner,sun4i-a10-apb1-clk " , . data = & sun4i_apb1_data , } ,
{ . compatible = " allwinner,sun4i-a10-mod0-clk " , . data = & sun4i_mod0_data , } ,
{ . compatible = " allwinner,sun7i-a20-out-clk " , . data = & sun7i_a20_out_data , } ,
{ }
} ;
/* Matches for divider clocks */
static const struct of_device_id clk_div_match [ ] __initconst = {
{ . compatible = " allwinner,sun4i-axi-clk " , . data = & sun4i_axi_data , } ,
{ . compatible = " allwinner,sun4i-ahb-clk " , . data = & sun4i_ahb_data , } ,
{ . compatible = " allwinner,sun4i-apb0-clk " , . data = & sun4i_apb0_data , } ,
{ . compatible = " allwinner,sun4i-a10-a xi-clk " , . data = & sun4i_axi_data , } ,
{ . compatible = " allwinner,sun4i-a10-a hb-clk " , . data = & sun4i_ahb_data , } ,
{ . compatible = " allwinner,sun4i-a10-a pb0-clk " , . data = & sun4i_apb0_data , } ,
{ . compatible = " allwinner,sun6i-a31-apb2-div-clk " , . data = & sun6i_a31_apb2_div_data , } ,
{ }
} ;
/* Matches for divided outputs */
static const struct of_device_id clk_divs_match [ ] __initconst = {
{ . compatible = " allwinner,sun4i-pll5-clk " , . data = & pll5_divs_data , } ,
{ . compatible = " allwinner,sun4i-pll6-clk " , . data = & pll6_divs_data , } ,
{ . compatible = " allwinner,sun4i-a10- pll5-clk " , . data = & pll5_divs_data , } ,
{ . compatible = " allwinner,sun4i-a10- pll6-clk " , . data = & pll6_divs_data , } ,
{ }
} ;
/* Matches for mux clocks */
static const struct of_device_id clk_mux_match [ ] __initconst = {
{ . compatible = " allwinner,sun4i-cpu-clk " , . data = & sun4i_cpu_mux_data , } ,
{ . compatible = " allwinner,sun4i-apb1-mux-clk " , . data = & sun4i_apb1_mux_data , } ,
{ . compatible = " allwinner,sun4i-a10- cpu-clk " , . data = & sun4i_cpu_mux_data , } ,
{ . compatible = " allwinner,sun4i-a10-a pb1-mux-clk " , . data = & sun4i_apb1_mux_data , } ,
{ . compatible = " allwinner,sun6i-a31-ahb1-mux-clk " , . data = & sun6i_a31_ahb1_mux_data , } ,
{ }
} ;
/* Matches for gate clocks */
static const struct of_device_id clk_gates_match [ ] __initconst = {
{ . compatible = " allwinner,sun4i-axi-gates-clk " , . data = & sun4i_axi_gates_data , } ,
{ . compatible = " allwinner,sun4i-ahb-gates-clk " , . data = & sun4i_ahb_gates_data , } ,
{ . compatible = " allwinner,sun4i-a10-a xi-gates-clk " , . data = & sun4i_axi_gates_data , } ,
{ . compatible = " allwinner,sun4i-a10-a hb-gates-clk " , . data = & sun4i_ahb_gates_data , } ,
{ . compatible = " allwinner,sun5i-a10s-ahb-gates-clk " , . data = & sun5i_a10s_ahb_gates_data , } ,
{ . compatible = " allwinner,sun5i-a13-ahb-gates-clk " , . data = & sun5i_a13_ahb_gates_data , } ,
{ . compatible = " allwinner,sun6i-a31-ahb1-gates-clk " , . data = & sun6i_a31_ahb1_gates_data , } ,
{ . compatible = " allwinner,sun7i-a20-ahb-gates-clk " , . data = & sun7i_a20_ahb_gates_data , } ,
{ . compatible = " allwinner,sun4i-apb0-gates-clk " , . data = & sun4i_apb0_gates_data , } ,
{ . compatible = " allwinner,sun4i-a10-a pb0-gates-clk " , . data = & sun4i_apb0_gates_data , } ,
{ . compatible = " allwinner,sun5i-a10s-apb0-gates-clk " , . data = & sun5i_a10s_apb0_gates_data , } ,
{ . compatible = " allwinner,sun5i-a13-apb0-gates-clk " , . data = & sun5i_a13_apb0_gates_data , } ,
{ . compatible = " allwinner,sun7i-a20-apb0-gates-clk " , . data = & sun7i_a20_apb0_gates_data , } ,
{ . compatible = " allwinner,sun4i-apb1-gates-clk " , . data = & sun4i_apb1_gates_data , } ,
{ . compatible = " allwinner,sun4i-a10-a pb1-gates-clk " , . data = & sun4i_apb1_gates_data , } ,
{ . compatible = " allwinner,sun5i-a10s-apb1-gates-clk " , . data = & sun5i_a10s_apb1_gates_data , } ,
{ . compatible = " allwinner,sun5i-a13-apb1-gates-clk " , . data = & sun5i_a13_apb1_gates_data , } ,
{ . compatible = " allwinner,sun6i-a31-apb1-gates-clk " , . data = & sun6i_a31_apb1_gates_data , } ,
{ . compatible = " allwinner,sun7i-a20-apb1-gates-clk " , . data = & sun7i_a20_apb1_gates_data , } ,
{ . compatible = " allwinner,sun6i-a31-apb2-gates-clk " , . data = & sun6i_a31_apb2_gates_data , } ,
{ . compatible = " allwinner,sun4i-a10-usb-clk " , . data = & sun4i_a10_usb_gates_data , } ,
{ . compatible = " allwinner,sun5i-a13-usb-clk " , . data = & sun5i_a13_usb_gates_data , } ,
{ }
} ;