2014-09-28 06:28:53 +04:00
/*
* Regulator driver for PWM Regulators
*
* Copyright ( C ) 2014 - STMicroelectronics Inc .
*
* Author : Lee Jones < lee . jones @ linaro . org >
*
* 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 .
*/
# include <linux/module.h>
# include <linux/init.h>
# include <linux/err.h>
# include <linux/regulator/driver.h>
# include <linux/regulator/machine.h>
# include <linux/regulator/of_regulator.h>
# include <linux/of.h>
# include <linux/of_device.h>
# include <linux/pwm.h>
2016-06-23 10:39:44 +03:00
# include <linux/gpio/consumer.h>
2014-09-28 06:28:53 +04:00
2016-06-14 12:13:21 +03:00
struct pwm_continuous_reg_data {
unsigned int min_uV_dutycycle ;
unsigned int max_uV_dutycycle ;
unsigned int dutycycle_unit ;
} ;
2014-09-28 06:28:53 +04:00
struct pwm_regulator_data {
2015-07-07 18:06:51 +03:00
/* Shared */
2014-09-28 06:28:53 +04:00
struct pwm_device * pwm ;
2015-07-07 18:06:51 +03:00
/* Voltage table */
struct pwm_voltages * duty_cycle_table ;
2016-03-08 13:53:22 +03:00
2016-06-14 12:13:21 +03:00
/* Continuous mode info */
struct pwm_continuous_reg_data continuous ;
2016-03-08 13:53:22 +03:00
/* regulator descriptor */
struct regulator_desc desc ;
/* Regulator ops */
struct regulator_ops ops ;
2014-09-28 06:28:53 +04:00
int state ;
2015-07-07 18:06:51 +03:00
2016-06-23 10:39:44 +03:00
/* Enable GPIO */
struct gpio_desc * enb_gpio ;
2014-09-28 06:28:53 +04:00
} ;
struct pwm_voltages {
unsigned int uV ;
unsigned int dutycycle ;
} ;
2015-07-07 18:06:51 +03:00
/**
* Voltage table call - backs
*/
2016-06-14 12:13:19 +03:00
static void pwm_regulator_init_state ( struct regulator_dev * rdev )
{
struct pwm_regulator_data * drvdata = rdev_get_drvdata ( rdev ) ;
struct pwm_state pwm_state ;
unsigned int dutycycle ;
int i ;
pwm_get_state ( drvdata - > pwm , & pwm_state ) ;
dutycycle = pwm_get_relative_duty_cycle ( & pwm_state , 100 ) ;
for ( i = 0 ; i < rdev - > desc - > n_voltages ; i + + ) {
if ( dutycycle = = drvdata - > duty_cycle_table [ i ] . dutycycle ) {
drvdata - > state = i ;
return ;
}
}
}
2015-06-05 21:42:47 +03:00
static int pwm_regulator_get_voltage_sel ( struct regulator_dev * rdev )
2014-09-28 06:28:53 +04:00
{
2015-06-05 21:42:47 +03:00
struct pwm_regulator_data * drvdata = rdev_get_drvdata ( rdev ) ;
2014-09-28 06:28:53 +04:00
2016-06-14 12:13:19 +03:00
if ( drvdata - > state < 0 )
pwm_regulator_init_state ( rdev ) ;
2014-09-28 06:28:53 +04:00
return drvdata - > state ;
}
2015-06-05 21:42:47 +03:00
static int pwm_regulator_set_voltage_sel ( struct regulator_dev * rdev ,
2014-09-28 06:28:53 +04:00
unsigned selector )
{
2015-06-05 21:42:47 +03:00
struct pwm_regulator_data * drvdata = rdev_get_drvdata ( rdev ) ;
2016-06-14 12:13:18 +03:00
struct pwm_state pstate ;
2014-09-28 06:28:53 +04:00
int ret ;
2016-06-14 12:13:18 +03:00
pwm_init_state ( drvdata - > pwm , & pstate ) ;
pwm_set_relative_duty_cycle ( & pstate ,
drvdata - > duty_cycle_table [ selector ] . dutycycle , 100 ) ;
2014-09-28 06:28:53 +04:00
2016-06-14 12:13:18 +03:00
ret = pwm_apply_state ( drvdata - > pwm , & pstate ) ;
2014-09-28 06:28:53 +04:00
if ( ret ) {
2016-03-14 15:50:13 +03:00
dev_err ( & rdev - > dev , " Failed to configure PWM: %d \n " , ret ) ;
2014-09-28 06:28:53 +04:00
return ret ;
}
drvdata - > state = selector ;
return 0 ;
}
2015-06-05 21:42:47 +03:00
static int pwm_regulator_list_voltage ( struct regulator_dev * rdev ,
2014-09-28 06:28:53 +04:00
unsigned selector )
{
2015-06-05 21:42:47 +03:00
struct pwm_regulator_data * drvdata = rdev_get_drvdata ( rdev ) ;
2014-09-28 06:28:53 +04:00
2015-06-05 21:42:47 +03:00
if ( selector > = rdev - > desc - > n_voltages )
2014-09-28 06:28:53 +04:00
return - EINVAL ;
return drvdata - > duty_cycle_table [ selector ] . uV ;
}
2015-07-07 18:06:51 +03:00
2015-09-21 12:33:28 +03:00
static int pwm_regulator_enable ( struct regulator_dev * dev )
{
struct pwm_regulator_data * drvdata = rdev_get_drvdata ( dev ) ;
2016-06-23 10:39:44 +03:00
if ( drvdata - > enb_gpio )
gpiod_set_value_cansleep ( drvdata - > enb_gpio , 1 ) ;
2015-09-21 12:33:28 +03:00
return pwm_enable ( drvdata - > pwm ) ;
}
static int pwm_regulator_disable ( struct regulator_dev * dev )
{
struct pwm_regulator_data * drvdata = rdev_get_drvdata ( dev ) ;
pwm_disable ( drvdata - > pwm ) ;
2016-06-23 10:39:44 +03:00
if ( drvdata - > enb_gpio )
gpiod_set_value_cansleep ( drvdata - > enb_gpio , 0 ) ;
2015-09-21 12:33:28 +03:00
return 0 ;
}
static int pwm_regulator_is_enabled ( struct regulator_dev * dev )
{
struct pwm_regulator_data * drvdata = rdev_get_drvdata ( dev ) ;
2016-06-23 10:39:44 +03:00
if ( drvdata - > enb_gpio & & ! gpiod_get_value_cansleep ( drvdata - > enb_gpio ) )
return false ;
2015-09-21 12:33:28 +03:00
return pwm_is_enabled ( drvdata - > pwm ) ;
}
2015-07-07 18:06:51 +03:00
static int pwm_regulator_get_voltage ( struct regulator_dev * rdev )
{
struct pwm_regulator_data * drvdata = rdev_get_drvdata ( rdev ) ;
2016-06-14 12:13:21 +03:00
unsigned int min_uV_duty = drvdata - > continuous . min_uV_dutycycle ;
unsigned int max_uV_duty = drvdata - > continuous . max_uV_dutycycle ;
unsigned int duty_unit = drvdata - > continuous . dutycycle_unit ;
2016-06-14 12:13:20 +03:00
int min_uV = rdev - > constraints - > min_uV ;
2016-06-14 12:13:21 +03:00
int max_uV = rdev - > constraints - > max_uV ;
int diff_uV = max_uV - min_uV ;
2016-06-14 12:13:20 +03:00
struct pwm_state pstate ;
2016-06-14 12:13:21 +03:00
unsigned int diff_duty ;
unsigned int voltage ;
2015-07-07 18:06:51 +03:00
2016-06-14 12:13:20 +03:00
pwm_get_state ( drvdata - > pwm , & pstate ) ;
2016-06-14 12:13:21 +03:00
voltage = pwm_get_relative_duty_cycle ( & pstate , duty_unit ) ;
/*
* The dutycycle for min_uV might be greater than the one for max_uV .
* This is happening when the user needs an inversed polarity , but the
* PWM device does not support inversing it in hardware .
*/
if ( max_uV_duty < min_uV_duty ) {
voltage = min_uV_duty - voltage ;
diff_duty = min_uV_duty - max_uV_duty ;
} else {
voltage = voltage - min_uV_duty ;
diff_duty = max_uV_duty - min_uV_duty ;
}
voltage = DIV_ROUND_CLOSEST_ULL ( ( u64 ) voltage * diff_uV , diff_duty ) ;
return voltage + min_uV ;
2015-07-07 18:06:51 +03:00
}
static int pwm_regulator_set_voltage ( struct regulator_dev * rdev ,
2016-06-14 12:13:21 +03:00
int req_min_uV , int req_max_uV ,
unsigned int * selector )
2015-07-07 18:06:51 +03:00
{
struct pwm_regulator_data * drvdata = rdev_get_drvdata ( rdev ) ;
2016-06-14 12:13:21 +03:00
unsigned int min_uV_duty = drvdata - > continuous . min_uV_dutycycle ;
unsigned int max_uV_duty = drvdata - > continuous . max_uV_dutycycle ;
unsigned int duty_unit = drvdata - > continuous . dutycycle_unit ;
int min_uV = rdev - > constraints - > min_uV ;
int max_uV = rdev - > constraints - > max_uV ;
int diff_uV = max_uV - min_uV ;
2016-06-14 12:13:18 +03:00
struct pwm_state pstate ;
2016-06-14 12:13:21 +03:00
unsigned int diff_duty ;
unsigned int dutycycle ;
2015-07-07 18:06:51 +03:00
int ret ;
2016-06-14 12:13:18 +03:00
pwm_init_state ( drvdata - > pwm , & pstate ) ;
2016-04-05 12:39:48 +03:00
2016-06-14 12:13:21 +03:00
/*
* The dutycycle for min_uV might be greater than the one for max_uV .
* This is happening when the user needs an inversed polarity , but the
* PWM device does not support inversing it in hardware .
*/
if ( max_uV_duty < min_uV_duty )
diff_duty = min_uV_duty - max_uV_duty ;
else
diff_duty = max_uV_duty - min_uV_duty ;
dutycycle = DIV_ROUND_CLOSEST_ULL ( ( u64 ) ( req_min_uV - min_uV ) *
diff_duty ,
diff_uV ) ;
if ( max_uV_duty < min_uV_duty )
dutycycle = min_uV_duty - dutycycle ;
else
dutycycle = min_uV_duty + dutycycle ;
pwm_set_relative_duty_cycle ( & pstate , dutycycle , duty_unit ) ;
2015-07-07 18:06:51 +03:00
2016-06-14 12:13:18 +03:00
ret = pwm_apply_state ( drvdata - > pwm , & pstate ) ;
2015-07-07 18:06:51 +03:00
if ( ret ) {
2016-03-14 15:50:13 +03:00
dev_err ( & rdev - > dev , " Failed to configure PWM: %d \n " , ret ) ;
2015-07-07 18:06:51 +03:00
return ret ;
}
return 0 ;
}
2015-07-06 11:58:29 +03:00
static struct regulator_ops pwm_regulator_voltage_table_ops = {
2014-09-28 06:28:53 +04:00
. set_voltage_sel = pwm_regulator_set_voltage_sel ,
. get_voltage_sel = pwm_regulator_get_voltage_sel ,
. list_voltage = pwm_regulator_list_voltage ,
. map_voltage = regulator_map_voltage_iterate ,
2015-09-21 12:33:28 +03:00
. enable = pwm_regulator_enable ,
. disable = pwm_regulator_disable ,
. is_enabled = pwm_regulator_is_enabled ,
2014-09-28 06:28:53 +04:00
} ;
2015-07-07 18:06:51 +03:00
static struct regulator_ops pwm_regulator_voltage_continuous_ops = {
. get_voltage = pwm_regulator_get_voltage ,
. set_voltage = pwm_regulator_set_voltage ,
2015-09-21 12:33:28 +03:00
. enable = pwm_regulator_enable ,
. disable = pwm_regulator_disable ,
. is_enabled = pwm_regulator_is_enabled ,
2015-07-07 18:06:51 +03:00
} ;
2015-06-05 21:42:45 +03:00
static struct regulator_desc pwm_regulator_desc = {
2014-09-28 06:28:53 +04:00
. name = " pwm-regulator " ,
. type = REGULATOR_VOLTAGE ,
. owner = THIS_MODULE ,
. supply_name = " pwm " ,
} ;
2015-07-06 11:58:29 +03:00
static int pwm_regulator_init_table ( struct platform_device * pdev ,
struct pwm_regulator_data * drvdata )
{
struct device_node * np = pdev - > dev . of_node ;
struct pwm_voltages * duty_cycle_table ;
2015-07-10 10:45:36 +03:00
unsigned int length = 0 ;
2015-07-06 11:58:29 +03:00
int ret ;
of_find_property ( np , " voltage-table " , & length ) ;
if ( ( length < sizeof ( * duty_cycle_table ) ) | |
( length % sizeof ( * duty_cycle_table ) ) ) {
2016-03-14 15:50:13 +03:00
dev_err ( & pdev - > dev , " voltage-table length(%d) is invalid \n " ,
2015-07-06 11:58:29 +03:00
length ) ;
return - EINVAL ;
}
duty_cycle_table = devm_kzalloc ( & pdev - > dev , length , GFP_KERNEL ) ;
if ( ! duty_cycle_table )
return - ENOMEM ;
ret = of_property_read_u32_array ( np , " voltage-table " ,
( u32 * ) duty_cycle_table ,
length / sizeof ( u32 ) ) ;
if ( ret ) {
2016-03-14 15:50:13 +03:00
dev_err ( & pdev - > dev , " Failed to read voltage-table: %d \n " , ret ) ;
2015-07-06 11:58:29 +03:00
return ret ;
}
2016-06-14 12:13:19 +03:00
drvdata - > state = - EINVAL ;
2015-07-06 11:58:29 +03:00
drvdata - > duty_cycle_table = duty_cycle_table ;
2016-03-08 13:53:22 +03:00
memcpy ( & drvdata - > ops , & pwm_regulator_voltage_table_ops ,
sizeof ( drvdata - > ops ) ) ;
drvdata - > desc . ops = & drvdata - > ops ;
drvdata - > desc . n_voltages = length / sizeof ( * duty_cycle_table ) ;
2015-07-06 11:58:29 +03:00
return 0 ;
}
2015-07-07 18:06:51 +03:00
static int pwm_regulator_init_continuous ( struct platform_device * pdev ,
struct pwm_regulator_data * drvdata )
{
2016-06-14 12:13:21 +03:00
u32 dutycycle_range [ 2 ] = { 0 , 100 } ;
u32 dutycycle_unit = 100 ;
2016-03-08 13:53:22 +03:00
memcpy ( & drvdata - > ops , & pwm_regulator_voltage_continuous_ops ,
sizeof ( drvdata - > ops ) ) ;
drvdata - > desc . ops = & drvdata - > ops ;
drvdata - > desc . continuous_voltage_range = true ;
2015-07-07 18:06:51 +03:00
2016-06-14 12:13:21 +03:00
of_property_read_u32_array ( pdev - > dev . of_node ,
" pwm-dutycycle-range " ,
dutycycle_range , 2 ) ;
of_property_read_u32 ( pdev - > dev . of_node , " pwm-dutycycle-unit " ,
& dutycycle_unit ) ;
if ( dutycycle_range [ 0 ] > dutycycle_unit | |
dutycycle_range [ 1 ] > dutycycle_unit )
return - EINVAL ;
drvdata - > continuous . dutycycle_unit = dutycycle_unit ;
drvdata - > continuous . min_uV_dutycycle = dutycycle_range [ 0 ] ;
drvdata - > continuous . max_uV_dutycycle = dutycycle_range [ 1 ] ;
2015-07-07 18:06:51 +03:00
return 0 ;
}
2014-09-28 06:28:53 +04:00
static int pwm_regulator_probe ( struct platform_device * pdev )
{
2015-07-07 18:06:53 +03:00
const struct regulator_init_data * init_data ;
2014-09-28 06:28:53 +04:00
struct pwm_regulator_data * drvdata ;
struct regulator_dev * regulator ;
struct regulator_config config = { } ;
struct device_node * np = pdev - > dev . of_node ;
2016-06-23 10:39:44 +03:00
enum gpiod_flags gpio_flags ;
2015-07-06 11:58:29 +03:00
int ret ;
2014-09-28 06:28:53 +04:00
if ( ! np ) {
dev_err ( & pdev - > dev , " Device Tree node missing \n " ) ;
return - EINVAL ;
}
drvdata = devm_kzalloc ( & pdev - > dev , sizeof ( * drvdata ) , GFP_KERNEL ) ;
if ( ! drvdata )
return - ENOMEM ;
2016-03-08 13:53:22 +03:00
memcpy ( & drvdata - > desc , & pwm_regulator_desc , sizeof ( drvdata - > desc ) ) ;
2015-07-07 18:06:51 +03:00
if ( of_find_property ( np , " voltage-table " , NULL ) )
2015-07-06 11:58:29 +03:00
ret = pwm_regulator_init_table ( pdev , drvdata ) ;
2015-07-07 18:06:51 +03:00
else
ret = pwm_regulator_init_continuous ( pdev , drvdata ) ;
if ( ret )
return ret ;
2014-09-28 06:28:53 +04:00
2015-07-07 18:06:53 +03:00
init_data = of_get_regulator_init_data ( & pdev - > dev , np ,
2016-03-08 13:53:22 +03:00
& drvdata - > desc ) ;
2015-07-07 18:06:53 +03:00
if ( ! init_data )
2014-09-28 06:28:53 +04:00
return - ENOMEM ;
config . of_node = np ;
config . dev = & pdev - > dev ;
config . driver_data = drvdata ;
2015-07-07 18:06:53 +03:00
config . init_data = init_data ;
2014-09-28 06:28:53 +04:00
drvdata - > pwm = devm_pwm_get ( & pdev - > dev , NULL ) ;
if ( IS_ERR ( drvdata - > pwm ) ) {
2016-03-14 15:50:13 +03:00
ret = PTR_ERR ( drvdata - > pwm ) ;
dev_err ( & pdev - > dev , " Failed to get PWM: %d \n " , ret ) ;
return ret ;
2014-09-28 06:28:53 +04:00
}
2016-06-23 10:39:44 +03:00
if ( init_data - > constraints . boot_on | | init_data - > constraints . always_on )
gpio_flags = GPIOD_OUT_HIGH ;
else
gpio_flags = GPIOD_OUT_LOW ;
drvdata - > enb_gpio = devm_gpiod_get_optional ( & pdev - > dev , " enable " ,
gpio_flags ) ;
if ( IS_ERR ( drvdata - > enb_gpio ) ) {
ret = PTR_ERR ( drvdata - > enb_gpio ) ;
dev_err ( & pdev - > dev , " Failed to get enable GPIO: %d \n " , ret ) ;
return ret ;
}
2016-06-14 12:13:17 +03:00
ret = pwm_adjust_config ( drvdata - > pwm ) ;
if ( ret )
return ret ;
2016-04-14 22:17:27 +03:00
2014-09-28 06:28:53 +04:00
regulator = devm_regulator_register ( & pdev - > dev ,
2016-03-08 13:53:22 +03:00
& drvdata - > desc , & config ) ;
2014-09-28 06:28:53 +04:00
if ( IS_ERR ( regulator ) ) {
2016-03-14 15:50:13 +03:00
ret = PTR_ERR ( regulator ) ;
dev_err ( & pdev - > dev , " Failed to register regulator %s: %d \n " ,
drvdata - > desc . name , ret ) ;
return ret ;
2014-09-28 06:28:53 +04:00
}
return 0 ;
}
static const struct of_device_id pwm_of_match [ ] = {
{ . compatible = " pwm-regulator " } ,
{ } ,
} ;
MODULE_DEVICE_TABLE ( of , pwm_of_match ) ;
static struct platform_driver pwm_regulator_driver = {
. driver = {
. name = " pwm-regulator " ,
. of_match_table = of_match_ptr ( pwm_of_match ) ,
} ,
. probe = pwm_regulator_probe ,
} ;
module_platform_driver ( pwm_regulator_driver ) ;
MODULE_LICENSE ( " GPL " ) ;
MODULE_AUTHOR ( " Lee Jones <lee.jones@linaro.org> " ) ;
MODULE_DESCRIPTION ( " PWM Regulator Driver " ) ;
MODULE_ALIAS ( " platform:pwm-regulator " ) ;