2019-05-27 09:55:06 +03:00
// SPDX-License-Identifier: GPL-2.0-or-later
2015-01-20 18:46:31 +03:00
/*
* Copyright 2015 Chen - Yu Tsai
*
* Chen - Yu Tsai < wens @ csie . org >
*/
2015-06-20 01:00:46 +03:00
# include <linux/clk.h>
2015-01-20 18:46:31 +03:00
# include <linux/clk-provider.h>
2017-12-18 06:57:51 +03:00
# include <linux/delay.h>
2016-07-05 00:12:18 +03:00
# include <linux/init.h>
2019-04-19 01:20:22 +03:00
# include <linux/io.h>
2015-01-20 18:46:31 +03:00
# include <linux/of.h>
# include <linux/of_device.h>
# include <linux/reset.h>
# include <linux/platform_device.h>
# include <linux/reset-controller.h>
2015-06-20 01:00:46 +03:00
# include <linux/slab.h>
2015-01-20 18:46:31 +03:00
# include <linux/spinlock.h>
# define SUN9I_MMC_WIDTH 4
# define SUN9I_MMC_GATE_BIT 16
# define SUN9I_MMC_RESET_BIT 18
struct sun9i_mmc_clk_data {
spinlock_t lock ;
void __iomem * membase ;
struct clk * clk ;
struct reset_control * reset ;
struct clk_onecell_data clk_data ;
struct reset_controller_dev rcdev ;
} ;
static int sun9i_mmc_reset_assert ( struct reset_controller_dev * rcdev ,
unsigned long id )
{
struct sun9i_mmc_clk_data * data = container_of ( rcdev ,
struct sun9i_mmc_clk_data ,
rcdev ) ;
unsigned long flags ;
void __iomem * reg = data - > membase + SUN9I_MMC_WIDTH * id ;
u32 val ;
clk_prepare_enable ( data - > clk ) ;
spin_lock_irqsave ( & data - > lock , flags ) ;
val = readl ( reg ) ;
writel ( val & ~ BIT ( SUN9I_MMC_RESET_BIT ) , reg ) ;
spin_unlock_irqrestore ( & data - > lock , flags ) ;
clk_disable_unprepare ( data - > clk ) ;
return 0 ;
}
static int sun9i_mmc_reset_deassert ( struct reset_controller_dev * rcdev ,
unsigned long id )
{
struct sun9i_mmc_clk_data * data = container_of ( rcdev ,
struct sun9i_mmc_clk_data ,
rcdev ) ;
unsigned long flags ;
void __iomem * reg = data - > membase + SUN9I_MMC_WIDTH * id ;
u32 val ;
clk_prepare_enable ( data - > clk ) ;
spin_lock_irqsave ( & data - > lock , flags ) ;
val = readl ( reg ) ;
writel ( val | BIT ( SUN9I_MMC_RESET_BIT ) , reg ) ;
spin_unlock_irqrestore ( & data - > lock , flags ) ;
clk_disable_unprepare ( data - > clk ) ;
return 0 ;
}
2017-12-18 06:57:51 +03:00
static int sun9i_mmc_reset_reset ( struct reset_controller_dev * rcdev ,
unsigned long id )
{
sun9i_mmc_reset_assert ( rcdev , id ) ;
udelay ( 10 ) ;
sun9i_mmc_reset_deassert ( rcdev , id ) ;
return 0 ;
}
2016-02-25 12:45:10 +03:00
static const struct reset_control_ops sun9i_mmc_reset_ops = {
2015-01-20 18:46:31 +03:00
. assert = sun9i_mmc_reset_assert ,
. deassert = sun9i_mmc_reset_deassert ,
2017-12-18 06:57:51 +03:00
. reset = sun9i_mmc_reset_reset ,
2015-01-20 18:46:31 +03:00
} ;
static int sun9i_a80_mmc_config_clk_probe ( struct platform_device * pdev )
{
struct device_node * np = pdev - > dev . of_node ;
struct sun9i_mmc_clk_data * data ;
struct clk_onecell_data * clk_data ;
const char * clk_name = np - > name ;
const char * clk_parent ;
struct resource * r ;
int count , i , ret ;
data = devm_kzalloc ( & pdev - > dev , sizeof ( * data ) , GFP_KERNEL ) ;
if ( ! data )
return - ENOMEM ;
spin_lock_init ( & data - > lock ) ;
r = platform_get_resource ( pdev , IORESOURCE_MEM , 0 ) ;
/* one clock/reset pair per word */
2016-04-11 07:53:03 +03:00
count = DIV_ROUND_UP ( ( resource_size ( r ) ) , SUN9I_MMC_WIDTH ) ;
2015-01-20 18:46:31 +03:00
data - > membase = devm_ioremap_resource ( & pdev - > dev , r ) ;
if ( IS_ERR ( data - > membase ) )
return PTR_ERR ( data - > membase ) ;
clk_data = & data - > clk_data ;
clk_data - > clk_num = count ;
clk_data - > clks = devm_kcalloc ( & pdev - > dev , count , sizeof ( struct clk * ) ,
GFP_KERNEL ) ;
if ( ! clk_data - > clks )
return - ENOMEM ;
data - > clk = devm_clk_get ( & pdev - > dev , NULL ) ;
if ( IS_ERR ( data - > clk ) ) {
dev_err ( & pdev - > dev , " Could not get clock \n " ) ;
return PTR_ERR ( data - > clk ) ;
}
2017-07-19 18:25:13 +03:00
data - > reset = devm_reset_control_get_exclusive ( & pdev - > dev , NULL ) ;
2015-01-20 18:46:31 +03:00
if ( IS_ERR ( data - > reset ) ) {
dev_err ( & pdev - > dev , " Could not get reset control \n " ) ;
return PTR_ERR ( data - > reset ) ;
}
ret = reset_control_deassert ( data - > reset ) ;
if ( ret ) {
dev_err ( & pdev - > dev , " Reset deassert err %d \n " , ret ) ;
return ret ;
}
clk_parent = __clk_get_name ( data - > clk ) ;
for ( i = 0 ; i < count ; i + + ) {
of_property_read_string_index ( np , " clock-output-names " ,
i , & clk_name ) ;
clk_data - > clks [ i ] = clk_register_gate ( & pdev - > dev , clk_name ,
clk_parent , 0 ,
data - > membase + SUN9I_MMC_WIDTH * i ,
SUN9I_MMC_GATE_BIT , 0 ,
& data - > lock ) ;
if ( IS_ERR ( clk_data - > clks [ i ] ) ) {
ret = PTR_ERR ( clk_data - > clks [ i ] ) ;
goto err_clk_register ;
}
}
ret = of_clk_add_provider ( np , of_clk_src_onecell_get , clk_data ) ;
if ( ret )
goto err_clk_provider ;
data - > rcdev . owner = THIS_MODULE ;
data - > rcdev . nr_resets = count ;
data - > rcdev . ops = & sun9i_mmc_reset_ops ;
data - > rcdev . of_node = pdev - > dev . of_node ;
ret = reset_controller_register ( & data - > rcdev ) ;
if ( ret )
goto err_rc_reg ;
platform_set_drvdata ( pdev , data ) ;
return 0 ;
err_rc_reg :
of_clk_del_provider ( np ) ;
err_clk_provider :
for ( i = 0 ; i < count ; i + + )
clk_unregister ( clk_data - > clks [ i ] ) ;
err_clk_register :
reset_control_assert ( data - > reset ) ;
return ret ;
}
static const struct of_device_id sun9i_a80_mmc_config_clk_dt_ids [ ] = {
{ . compatible = " allwinner,sun9i-a80-mmc-config-clk " } ,
{ /* sentinel */ }
} ;
static struct platform_driver sun9i_a80_mmc_config_clk_driver = {
. driver = {
. name = " sun9i-a80-mmc-config-clk " ,
2016-07-05 00:12:18 +03:00
. suppress_bind_attrs = true ,
2015-01-20 18:46:31 +03:00
. of_match_table = sun9i_a80_mmc_config_clk_dt_ids ,
} ,
. probe = sun9i_a80_mmc_config_clk_probe ,
} ;
2016-07-05 00:12:18 +03:00
builtin_platform_driver ( sun9i_a80_mmc_config_clk_driver ) ;