2019-05-27 08:55:06 +02:00
// SPDX-License-Identifier: GPL-2.0-or-later
2014-01-06 10:27:37 -06:00
/*
* Copyright 2011 - 2012 Calxeda , Inc .
* Copyright ( C ) 2012 - 2013 Altera Corporation < www . altera . com >
*
* Based from clk - highbank . c
*/
2015-06-19 15:00:46 -07:00
# include <linux/slab.h>
2014-01-06 10:27:37 -06:00
# include <linux/clk-provider.h>
# include <linux/io.h>
2014-01-06 12:17:24 -06:00
# include <linux/mfd/syscon.h>
2014-01-06 10:27:37 -06:00
# include <linux/of.h>
2014-01-06 12:17:24 -06:00
# include <linux/regmap.h>
2014-01-06 10:27:37 -06:00
# include "clk.h"
# define SOCFPGA_L4_MP_CLK "l4_mp_clk"
# define SOCFPGA_L4_SP_CLK "l4_sp_clk"
# define SOCFPGA_NAND_CLK "nand_clk"
# define SOCFPGA_NAND_X_CLK "nand_x_clk"
# define SOCFPGA_MMC_CLK "sdmmc_clk"
# define SOCFPGA_GPIO_DB_CLK_OFFSET 0xA8
# define to_socfpga_gate_clk(p) container_of(p, struct socfpga_gate_clk, hw.hw)
2014-01-06 12:17:24 -06:00
/* SDMMC Group for System Manager defines */
# define SYSMGR_SDMMCGRP_CTRL_OFFSET 0x108
2014-01-06 10:27:37 -06:00
static u8 socfpga_clk_get_parent ( struct clk_hw * hwclk )
{
u32 l4_src ;
u32 perpll_src ;
2019-07-31 12:35:14 -07:00
const char * name = clk_hw_get_name ( hwclk ) ;
2014-01-06 10:27:37 -06:00
2019-07-31 12:35:14 -07:00
if ( streq ( name , SOCFPGA_L4_MP_CLK ) ) {
2014-01-06 10:27:37 -06:00
l4_src = readl ( clk_mgr_base_addr + CLKMGR_L4SRC ) ;
2021-12-30 15:03:21 +00:00
return l4_src & 0x1 ;
2014-01-06 10:27:37 -06:00
}
2019-07-31 12:35:14 -07:00
if ( streq ( name , SOCFPGA_L4_SP_CLK ) ) {
2014-01-06 10:27:37 -06:00
l4_src = readl ( clk_mgr_base_addr + CLKMGR_L4SRC ) ;
return ! ! ( l4_src & 2 ) ;
}
perpll_src = readl ( clk_mgr_base_addr + CLKMGR_PERPLL_SRC ) ;
2019-07-31 12:35:14 -07:00
if ( streq ( name , SOCFPGA_MMC_CLK ) )
2021-12-30 15:03:21 +00:00
return perpll_src & 0x3 ;
2019-07-31 12:35:14 -07:00
if ( streq ( name , SOCFPGA_NAND_CLK ) | |
2019-08-13 17:24:02 -07:00
streq ( name , SOCFPGA_NAND_X_CLK ) )
return ( perpll_src > > 2 ) & 3 ;
2014-01-06 10:27:37 -06:00
/* QSPI clock */
return ( perpll_src > > 4 ) & 3 ;
}
static int socfpga_clk_set_parent ( struct clk_hw * hwclk , u8 parent )
{
u32 src_reg ;
2019-07-31 12:35:14 -07:00
const char * name = clk_hw_get_name ( hwclk ) ;
2014-01-06 10:27:37 -06:00
2019-07-31 12:35:14 -07:00
if ( streq ( name , SOCFPGA_L4_MP_CLK ) ) {
2014-01-06 10:27:37 -06:00
src_reg = readl ( clk_mgr_base_addr + CLKMGR_L4SRC ) ;
src_reg & = ~ 0x1 ;
src_reg | = parent ;
writel ( src_reg , clk_mgr_base_addr + CLKMGR_L4SRC ) ;
2019-07-31 12:35:14 -07:00
} else if ( streq ( name , SOCFPGA_L4_SP_CLK ) ) {
2014-01-06 10:27:37 -06:00
src_reg = readl ( clk_mgr_base_addr + CLKMGR_L4SRC ) ;
src_reg & = ~ 0x2 ;
src_reg | = ( parent < < 1 ) ;
writel ( src_reg , clk_mgr_base_addr + CLKMGR_L4SRC ) ;
} else {
src_reg = readl ( clk_mgr_base_addr + CLKMGR_PERPLL_SRC ) ;
2019-07-31 12:35:14 -07:00
if ( streq ( name , SOCFPGA_MMC_CLK ) ) {
2014-01-06 10:27:37 -06:00
src_reg & = ~ 0x3 ;
src_reg | = parent ;
2019-07-31 12:35:14 -07:00
} else if ( streq ( name , SOCFPGA_NAND_CLK ) | |
streq ( name , SOCFPGA_NAND_X_CLK ) ) {
2014-01-06 10:27:37 -06:00
src_reg & = ~ 0xC ;
src_reg | = ( parent < < 2 ) ;
} else { /* QSPI clock */
src_reg & = ~ 0x30 ;
src_reg | = ( parent < < 4 ) ;
}
writel ( src_reg , clk_mgr_base_addr + CLKMGR_PERPLL_SRC ) ;
}
return 0 ;
}
static unsigned long socfpga_clk_recalc_rate ( struct clk_hw * hwclk ,
unsigned long parent_rate )
{
struct socfpga_gate_clk * socfpgaclk = to_socfpga_gate_clk ( hwclk ) ;
u32 div = 1 , val ;
if ( socfpgaclk - > fixed_div )
div = socfpgaclk - > fixed_div ;
else if ( socfpgaclk - > div_reg ) {
val = readl ( socfpgaclk - > div_reg ) > > socfpgaclk - > shift ;
2015-07-13 17:07:43 +03:00
val & = GENMASK ( socfpgaclk - > width - 1 , 0 ) ;
2014-01-06 10:27:37 -06:00
/* Check for GPIO_DB_CLK by its offset */
2021-03-14 12:07:09 +01:00
if ( ( uintptr_t ) socfpgaclk - > div_reg & SOCFPGA_GPIO_DB_CLK_OFFSET )
2014-01-06 10:27:37 -06:00
div = val + 1 ;
else
div = ( 1 < < val ) ;
}
return parent_rate / div ;
}
static struct clk_ops gateclk_ops = {
. recalc_rate = socfpga_clk_recalc_rate ,
. get_parent = socfpga_clk_get_parent ,
. set_parent = socfpga_clk_set_parent ,
} ;
2018-12-04 11:14:02 -08:00
void __init socfpga_gate_init ( struct device_node * node )
2014-01-06 10:27:37 -06:00
{
u32 clk_gate [ 2 ] ;
u32 div_reg [ 3 ] ;
u32 fixed_div ;
2021-03-02 15:41:49 -06:00
struct clk_hw * hw_clk ;
2014-01-06 10:27:37 -06:00
struct socfpga_gate_clk * socfpga_clk ;
const char * clk_name = node - > name ;
const char * parent_name [ SOCFPGA_MAX_PARENTS ] ;
struct clk_init_data init ;
2018-12-04 11:14:02 -08:00
struct clk_ops * ops ;
2014-01-06 10:27:37 -06:00
int rc ;
2021-03-02 15:41:49 -06:00
int err ;
2014-01-06 10:27:37 -06:00
socfpga_clk = kzalloc ( sizeof ( * socfpga_clk ) , GFP_KERNEL ) ;
if ( WARN_ON ( ! socfpga_clk ) )
return ;
2018-12-04 11:14:02 -08:00
ops = kmemdup ( & gateclk_ops , sizeof ( gateclk_ops ) , GFP_KERNEL ) ;
2022-11-23 11:16:22 +08:00
if ( WARN_ON ( ! ops ) ) {
kfree ( socfpga_clk ) ;
2018-12-04 11:14:02 -08:00
return ;
2022-11-23 11:16:22 +08:00
}
2018-12-04 11:14:02 -08:00
2014-01-06 10:27:37 -06:00
rc = of_property_read_u32_array ( node , " clk-gate " , clk_gate , 2 ) ;
if ( rc )
clk_gate [ 0 ] = 0 ;
if ( clk_gate [ 0 ] ) {
socfpga_clk - > hw . reg = clk_mgr_base_addr + clk_gate [ 0 ] ;
socfpga_clk - > hw . bit_idx = clk_gate [ 1 ] ;
2018-12-04 11:14:02 -08:00
ops - > enable = clk_gate_ops . enable ;
ops - > disable = clk_gate_ops . disable ;
2014-01-06 10:27:37 -06:00
}
rc = of_property_read_u32 ( node , " fixed-divider " , & fixed_div ) ;
if ( rc )
socfpga_clk - > fixed_div = 0 ;
else
socfpga_clk - > fixed_div = fixed_div ;
rc = of_property_read_u32_array ( node , " div-reg " , div_reg , 3 ) ;
if ( ! rc ) {
socfpga_clk - > div_reg = clk_mgr_base_addr + div_reg [ 0 ] ;
socfpga_clk - > shift = div_reg [ 1 ] ;
socfpga_clk - > width = div_reg [ 2 ] ;
} else {
2015-05-01 12:39:52 -07:00
socfpga_clk - > div_reg = NULL ;
2014-01-06 10:27:37 -06:00
}
of_property_read_string ( node , " clock-output-names " , & clk_name ) ;
init . name = clk_name ;
init . ops = ops ;
init . flags = 0 ;
2015-06-05 11:26:14 -05:00
init . num_parents = of_clk_parent_fill ( node , parent_name , SOCFPGA_MAX_PARENTS ) ;
2018-12-04 11:14:02 -08:00
if ( init . num_parents < 2 ) {
ops - > get_parent = NULL ;
ops - > set_parent = NULL ;
}
2014-01-06 10:27:37 -06:00
init . parent_names = parent_name ;
socfpga_clk - > hw . hw . init = & init ;
2021-03-02 15:41:49 -06:00
hw_clk = & socfpga_clk - > hw . hw ;
err = clk_hw_register ( NULL , hw_clk ) ;
if ( err ) {
2022-11-23 11:16:22 +08:00
kfree ( ops ) ;
2014-01-06 10:27:37 -06:00
kfree ( socfpga_clk ) ;
return ;
}
2021-03-02 15:41:49 -06:00
rc = of_clk_add_provider ( node , of_clk_src_simple_get , hw_clk ) ;
2014-01-06 10:27:37 -06:00
if ( WARN_ON ( rc ) )
return ;
}