2014-10-06 17:48:43 +04:00
/*
* Copyright ( C ) 2014 Free Electrons
* Copyright ( C ) 2014 Atmel
*
* Author : Boris BREZILLON < boris . brezillon @ free - electrons . com >
*
* This program is free software ; you can redistribute it and / or modify it
* under the terms of the GNU General Public License version 2 as published by
* the Free Software Foundation .
*
* 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 .
*
* You should have received a copy of the GNU General Public License along with
* this program . If not , see < http : //www.gnu.org/licenses/>.
*/
# include <linux/clk.h>
2015-07-07 20:16:43 +03:00
# include <linux/iopoll.h>
2014-10-06 17:48:43 +04:00
# include <linux/mfd/atmel-hlcdc.h>
# include <linux/mfd/core.h>
# include <linux/module.h>
# include <linux/platform_device.h>
# include <linux/regmap.h>
# define ATMEL_HLCDC_REG_MAX (0x4000 - 0x4)
2015-07-07 20:16:43 +03:00
struct atmel_hlcdc_regmap {
void __iomem * regs ;
} ;
2014-10-06 17:48:43 +04:00
static const struct mfd_cell atmel_hlcdc_cells [ ] = {
{
. name = " atmel-hlcdc-pwm " ,
. of_compatible = " atmel,hlcdc-pwm " ,
} ,
{
. name = " atmel-hlcdc-dc " ,
. of_compatible = " atmel,hlcdc-display-controller " ,
} ,
} ;
2015-07-07 20:16:43 +03:00
static int regmap_atmel_hlcdc_reg_write ( void * context , unsigned int reg ,
unsigned int val )
{
struct atmel_hlcdc_regmap * hregmap = context ;
if ( reg < = ATMEL_HLCDC_DIS ) {
u32 status ;
2016-09-06 15:19:29 +03:00
readl_poll_timeout_atomic ( hregmap - > regs + ATMEL_HLCDC_SR ,
status , ! ( status & ATMEL_HLCDC_SIP ) ,
1 , 100 ) ;
2015-07-07 20:16:43 +03:00
}
writel ( val , hregmap - > regs + reg ) ;
return 0 ;
}
static int regmap_atmel_hlcdc_reg_read ( void * context , unsigned int reg ,
unsigned int * val )
{
struct atmel_hlcdc_regmap * hregmap = context ;
* val = readl ( hregmap - > regs + reg ) ;
return 0 ;
}
2014-10-06 17:48:43 +04:00
static const struct regmap_config atmel_hlcdc_regmap_config = {
. reg_bits = 32 ,
. val_bits = 32 ,
. reg_stride = 4 ,
. max_register = ATMEL_HLCDC_REG_MAX ,
2015-07-07 20:16:43 +03:00
. reg_write = regmap_atmel_hlcdc_reg_write ,
. reg_read = regmap_atmel_hlcdc_reg_read ,
. fast_io = true ,
2014-10-06 17:48:43 +04:00
} ;
static int atmel_hlcdc_probe ( struct platform_device * pdev )
{
2015-07-07 20:16:43 +03:00
struct atmel_hlcdc_regmap * hregmap ;
2014-10-06 17:48:43 +04:00
struct device * dev = & pdev - > dev ;
struct atmel_hlcdc * hlcdc ;
struct resource * res ;
2015-07-07 20:16:43 +03:00
hregmap = devm_kzalloc ( dev , sizeof ( * hregmap ) , GFP_KERNEL ) ;
if ( ! hregmap )
return - ENOMEM ;
2014-10-06 17:48:43 +04:00
hlcdc = devm_kzalloc ( dev , sizeof ( * hlcdc ) , GFP_KERNEL ) ;
if ( ! hlcdc )
return - ENOMEM ;
res = platform_get_resource ( pdev , IORESOURCE_MEM , 0 ) ;
2015-07-07 20:16:43 +03:00
hregmap - > regs = devm_ioremap_resource ( dev , res ) ;
if ( IS_ERR ( hregmap - > regs ) )
return PTR_ERR ( hregmap - > regs ) ;
2014-10-06 17:48:43 +04:00
hlcdc - > irq = platform_get_irq ( pdev , 0 ) ;
if ( hlcdc - > irq < 0 )
return hlcdc - > irq ;
hlcdc - > periph_clk = devm_clk_get ( dev , " periph_clk " ) ;
if ( IS_ERR ( hlcdc - > periph_clk ) ) {
dev_err ( dev , " failed to get peripheral clock \n " ) ;
return PTR_ERR ( hlcdc - > periph_clk ) ;
}
hlcdc - > sys_clk = devm_clk_get ( dev , " sys_clk " ) ;
if ( IS_ERR ( hlcdc - > sys_clk ) ) {
dev_err ( dev , " failed to get system clock \n " ) ;
return PTR_ERR ( hlcdc - > sys_clk ) ;
}
hlcdc - > slow_clk = devm_clk_get ( dev , " slow_clk " ) ;
if ( IS_ERR ( hlcdc - > slow_clk ) ) {
dev_err ( dev , " failed to get slow clock \n " ) ;
return PTR_ERR ( hlcdc - > slow_clk ) ;
}
2015-07-07 20:16:43 +03:00
hlcdc - > regmap = devm_regmap_init ( dev , NULL , hregmap ,
& atmel_hlcdc_regmap_config ) ;
2014-10-06 17:48:43 +04:00
if ( IS_ERR ( hlcdc - > regmap ) )
return PTR_ERR ( hlcdc - > regmap ) ;
dev_set_drvdata ( dev , hlcdc ) ;
2016-04-07 21:42:59 +03:00
return devm_mfd_add_devices ( dev , - 1 , atmel_hlcdc_cells ,
ARRAY_SIZE ( atmel_hlcdc_cells ) ,
NULL , 0 , NULL ) ;
2014-10-06 17:48:43 +04:00
}
static const struct of_device_id atmel_hlcdc_match [ ] = {
2015-07-31 19:45:54 +03:00
{ . compatible = " atmel,at91sam9n12-hlcdc " } ,
{ . compatible = " atmel,at91sam9x5-hlcdc " } ,
{ . compatible = " atmel,sama5d2-hlcdc " } ,
2014-10-06 17:48:43 +04:00
{ . compatible = " atmel,sama5d3-hlcdc " } ,
2015-07-31 19:45:54 +03:00
{ . compatible = " atmel,sama5d4-hlcdc " } ,
2014-10-06 17:48:43 +04:00
{ /* sentinel */ } ,
} ;
2015-09-17 21:15:44 +03:00
MODULE_DEVICE_TABLE ( of , atmel_hlcdc_match ) ;
2014-10-06 17:48:43 +04:00
static struct platform_driver atmel_hlcdc_driver = {
. probe = atmel_hlcdc_probe ,
. driver = {
. name = " atmel-hlcdc " ,
. of_match_table = atmel_hlcdc_match ,
} ,
} ;
module_platform_driver ( atmel_hlcdc_driver ) ;
MODULE_ALIAS ( " platform:atmel-hlcdc " ) ;
MODULE_AUTHOR ( " Boris Brezillon <boris.brezillon@free-electrons.com> " ) ;
MODULE_DESCRIPTION ( " Atmel HLCDC driver " ) ;
MODULE_LICENSE ( " GPL v2 " ) ;