2014-09-28 10:28:53 +08: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 .
*/
2015-07-07 16:06:51 +01:00
# include <linux/delay.h>
2014-09-28 10:28:53 +08:00
# 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>
struct pwm_regulator_data {
2015-07-07 16:06:51 +01:00
/* Shared */
2014-09-28 10:28:53 +08:00
struct pwm_device * pwm ;
2015-07-07 16:06:51 +01:00
/* Voltage table */
struct pwm_voltages * duty_cycle_table ;
2014-09-28 10:28:53 +08:00
int state ;
2015-07-07 16:06:51 +01:00
/* Continuous voltage */
int volt_uV ;
2014-09-28 10:28:53 +08:00
} ;
struct pwm_voltages {
unsigned int uV ;
unsigned int dutycycle ;
} ;
2015-07-07 16:06:51 +01:00
/**
* Voltage table call - backs
*/
2015-06-05 19:42:47 +01:00
static int pwm_regulator_get_voltage_sel ( struct regulator_dev * rdev )
2014-09-28 10:28:53 +08:00
{
2015-06-05 19:42:47 +01:00
struct pwm_regulator_data * drvdata = rdev_get_drvdata ( rdev ) ;
2014-09-28 10:28:53 +08:00
return drvdata - > state ;
}
2015-06-05 19:42:47 +01:00
static int pwm_regulator_set_voltage_sel ( struct regulator_dev * rdev ,
2014-09-28 10:28:53 +08:00
unsigned selector )
{
2015-06-05 19:42:47 +01:00
struct pwm_regulator_data * drvdata = rdev_get_drvdata ( rdev ) ;
2014-09-28 10:28:53 +08:00
unsigned int pwm_reg_period ;
int dutycycle ;
int ret ;
pwm_reg_period = pwm_get_period ( drvdata - > pwm ) ;
dutycycle = ( pwm_reg_period *
drvdata - > duty_cycle_table [ selector ] . dutycycle ) / 100 ;
ret = pwm_config ( drvdata - > pwm , dutycycle , pwm_reg_period ) ;
if ( ret ) {
2015-06-05 19:42:47 +01:00
dev_err ( & rdev - > dev , " Failed to configure PWM \n " ) ;
2014-09-28 10:28:53 +08:00
return ret ;
}
drvdata - > state = selector ;
return 0 ;
}
2015-06-05 19:42:47 +01:00
static int pwm_regulator_list_voltage ( struct regulator_dev * rdev ,
2014-09-28 10:28:53 +08:00
unsigned selector )
{
2015-06-05 19:42:47 +01:00
struct pwm_regulator_data * drvdata = rdev_get_drvdata ( rdev ) ;
2014-09-28 10:28:53 +08:00
2015-06-05 19:42:47 +01:00
if ( selector > = rdev - > desc - > n_voltages )
2014-09-28 10:28:53 +08:00
return - EINVAL ;
return drvdata - > duty_cycle_table [ selector ] . uV ;
}
2015-07-07 16:06:51 +01:00
2015-09-21 11:33:28 +02:00
static int pwm_regulator_enable ( struct regulator_dev * dev )
{
struct pwm_regulator_data * drvdata = rdev_get_drvdata ( dev ) ;
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 ) ;
return 0 ;
}
static int pwm_regulator_is_enabled ( struct regulator_dev * dev )
{
struct pwm_regulator_data * drvdata = rdev_get_drvdata ( dev ) ;
return pwm_is_enabled ( drvdata - > pwm ) ;
}
2015-07-07 16:06:51 +01:00
/**
* Continuous voltage call - backs
*/
2015-07-09 16:35:28 +01:00
static int pwm_voltage_to_duty_cycle_percentage ( struct regulator_dev * rdev , int req_uV )
2015-07-07 16:06:51 +01:00
{
2015-07-07 16:06:52 +01:00
int min_uV = rdev - > constraints - > min_uV ;
int max_uV = rdev - > constraints - > max_uV ;
int diff = max_uV - min_uV ;
2015-07-09 16:35:28 +01:00
return 100 - ( ( ( req_uV * 100 ) - ( min_uV * 100 ) ) / diff ) ;
2015-07-07 16:06:51 +01:00
}
static int pwm_regulator_get_voltage ( struct regulator_dev * rdev )
{
struct pwm_regulator_data * drvdata = rdev_get_drvdata ( rdev ) ;
return drvdata - > volt_uV ;
}
static int pwm_regulator_set_voltage ( struct regulator_dev * rdev ,
int min_uV , int max_uV ,
unsigned * selector )
{
struct pwm_regulator_data * drvdata = rdev_get_drvdata ( rdev ) ;
unsigned int ramp_delay = rdev - > constraints - > ramp_delay ;
2015-07-09 16:35:28 +01:00
unsigned int period = pwm_get_period ( drvdata - > pwm ) ;
2015-07-07 16:06:51 +01:00
int duty_cycle ;
int ret ;
2015-07-09 16:35:28 +01:00
duty_cycle = pwm_voltage_to_duty_cycle_percentage ( rdev , min_uV ) ;
2015-07-07 16:06:51 +01:00
2015-07-09 16:35:28 +01:00
ret = pwm_config ( drvdata - > pwm , ( period / 100 ) * duty_cycle , period ) ;
2015-07-07 16:06:51 +01:00
if ( ret ) {
dev_err ( & rdev - > dev , " Failed to configure PWM \n " ) ;
return ret ;
}
ret = pwm_enable ( drvdata - > pwm ) ;
if ( ret ) {
dev_err ( & rdev - > dev , " Failed to enable PWM \n " ) ;
return ret ;
}
drvdata - > volt_uV = min_uV ;
/* Delay required by PWM regulator to settle to the new voltage */
usleep_range ( ramp_delay , ramp_delay + 1000 ) ;
return 0 ;
}
2015-07-06 09:58:29 +01:00
static struct regulator_ops pwm_regulator_voltage_table_ops = {
2014-09-28 10:28:53 +08: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 11:33:28 +02:00
. enable = pwm_regulator_enable ,
. disable = pwm_regulator_disable ,
. is_enabled = pwm_regulator_is_enabled ,
2014-09-28 10:28:53 +08:00
} ;
2015-07-07 16:06:51 +01: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 11:33:28 +02:00
. enable = pwm_regulator_enable ,
. disable = pwm_regulator_disable ,
. is_enabled = pwm_regulator_is_enabled ,
2015-07-07 16:06:51 +01:00
} ;
2015-06-05 19:42:45 +01:00
static struct regulator_desc pwm_regulator_desc = {
2014-09-28 10:28:53 +08:00
. name = " pwm-regulator " ,
. type = REGULATOR_VOLTAGE ,
. owner = THIS_MODULE ,
. supply_name = " pwm " ,
} ;
2015-07-06 09:58:29 +01: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 08:45:36 +01:00
unsigned int length = 0 ;
2015-07-06 09:58:29 +01:00
int ret ;
of_find_property ( np , " voltage-table " , & length ) ;
if ( ( length < sizeof ( * duty_cycle_table ) ) | |
( length % sizeof ( * duty_cycle_table ) ) ) {
dev_err ( & pdev - > dev ,
" voltage-table length(%d) is invalid \n " ,
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 ) {
dev_err ( & pdev - > dev , " Failed to read voltage-table \n " ) ;
return ret ;
}
drvdata - > duty_cycle_table = duty_cycle_table ;
pwm_regulator_desc . ops = & pwm_regulator_voltage_table_ops ;
pwm_regulator_desc . n_voltages = length / sizeof ( * duty_cycle_table ) ;
return 0 ;
}
2015-07-07 16:06:51 +01:00
static int pwm_regulator_init_continuous ( struct platform_device * pdev ,
struct pwm_regulator_data * drvdata )
{
pwm_regulator_desc . ops = & pwm_regulator_voltage_continuous_ops ;
pwm_regulator_desc . continuous_voltage_range = true ;
return 0 ;
}
2014-09-28 10:28:53 +08:00
static int pwm_regulator_probe ( struct platform_device * pdev )
{
2015-07-07 16:06:53 +01:00
const struct regulator_init_data * init_data ;
2014-09-28 10:28:53 +08:00
struct pwm_regulator_data * drvdata ;
struct regulator_dev * regulator ;
struct regulator_config config = { } ;
struct device_node * np = pdev - > dev . of_node ;
2015-07-06 09:58:29 +01:00
int ret ;
2014-09-28 10:28:53 +08: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 ;
2015-07-07 16:06:51 +01:00
if ( of_find_property ( np , " voltage-table " , NULL ) )
2015-07-06 09:58:29 +01:00
ret = pwm_regulator_init_table ( pdev , drvdata ) ;
2015-07-07 16:06:51 +01:00
else
ret = pwm_regulator_init_continuous ( pdev , drvdata ) ;
if ( ret )
return ret ;
2014-09-28 10:28:53 +08:00
2015-07-07 16:06:53 +01:00
init_data = of_get_regulator_init_data ( & pdev - > dev , np ,
& pwm_regulator_desc ) ;
if ( ! init_data )
2014-09-28 10:28:53 +08:00
return - ENOMEM ;
config . of_node = np ;
config . dev = & pdev - > dev ;
config . driver_data = drvdata ;
2015-07-07 16:06:53 +01:00
config . init_data = init_data ;
2014-09-28 10:28:53 +08:00
drvdata - > pwm = devm_pwm_get ( & pdev - > dev , NULL ) ;
if ( IS_ERR ( drvdata - > pwm ) ) {
dev_err ( & pdev - > dev , " Failed to get PWM \n " ) ;
return PTR_ERR ( drvdata - > pwm ) ;
}
regulator = devm_regulator_register ( & pdev - > dev ,
2015-06-05 19:42:45 +01:00
& pwm_regulator_desc , & config ) ;
2014-09-28 10:28:53 +08:00
if ( IS_ERR ( regulator ) ) {
dev_err ( & pdev - > dev , " Failed to register regulator %s \n " ,
2015-06-05 19:42:45 +01:00
pwm_regulator_desc . name ) ;
2014-09-28 10:28:53 +08:00
return PTR_ERR ( regulator ) ;
}
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 " ) ;