2020-06-11 11:23:16 +02:00
// SPDX-License-Identifier: GPL-2.0-or-later
// Copyright 2020 Cerno
# include <linux/clk-provider.h>
# include <linux/module.h>
# include <linux/platform_device.h>
# include <linux/reset-controller.h>
# include <linux/reset/reset-simple.h>
# define DVP_HT_RPI_SW_INIT 0x04
# define DVP_HT_RPI_MISC_CONFIG 0x08
# define NR_CLOCKS 2
# define NR_RESETS 6
struct clk_dvp {
struct clk_hw_onecell_data * data ;
struct reset_simple_data reset ;
} ;
static const struct clk_parent_data clk_dvp_parent = {
. index = 0 ,
} ;
static int clk_dvp_probe ( struct platform_device * pdev )
{
struct clk_hw_onecell_data * data ;
struct resource * res ;
struct clk_dvp * dvp ;
void __iomem * base ;
int ret ;
dvp = devm_kzalloc ( & pdev - > dev , sizeof ( * dvp ) , GFP_KERNEL ) ;
if ( ! dvp )
return - ENOMEM ;
platform_set_drvdata ( pdev , dvp ) ;
dvp - > data = devm_kzalloc ( & pdev - > dev ,
struct_size ( dvp - > data , hws , NR_CLOCKS ) ,
GFP_KERNEL ) ;
if ( ! dvp - > data )
return - ENOMEM ;
data = dvp - > data ;
base = devm_platform_get_and_ioremap_resource ( pdev , 0 , & res ) ;
if ( IS_ERR ( base ) )
return PTR_ERR ( base ) ;
dvp - > reset . rcdev . owner = THIS_MODULE ;
dvp - > reset . rcdev . nr_resets = NR_RESETS ;
dvp - > reset . rcdev . ops = & reset_simple_ops ;
dvp - > reset . rcdev . of_node = pdev - > dev . of_node ;
dvp - > reset . membase = base + DVP_HT_RPI_SW_INIT ;
spin_lock_init ( & dvp - > reset . lock ) ;
ret = devm_reset_controller_register ( & pdev - > dev , & dvp - > reset . rcdev ) ;
if ( ret )
return ret ;
data - > hws [ 0 ] = clk_hw_register_gate_parent_data ( & pdev - > dev ,
" hdmi0-108MHz " ,
& clk_dvp_parent , 0 ,
base + DVP_HT_RPI_MISC_CONFIG , 3 ,
CLK_GATE_SET_TO_DISABLE ,
& dvp - > reset . lock ) ;
if ( IS_ERR ( data - > hws [ 0 ] ) )
return PTR_ERR ( data - > hws [ 0 ] ) ;
data - > hws [ 1 ] = clk_hw_register_gate_parent_data ( & pdev - > dev ,
" hdmi1-108MHz " ,
& clk_dvp_parent , 0 ,
base + DVP_HT_RPI_MISC_CONFIG , 4 ,
CLK_GATE_SET_TO_DISABLE ,
& dvp - > reset . lock ) ;
if ( IS_ERR ( data - > hws [ 1 ] ) ) {
ret = PTR_ERR ( data - > hws [ 1 ] ) ;
goto unregister_clk0 ;
}
data - > num = NR_CLOCKS ;
ret = of_clk_add_hw_provider ( pdev - > dev . of_node , of_clk_hw_onecell_get ,
data ) ;
if ( ret )
goto unregister_clk1 ;
return 0 ;
unregister_clk1 :
clk_hw_unregister_gate ( data - > hws [ 1 ] ) ;
unregister_clk0 :
clk_hw_unregister_gate ( data - > hws [ 0 ] ) ;
return ret ;
} ;
static int clk_dvp_remove ( struct platform_device * pdev )
{
struct clk_dvp * dvp = platform_get_drvdata ( pdev ) ;
struct clk_hw_onecell_data * data = dvp - > data ;
clk_hw_unregister_gate ( data - > hws [ 1 ] ) ;
clk_hw_unregister_gate ( data - > hws [ 0 ] ) ;
return 0 ;
}
static const struct of_device_id clk_dvp_dt_ids [ ] = {
{ . compatible = " brcm,brcm2711-dvp " , } ,
{ /* sentinel */ }
} ;
static struct platform_driver clk_dvp_driver = {
. probe = clk_dvp_probe ,
. remove = clk_dvp_remove ,
. driver = {
. name = " brcm2711-dvp " ,
. of_match_table = clk_dvp_dt_ids ,
} ,
} ;
module_platform_driver ( clk_dvp_driver ) ;
2020-06-26 13:25:13 +02:00
MODULE_AUTHOR ( " Maxime Ripard <maxime@cerno.tech> " ) ;
MODULE_DESCRIPTION ( " BCM2711 DVP clock driver " ) ;
MODULE_LICENSE ( " GPL " ) ;