2017-12-05 15:55:59 +01:00
// SPDX-License-Identifier: GPL-2.0
2017-04-10 17:49:50 +02:00
/*
* This file is part of STM32 DAC driver
*
* Copyright ( C ) 2017 , STMicroelectronics - All Rights Reserved
* Author : Fabrice Gasnier < fabrice . gasnier @ st . com > .
*
*/
# include <linux/clk.h>
# include <linux/delay.h>
# include <linux/module.h>
# include <linux/of_platform.h>
2019-10-09 15:48:38 +02:00
# include <linux/pm_runtime.h>
2017-04-10 17:49:50 +02:00
# include <linux/regulator/consumer.h>
# include <linux/reset.h>
# include "stm32-dac-core.h"
/**
* struct stm32_dac_priv - stm32 DAC core private data
* @ pclk : peripheral clock common for all DACs
* @ vref : regulator reference
* @ common : Common data for all DAC instances
*/
struct stm32_dac_priv {
struct clk * pclk ;
struct regulator * vref ;
struct stm32_dac_common common ;
} ;
2017-07-10 15:23:59 +02:00
/**
* struct stm32_dac_cfg - DAC configuration
* @ has_hfsel : DAC has high frequency control
*/
struct stm32_dac_cfg {
bool has_hfsel ;
} ;
2017-04-10 17:49:50 +02:00
static struct stm32_dac_priv * to_stm32_dac_priv ( struct stm32_dac_common * com )
{
return container_of ( com , struct stm32_dac_priv , common ) ;
}
static const struct regmap_config stm32_dac_regmap_cfg = {
. reg_bits = 32 ,
. val_bits = 32 ,
. reg_stride = sizeof ( u32 ) ,
. max_register = 0x3fc ,
} ;
2019-10-09 15:48:38 +02:00
static int stm32_dac_core_hw_start ( struct device * dev )
{
struct stm32_dac_common * common = dev_get_drvdata ( dev ) ;
struct stm32_dac_priv * priv = to_stm32_dac_priv ( common ) ;
int ret ;
ret = regulator_enable ( priv - > vref ) ;
if ( ret < 0 ) {
dev_err ( dev , " vref enable failed: %d \n " , ret ) ;
return ret ;
}
ret = clk_prepare_enable ( priv - > pclk ) ;
if ( ret < 0 ) {
dev_err ( dev , " pclk enable failed: %d \n " , ret ) ;
goto err_regulator_disable ;
}
return 0 ;
err_regulator_disable :
regulator_disable ( priv - > vref ) ;
return ret ;
}
static void stm32_dac_core_hw_stop ( struct device * dev )
{
struct stm32_dac_common * common = dev_get_drvdata ( dev ) ;
struct stm32_dac_priv * priv = to_stm32_dac_priv ( common ) ;
clk_disable_unprepare ( priv - > pclk ) ;
regulator_disable ( priv - > vref ) ;
}
2017-04-10 17:49:50 +02:00
static int stm32_dac_probe ( struct platform_device * pdev )
{
struct device * dev = & pdev - > dev ;
2017-07-10 15:23:59 +02:00
const struct stm32_dac_cfg * cfg ;
2017-04-10 17:49:50 +02:00
struct stm32_dac_priv * priv ;
struct regmap * regmap ;
struct resource * res ;
void __iomem * mmio ;
2020-01-13 14:14:25 +01:00
struct reset_control * rst ;
2017-04-10 17:49:50 +02:00
int ret ;
if ( ! dev - > of_node )
return - ENODEV ;
priv = devm_kzalloc ( dev , sizeof ( * priv ) , GFP_KERNEL ) ;
if ( ! priv )
return - ENOMEM ;
2019-10-09 15:48:38 +02:00
platform_set_drvdata ( pdev , & priv - > common ) ;
2017-07-10 15:23:59 +02:00
cfg = ( const struct stm32_dac_cfg * )
of_match_device ( dev - > driver - > of_match_table , dev ) - > data ;
2017-04-10 17:49:50 +02:00
res = platform_get_resource ( pdev , IORESOURCE_MEM , 0 ) ;
mmio = devm_ioremap_resource ( dev , res ) ;
if ( IS_ERR ( mmio ) )
return PTR_ERR ( mmio ) ;
2019-10-09 15:48:38 +02:00
regmap = devm_regmap_init_mmio_clk ( dev , " pclk " , mmio ,
& stm32_dac_regmap_cfg ) ;
2017-04-10 17:49:50 +02:00
if ( IS_ERR ( regmap ) )
return PTR_ERR ( regmap ) ;
priv - > common . regmap = regmap ;
2019-10-09 15:48:38 +02:00
priv - > pclk = devm_clk_get ( dev , " pclk " ) ;
if ( IS_ERR ( priv - > pclk ) ) {
ret = PTR_ERR ( priv - > pclk ) ;
dev_err ( dev , " pclk get failed \n " ) ;
return ret ;
}
2017-04-10 17:49:50 +02:00
priv - > vref = devm_regulator_get ( dev , " vref " ) ;
if ( IS_ERR ( priv - > vref ) ) {
ret = PTR_ERR ( priv - > vref ) ;
dev_err ( dev , " vref get failed, %d \n " , ret ) ;
return ret ;
}
2019-10-09 15:48:38 +02:00
pm_runtime_get_noresume ( dev ) ;
pm_runtime_set_active ( dev ) ;
pm_runtime_enable ( dev ) ;
ret = stm32_dac_core_hw_start ( dev ) ;
if ( ret )
goto err_pm_stop ;
2017-04-10 17:49:50 +02:00
ret = regulator_get_voltage ( priv - > vref ) ;
if ( ret < 0 ) {
dev_err ( dev , " vref get voltage failed, %d \n " , ret ) ;
2019-10-09 15:48:38 +02:00
goto err_hw_stop ;
2017-04-10 17:49:50 +02:00
}
priv - > common . vref_mv = ret / 1000 ;
dev_dbg ( dev , " vref+=%dmV \n " , priv - > common . vref_mv ) ;
2020-01-13 14:14:26 +01:00
rst = devm_reset_control_get_optional_exclusive ( dev , NULL ) ;
if ( rst ) {
if ( IS_ERR ( rst ) ) {
2020-08-29 08:47:16 +02:00
ret = dev_err_probe ( dev , PTR_ERR ( rst ) , " reset get failed \n " ) ;
2020-01-13 14:14:26 +01:00
goto err_hw_stop ;
}
2020-01-13 14:14:25 +01:00
reset_control_assert ( rst ) ;
2017-04-10 17:49:50 +02:00
udelay ( 2 ) ;
2020-01-13 14:14:25 +01:00
reset_control_deassert ( rst ) ;
2017-04-10 17:49:50 +02:00
}
2017-07-10 15:23:59 +02:00
if ( cfg & & cfg - > has_hfsel ) {
/* When clock speed is higher than 80MHz, set HFSEL */
priv - > common . hfsel = ( clk_get_rate ( priv - > pclk ) > 80000000UL ) ;
ret = regmap_update_bits ( regmap , STM32_DAC_CR ,
STM32H7_DAC_CR_HFSEL ,
priv - > common . hfsel ?
STM32H7_DAC_CR_HFSEL : 0 ) ;
if ( ret )
2019-10-09 15:48:38 +02:00
goto err_hw_stop ;
2017-07-10 15:23:59 +02:00
}
2017-04-10 17:49:50 +02:00
ret = of_platform_populate ( pdev - > dev . of_node , NULL , NULL , dev ) ;
if ( ret < 0 ) {
dev_err ( dev , " failed to populate DT children \n " ) ;
2019-10-09 15:48:38 +02:00
goto err_hw_stop ;
2017-04-10 17:49:50 +02:00
}
2019-10-09 15:48:38 +02:00
pm_runtime_put ( dev ) ;
2017-04-10 17:49:50 +02:00
return 0 ;
2019-10-09 15:48:38 +02:00
err_hw_stop :
stm32_dac_core_hw_stop ( dev ) ;
err_pm_stop :
pm_runtime_disable ( dev ) ;
pm_runtime_set_suspended ( dev ) ;
pm_runtime_put_noidle ( dev ) ;
2017-04-10 17:49:50 +02:00
return ret ;
}
static int stm32_dac_remove ( struct platform_device * pdev )
{
2019-10-09 15:48:38 +02:00
pm_runtime_get_sync ( & pdev - > dev ) ;
of_platform_depopulate ( & pdev - > dev ) ;
stm32_dac_core_hw_stop ( & pdev - > dev ) ;
pm_runtime_disable ( & pdev - > dev ) ;
pm_runtime_set_suspended ( & pdev - > dev ) ;
pm_runtime_put_noidle ( & pdev - > dev ) ;
return 0 ;
}
static int __maybe_unused stm32_dac_core_resume ( struct device * dev )
{
struct stm32_dac_common * common = dev_get_drvdata ( dev ) ;
2017-04-10 17:49:50 +02:00
struct stm32_dac_priv * priv = to_stm32_dac_priv ( common ) ;
2019-10-09 15:48:38 +02:00
int ret ;
2017-04-10 17:49:50 +02:00
2019-10-09 15:48:38 +02:00
if ( priv - > common . hfsel ) {
/* restore hfsel (maybe lost under low power state) */
ret = regmap_update_bits ( priv - > common . regmap , STM32_DAC_CR ,
STM32H7_DAC_CR_HFSEL ,
STM32H7_DAC_CR_HFSEL ) ;
if ( ret )
return ret ;
}
return pm_runtime_force_resume ( dev ) ;
}
static int __maybe_unused stm32_dac_core_runtime_suspend ( struct device * dev )
{
stm32_dac_core_hw_stop ( dev ) ;
2017-04-10 17:49:50 +02:00
return 0 ;
}
2019-10-09 15:48:38 +02:00
static int __maybe_unused stm32_dac_core_runtime_resume ( struct device * dev )
{
return stm32_dac_core_hw_start ( dev ) ;
}
static const struct dev_pm_ops stm32_dac_core_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS ( pm_runtime_force_suspend , stm32_dac_core_resume )
SET_RUNTIME_PM_OPS ( stm32_dac_core_runtime_suspend ,
stm32_dac_core_runtime_resume ,
NULL )
} ;
2017-07-10 15:23:59 +02:00
static const struct stm32_dac_cfg stm32h7_dac_cfg = {
. has_hfsel = true ,
} ;
2017-04-10 17:49:50 +02:00
static const struct of_device_id stm32_dac_of_match [ ] = {
2017-07-10 15:23:59 +02:00
{
. compatible = " st,stm32f4-dac-core " ,
} , {
. compatible = " st,stm32h7-dac-core " ,
. data = ( void * ) & stm32h7_dac_cfg ,
} ,
2017-04-10 17:49:50 +02:00
{ } ,
} ;
MODULE_DEVICE_TABLE ( of , stm32_dac_of_match ) ;
static struct platform_driver stm32_dac_driver = {
. probe = stm32_dac_probe ,
. remove = stm32_dac_remove ,
. driver = {
. name = " stm32-dac-core " ,
. of_match_table = stm32_dac_of_match ,
2019-10-09 15:48:38 +02:00
. pm = & stm32_dac_core_pm_ops ,
2017-04-10 17:49:50 +02:00
} ,
} ;
module_platform_driver ( stm32_dac_driver ) ;
MODULE_AUTHOR ( " Fabrice Gasnier <fabrice.gasnier@st.com> " ) ;
MODULE_DESCRIPTION ( " STMicroelectronics STM32 DAC core driver " ) ;
MODULE_LICENSE ( " GPL v2 " ) ;
MODULE_ALIAS ( " platform:stm32-dac-core " ) ;