2014-03-22 04:59:37 +04: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 21:55:42 +03:00
# include <linux/module.h>
2014-03-22 04:59:37 +04:00
# include <linux/regmap.h>
# include <linux/platform_device.h>
# include <linux/clk-provider.h>
# include <linux/reset-controller.h>
# include "common.h"
2014-09-05 00:21:50 +04:00
# include "clk-rcg.h"
2014-03-22 04:59:37 +04:00
# include "clk-regmap.h"
# include "reset.h"
2015-08-06 13:37:43 +03:00
# include "gdsc.h"
2014-03-22 04:59:37 +04:00
struct qcom_cc {
struct qcom_reset_controller reset ;
struct clk_onecell_data data ;
struct clk * clks [ ] ;
} ;
2014-09-05 00:21:50 +04: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 19:30:26 +03:00
int qcom_find_src_index ( struct clk_hw * hw , const struct parent_map * map , u8 src )
{
2015-06-26 02:53:23 +03:00
int i , num_parents = clk_hw_get_num_parents ( hw ) ;
2015-03-20 19:30:26 +03: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-16 01:59:21 +04:00
struct regmap *
qcom_cc_map ( struct platform_device * pdev , const struct qcom_cc_desc * desc )
2014-03-22 04:59:37 +04:00
{
void __iomem * base ;
struct resource * res ;
2014-07-16 01:59:21 +04: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 ) ;
int qcom_cc_really_probe ( struct platform_device * pdev ,
const struct qcom_cc_desc * desc , struct regmap * regmap )
{
2014-03-22 04:59:37 +04:00
int i , ret ;
struct device * dev = & pdev - > dev ;
struct clk * clk ;
struct clk_onecell_data * data ;
struct clk * * clks ;
struct qcom_reset_controller * reset ;
struct qcom_cc * cc ;
size_t num_clks = desc - > num_clks ;
struct clk_regmap * * rclks = desc - > clks ;
cc = devm_kzalloc ( dev , sizeof ( * cc ) + sizeof ( * clks ) * num_clks ,
GFP_KERNEL ) ;
if ( ! cc )
return - ENOMEM ;
clks = cc - > clks ;
data = & cc - > data ;
data - > clks = clks ;
data - > clk_num = num_clks ;
for ( i = 0 ; i < num_clks ; i + + ) {
2014-05-17 03:07:14 +04:00
if ( ! rclks [ i ] ) {
clks [ i ] = ERR_PTR ( - ENOENT ) ;
2014-03-22 04:59:37 +04:00
continue ;
2014-05-17 03:07:14 +04:00
}
2014-03-22 04:59:37 +04:00
clk = devm_clk_register_regmap ( dev , rclks [ i ] ) ;
if ( IS_ERR ( clk ) )
return PTR_ERR ( clk ) ;
clks [ i ] = clk ;
}
ret = of_clk_add_provider ( dev - > of_node , of_clk_src_onecell_get , data ) ;
if ( ret )
return ret ;
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 ;
platform_set_drvdata ( pdev , & reset - > rcdev ) ;
ret = reset_controller_register ( & reset - > rcdev ) ;
if ( ret )
2015-08-06 13:37:43 +03:00
goto err_reset ;
2014-03-22 04:59:37 +04:00
2015-08-06 13:37:43 +03:00
if ( desc - > gdscs & & desc - > num_gdscs ) {
2015-08-06 13:37:45 +03:00
ret = gdsc_register ( dev , desc - > gdscs , desc - > num_gdscs ,
& reset - > rcdev , regmap ) ;
2015-08-06 13:37:43 +03:00
if ( ret )
goto err_pd ;
}
return 0 ;
err_pd :
reset_controller_unregister ( & reset - > rcdev ) ;
err_reset :
of_clk_del_provider ( dev - > of_node ) ;
2014-03-22 04:59:37 +04:00
return ret ;
}
2014-07-16 01:59:21 +04: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-22 04:59:37 +04:00
EXPORT_SYMBOL_GPL ( qcom_cc_probe ) ;
void qcom_cc_remove ( struct platform_device * pdev )
{
2015-08-06 13:37:43 +03:00
gdsc_unregister ( & pdev - > dev ) ;
2014-03-22 04:59:37 +04:00
of_clk_del_provider ( pdev - > dev . of_node ) ;
reset_controller_unregister ( platform_get_drvdata ( pdev ) ) ;
}
EXPORT_SYMBOL_GPL ( qcom_cc_remove ) ;
2015-07-24 21:55:42 +03:00
MODULE_LICENSE ( " GPL v2 " ) ;