2008-04-30 19:27:12 +04:00
/*
* fixed . c
*
* Copyright 2008 Wolfson Microelectronics PLC .
*
* Author : Mark Brown < broonie @ opensource . wolfsonmicro . com >
*
2009-08-06 20:37:29 +04:00
* Copyright ( c ) 2009 Nokia Corporation
* Roger Quadros < ext - roger . quadros @ nokia . com >
*
2008-04-30 19:27:12 +04:00
* This program is free software ; you can redistribute it and / or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation ; either version 2 of the
* License , or ( at your option ) any later version .
*
* This is useful for systems with mixed controllable and
* non - controllable regulators , as well as for allowing testing on
* systems with no controllable regulators .
*/
# include <linux/err.h>
# include <linux/mutex.h>
# include <linux/platform_device.h>
# include <linux/regulator/driver.h>
# include <linux/regulator/fixed.h>
2009-08-06 20:37:29 +04:00
# include <linux/gpio.h>
2010-01-12 13:25:13 +03:00
# include <linux/delay.h>
2008-04-30 19:27:12 +04:00
struct fixed_voltage_data {
struct regulator_desc desc ;
struct regulator_dev * dev ;
int microvolts ;
2009-08-06 20:37:29 +04:00
int gpio ;
2010-01-12 13:25:13 +03:00
unsigned startup_delay ;
2009-08-06 20:37:29 +04:00
unsigned enable_high : 1 ;
unsigned is_enabled : 1 ;
2008-04-30 19:27:12 +04:00
} ;
static int fixed_voltage_is_enabled ( struct regulator_dev * dev )
{
2009-08-06 20:37:29 +04:00
struct fixed_voltage_data * data = rdev_get_drvdata ( dev ) ;
return data - > is_enabled ;
2008-04-30 19:27:12 +04:00
}
static int fixed_voltage_enable ( struct regulator_dev * dev )
{
2009-08-06 20:37:29 +04:00
struct fixed_voltage_data * data = rdev_get_drvdata ( dev ) ;
if ( gpio_is_valid ( data - > gpio ) ) {
gpio_set_value_cansleep ( data - > gpio , data - > enable_high ) ;
data - > is_enabled = 1 ;
2010-01-12 13:25:13 +03:00
if ( data - > startup_delay )
udelay ( data - > startup_delay ) ;
2009-08-06 20:37:29 +04:00
}
return 0 ;
}
static int fixed_voltage_disable ( struct regulator_dev * dev )
{
struct fixed_voltage_data * data = rdev_get_drvdata ( dev ) ;
if ( gpio_is_valid ( data - > gpio ) ) {
gpio_set_value_cansleep ( data - > gpio , ! data - > enable_high ) ;
data - > is_enabled = 0 ;
}
2008-04-30 19:27:12 +04:00
return 0 ;
}
static int fixed_voltage_get_voltage ( struct regulator_dev * dev )
{
struct fixed_voltage_data * data = rdev_get_drvdata ( dev ) ;
return data - > microvolts ;
}
2009-04-28 14:13:54 +04:00
static int fixed_voltage_list_voltage ( struct regulator_dev * dev ,
unsigned selector )
{
struct fixed_voltage_data * data = rdev_get_drvdata ( dev ) ;
if ( selector ! = 0 )
return - EINVAL ;
return data - > microvolts ;
}
2008-04-30 19:27:12 +04:00
static struct regulator_ops fixed_voltage_ops = {
. is_enabled = fixed_voltage_is_enabled ,
. enable = fixed_voltage_enable ,
2009-08-06 20:37:29 +04:00
. disable = fixed_voltage_disable ,
2008-04-30 19:27:12 +04:00
. get_voltage = fixed_voltage_get_voltage ,
2009-04-28 14:13:54 +04:00
. list_voltage = fixed_voltage_list_voltage ,
2008-04-30 19:27:12 +04:00
} ;
static int regulator_fixed_voltage_probe ( struct platform_device * pdev )
{
struct fixed_voltage_config * config = pdev - > dev . platform_data ;
struct fixed_voltage_data * drvdata ;
int ret ;
drvdata = kzalloc ( sizeof ( struct fixed_voltage_data ) , GFP_KERNEL ) ;
if ( drvdata = = NULL ) {
2009-08-03 21:49:53 +04:00
dev_err ( & pdev - > dev , " Failed to allocate device data \n " ) ;
2008-04-30 19:27:12 +04:00
ret = - ENOMEM ;
goto err ;
}
drvdata - > desc . name = kstrdup ( config - > supply_name , GFP_KERNEL ) ;
if ( drvdata - > desc . name = = NULL ) {
2009-08-03 21:49:53 +04:00
dev_err ( & pdev - > dev , " Failed to allocate supply name \n " ) ;
2008-04-30 19:27:12 +04:00
ret = - ENOMEM ;
goto err ;
}
drvdata - > desc . type = REGULATOR_VOLTAGE ;
drvdata - > desc . owner = THIS_MODULE ;
2009-04-28 14:13:54 +04:00
drvdata - > desc . ops = & fixed_voltage_ops ;
drvdata - > desc . n_voltages = 1 ;
2008-04-30 19:27:12 +04:00
drvdata - > microvolts = config - > microvolts ;
2009-08-06 20:37:29 +04:00
drvdata - > gpio = config - > gpio ;
2010-01-12 13:25:13 +03:00
drvdata - > startup_delay = config - > startup_delay ;
2009-08-06 20:37:29 +04:00
if ( gpio_is_valid ( config - > gpio ) ) {
drvdata - > enable_high = config - > enable_high ;
/* FIXME: Remove below print warning
*
* config - > gpio must be set to - EINVAL by platform code if
* GPIO control is not required . However , early adopters
* not requiring GPIO control may forget to initialize
* config - > gpio to - EINVAL . This will cause GPIO 0 to be used
* for GPIO control .
*
* This warning will be removed once there are a couple of users
* for this driver .
*/
if ( ! config - > gpio )
dev_warn ( & pdev - > dev ,
" using GPIO 0 for regulator enable control \n " ) ;
ret = gpio_request ( config - > gpio , config - > supply_name ) ;
if ( ret ) {
dev_err ( & pdev - > dev ,
" Could not obtain regulator enable GPIO %d: %d \n " ,
config - > gpio , ret ) ;
goto err_name ;
}
/* set output direction without changing state
* to prevent glitch
*/
drvdata - > is_enabled = config - > enabled_at_boot ;
ret = drvdata - > is_enabled ?
config - > enable_high : ! config - > enable_high ;
ret = gpio_direction_output ( config - > gpio , ret ) ;
if ( ret ) {
dev_err ( & pdev - > dev ,
" Could not configure regulator enable GPIO %d direction: %d \n " ,
config - > gpio , ret ) ;
goto err_gpio ;
}
} else {
/* Regulator without GPIO control is considered
* always enabled
*/
drvdata - > is_enabled = 1 ;
}
2008-04-30 19:27:12 +04:00
2009-01-19 16:37:04 +03:00
drvdata - > dev = regulator_register ( & drvdata - > desc , & pdev - > dev ,
config - > init_data , drvdata ) ;
2008-04-30 19:27:12 +04:00
if ( IS_ERR ( drvdata - > dev ) ) {
ret = PTR_ERR ( drvdata - > dev ) ;
2009-08-03 21:49:53 +04:00
dev_err ( & pdev - > dev , " Failed to register regulator: %d \n " , ret ) ;
2009-08-06 20:37:29 +04:00
goto err_gpio ;
2008-04-30 19:27:12 +04:00
}
platform_set_drvdata ( pdev , drvdata ) ;
dev_dbg ( & pdev - > dev , " %s supplying %duV \n " , drvdata - > desc . name ,
drvdata - > microvolts ) ;
return 0 ;
2009-08-06 20:37:29 +04:00
err_gpio :
if ( gpio_is_valid ( config - > gpio ) )
gpio_free ( config - > gpio ) ;
2008-04-30 19:27:12 +04:00
err_name :
kfree ( drvdata - > desc . name ) ;
err :
kfree ( drvdata ) ;
return ret ;
}
static int regulator_fixed_voltage_remove ( struct platform_device * pdev )
{
struct fixed_voltage_data * drvdata = platform_get_drvdata ( pdev ) ;
regulator_unregister ( drvdata - > dev ) ;
2009-08-06 20:37:29 +04:00
if ( gpio_is_valid ( drvdata - > gpio ) )
gpio_free ( drvdata - > gpio ) ;
2009-11-16 12:05:03 +03:00
kfree ( drvdata - > desc . name ) ;
kfree ( drvdata ) ;
2009-08-06 20:37:29 +04:00
2008-04-30 19:27:12 +04:00
return 0 ;
}
static struct platform_driver regulator_fixed_voltage_driver = {
. probe = regulator_fixed_voltage_probe ,
. remove = regulator_fixed_voltage_remove ,
. driver = {
. name = " reg-fixed-voltage " ,
} ,
} ;
static int __init regulator_fixed_voltage_init ( void )
{
return platform_driver_register ( & regulator_fixed_voltage_driver ) ;
}
2009-04-27 21:21:18 +04:00
subsys_initcall ( regulator_fixed_voltage_init ) ;
2008-04-30 19:27:12 +04:00
static void __exit regulator_fixed_voltage_exit ( void )
{
platform_driver_unregister ( & regulator_fixed_voltage_driver ) ;
}
module_exit ( regulator_fixed_voltage_exit ) ;
MODULE_AUTHOR ( " Mark Brown <broonie@opensource.wolfsonmicro.com> " ) ;
MODULE_DESCRIPTION ( " Fixed voltage regulator " ) ;
MODULE_LICENSE ( " GPL " ) ;
2009-04-28 14:13:55 +04:00
MODULE_ALIAS ( " platform:reg-fixed-voltage " ) ;