2014-03-21 17:59:37 -07:00
/*
* Copyright ( c ) 2013 - 2014 , The Linux Foundation . All rights reserved .
*
* This software is licensed under the terms of the GNU General Public
* License version 2 , as published by the Free Software Foundation , and
* may be copied , distributed , and modified under those terms .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*/
# include <linux/export.h>
2015-07-24 11:55:42 -07:00
# include <linux/module.h>
2014-03-21 17:59:37 -07:00
# include <linux/regmap.h>
# include <linux/platform_device.h>
# include <linux/clk-provider.h>
# include <linux/reset-controller.h>
2015-10-26 17:11:32 -07:00
# include <linux/of.h>
2014-03-21 17:59:37 -07:00
# include "common.h"
2014-09-04 13:21:50 -07:00
# include "clk-rcg.h"
2014-03-21 17:59:37 -07:00
# include "clk-regmap.h"
# include "reset.h"
2015-08-06 16:07:43 +05:30
# include "gdsc.h"
2014-03-21 17:59:37 -07:00
struct qcom_cc {
struct qcom_reset_controller reset ;
2016-08-16 15:38:27 -07:00
struct clk_regmap * * rclks ;
size_t num_rclks ;
2014-03-21 17:59:37 -07:00
} ;
2014-09-04 13:21:50 -07:00
const
struct freq_tbl * qcom_find_freq ( const struct freq_tbl * f , unsigned long rate )
{
if ( ! f )
return NULL ;
for ( ; f - > freq ; f + + )
if ( rate < = f - > freq )
return f ;
/* Default to our fastest rate */
return f - 1 ;
}
EXPORT_SYMBOL_GPL ( qcom_find_freq ) ;
2015-03-20 18:30:26 +02:00
int qcom_find_src_index ( struct clk_hw * hw , const struct parent_map * map , u8 src )
{
2015-06-25 16:53:23 -07:00
int i , num_parents = clk_hw_get_num_parents ( hw ) ;
2015-03-20 18:30:26 +02:00
for ( i = 0 ; i < num_parents ; i + + )
if ( src = = map [ i ] . src )
return i ;
return - ENOENT ;
}
EXPORT_SYMBOL_GPL ( qcom_find_src_index ) ;
2014-07-15 14:59:21 -07:00
struct regmap *
qcom_cc_map ( struct platform_device * pdev , const struct qcom_cc_desc * desc )
2014-03-21 17:59:37 -07:00
{
void __iomem * base ;
struct resource * res ;
2014-07-15 14:59:21 -07:00
struct device * dev = & pdev - > dev ;
res = platform_get_resource ( pdev , IORESOURCE_MEM , 0 ) ;
base = devm_ioremap_resource ( dev , res ) ;
if ( IS_ERR ( base ) )
return ERR_CAST ( base ) ;
return devm_regmap_init_mmio ( dev , base , desc - > config ) ;
}
EXPORT_SYMBOL_GPL ( qcom_cc_map ) ;
2015-10-07 23:59:57 -07:00
static void qcom_cc_del_clk_provider ( void * data )
{
of_clk_del_provider ( data ) ;
}
static void qcom_cc_reset_unregister ( void * data )
{
reset_controller_unregister ( data ) ;
}
static void qcom_cc_gdsc_unregister ( void * data )
{
gdsc_unregister ( data ) ;
}
2015-10-26 17:11:32 -07:00
/*
* Backwards compatibility with old DTs . Register a pass - through factor 1 / 1
* clock to translate ' path ' clk into ' name ' clk and regsiter the ' path '
* clk as a fixed rate clock if it isn ' t present .
*/
static int _qcom_cc_register_board_clk ( struct device * dev , const char * path ,
const char * name , unsigned long rate ,
bool add_factor )
{
struct device_node * node = NULL ;
struct device_node * clocks_node ;
struct clk_fixed_factor * factor ;
struct clk_fixed_rate * fixed ;
struct clk_init_data init_data = { } ;
2016-08-16 15:38:27 -07:00
int ret ;
2015-10-26 17:11:32 -07:00
clocks_node = of_find_node_by_path ( " /clocks " ) ;
if ( clocks_node )
node = of_find_node_by_name ( clocks_node , path ) ;
of_node_put ( clocks_node ) ;
if ( ! node ) {
fixed = devm_kzalloc ( dev , sizeof ( * fixed ) , GFP_KERNEL ) ;
if ( ! fixed )
return - EINVAL ;
fixed - > fixed_rate = rate ;
fixed - > hw . init = & init_data ;
init_data . name = path ;
init_data . ops = & clk_fixed_rate_ops ;
2016-08-16 15:38:27 -07:00
ret = devm_clk_hw_register ( dev , & fixed - > hw ) ;
if ( ret )
return ret ;
2015-10-26 17:11:32 -07:00
}
of_node_put ( node ) ;
if ( add_factor ) {
factor = devm_kzalloc ( dev , sizeof ( * factor ) , GFP_KERNEL ) ;
if ( ! factor )
return - EINVAL ;
factor - > mult = factor - > div = 1 ;
factor - > hw . init = & init_data ;
init_data . name = name ;
init_data . parent_names = & path ;
init_data . num_parents = 1 ;
init_data . flags = 0 ;
init_data . ops = & clk_fixed_factor_ops ;
2016-08-16 15:38:27 -07:00
ret = devm_clk_hw_register ( dev , & factor - > hw ) ;
if ( ret )
return ret ;
2015-10-26 17:11:32 -07:00
}
return 0 ;
}
int qcom_cc_register_board_clk ( struct device * dev , const char * path ,
const char * name , unsigned long rate )
{
bool add_factor = true ;
2016-11-02 17:56:58 +02:00
/*
* TODO : The RPM clock driver currently does not support the xo clock .
* When xo is added to the RPM clock driver , we should change this
* function to skip registration of xo factor clocks .
*/
2015-10-26 17:11:32 -07:00
return _qcom_cc_register_board_clk ( dev , path , name , rate , add_factor ) ;
}
EXPORT_SYMBOL_GPL ( qcom_cc_register_board_clk ) ;
int qcom_cc_register_sleep_clk ( struct device * dev )
{
return _qcom_cc_register_board_clk ( dev , " sleep_clk " , " sleep_clk_src " ,
32768 , true ) ;
}
EXPORT_SYMBOL_GPL ( qcom_cc_register_sleep_clk ) ;
2016-08-16 15:38:27 -07:00
static struct clk_hw * qcom_cc_clk_hw_get ( struct of_phandle_args * clkspec ,
void * data )
{
struct qcom_cc * cc = data ;
unsigned int idx = clkspec - > args [ 0 ] ;
if ( idx > = cc - > num_rclks ) {
pr_err ( " %s: invalid index %u \n " , __func__ , idx ) ;
return ERR_PTR ( - EINVAL ) ;
}
return cc - > rclks [ idx ] ? & cc - > rclks [ idx ] - > hw : ERR_PTR ( - ENOENT ) ;
}
2014-07-15 14:59:21 -07:00
int qcom_cc_really_probe ( struct platform_device * pdev ,
const struct qcom_cc_desc * desc , struct regmap * regmap )
{
2014-03-21 17:59:37 -07:00
int i , ret ;
struct device * dev = & pdev - > dev ;
struct qcom_reset_controller * reset ;
struct qcom_cc * cc ;
2015-12-01 21:42:11 +05:30
struct gdsc_desc * scd ;
2014-03-21 17:59:37 -07:00
size_t num_clks = desc - > num_clks ;
struct clk_regmap * * rclks = desc - > clks ;
2016-08-16 15:38:27 -07:00
cc = devm_kzalloc ( dev , sizeof ( * cc ) , GFP_KERNEL ) ;
2014-03-21 17:59:37 -07:00
if ( ! cc )
return - ENOMEM ;
2016-08-16 15:38:27 -07:00
cc - > rclks = rclks ;
cc - > num_rclks = num_clks ;
2014-03-21 17:59:37 -07:00
for ( i = 0 ; i < num_clks ; i + + ) {
2016-08-16 15:38:27 -07:00
if ( ! rclks [ i ] )
2014-03-21 17:59:37 -07:00
continue ;
2016-08-16 15:38:27 -07:00
ret = devm_clk_register_regmap ( dev , rclks [ i ] ) ;
if ( ret )
return ret ;
2014-03-21 17:59:37 -07:00
}
2016-08-16 15:38:27 -07:00
ret = of_clk_add_hw_provider ( dev - > of_node , qcom_cc_clk_hw_get , cc ) ;
2014-03-21 17:59:37 -07:00
if ( ret )
return ret ;
2015-12-23 17:57:20 +05:30
ret = devm_add_action_or_reset ( dev , qcom_cc_del_clk_provider ,
pdev - > dev . of_node ) ;
if ( ret )
return ret ;
2015-10-07 23:59:57 -07:00
2014-03-21 17:59:37 -07:00
reset = & cc - > reset ;
reset - > rcdev . of_node = dev - > of_node ;
reset - > rcdev . ops = & qcom_reset_ops ;
reset - > rcdev . owner = dev - > driver - > owner ;
reset - > rcdev . nr_resets = desc - > num_resets ;
reset - > regmap = regmap ;
reset - > reset_map = desc - > resets ;
ret = reset_controller_register ( & reset - > rcdev ) ;
if ( ret )
2015-10-07 23:59:57 -07:00
return ret ;
2015-12-23 17:57:20 +05:30
ret = devm_add_action_or_reset ( dev , qcom_cc_reset_unregister ,
& reset - > rcdev ) ;
if ( ret )
return ret ;
2014-03-21 17:59:37 -07:00
2015-08-06 16:07:43 +05:30
if ( desc - > gdscs & & desc - > num_gdscs ) {
2015-12-01 21:42:11 +05:30
scd = devm_kzalloc ( dev , sizeof ( * scd ) , GFP_KERNEL ) ;
if ( ! scd )
return - ENOMEM ;
scd - > dev = dev ;
scd - > scs = desc - > gdscs ;
scd - > num = desc - > num_gdscs ;
ret = gdsc_register ( scd , & reset - > rcdev , regmap ) ;
if ( ret )
return ret ;
ret = devm_add_action_or_reset ( dev , qcom_cc_gdsc_unregister ,
scd ) ;
2015-08-06 16:07:43 +05:30
if ( ret )
2015-10-07 23:59:57 -07:00
return ret ;
2015-08-06 16:07:43 +05:30
}
2015-12-01 21:42:11 +05:30
return 0 ;
2014-03-21 17:59:37 -07:00
}
2014-07-15 14:59:21 -07:00
EXPORT_SYMBOL_GPL ( qcom_cc_really_probe ) ;
int qcom_cc_probe ( struct platform_device * pdev , const struct qcom_cc_desc * desc )
{
struct regmap * regmap ;
regmap = qcom_cc_map ( pdev , desc ) ;
if ( IS_ERR ( regmap ) )
return PTR_ERR ( regmap ) ;
return qcom_cc_really_probe ( pdev , desc , regmap ) ;
}
2014-03-21 17:59:37 -07:00
EXPORT_SYMBOL_GPL ( qcom_cc_probe ) ;
2015-07-24 11:55:42 -07:00
MODULE_LICENSE ( " GPL v2 " ) ;