2019-06-04 10:11:33 +02:00
// SPDX-License-Identifier: GPL-2.0-only
2016-04-04 11:23:23 +02:00
/*
* ARTPEC - 6 clock initialization
*
2022-02-22 11:51:53 -08:00
* Copyright 2015 - 2016 Axis Communications AB .
2016-04-04 11:23:23 +02:00
*/
# include <linux/clk-provider.h>
# include <linux/device.h>
# include <linux/io.h>
# include <linux/of.h>
# include <linux/of_address.h>
# include <linux/platform_device.h>
# include <linux/slab.h>
# include <dt-bindings/clock/axis,artpec6-clkctrl.h>
# define NUM_I2S_CLOCKS 2
struct artpec6_clkctrl_drvdata {
struct clk * clk_table [ ARTPEC6_CLK_NUMCLOCKS ] ;
void __iomem * syscon_base ;
struct clk_onecell_data clk_data ;
spinlock_t i2scfg_lock ;
} ;
static struct artpec6_clkctrl_drvdata * clkdata ;
static const char * const i2s_clk_names [ NUM_I2S_CLOCKS ] = {
" i2s0 " ,
" i2s1 " ,
} ;
static const int i2s_clk_indexes [ NUM_I2S_CLOCKS ] = {
ARTPEC6_CLK_I2S0_CLK ,
ARTPEC6_CLK_I2S1_CLK ,
} ;
static void of_artpec6_clkctrl_setup ( struct device_node * np )
{
int i ;
const char * sys_refclk_name ;
u32 pll_mode , pll_m , pll_n ;
struct clk * * clks ;
/* Mandatory parent clock. */
i = of_property_match_string ( np , " clock-names " , " sys_refclk " ) ;
if ( i < 0 )
return ;
sys_refclk_name = of_clk_get_parent_name ( np , i ) ;
clkdata = kzalloc ( sizeof ( * clkdata ) , GFP_KERNEL ) ;
if ( ! clkdata )
return ;
clks = clkdata - > clk_table ;
for ( i = 0 ; i < ARTPEC6_CLK_NUMCLOCKS ; + + i )
clks [ i ] = ERR_PTR ( - EPROBE_DEFER ) ;
clkdata - > syscon_base = of_iomap ( np , 0 ) ;
BUG_ON ( clkdata - > syscon_base = = NULL ) ;
/* Read PLL1 factors configured by boot strap pins. */
pll_mode = ( readl ( clkdata - > syscon_base ) > > 6 ) & 3 ;
switch ( pll_mode ) {
case 0 : /* DDR3-2133 mode */
pll_m = 4 ;
pll_n = 85 ;
break ;
case 1 : /* DDR3-1866 mode */
pll_m = 6 ;
pll_n = 112 ;
break ;
case 2 : /* DDR3-1600 mode */
pll_m = 4 ;
pll_n = 64 ;
break ;
case 3 : /* DDR3-1333 mode */
pll_m = 8 ;
pll_n = 106 ;
break ;
}
clks [ ARTPEC6_CLK_CPU ] =
clk_register_fixed_factor ( NULL , " cpu " , sys_refclk_name , 0 , pll_n ,
pll_m ) ;
clks [ ARTPEC6_CLK_CPU_PERIPH ] =
clk_register_fixed_factor ( NULL , " cpu_periph " , " cpu " , 0 , 1 , 2 ) ;
/* EPROBE_DEFER on the apb_clock is not handled in amba devices. */
clks [ ARTPEC6_CLK_UART_PCLK ] =
clk_register_fixed_factor ( NULL , " uart_pclk " , " cpu " , 0 , 1 , 8 ) ;
clks [ ARTPEC6_CLK_UART_REFCLK ] =
clk_register_fixed_rate ( NULL , " uart_ref " , sys_refclk_name , 0 ,
50000000 ) ;
clks [ ARTPEC6_CLK_SPI_PCLK ] =
clk_register_fixed_factor ( NULL , " spi_pclk " , " cpu " , 0 , 1 , 8 ) ;
clks [ ARTPEC6_CLK_SPI_SSPCLK ] =
clk_register_fixed_rate ( NULL , " spi_sspclk " , sys_refclk_name , 0 ,
50000000 ) ;
clks [ ARTPEC6_CLK_DBG_PCLK ] =
clk_register_fixed_factor ( NULL , " dbg_pclk " , " cpu " , 0 , 1 , 8 ) ;
clkdata - > clk_data . clks = clkdata - > clk_table ;
clkdata - > clk_data . clk_num = ARTPEC6_CLK_NUMCLOCKS ;
of_clk_add_provider ( np , of_clk_src_onecell_get , & clkdata - > clk_data ) ;
}
2016-07-05 18:23:26 +02:00
CLK_OF_DECLARE_DRIVER ( artpec6_clkctrl , " axis,artpec6-clkctrl " ,
of_artpec6_clkctrl_setup ) ;
2016-04-04 11:23:23 +02:00
static int artpec6_clkctrl_probe ( struct platform_device * pdev )
{
int propidx ;
struct device_node * np = pdev - > dev . of_node ;
struct device * dev = & pdev - > dev ;
struct clk * * clks = clkdata - > clk_table ;
const char * sys_refclk_name ;
const char * i2s_refclk_name = NULL ;
const char * frac_clk_name [ 2 ] = { NULL , NULL } ;
const char * i2s_mux_parents [ 2 ] ;
u32 muxreg ;
int i ;
int err = 0 ;
/* Mandatory parent clock. */
propidx = of_property_match_string ( np , " clock-names " , " sys_refclk " ) ;
if ( propidx < 0 )
return - EINVAL ;
sys_refclk_name = of_clk_get_parent_name ( np , propidx ) ;
/* Find clock names of optional parent clocks. */
propidx = of_property_match_string ( np , " clock-names " , " i2s_refclk " ) ;
if ( propidx > = 0 )
i2s_refclk_name = of_clk_get_parent_name ( np , propidx ) ;
propidx = of_property_match_string ( np , " clock-names " , " frac_clk0 " ) ;
if ( propidx > = 0 )
frac_clk_name [ 0 ] = of_clk_get_parent_name ( np , propidx ) ;
propidx = of_property_match_string ( np , " clock-names " , " frac_clk1 " ) ;
if ( propidx > = 0 )
frac_clk_name [ 1 ] = of_clk_get_parent_name ( np , propidx ) ;
spin_lock_init ( & clkdata - > i2scfg_lock ) ;
clks [ ARTPEC6_CLK_NAND_CLKA ] =
clk_register_fixed_factor ( dev , " nand_clka " , " cpu " , 0 , 1 , 8 ) ;
clks [ ARTPEC6_CLK_NAND_CLKB ] =
clk_register_fixed_rate ( dev , " nand_clkb " , sys_refclk_name , 0 ,
100000000 ) ;
clks [ ARTPEC6_CLK_ETH_ACLK ] =
clk_register_fixed_factor ( dev , " eth_aclk " , " cpu " , 0 , 1 , 4 ) ;
clks [ ARTPEC6_CLK_DMA_ACLK ] =
clk_register_fixed_factor ( dev , " dma_aclk " , " cpu " , 0 , 1 , 4 ) ;
clks [ ARTPEC6_CLK_PTP_REF ] =
clk_register_fixed_rate ( dev , " ptp_ref " , sys_refclk_name , 0 ,
100000000 ) ;
clks [ ARTPEC6_CLK_SD_PCLK ] =
clk_register_fixed_rate ( dev , " sd_pclk " , sys_refclk_name , 0 ,
100000000 ) ;
clks [ ARTPEC6_CLK_SD_IMCLK ] =
clk_register_fixed_rate ( dev , " sd_imclk " , sys_refclk_name , 0 ,
100000000 ) ;
clks [ ARTPEC6_CLK_I2S_HST ] =
clk_register_fixed_factor ( dev , " i2s_hst " , " cpu " , 0 , 1 , 8 ) ;
for ( i = 0 ; i < NUM_I2S_CLOCKS ; + + i ) {
if ( i2s_refclk_name & & frac_clk_name [ i ] ) {
i2s_mux_parents [ 0 ] = frac_clk_name [ i ] ;
i2s_mux_parents [ 1 ] = i2s_refclk_name ;
clks [ i2s_clk_indexes [ i ] ] =
clk_register_mux ( dev , i2s_clk_names [ i ] ,
i2s_mux_parents , 2 ,
CLK_SET_RATE_NO_REPARENT |
CLK_SET_RATE_PARENT ,
clkdata - > syscon_base + 0x14 , i , 1 ,
0 , & clkdata - > i2scfg_lock ) ;
} else if ( frac_clk_name [ i ] ) {
/* Lock the mux for internal clock reference. */
muxreg = readl ( clkdata - > syscon_base + 0x14 ) ;
muxreg & = ~ BIT ( i ) ;
writel ( muxreg , clkdata - > syscon_base + 0x14 ) ;
clks [ i2s_clk_indexes [ i ] ] =
clk_register_fixed_factor ( dev , i2s_clk_names [ i ] ,
frac_clk_name [ i ] , 0 , 1 ,
1 ) ;
} else if ( i2s_refclk_name ) {
/* Lock the mux for external clock reference. */
muxreg = readl ( clkdata - > syscon_base + 0x14 ) ;
muxreg | = BIT ( i ) ;
writel ( muxreg , clkdata - > syscon_base + 0x14 ) ;
clks [ i2s_clk_indexes [ i ] ] =
clk_register_fixed_factor ( dev , i2s_clk_names [ i ] ,
i2s_refclk_name , 0 , 1 , 1 ) ;
}
}
clks [ ARTPEC6_CLK_I2C ] =
clk_register_fixed_rate ( dev , " i2c " , sys_refclk_name , 0 , 100000000 ) ;
clks [ ARTPEC6_CLK_SYS_TIMER ] =
clk_register_fixed_rate ( dev , " timer " , sys_refclk_name , 0 ,
100000000 ) ;
clks [ ARTPEC6_CLK_FRACDIV_IN ] =
clk_register_fixed_rate ( dev , " fracdiv_in " , sys_refclk_name , 0 ,
600000000 ) ;
for ( i = 0 ; i < ARTPEC6_CLK_NUMCLOCKS ; + + i ) {
if ( IS_ERR ( clks [ i ] ) & & PTR_ERR ( clks [ i ] ) ! = - EPROBE_DEFER ) {
dev_err ( dev ,
" Failed to register clock at index %d err=%ld \n " ,
i , PTR_ERR ( clks [ i ] ) ) ;
err = PTR_ERR ( clks [ i ] ) ;
}
}
return err ;
}
static const struct of_device_id artpec_clkctrl_of_match [ ] = {
{ . compatible = " axis,artpec6-clkctrl " } ,
{ }
} ;
static struct platform_driver artpec6_clkctrl_driver = {
. probe = artpec6_clkctrl_probe ,
. driver = {
. name = " artpec6_clkctrl " ,
. of_match_table = artpec_clkctrl_of_match ,
} ,
} ;
builtin_platform_driver ( artpec6_clkctrl_driver ) ;