2019-05-27 09:55:06 +03:00
// SPDX-License-Identifier: GPL-2.0-or-later
2017-11-17 12:27:31 +03:00
/*
* Hisilicon clock driver
*
* Copyright ( c ) 2013 - 2017 Hisilicon Limited .
* Copyright ( c ) 2017 Linaro Limited .
*
* Author : Kai Zhao < zhaokai1 @ hisilicon . com >
* Tao Wang < kevin . wangtao @ hisilicon . com >
* Leo Yan < leo . yan @ linaro . org >
*/
# include <linux/clk-provider.h>
# include <linux/device.h>
# include <linux/err.h>
# include <linux/init.h>
2019-04-19 01:20:22 +03:00
# include <linux/io.h>
2017-11-17 12:27:31 +03:00
# include <linux/mailbox_client.h>
# include <linux/module.h>
# include <linux/of.h>
# include <linux/platform_device.h>
# include <dt-bindings/clock/hi3660-clock.h>
# define HI3660_STUB_CLOCK_DATA (0x70)
# define MHZ (1000 * 1000)
# define DEFINE_CLK_STUB(_id, _cmd, _name) \
{ \
. id = ( _id ) , \
. cmd = ( _cmd ) , \
. hw . init = & ( struct clk_init_data ) { \
. name = # _name , \
. ops = & hi3660_stub_clk_ops , \
. num_parents = 0 , \
. flags = CLK_GET_RATE_NOCACHE , \
} , \
} ,
# define to_stub_clk(_hw) container_of(_hw, struct hi3660_stub_clk, hw)
struct hi3660_stub_clk_chan {
struct mbox_client cl ;
struct mbox_chan * mbox ;
} ;
struct hi3660_stub_clk {
unsigned int id ;
struct clk_hw hw ;
unsigned int cmd ;
unsigned int msg [ 8 ] ;
unsigned int rate ;
} ;
static void __iomem * freq_reg ;
static struct hi3660_stub_clk_chan stub_clk_chan ;
static unsigned long hi3660_stub_clk_recalc_rate ( struct clk_hw * hw ,
unsigned long parent_rate )
{
struct hi3660_stub_clk * stub_clk = to_stub_clk ( hw ) ;
/*
* LPM3 writes back the CPU frequency in shared SRAM so read
* back the frequency .
*/
stub_clk - > rate = readl ( freq_reg + ( stub_clk - > id < < 2 ) ) * MHZ ;
return stub_clk - > rate ;
}
static long hi3660_stub_clk_round_rate ( struct clk_hw * hw , unsigned long rate ,
unsigned long * prate )
{
/*
* LPM3 handles rate rounding so just return whatever
* rate is requested .
*/
return rate ;
}
static int hi3660_stub_clk_set_rate ( struct clk_hw * hw , unsigned long rate ,
unsigned long parent_rate )
{
struct hi3660_stub_clk * stub_clk = to_stub_clk ( hw ) ;
stub_clk - > msg [ 0 ] = stub_clk - > cmd ;
stub_clk - > msg [ 1 ] = rate / MHZ ;
dev_dbg ( stub_clk_chan . cl . dev , " set rate msg[0]=0x%x msg[1]=0x%x \n " ,
stub_clk - > msg [ 0 ] , stub_clk - > msg [ 1 ] ) ;
mbox_send_message ( stub_clk_chan . mbox , stub_clk - > msg ) ;
mbox_client_txdone ( stub_clk_chan . mbox , 0 ) ;
stub_clk - > rate = rate ;
return 0 ;
}
static const struct clk_ops hi3660_stub_clk_ops = {
. recalc_rate = hi3660_stub_clk_recalc_rate ,
. round_rate = hi3660_stub_clk_round_rate ,
. set_rate = hi3660_stub_clk_set_rate ,
} ;
static struct hi3660_stub_clk hi3660_stub_clks [ HI3660_CLK_STUB_NUM ] = {
DEFINE_CLK_STUB ( HI3660_CLK_STUB_CLUSTER0 , 0x0001030A , " cpu-cluster.0 " )
DEFINE_CLK_STUB ( HI3660_CLK_STUB_CLUSTER1 , 0x0002030A , " cpu-cluster.1 " )
DEFINE_CLK_STUB ( HI3660_CLK_STUB_GPU , 0x0003030A , " clk-g3d " )
DEFINE_CLK_STUB ( HI3660_CLK_STUB_DDR , 0x00040309 , " clk-ddrc " )
} ;
static struct clk_hw * hi3660_stub_clk_hw_get ( struct of_phandle_args * clkspec ,
void * data )
{
unsigned int idx = clkspec - > args [ 0 ] ;
if ( idx > = HI3660_CLK_STUB_NUM ) {
pr_err ( " %s: invalid index %u \n " , __func__ , idx ) ;
return ERR_PTR ( - EINVAL ) ;
}
return & hi3660_stub_clks [ idx ] . hw ;
}
static int hi3660_stub_clk_probe ( struct platform_device * pdev )
{
struct device * dev = & pdev - > dev ;
struct resource * res ;
unsigned int i ;
int ret ;
/* Use mailbox client without blocking */
stub_clk_chan . cl . dev = dev ;
stub_clk_chan . cl . tx_done = NULL ;
stub_clk_chan . cl . tx_block = false ;
stub_clk_chan . cl . knows_txdone = false ;
/* Allocate mailbox channel */
stub_clk_chan . mbox = mbox_request_channel ( & stub_clk_chan . cl , 0 ) ;
if ( IS_ERR ( stub_clk_chan . mbox ) )
return PTR_ERR ( stub_clk_chan . mbox ) ;
res = platform_get_resource ( pdev , IORESOURCE_MEM , 0 ) ;
clk: hisilicon: hi3660:Fix potential NULL dereference in hi3660_stub_clk_probe()
platform_get_resource() may return NULL, add proper check to
avoid potential NULL dereferencing.
This is detected by Coccinelle semantic patch.
@@
expression pdev, res, n, t, e, e1, e2;
@@
res = platform_get_resource(pdev, t, n);
+ if (!res)
+ return -EINVAL;
... when != res == NULL
e = devm_ioremap(e1, res->start, e2);
Fixes: 4f16f7ff3bc0 ("clk: hisilicon: Add support for Hi3660 stub clocks")
Signed-off-by: Wei Yongjun <weiyongjun1@huawei.com>
Signed-off-by: Stephen Boyd <sboyd@kernel.org>
2018-01-04 09:36:34 +03:00
if ( ! res )
return - EINVAL ;
2017-11-17 12:27:31 +03:00
freq_reg = devm_ioremap ( dev , res - > start , resource_size ( res ) ) ;
if ( ! freq_reg )
return - ENOMEM ;
freq_reg + = HI3660_STUB_CLOCK_DATA ;
for ( i = 0 ; i < HI3660_CLK_STUB_NUM ; i + + ) {
ret = devm_clk_hw_register ( & pdev - > dev , & hi3660_stub_clks [ i ] . hw ) ;
if ( ret )
return ret ;
}
return devm_of_clk_add_hw_provider ( & pdev - > dev , hi3660_stub_clk_hw_get ,
hi3660_stub_clks ) ;
}
static const struct of_device_id hi3660_stub_clk_of_match [ ] = {
{ . compatible = " hisilicon,hi3660-stub-clk " , } ,
{ }
} ;
static struct platform_driver hi3660_stub_clk_driver = {
. probe = hi3660_stub_clk_probe ,
. driver = {
. name = " hi3660-stub-clk " ,
. of_match_table = hi3660_stub_clk_of_match ,
} ,
} ;
static int __init hi3660_stub_clk_init ( void )
{
return platform_driver_register ( & hi3660_stub_clk_driver ) ;
}
subsys_initcall ( hi3660_stub_clk_init ) ;