2013-08-06 18:30:48 +04:00
/*
* da9210 - regulator . c - Regulator device driver for DA9210
* Copyright ( C ) 2013 Dialog Semiconductor Ltd .
*
* This library is free software ; you can redistribute it and / or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation ; either
* version 2 of the License , or ( at your option ) any later version .
*
* This library 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
* Library General Public License for more details .
*
* You should have received a copy of the GNU Library General Public
* License along with this library ; if not , write to the
* Free Software Foundation , Inc . , 51 Franklin St , Fifth Floor ,
* Boston , MA 02110 - 1301 , USA .
*/
# include <linux/err.h>
# include <linux/i2c.h>
# include <linux/module.h>
2015-06-24 15:14:21 +03:00
# include <linux/interrupt.h>
# include <linux/irq.h>
2013-08-06 18:30:48 +04:00
# include <linux/regulator/driver.h>
# include <linux/regulator/machine.h>
2016-07-15 12:29:41 +03:00
# include <linux/of_device.h>
2013-09-12 16:28:35 +04:00
# include <linux/regulator/of_regulator.h>
2013-08-06 18:30:48 +04:00
# include <linux/regmap.h>
# include "da9210-regulator.h"
struct da9210 {
struct regulator_dev * rdev ;
struct regmap * regmap ;
} ;
static const struct regmap_config da9210_regmap_config = {
. reg_bits = 8 ,
. val_bits = 8 ,
} ;
static int da9210_set_current_limit ( struct regulator_dev * rdev , int min_uA ,
int max_uA ) ;
static int da9210_get_current_limit ( struct regulator_dev * rdev ) ;
2015-12-19 18:07:09 +03:00
static const struct regulator_ops da9210_buck_ops = {
2013-08-06 18:30:48 +04:00
. enable = regulator_enable_regmap ,
. disable = regulator_disable_regmap ,
. is_enabled = regulator_is_enabled_regmap ,
. set_voltage_sel = regulator_set_voltage_sel_regmap ,
. get_voltage_sel = regulator_get_voltage_sel_regmap ,
. list_voltage = regulator_list_voltage_linear ,
. set_current_limit = da9210_set_current_limit ,
. get_current_limit = da9210_get_current_limit ,
} ;
/* Default limits measured in millivolts and milliamps */
# define DA9210_MIN_MV 300
# define DA9210_MAX_MV 1570
# define DA9210_STEP_MV 10
/* Current limits for buck (uA) indices corresponds with register values */
static const int da9210_buck_limits [ ] = {
1600000 , 1800000 , 2000000 , 2200000 , 2400000 , 2600000 , 2800000 , 3000000 ,
3200000 , 3400000 , 3600000 , 3800000 , 4000000 , 4200000 , 4400000 , 4600000
} ;
static const struct regulator_desc da9210_reg = {
. name = " DA9210 " ,
. id = 0 ,
. ops = & da9210_buck_ops ,
. type = REGULATOR_VOLTAGE ,
. n_voltages = ( ( DA9210_MAX_MV - DA9210_MIN_MV ) / DA9210_STEP_MV ) + 1 ,
. min_uV = ( DA9210_MIN_MV * 1000 ) ,
. uV_step = ( DA9210_STEP_MV * 1000 ) ,
. vsel_reg = DA9210_REG_VBUCK_A ,
. vsel_mask = DA9210_VBUCK_MASK ,
. enable_reg = DA9210_REG_BUCK_CONT ,
. enable_mask = DA9210_BUCK_EN ,
. owner = THIS_MODULE ,
} ;
static int da9210_set_current_limit ( struct regulator_dev * rdev , int min_uA ,
int max_uA )
{
struct da9210 * chip = rdev_get_drvdata ( rdev ) ;
unsigned int sel ;
int i ;
/* search for closest to maximum */
for ( i = ARRAY_SIZE ( da9210_buck_limits ) - 1 ; i > = 0 ; i - - ) {
if ( min_uA < = da9210_buck_limits [ i ] & &
max_uA > = da9210_buck_limits [ i ] ) {
sel = i ;
sel = sel < < DA9210_BUCK_ILIM_SHIFT ;
return regmap_update_bits ( chip - > regmap ,
DA9210_REG_BUCK_ILIM ,
DA9210_BUCK_ILIM_MASK , sel ) ;
}
}
return - EINVAL ;
}
static int da9210_get_current_limit ( struct regulator_dev * rdev )
{
struct da9210 * chip = rdev_get_drvdata ( rdev ) ;
unsigned int data ;
unsigned int sel ;
int ret ;
ret = regmap_read ( chip - > regmap , DA9210_REG_BUCK_ILIM , & data ) ;
if ( ret < 0 )
return ret ;
/* select one of 16 values: 0000 (1600mA) to 1111 (4600mA) */
sel = ( data & DA9210_BUCK_ILIM_MASK ) > > DA9210_BUCK_ILIM_SHIFT ;
return da9210_buck_limits [ sel ] ;
}
2015-06-24 15:14:21 +03:00
static irqreturn_t da9210_irq_handler ( int irq , void * data )
{
struct da9210 * chip = data ;
unsigned int val , handled = 0 ;
int error , ret = IRQ_NONE ;
error = regmap_read ( chip - > regmap , DA9210_REG_EVENT_B , & val ) ;
if ( error < 0 )
goto error_i2c ;
2018-11-19 00:56:17 +03:00
regulator_lock ( chip - > rdev ) ;
2016-01-12 20:28:51 +03:00
2015-06-24 15:14:21 +03:00
if ( val & DA9210_E_OVCURR ) {
regulator_notifier_call_chain ( chip - > rdev ,
REGULATOR_EVENT_OVER_CURRENT ,
NULL ) ;
handled | = DA9210_E_OVCURR ;
}
if ( val & DA9210_E_NPWRGOOD ) {
regulator_notifier_call_chain ( chip - > rdev ,
REGULATOR_EVENT_UNDER_VOLTAGE ,
NULL ) ;
handled | = DA9210_E_NPWRGOOD ;
}
if ( val & ( DA9210_E_TEMP_WARN | DA9210_E_TEMP_CRIT ) ) {
regulator_notifier_call_chain ( chip - > rdev ,
REGULATOR_EVENT_OVER_TEMP , NULL ) ;
handled | = val & ( DA9210_E_TEMP_WARN | DA9210_E_TEMP_CRIT ) ;
}
if ( val & DA9210_E_VMAX ) {
regulator_notifier_call_chain ( chip - > rdev ,
REGULATOR_EVENT_REGULATION_OUT ,
NULL ) ;
handled | = DA9210_E_VMAX ;
}
2016-01-12 20:28:51 +03:00
2018-11-19 00:56:17 +03:00
regulator_unlock ( chip - > rdev ) ;
2016-01-12 20:28:51 +03:00
2015-06-24 15:14:21 +03:00
if ( handled ) {
/* Clear handled events */
error = regmap_write ( chip - > regmap , DA9210_REG_EVENT_B , handled ) ;
if ( error < 0 )
goto error_i2c ;
ret = IRQ_HANDLED ;
}
return ret ;
error_i2c :
dev_err ( regmap_get_device ( chip - > regmap ) , " I2C error : %d \n " , error ) ;
return ret ;
}
2013-08-06 18:30:48 +04:00
/*
* I2C driver interface functions
*/
2016-07-15 12:29:41 +03:00
static const struct of_device_id da9210_dt_ids [ ] = {
{ . compatible = " dlg,da9210 " , } ,
{ }
} ;
MODULE_DEVICE_TABLE ( of , da9210_dt_ids ) ;
2013-08-06 18:30:48 +04:00
static int da9210_i2c_probe ( struct i2c_client * i2c ,
const struct i2c_device_id * id )
{
struct da9210 * chip ;
2013-09-12 16:28:35 +04:00
struct device * dev = & i2c - > dev ;
struct da9210_pdata * pdata = dev_get_platdata ( dev ) ;
2013-08-06 18:30:48 +04:00
struct regulator_dev * rdev = NULL ;
struct regulator_config config = { } ;
int error ;
2016-07-15 12:29:41 +03:00
const struct of_device_id * match ;
if ( i2c - > dev . of_node & & ! pdata ) {
match = of_match_device ( of_match_ptr ( da9210_dt_ids ) ,
& i2c - > dev ) ;
if ( ! match ) {
dev_err ( & i2c - > dev , " Error: No device match found \n " ) ;
return - ENODEV ;
}
}
2013-08-06 18:30:48 +04:00
chip = devm_kzalloc ( & i2c - > dev , sizeof ( struct da9210 ) , GFP_KERNEL ) ;
2014-02-20 12:53:02 +04:00
if ( ! chip )
2013-08-06 18:30:48 +04:00
return - ENOMEM ;
chip - > regmap = devm_regmap_init_i2c ( i2c , & da9210_regmap_config ) ;
if ( IS_ERR ( chip - > regmap ) ) {
error = PTR_ERR ( chip - > regmap ) ;
dev_err ( & i2c - > dev , " Failed to allocate register map: %d \n " ,
error ) ;
return error ;
}
config . dev = & i2c - > dev ;
2013-09-12 16:28:35 +04:00
config . init_data = pdata ? & pdata - > da9210_constraints :
2014-11-10 16:43:53 +03:00
of_get_regulator_init_data ( dev , dev - > of_node , & da9210_reg ) ;
2013-08-06 18:30:48 +04:00
config . driver_data = chip ;
config . regmap = chip - > regmap ;
2013-09-12 16:28:35 +04:00
config . of_node = dev - > of_node ;
2013-08-06 18:30:48 +04:00
2015-02-17 15:11:11 +03:00
/* Mask all interrupt sources to deassert interrupt line */
error = regmap_write ( chip - > regmap , DA9210_REG_MASK_A , ~ 0 ) ;
if ( ! error )
error = regmap_write ( chip - > regmap , DA9210_REG_MASK_B , ~ 0 ) ;
if ( error ) {
dev_err ( & i2c - > dev , " Failed to write to mask reg: %d \n " , error ) ;
return error ;
}
2013-09-30 04:53:57 +04:00
rdev = devm_regulator_register ( & i2c - > dev , & da9210_reg , & config ) ;
2013-08-06 18:30:48 +04:00
if ( IS_ERR ( rdev ) ) {
dev_err ( & i2c - > dev , " Failed to register DA9210 regulator \n " ) ;
return PTR_ERR ( rdev ) ;
}
chip - > rdev = rdev ;
2015-06-24 15:14:21 +03:00
if ( i2c - > irq ) {
error = devm_request_threaded_irq ( & i2c - > dev , i2c - > irq , NULL ,
da9210_irq_handler ,
IRQF_TRIGGER_LOW |
IRQF_ONESHOT | IRQF_SHARED ,
" da9210 " , chip ) ;
if ( error ) {
dev_err ( & i2c - > dev , " Failed to request IRQ%u: %d \n " ,
i2c - > irq , error ) ;
return error ;
}
error = regmap_update_bits ( chip - > regmap , DA9210_REG_MASK_B ,
DA9210_M_OVCURR | DA9210_M_NPWRGOOD |
DA9210_M_TEMP_WARN |
DA9210_M_TEMP_CRIT | DA9210_M_VMAX , 0 ) ;
if ( error < 0 ) {
dev_err ( & i2c - > dev , " Failed to update mask reg: %d \n " ,
error ) ;
return error ;
}
} else {
dev_warn ( & i2c - > dev , " No IRQ configured \n " ) ;
}
2013-08-06 18:30:48 +04:00
i2c_set_clientdata ( i2c , chip ) ;
return 0 ;
}
static const struct i2c_device_id da9210_i2c_id [ ] = {
{ " da9210 " , 0 } ,
{ } ,
} ;
MODULE_DEVICE_TABLE ( i2c , da9210_i2c_id ) ;
static struct i2c_driver da9210_regulator_driver = {
. driver = {
. name = " da9210 " ,
2016-07-15 12:29:41 +03:00
. of_match_table = of_match_ptr ( da9210_dt_ids ) ,
2013-08-06 18:30:48 +04:00
} ,
. probe = da9210_i2c_probe ,
. id_table = da9210_i2c_id ,
} ;
module_i2c_driver ( da9210_regulator_driver ) ;
MODULE_AUTHOR ( " S Twiss <stwiss.opensource@diasemi.com> " ) ;
MODULE_DESCRIPTION ( " Regulator device driver for Dialog DA9210 " ) ;
MODULE_LICENSE ( " GPL v2 " ) ;