2018-08-07 19:17:12 +03:00
// SPDX-License-Identifier: GPL-2.0+
//
// clk-s2mps11.c - Clock driver for S2MPS11.
//
// Copyright (C) 2013,2014 Samsung Electornics
2013-07-07 15:44:20 +04:00
# 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>
2018-08-07 19:17:13 +03:00
# include <dt-bindings/clock/samsung,s2mps11.h>
2013-07-07 15:44:20 +04:00
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
}
2018-10-27 08:47:40 +03:00
static const struct clk_ops s2mps11_clk_ops = {
2013-07-07 15:44:20 +04:00
. 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 ,
} ,
[ S2MPS11_CLK_CP ] = {
. name = " s2mps11_cp " ,
. ops = & s2mps11_clk_ops ,
} ,
[ S2MPS11_CLK_BT ] = {
. name = " s2mps11_bt " ,
. ops = & s2mps11_clk_ops ,
} ,
} ;
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 ) ;
2016-01-20 13:14:24 +03:00
struct s2mps11_clk * s2mps11_clks ;
2016-06-02 02:15:24 +03:00
struct clk_hw_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 ,
2016-01-20 13:14:24 +03:00
sizeof ( * s2mps11_clks ) , GFP_KERNEL ) ;
2013-07-07 15:44:20 +04:00
if ( ! s2mps11_clks )
return - ENOMEM ;
treewide: Use struct_size() for devm_kmalloc() and friends
Replaces open-coded struct size calculations with struct_size() for
devm_*, f2fs_*, and sock_* allocations. Automatically generated (and
manually adjusted) from the following Coccinelle script:
// Direct reference to struct field.
@@
identifier alloc =~ "devm_kmalloc|devm_kzalloc|sock_kmalloc|f2fs_kmalloc|f2fs_kzalloc";
expression HANDLE;
expression GFP;
identifier VAR, ELEMENT;
expression COUNT;
@@
- alloc(HANDLE, sizeof(*VAR) + COUNT * sizeof(*VAR->ELEMENT), GFP)
+ alloc(HANDLE, struct_size(VAR, ELEMENT, COUNT), GFP)
// mr = kzalloc(sizeof(*mr) + m * sizeof(mr->map[0]), GFP_KERNEL);
@@
identifier alloc =~ "devm_kmalloc|devm_kzalloc|sock_kmalloc|f2fs_kmalloc|f2fs_kzalloc";
expression HANDLE;
expression GFP;
identifier VAR, ELEMENT;
expression COUNT;
@@
- alloc(HANDLE, sizeof(*VAR) + COUNT * sizeof(VAR->ELEMENT[0]), GFP)
+ alloc(HANDLE, struct_size(VAR, ELEMENT, COUNT), GFP)
// Same pattern, but can't trivially locate the trailing element name,
// or variable name.
@@
identifier alloc =~ "devm_kmalloc|devm_kzalloc|sock_kmalloc|f2fs_kmalloc|f2fs_kzalloc";
expression HANDLE;
expression GFP;
expression SOMETHING, COUNT, ELEMENT;
@@
- alloc(HANDLE, sizeof(SOMETHING) + COUNT * sizeof(ELEMENT), GFP)
+ alloc(HANDLE, CHECKME_struct_size(&SOMETHING, ELEMENT, COUNT), GFP)
Signed-off-by: Kees Cook <keescook@chromium.org>
2018-05-09 02:08:53 +03:00
clk_data = devm_kzalloc ( & pdev - > dev ,
struct_size ( clk_data , hws , S2MPS11_CLKS_NUM ) ,
2016-06-02 02:15:24 +03:00
GFP_KERNEL ) ;
2016-01-20 13:14:23 +03:00
if ( ! clk_data )
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 ) ;
2016-01-20 13:14:24 +03:00
for ( i = 0 ; i < S2MPS11_CLKS_NUM ; i + + ) {
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 */
2016-01-20 13:14:24 +03:00
s2mps11_clks [ i ] . iodev = iodev ;
s2mps11_clks [ i ] . hw . init = & s2mps11_clks_init [ i ] ;
s2mps11_clks [ i ] . mask = 1 < < i ;
s2mps11_clks [ i ] . reg = s2mps11_reg ;
s2mps11_clks [ i ] . clk = devm_clk_register ( & pdev - > dev ,
& s2mps11_clks [ i ] . hw ) ;
if ( IS_ERR ( s2mps11_clks [ i ] . clk ) ) {
2013-07-07 15:44:20 +04:00
dev_err ( & pdev - > dev , " Fail to register : %s \n " ,
2016-01-20 13:14:24 +03:00
s2mps11_clks_init [ i ] . name ) ;
ret = PTR_ERR ( s2mps11_clks [ i ] . clk ) ;
2013-07-07 15:44:20 +04:00
goto err_reg ;
}
2016-06-02 02:15:24 +03:00
s2mps11_clks [ i ] . lookup = clkdev_hw_create ( & s2mps11_clks [ i ] . hw ,
2016-01-20 13:14:24 +03:00
s2mps11_clks_init [ i ] . name , NULL ) ;
if ( ! s2mps11_clks [ i ] . lookup ) {
2013-07-07 15:44:20 +04:00
ret = - ENOMEM ;
2015-07-16 16:59:43 +03:00
goto err_reg ;
2013-07-07 15:44:20 +04:00
}
2016-06-02 02:15:24 +03:00
clk_data - > hws [ i ] = & s2mps11_clks [ i ] . hw ;
2014-05-21 15:23:01 +04:00
}
2013-07-07 15:44:20 +04:00
2016-06-02 02:15:24 +03:00
clk_data - > num = S2MPS11_CLKS_NUM ;
of_clk_add_hw_provider ( s2mps11_clks - > clk_np , of_clk_hw_onecell_get ,
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 :
2020-12-12 15:28:18 +03:00
of_node_put ( s2mps11_clks [ 0 ] . clk_np ) ;
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 ) ;
2018-08-29 22:20:10 +03:00
# ifdef CONFIG_OF
/*
* Device is instantiated through parent MFD device and device matching is done
* through platform_device_id .
*
* However if device ' s DT node contains proper clock compatible and driver is
* built as a module , then the * module * matching will be done trough DT aliases .
* This requires of_device_id table . In the same time this will not change the
* actual * device * matching so do not add . of_match_table .
*/
2018-10-18 22:13:40 +03:00
static const struct of_device_id s2mps11_dt_match [ ] __used = {
2018-08-29 22:20:10 +03:00
{
. compatible = " samsung,s2mps11-clk " ,
. data = ( void * ) S2MPS11X ,
} , {
. compatible = " samsung,s2mps13-clk " ,
. data = ( void * ) S2MPS13X ,
} , {
. compatible = " samsung,s2mps14-clk " ,
. data = ( void * ) S2MPS14X ,
} , {
. compatible = " samsung,s5m8767-clk " ,
. data = ( void * ) S5M8767X ,
} , {
/* Sentinel */
} ,
} ;
MODULE_DEVICE_TABLE ( of , s2mps11_dt_match ) ;
# endif
2013-07-07 15:44:20 +04:00
static struct platform_driver s2mps11_clk_driver = {
. driver = {
. name = " s2mps11-clk " ,
} ,
. probe = s2mps11_clk_probe ,
. remove = s2mps11_clk_remove ,
. id_table = s2mps11_clk_id ,
} ;
2020-09-21 23:35:57 +03:00
module_platform_driver ( s2mps11_clk_driver ) ;
2013-07-07 15:44:20 +04:00
MODULE_DESCRIPTION ( " S2MPS11 Clock Driver " ) ;
MODULE_AUTHOR ( " Yadwinder Singh Brar <yadi.brar@samsung.com> " ) ;
MODULE_LICENSE ( " GPL " ) ;