2019-05-27 09:55:01 +03:00
// SPDX-License-Identifier: GPL-2.0-or-later
2014-12-22 19:51:06 +03:00
/*
* DA9150 Core MFD Driver
*
* Copyright ( c ) 2014 Dialog Semiconductor
*
* Author : Adam Thomson < Adam . Thomson . Opensource @ diasemi . com >
*/
# include <linux/kernel.h>
# include <linux/module.h>
# include <linux/platform_device.h>
# include <linux/i2c.h>
# include <linux/regmap.h>
# include <linux/slab.h>
# include <linux/irq.h>
# include <linux/interrupt.h>
# include <linux/mfd/core.h>
# include <linux/mfd/da9150/core.h>
# include <linux/mfd/da9150/registers.h>
2015-10-07 16:54:08 +03:00
/* Raw device access, used for QIF */
static int da9150_i2c_read_device ( struct i2c_client * client , u8 addr , int count ,
u8 * buf )
{
struct i2c_msg xfer ;
int ret ;
/*
* Read is split into two transfers as device expects STOP / START rather
* than repeated start to carry out this kind of access .
*/
/* Write address */
xfer . addr = client - > addr ;
xfer . flags = 0 ;
xfer . len = 1 ;
xfer . buf = & addr ;
ret = i2c_transfer ( client - > adapter , & xfer , 1 ) ;
if ( ret ! = 1 ) {
if ( ret < 0 )
return ret ;
else
return - EIO ;
}
/* Read data */
xfer . addr = client - > addr ;
xfer . flags = I2C_M_RD ;
xfer . len = count ;
xfer . buf = buf ;
ret = i2c_transfer ( client - > adapter , & xfer , 1 ) ;
if ( ret = = 1 )
return 0 ;
else if ( ret < 0 )
return ret ;
else
return - EIO ;
}
static int da9150_i2c_write_device ( struct i2c_client * client , u8 addr ,
int count , const u8 * buf )
{
struct i2c_msg xfer ;
u8 * reg_data ;
int ret ;
reg_data = kzalloc ( 1 + count , GFP_KERNEL ) ;
if ( ! reg_data )
return - ENOMEM ;
reg_data [ 0 ] = addr ;
memcpy ( & reg_data [ 1 ] , buf , count ) ;
/* Write address & data */
xfer . addr = client - > addr ;
xfer . flags = 0 ;
xfer . len = 1 + count ;
xfer . buf = reg_data ;
ret = i2c_transfer ( client - > adapter , & xfer , 1 ) ;
kfree ( reg_data ) ;
if ( ret = = 1 )
return 0 ;
else if ( ret < 0 )
return ret ;
else
return - EIO ;
}
2014-12-22 19:51:06 +03:00
static bool da9150_volatile_reg ( struct device * dev , unsigned int reg )
{
switch ( reg ) {
case DA9150_PAGE_CON :
case DA9150_STATUS_A :
case DA9150_STATUS_B :
case DA9150_STATUS_C :
case DA9150_STATUS_D :
case DA9150_STATUS_E :
case DA9150_STATUS_F :
case DA9150_STATUS_G :
case DA9150_STATUS_H :
case DA9150_STATUS_I :
case DA9150_STATUS_J :
case DA9150_STATUS_K :
case DA9150_STATUS_L :
case DA9150_STATUS_N :
case DA9150_FAULT_LOG_A :
case DA9150_FAULT_LOG_B :
case DA9150_EVENT_E :
case DA9150_EVENT_F :
case DA9150_EVENT_G :
case DA9150_EVENT_H :
case DA9150_CONTROL_B :
case DA9150_CONTROL_C :
case DA9150_GPADC_MAN :
case DA9150_GPADC_RES_A :
case DA9150_GPADC_RES_B :
case DA9150_ADETVB_CFG_C :
case DA9150_ADETD_STAT :
case DA9150_ADET_CMPSTAT :
case DA9150_ADET_CTRL_A :
case DA9150_PPR_TCTR_B :
case DA9150_COREBTLD_STAT_A :
case DA9150_CORE_DATA_A :
case DA9150_CORE_DATA_B :
case DA9150_CORE_DATA_C :
case DA9150_CORE_DATA_D :
case DA9150_CORE2WIRE_STAT_A :
case DA9150_FW_CTRL_C :
case DA9150_FG_CTRL_B :
case DA9150_FW_CTRL_B :
case DA9150_GPADC_CMAN :
case DA9150_GPADC_CRES_A :
case DA9150_GPADC_CRES_B :
case DA9150_CC_ICHG_RES_A :
case DA9150_CC_ICHG_RES_B :
case DA9150_CC_IAVG_RES_A :
case DA9150_CC_IAVG_RES_B :
case DA9150_TAUX_CTRL_A :
case DA9150_TAUX_VALUE_H :
case DA9150_TAUX_VALUE_L :
case DA9150_TBAT_RES_A :
case DA9150_TBAT_RES_B :
return true ;
default :
return false ;
}
}
static const struct regmap_range_cfg da9150_range_cfg [ ] = {
{
. range_min = DA9150_PAGE_CON ,
. range_max = DA9150_TBAT_RES_B ,
. selector_reg = DA9150_PAGE_CON ,
. selector_mask = DA9150_I2C_PAGE_MASK ,
. selector_shift = DA9150_I2C_PAGE_SHIFT ,
. window_start = 0 ,
. window_len = 256 ,
} ,
} ;
2015-02-24 12:39:33 +03:00
static const struct regmap_config da9150_regmap_config = {
2014-12-22 19:51:06 +03:00
. reg_bits = 8 ,
. val_bits = 8 ,
. ranges = da9150_range_cfg ,
. num_ranges = ARRAY_SIZE ( da9150_range_cfg ) ,
. max_register = DA9150_TBAT_RES_B ,
. cache_type = REGCACHE_RBTREE ,
. volatile_reg = da9150_volatile_reg ,
} ;
2015-10-07 16:54:08 +03:00
void da9150_read_qif ( struct da9150 * da9150 , u8 addr , int count , u8 * buf )
{
int ret ;
ret = da9150_i2c_read_device ( da9150 - > core_qif , addr , count , buf ) ;
if ( ret < 0 )
dev_err ( da9150 - > dev , " Failed to read from QIF 0x%x: %d \n " ,
addr , ret ) ;
}
EXPORT_SYMBOL_GPL ( da9150_read_qif ) ;
void da9150_write_qif ( struct da9150 * da9150 , u8 addr , int count , const u8 * buf )
{
int ret ;
ret = da9150_i2c_write_device ( da9150 - > core_qif , addr , count , buf ) ;
if ( ret < 0 )
dev_err ( da9150 - > dev , " Failed to write to QIF 0x%x: %d \n " ,
addr , ret ) ;
}
EXPORT_SYMBOL_GPL ( da9150_write_qif ) ;
2014-12-22 19:51:06 +03:00
u8 da9150_reg_read ( struct da9150 * da9150 , u16 reg )
{
int val , ret ;
ret = regmap_read ( da9150 - > regmap , reg , & val ) ;
if ( ret )
dev_err ( da9150 - > dev , " Failed to read from reg 0x%x: %d \n " ,
reg , ret ) ;
return ( u8 ) val ;
}
EXPORT_SYMBOL_GPL ( da9150_reg_read ) ;
void da9150_reg_write ( struct da9150 * da9150 , u16 reg , u8 val )
{
int ret ;
ret = regmap_write ( da9150 - > regmap , reg , val ) ;
if ( ret )
dev_err ( da9150 - > dev , " Failed to write to reg 0x%x: %d \n " ,
reg , ret ) ;
}
EXPORT_SYMBOL_GPL ( da9150_reg_write ) ;
void da9150_set_bits ( struct da9150 * da9150 , u16 reg , u8 mask , u8 val )
{
int ret ;
ret = regmap_update_bits ( da9150 - > regmap , reg , mask , val ) ;
if ( ret )
dev_err ( da9150 - > dev , " Failed to set bits in reg 0x%x: %d \n " ,
reg , ret ) ;
}
EXPORT_SYMBOL_GPL ( da9150_set_bits ) ;
void da9150_bulk_read ( struct da9150 * da9150 , u16 reg , int count , u8 * buf )
{
int ret ;
ret = regmap_bulk_read ( da9150 - > regmap , reg , buf , count ) ;
if ( ret )
dev_err ( da9150 - > dev , " Failed to bulk read from reg 0x%x: %d \n " ,
reg , ret ) ;
}
EXPORT_SYMBOL_GPL ( da9150_bulk_read ) ;
void da9150_bulk_write ( struct da9150 * da9150 , u16 reg , int count , const u8 * buf )
{
int ret ;
ret = regmap_raw_write ( da9150 - > regmap , reg , buf , count ) ;
if ( ret )
dev_err ( da9150 - > dev , " Failed to bulk write to reg 0x%x %d \n " ,
reg , ret ) ;
}
EXPORT_SYMBOL_GPL ( da9150_bulk_write ) ;
2015-04-27 15:54:13 +03:00
static const struct regmap_irq da9150_irqs [ ] = {
2014-12-22 19:51:06 +03:00
[ DA9150_IRQ_VBUS ] = {
. reg_offset = 0 ,
. mask = DA9150_E_VBUS_MASK ,
} ,
[ DA9150_IRQ_CHG ] = {
. reg_offset = 0 ,
. mask = DA9150_E_CHG_MASK ,
} ,
[ DA9150_IRQ_TCLASS ] = {
. reg_offset = 0 ,
. mask = DA9150_E_TCLASS_MASK ,
} ,
[ DA9150_IRQ_TJUNC ] = {
. reg_offset = 0 ,
. mask = DA9150_E_TJUNC_MASK ,
} ,
[ DA9150_IRQ_VFAULT ] = {
. reg_offset = 0 ,
. mask = DA9150_E_VFAULT_MASK ,
} ,
[ DA9150_IRQ_CONF ] = {
. reg_offset = 1 ,
. mask = DA9150_E_CONF_MASK ,
} ,
[ DA9150_IRQ_DAT ] = {
. reg_offset = 1 ,
. mask = DA9150_E_DAT_MASK ,
} ,
[ DA9150_IRQ_DTYPE ] = {
. reg_offset = 1 ,
. mask = DA9150_E_DTYPE_MASK ,
} ,
[ DA9150_IRQ_ID ] = {
. reg_offset = 1 ,
. mask = DA9150_E_ID_MASK ,
} ,
[ DA9150_IRQ_ADP ] = {
. reg_offset = 1 ,
. mask = DA9150_E_ADP_MASK ,
} ,
[ DA9150_IRQ_SESS_END ] = {
. reg_offset = 1 ,
. mask = DA9150_E_SESS_END_MASK ,
} ,
[ DA9150_IRQ_SESS_VLD ] = {
. reg_offset = 1 ,
. mask = DA9150_E_SESS_VLD_MASK ,
} ,
[ DA9150_IRQ_FG ] = {
. reg_offset = 2 ,
. mask = DA9150_E_FG_MASK ,
} ,
[ DA9150_IRQ_GP ] = {
. reg_offset = 2 ,
. mask = DA9150_E_GP_MASK ,
} ,
[ DA9150_IRQ_TBAT ] = {
. reg_offset = 2 ,
. mask = DA9150_E_TBAT_MASK ,
} ,
[ DA9150_IRQ_GPIOA ] = {
. reg_offset = 2 ,
. mask = DA9150_E_GPIOA_MASK ,
} ,
[ DA9150_IRQ_GPIOB ] = {
. reg_offset = 2 ,
. mask = DA9150_E_GPIOB_MASK ,
} ,
[ DA9150_IRQ_GPIOC ] = {
. reg_offset = 2 ,
. mask = DA9150_E_GPIOC_MASK ,
} ,
[ DA9150_IRQ_GPIOD ] = {
. reg_offset = 2 ,
. mask = DA9150_E_GPIOD_MASK ,
} ,
[ DA9150_IRQ_GPADC ] = {
. reg_offset = 2 ,
. mask = DA9150_E_GPADC_MASK ,
} ,
[ DA9150_IRQ_WKUP ] = {
. reg_offset = 3 ,
. mask = DA9150_E_WKUP_MASK ,
} ,
} ;
2015-04-27 15:54:13 +03:00
static const struct regmap_irq_chip da9150_regmap_irq_chip = {
2014-12-22 19:51:06 +03:00
. name = " da9150_irq " ,
. status_base = DA9150_EVENT_E ,
. mask_base = DA9150_IRQ_MASK_E ,
. ack_base = DA9150_EVENT_E ,
. num_regs = DA9150_NUM_IRQ_REGS ,
. irqs = da9150_irqs ,
. num_irqs = ARRAY_SIZE ( da9150_irqs ) ,
} ;
2020-09-22 22:26:52 +03:00
static const struct resource da9150_gpadc_resources [ ] = {
2015-10-07 16:54:31 +03:00
DEFINE_RES_IRQ_NAMED ( DA9150_IRQ_GPADC , " GPADC " ) ,
2014-12-22 19:51:06 +03:00
} ;
2020-09-22 22:26:52 +03:00
static const struct resource da9150_charger_resources [ ] = {
2015-10-07 16:54:31 +03:00
DEFINE_RES_IRQ_NAMED ( DA9150_IRQ_CHG , " CHG_STATUS " ) ,
DEFINE_RES_IRQ_NAMED ( DA9150_IRQ_TJUNC , " CHG_TJUNC " ) ,
DEFINE_RES_IRQ_NAMED ( DA9150_IRQ_VFAULT , " CHG_VFAULT " ) ,
DEFINE_RES_IRQ_NAMED ( DA9150_IRQ_VBUS , " CHG_VBUS " ) ,
2014-12-22 19:51:06 +03:00
} ;
2020-09-22 22:26:52 +03:00
static const struct resource da9150_fg_resources [ ] = {
2015-10-07 16:54:08 +03:00
DEFINE_RES_IRQ_NAMED ( DA9150_IRQ_FG , " FG " ) ,
} ;
enum da9150_dev_idx {
DA9150_GPADC_IDX = 0 ,
DA9150_CHARGER_IDX ,
DA9150_FG_IDX ,
} ;
2014-12-22 19:51:06 +03:00
static struct mfd_cell da9150_devs [ ] = {
2015-10-07 16:54:08 +03:00
[ DA9150_GPADC_IDX ] = {
2014-12-22 19:51:06 +03:00
. name = " da9150-gpadc " ,
. of_compatible = " dlg,da9150-gpadc " ,
. resources = da9150_gpadc_resources ,
. num_resources = ARRAY_SIZE ( da9150_gpadc_resources ) ,
} ,
2015-10-07 16:54:08 +03:00
[ DA9150_CHARGER_IDX ] = {
2014-12-22 19:51:06 +03:00
. name = " da9150-charger " ,
. of_compatible = " dlg,da9150-charger " ,
. resources = da9150_charger_resources ,
. num_resources = ARRAY_SIZE ( da9150_charger_resources ) ,
} ,
2015-10-07 16:54:08 +03:00
[ DA9150_FG_IDX ] = {
. name = " da9150-fuel-gauge " ,
. of_compatible = " dlg,da9150-fuel-gauge " ,
. resources = da9150_fg_resources ,
. num_resources = ARRAY_SIZE ( da9150_fg_resources ) ,
} ,
2014-12-22 19:51:06 +03:00
} ;
static int da9150_probe ( struct i2c_client * client ,
const struct i2c_device_id * id )
{
struct da9150 * da9150 ;
struct da9150_pdata * pdata = dev_get_platdata ( & client - > dev ) ;
2015-10-07 16:54:08 +03:00
int qif_addr ;
2014-12-22 19:51:06 +03:00
int ret ;
da9150 = devm_kzalloc ( & client - > dev , sizeof ( * da9150 ) , GFP_KERNEL ) ;
if ( ! da9150 )
return - ENOMEM ;
da9150 - > dev = & client - > dev ;
da9150 - > irq = client - > irq ;
i2c_set_clientdata ( client , da9150 ) ;
da9150 - > regmap = devm_regmap_init_i2c ( client , & da9150_regmap_config ) ;
if ( IS_ERR ( da9150 - > regmap ) ) {
ret = PTR_ERR ( da9150 - > regmap ) ;
dev_err ( da9150 - > dev , " Failed to allocate register map: %d \n " ,
ret ) ;
return ret ;
}
2015-10-07 16:54:08 +03:00
/* Setup secondary I2C interface for QIF access */
qif_addr = da9150_reg_read ( da9150 , DA9150_CORE2WIRE_CTRL_A ) ;
qif_addr = ( qif_addr & DA9150_CORE_BASE_ADDR_MASK ) > > 1 ;
qif_addr | = DA9150_QIF_I2C_ADDR_LSB ;
2019-07-22 20:26:12 +03:00
da9150 - > core_qif = i2c_new_dummy_device ( client - > adapter , qif_addr ) ;
if ( IS_ERR ( da9150 - > core_qif ) ) {
2015-10-07 16:54:08 +03:00
dev_err ( da9150 - > dev , " Failed to attach QIF client \n " ) ;
2019-07-22 20:26:12 +03:00
return PTR_ERR ( da9150 - > core_qif ) ;
2015-10-07 16:54:08 +03:00
}
i2c_set_clientdata ( da9150 - > core_qif , da9150 ) ;
if ( pdata ) {
da9150 - > irq_base = pdata - > irq_base ;
da9150_devs [ DA9150_FG_IDX ] . platform_data = pdata - > fg_pdata ;
da9150_devs [ DA9150_FG_IDX ] . pdata_size =
sizeof ( struct da9150_fg_pdata ) ;
} else {
da9150 - > irq_base = - 1 ;
}
2014-12-22 19:51:06 +03:00
ret = regmap_add_irq_chip ( da9150 - > regmap , da9150 - > irq ,
IRQF_TRIGGER_LOW | IRQF_ONESHOT ,
da9150 - > irq_base , & da9150_regmap_irq_chip ,
& da9150 - > regmap_irq_data ) ;
2015-10-07 16:54:08 +03:00
if ( ret ) {
dev_err ( da9150 - > dev , " Failed to add regmap irq chip: %d \n " ,
ret ) ;
goto regmap_irq_fail ;
}
2014-12-22 19:51:06 +03:00
da9150 - > irq_base = regmap_irq_chip_get_base ( da9150 - > regmap_irq_data ) ;
2015-10-07 16:54:08 +03:00
2014-12-22 19:51:06 +03:00
enable_irq_wake ( da9150 - > irq ) ;
ret = mfd_add_devices ( da9150 - > dev , - 1 , da9150_devs ,
ARRAY_SIZE ( da9150_devs ) , NULL ,
da9150 - > irq_base , NULL ) ;
if ( ret ) {
dev_err ( da9150 - > dev , " Failed to add child devices: %d \n " , ret ) ;
2015-10-07 16:54:08 +03:00
goto mfd_fail ;
2014-12-22 19:51:06 +03:00
}
return 0 ;
2015-10-07 16:54:08 +03:00
mfd_fail :
regmap_del_irq_chip ( da9150 - > irq , da9150 - > regmap_irq_data ) ;
regmap_irq_fail :
i2c_unregister_device ( da9150 - > core_qif ) ;
return ret ;
2014-12-22 19:51:06 +03:00
}
static int da9150_remove ( struct i2c_client * client )
{
struct da9150 * da9150 = i2c_get_clientdata ( client ) ;
regmap_del_irq_chip ( da9150 - > irq , da9150 - > regmap_irq_data ) ;
mfd_remove_devices ( da9150 - > dev ) ;
2015-10-07 16:54:08 +03:00
i2c_unregister_device ( da9150 - > core_qif ) ;
2014-12-22 19:51:06 +03:00
return 0 ;
}
static void da9150_shutdown ( struct i2c_client * client )
{
struct da9150 * da9150 = i2c_get_clientdata ( client ) ;
/* Make sure we have a wakup source for the device */
da9150_set_bits ( da9150 , DA9150_CONFIG_D ,
DA9150_WKUP_PM_EN_MASK ,
DA9150_WKUP_PM_EN_MASK ) ;
/* Set device to DISABLED mode */
da9150_set_bits ( da9150 , DA9150_CONTROL_C ,
DA9150_DISABLE_MASK , DA9150_DISABLE_MASK ) ;
}
static const struct i2c_device_id da9150_i2c_id [ ] = {
{ " da9150 " , } ,
{ }
} ;
MODULE_DEVICE_TABLE ( i2c , da9150_i2c_id ) ;
static const struct of_device_id da9150_of_match [ ] = {
{ . compatible = " dlg,da9150 " , } ,
{ }
} ;
MODULE_DEVICE_TABLE ( of , da9150_of_match ) ;
static struct i2c_driver da9150_driver = {
. driver = {
. name = " da9150 " ,
2020-11-20 19:21:22 +03:00
. of_match_table = da9150_of_match ,
2014-12-22 19:51:06 +03:00
} ,
. probe = da9150_probe ,
. remove = da9150_remove ,
. shutdown = da9150_shutdown ,
. id_table = da9150_i2c_id ,
} ;
module_i2c_driver ( da9150_driver ) ;
MODULE_DESCRIPTION ( " MFD Core Driver for DA9150 " ) ;
MODULE_AUTHOR ( " Adam Thomson <Adam.Thomson.Opensource@diasemi.com> " ) ;
MODULE_LICENSE ( " GPL " ) ;