2013-07-07 15:44:20 +04:00
/*
* clk - s2mps11 . c - Clock driver for S2MPS11 .
*
2014-05-21 15:23:01 +04:00
* Copyright ( C ) 2013 , 2014 Samsung Electornics
2013-07-07 15:44:20 +04:00
*
* This program is free software ; you can redistribute it and / or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation ; either version 2 of the License , or ( at your
* option ) any later version .
*
* 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/module.h>
# include <linux/err.h>
# include <linux/of.h>
# include <linux/clkdev.h>
# include <linux/regmap.h>
# include <linux/clk-provider.h>
# include <linux/platform_device.h>
# include <linux/mfd/samsung/s2mps11.h>
2014-11-18 11:59:41 +03:00
# include <linux/mfd/samsung/s2mps13.h>
2014-05-21 15:23:01 +04:00
# include <linux/mfd/samsung/s2mps14.h>
2013-12-26 14:18:59 +04:00
# include <linux/mfd/samsung/s5m8767.h>
2013-07-07 15:44:20 +04:00
# include <linux/mfd/samsung/core.h>
# define s2mps11_name(a) (a->hw.init->name)
enum {
S2MPS11_CLK_AP = 0 ,
S2MPS11_CLK_CP ,
S2MPS11_CLK_BT ,
S2MPS11_CLKS_NUM ,
} ;
struct s2mps11_clk {
struct sec_pmic_dev * iodev ;
2014-05-21 15:22:59 +04:00
struct device_node * clk_np ;
2013-07-07 15:44:20 +04:00
struct clk_hw hw ;
struct clk * clk ;
struct clk_lookup * lookup ;
u32 mask ;
2013-12-26 14:18:58 +04:00
unsigned int reg ;
2013-07-07 15:44:20 +04:00
} ;
static struct s2mps11_clk * to_s2mps11_clk ( struct clk_hw * hw )
{
return container_of ( hw , struct s2mps11_clk , hw ) ;
}
static int s2mps11_clk_prepare ( struct clk_hw * hw )
{
struct s2mps11_clk * s2mps11 = to_s2mps11_clk ( hw ) ;
2015-07-22 12:34:53 +03:00
return regmap_update_bits ( s2mps11 - > iodev - > regmap_pmic ,
2013-12-26 14:18:58 +04:00
s2mps11 - > reg ,
2013-07-07 15:44:20 +04:00
s2mps11 - > mask , s2mps11 - > mask ) ;
}
static void s2mps11_clk_unprepare ( struct clk_hw * hw )
{
struct s2mps11_clk * s2mps11 = to_s2mps11_clk ( hw ) ;
2015-07-22 12:34:53 +03:00
regmap_update_bits ( s2mps11 - > iodev - > regmap_pmic , s2mps11 - > reg ,
2013-07-07 15:44:20 +04:00
s2mps11 - > mask , ~ s2mps11 - > mask ) ;
}
2014-07-01 21:13:59 +04:00
static int s2mps11_clk_is_prepared ( struct clk_hw * hw )
2013-07-07 15:44:20 +04:00
{
2014-07-01 21:13:59 +04:00
int ret ;
u32 val ;
2013-07-07 15:44:20 +04:00
struct s2mps11_clk * s2mps11 = to_s2mps11_clk ( hw ) ;
2014-07-01 21:13:59 +04:00
ret = regmap_read ( s2mps11 - > iodev - > regmap_pmic ,
s2mps11 - > reg , & val ) ;
if ( ret < 0 )
return - EINVAL ;
return val & s2mps11 - > mask ;
2013-07-07 15:44:20 +04:00
}
static unsigned long s2mps11_clk_recalc_rate ( struct clk_hw * hw ,
unsigned long parent_rate )
{
2014-07-01 21:13:59 +04:00
return 32768 ;
2013-07-07 15:44:20 +04:00
}
static struct clk_ops s2mps11_clk_ops = {
. prepare = s2mps11_clk_prepare ,
. unprepare = s2mps11_clk_unprepare ,
2014-07-01 21:13:59 +04:00
. is_prepared = s2mps11_clk_is_prepared ,
2013-07-07 15:44:20 +04:00
. recalc_rate = s2mps11_clk_recalc_rate ,
} ;
2016-01-20 13:14:22 +03:00
/* This s2mps11_clks_init tructure is common to s2mps11, s2mps13 and s2mps14 */
2013-07-07 15:44:20 +04:00
static struct clk_init_data s2mps11_clks_init [ S2MPS11_CLKS_NUM ] = {
[ S2MPS11_CLK_AP ] = {
. name = " s2mps11_ap " ,
. ops = & s2mps11_clk_ops ,
. flags = CLK_IS_ROOT ,
} ,
[ S2MPS11_CLK_CP ] = {
. name = " s2mps11_cp " ,
. ops = & s2mps11_clk_ops ,
. flags = CLK_IS_ROOT ,
} ,
[ S2MPS11_CLK_BT ] = {
. name = " s2mps11_bt " ,
. ops = & s2mps11_clk_ops ,
. flags = CLK_IS_ROOT ,
} ,
} ;
2014-05-21 15:23:01 +04:00
static struct device_node * s2mps11_clk_parse_dt ( struct platform_device * pdev ,
struct clk_init_data * clks_init )
2013-07-07 15:44:20 +04:00
{
struct sec_pmic_dev * iodev = dev_get_drvdata ( pdev - > dev . parent ) ;
struct device_node * clk_np ;
int i ;
if ( ! iodev - > dev - > of_node )
2014-03-21 16:18:17 +04:00
return ERR_PTR ( - EINVAL ) ;
2013-07-07 15:44:20 +04:00
2014-03-21 16:39:20 +04:00
clk_np = of_get_child_by_name ( iodev - > dev - > of_node , " clocks " ) ;
2013-07-07 15:44:20 +04:00
if ( ! clk_np ) {
dev_err ( & pdev - > dev , " could not find clock sub-node \n " ) ;
return ERR_PTR ( - EINVAL ) ;
}
2016-01-20 13:14:22 +03:00
for ( i = 0 ; i < S2MPS11_CLKS_NUM ; i + + )
2013-07-07 15:44:20 +04:00
of_property_read_string_index ( clk_np , " clock-output-names " , i ,
2014-05-21 15:23:01 +04:00
& clks_init [ i ] . name ) ;
2013-07-07 15:44:20 +04:00
return clk_np ;
}
static int s2mps11_clk_probe ( struct platform_device * pdev )
{
struct sec_pmic_dev * iodev = dev_get_drvdata ( pdev - > dev . parent ) ;
struct s2mps11_clk * s2mps11_clks , * s2mps11_clk ;
2016-01-20 13:14:23 +03:00
struct clk_onecell_data * clk_data ;
2013-12-26 14:18:58 +04:00
unsigned int s2mps11_reg ;
2013-07-07 15:44:20 +04:00
int i , ret = 0 ;
2016-01-20 13:14:22 +03:00
enum sec_device_type hwid = platform_get_device_id ( pdev ) - > driver_data ;
2013-07-07 15:44:20 +04:00
2015-07-22 12:34:53 +03:00
s2mps11_clks = devm_kcalloc ( & pdev - > dev , S2MPS11_CLKS_NUM ,
sizeof ( * s2mps11_clk ) , GFP_KERNEL ) ;
2013-07-07 15:44:20 +04:00
if ( ! s2mps11_clks )
return - ENOMEM ;
s2mps11_clk = s2mps11_clks ;
2016-01-20 13:14:23 +03:00
clk_data = devm_kzalloc ( & pdev - > dev , sizeof ( * clk_data ) , GFP_KERNEL ) ;
if ( ! clk_data )
return - ENOMEM ;
clk_data - > clks = devm_kcalloc ( & pdev - > dev , S2MPS11_CLKS_NUM ,
2015-07-22 12:34:53 +03:00
sizeof ( struct clk * ) , GFP_KERNEL ) ;
2016-01-20 13:14:23 +03:00
if ( ! clk_data - > clks )
2014-05-21 15:23:00 +04:00
return - ENOMEM ;
2016-01-20 13:14:22 +03:00
switch ( hwid ) {
2013-12-26 14:18:58 +04:00
case S2MPS11X :
s2mps11_reg = S2MPS11_REG_RTC_CTRL ;
2014-05-21 15:23:01 +04:00
break ;
2014-11-18 11:59:41 +03:00
case S2MPS13X :
s2mps11_reg = S2MPS13_REG_RTCCTRL ;
break ;
2014-05-21 15:23:01 +04:00
case S2MPS14X :
s2mps11_reg = S2MPS14_REG_RTCCTRL ;
2013-12-26 14:18:58 +04:00
break ;
2013-12-26 14:18:59 +04:00
case S5M8767X :
s2mps11_reg = S5M8767_REG_CTRL1 ;
break ;
2013-12-26 14:18:58 +04:00
default :
dev_err ( & pdev - > dev , " Invalid device type \n " ) ;
return - EINVAL ;
2014-11-26 02:45:12 +03:00
}
2013-12-26 14:18:58 +04:00
2014-05-21 15:23:01 +04:00
/* Store clocks of_node in first element of s2mps11_clks array */
2016-01-20 13:14:22 +03:00
s2mps11_clks - > clk_np = s2mps11_clk_parse_dt ( pdev , s2mps11_clks_init ) ;
2014-05-21 15:23:01 +04:00
if ( IS_ERR ( s2mps11_clks - > clk_np ) )
return PTR_ERR ( s2mps11_clks - > clk_np ) ;
2013-07-07 15:44:20 +04:00
for ( i = 0 ; i < S2MPS11_CLKS_NUM ; i + + , s2mps11_clk + + ) {
2016-01-20 13:14:22 +03:00
if ( i = = S2MPS11_CLK_CP & & hwid = = S2MPS14X )
2014-05-21 15:23:01 +04:00
continue ; /* Skip clocks not present in some devices */
2013-07-07 15:44:20 +04:00
s2mps11_clk - > iodev = iodev ;
2016-01-20 13:14:22 +03:00
s2mps11_clk - > hw . init = & s2mps11_clks_init [ i ] ;
2013-07-07 15:44:20 +04:00
s2mps11_clk - > mask = 1 < < i ;
2013-12-26 14:18:58 +04:00
s2mps11_clk - > reg = s2mps11_reg ;
2013-07-07 15:44:20 +04:00
s2mps11_clk - > clk = devm_clk_register ( & pdev - > dev ,
& s2mps11_clk - > hw ) ;
if ( IS_ERR ( s2mps11_clk - > clk ) ) {
dev_err ( & pdev - > dev , " Fail to register : %s \n " ,
s2mps11_name ( s2mps11_clk ) ) ;
ret = PTR_ERR ( s2mps11_clk - > clk ) ;
goto err_reg ;
}
2015-03-02 18:35:22 +03:00
s2mps11_clk - > lookup = clkdev_create ( s2mps11_clk - > clk ,
2014-06-27 16:21:10 +04:00
s2mps11_name ( s2mps11_clk ) , NULL ) ;
2013-07-07 15:44:20 +04:00
if ( ! s2mps11_clk - > lookup ) {
ret = - ENOMEM ;
2015-07-16 16:59:43 +03:00
goto err_reg ;
2013-07-07 15:44:20 +04:00
}
2016-01-20 13:14:23 +03:00
clk_data - > clks [ i ] = s2mps11_clks [ i ] . clk ;
2014-05-21 15:23:01 +04:00
}
2013-07-07 15:44:20 +04:00
2016-01-20 13:14:23 +03:00
clk_data - > clk_num = S2MPS11_CLKS_NUM ;
2014-05-21 15:23:00 +04:00
of_clk_add_provider ( s2mps11_clks - > clk_np , of_clk_src_onecell_get ,
2016-01-20 13:14:23 +03:00
clk_data ) ;
2013-07-07 15:44:20 +04:00
platform_set_drvdata ( pdev , s2mps11_clks ) ;
return ret ;
2015-07-16 16:59:43 +03:00
2013-07-07 15:44:20 +04:00
err_reg :
2015-07-16 16:59:43 +03:00
while ( - - i > = 0 )
clkdev_drop ( s2mps11_clks [ i ] . lookup ) ;
2013-07-07 15:44:20 +04:00
return ret ;
}
static int s2mps11_clk_remove ( struct platform_device * pdev )
{
struct s2mps11_clk * s2mps11_clks = platform_get_drvdata ( pdev ) ;
int i ;
2014-05-21 15:22:59 +04:00
of_clk_del_provider ( s2mps11_clks [ 0 ] . clk_np ) ;
/* Drop the reference obtained in s2mps11_clk_parse_dt */
of_node_put ( s2mps11_clks [ 0 ] . clk_np ) ;
2014-05-21 15:23:01 +04:00
for ( i = 0 ; i < S2MPS11_CLKS_NUM ; i + + ) {
/* Skip clocks not present on S2MPS14 */
if ( ! s2mps11_clks [ i ] . lookup )
continue ;
2013-07-07 15:44:20 +04:00
clkdev_drop ( s2mps11_clks [ i ] . lookup ) ;
2014-05-21 15:23:01 +04:00
}
2013-07-07 15:44:20 +04:00
return 0 ;
}
static const struct platform_device_id s2mps11_clk_id [ ] = {
2013-12-26 14:18:58 +04:00
{ " s2mps11-clk " , S2MPS11X } ,
2014-11-18 11:59:41 +03:00
{ " s2mps13-clk " , S2MPS13X } ,
2014-05-21 15:23:01 +04:00
{ " s2mps14-clk " , S2MPS14X } ,
2013-12-26 14:18:59 +04:00
{ " s5m8767-clk " , S5M8767X } ,
2013-07-07 15:44:20 +04:00
{ } ,
} ;
MODULE_DEVICE_TABLE ( platform , s2mps11_clk_id ) ;
static struct platform_driver s2mps11_clk_driver = {
. driver = {
. name = " s2mps11-clk " ,
} ,
. probe = s2mps11_clk_probe ,
. remove = s2mps11_clk_remove ,
. id_table = s2mps11_clk_id ,
} ;
static int __init s2mps11_clk_init ( void )
{
return platform_driver_register ( & s2mps11_clk_driver ) ;
}
subsys_initcall ( s2mps11_clk_init ) ;
2015-07-22 12:34:53 +03:00
static void __exit s2mps11_clk_cleanup ( void )
2013-07-07 15:44:20 +04:00
{
platform_driver_unregister ( & s2mps11_clk_driver ) ;
}
module_exit ( s2mps11_clk_cleanup ) ;
MODULE_DESCRIPTION ( " S2MPS11 Clock Driver " ) ;
MODULE_AUTHOR ( " Yadwinder Singh Brar <yadi.brar@samsung.com> " ) ;
MODULE_LICENSE ( " GPL " ) ;