2018-08-07 19:11:23 +03:00
// SPDX-License-Identifier: GPL-2.0+
//
// max77693.c - mfd core driver for the MAX 77693
//
// Copyright (C) 2012 Samsung Electronics
// SangYoung Son <hello.son@samsung.com>
//
// This program is not provided / owned by Maxim Integrated Products.
//
// This driver is based on max8997.c
2012-05-15 00:50:39 +04:00
# include <linux/module.h>
# include <linux/slab.h>
# include <linux/i2c.h>
# include <linux/err.h>
# include <linux/interrupt.h>
2013-10-18 14:41:57 +04:00
# include <linux/of.h>
2012-05-15 00:50:39 +04:00
# include <linux/pm_runtime.h>
# include <linux/mutex.h>
# include <linux/mfd/core.h>
# include <linux/mfd/max77693.h>
2015-07-15 15:59:50 +03:00
# include <linux/mfd/max77693-common.h>
2012-05-15 00:50:39 +04:00
# include <linux/mfd/max77693-private.h>
# include <linux/regulator/machine.h>
# include <linux/regmap.h>
# define I2C_ADDR_PMIC (0xCC >> 1) /* Charger, Flash LED */
# define I2C_ADDR_MUIC (0x4A >> 1)
# define I2C_ADDR_HAPTIC (0x90 >> 1)
2013-11-18 17:33:00 +04:00
static const struct mfd_cell max77693_devs [ ] = {
2012-05-15 00:50:39 +04:00
{ . name = " max77693-pmic " , } ,
2014-10-20 16:34:46 +04:00
{
. name = " max77693-charger " ,
. of_compatible = " maxim,max77693-charger " ,
} ,
2017-09-05 16:50:27 +03:00
{
. name = " max77693-muic " ,
. of_compatible = " maxim,max77693-muic " ,
} ,
2014-09-17 20:40:25 +04:00
{
. name = " max77693-haptic " ,
. of_compatible = " maxim,max77693-haptic " ,
} ,
2014-08-22 13:06:18 +04:00
{
2015-03-04 19:14:26 +03:00
. name = " max77693-led " ,
. of_compatible = " maxim,max77693-led " ,
2014-08-22 13:06:18 +04:00
} ,
2012-05-15 00:50:39 +04:00
} ;
static const struct regmap_config max77693_regmap_config = {
. reg_bits = 8 ,
. val_bits = 8 ,
. max_register = MAX77693_PMIC_REG_END ,
} ;
2014-05-21 10:52:48 +04:00
static const struct regmap_irq max77693_led_irqs [ ] = {
{ . mask = LED_IRQ_FLED2_OPEN , } ,
{ . mask = LED_IRQ_FLED2_SHORT , } ,
{ . mask = LED_IRQ_FLED1_OPEN , } ,
{ . mask = LED_IRQ_FLED1_SHORT , } ,
{ . mask = LED_IRQ_MAX_FLASH , } ,
} ;
static const struct regmap_irq_chip max77693_led_irq_chip = {
. name = " max77693-led " ,
. status_base = MAX77693_LED_REG_FLASH_INT ,
. mask_base = MAX77693_LED_REG_FLASH_INT_MASK ,
. mask_invert = false ,
. num_regs = 1 ,
. irqs = max77693_led_irqs ,
. num_irqs = ARRAY_SIZE ( max77693_led_irqs ) ,
} ;
static const struct regmap_irq max77693_topsys_irqs [ ] = {
{ . mask = TOPSYS_IRQ_T120C_INT , } ,
{ . mask = TOPSYS_IRQ_T140C_INT , } ,
{ . mask = TOPSYS_IRQ_LOWSYS_INT , } ,
} ;
static const struct regmap_irq_chip max77693_topsys_irq_chip = {
. name = " max77693-topsys " ,
. status_base = MAX77693_PMIC_REG_TOPSYS_INT ,
. mask_base = MAX77693_PMIC_REG_TOPSYS_INT_MASK ,
. mask_invert = false ,
. num_regs = 1 ,
. irqs = max77693_topsys_irqs ,
. num_irqs = ARRAY_SIZE ( max77693_topsys_irqs ) ,
} ;
static const struct regmap_irq max77693_charger_irqs [ ] = {
{ . mask = CHG_IRQ_BYP_I , } ,
{ . mask = CHG_IRQ_THM_I , } ,
{ . mask = CHG_IRQ_BAT_I , } ,
{ . mask = CHG_IRQ_CHG_I , } ,
{ . mask = CHG_IRQ_CHGIN_I , } ,
} ;
static const struct regmap_irq_chip max77693_charger_irq_chip = {
. name = " max77693-charger " ,
. status_base = MAX77693_CHG_REG_CHG_INT ,
. mask_base = MAX77693_CHG_REG_CHG_INT_MASK ,
. mask_invert = false ,
. num_regs = 1 ,
. irqs = max77693_charger_irqs ,
. num_irqs = ARRAY_SIZE ( max77693_charger_irqs ) ,
} ;
2013-12-20 13:35:08 +04:00
static const struct regmap_config max77693_regmap_muic_config = {
. reg_bits = 8 ,
. val_bits = 8 ,
. max_register = MAX77693_MUIC_REG_END ,
} ;
2014-05-21 10:52:48 +04:00
static const struct regmap_irq max77693_muic_irqs [ ] = {
{ . reg_offset = 0 , . mask = MUIC_IRQ_INT1_ADC , } ,
{ . reg_offset = 0 , . mask = MUIC_IRQ_INT1_ADC_LOW , } ,
{ . reg_offset = 0 , . mask = MUIC_IRQ_INT1_ADC_ERR , } ,
{ . reg_offset = 0 , . mask = MUIC_IRQ_INT1_ADC1K , } ,
{ . reg_offset = 1 , . mask = MUIC_IRQ_INT2_CHGTYP , } ,
{ . reg_offset = 1 , . mask = MUIC_IRQ_INT2_CHGDETREUN , } ,
{ . reg_offset = 1 , . mask = MUIC_IRQ_INT2_DCDTMR , } ,
{ . reg_offset = 1 , . mask = MUIC_IRQ_INT2_DXOVP , } ,
{ . reg_offset = 1 , . mask = MUIC_IRQ_INT2_VBVOLT , } ,
{ . reg_offset = 1 , . mask = MUIC_IRQ_INT2_VIDRM , } ,
{ . reg_offset = 2 , . mask = MUIC_IRQ_INT3_EOC , } ,
{ . reg_offset = 2 , . mask = MUIC_IRQ_INT3_CGMBC , } ,
{ . reg_offset = 2 , . mask = MUIC_IRQ_INT3_OVP , } ,
{ . reg_offset = 2 , . mask = MUIC_IRQ_INT3_MBCCHG_ERR , } ,
{ . reg_offset = 2 , . mask = MUIC_IRQ_INT3_CHG_ENABLED , } ,
{ . reg_offset = 2 , . mask = MUIC_IRQ_INT3_BAT_DET , } ,
} ;
static const struct regmap_irq_chip max77693_muic_irq_chip = {
. name = " max77693-muic " ,
. status_base = MAX77693_MUIC_REG_INT1 ,
. mask_base = MAX77693_MUIC_REG_INTMASK1 ,
. mask_invert = true ,
. num_regs = 3 ,
. irqs = max77693_muic_irqs ,
. num_irqs = ARRAY_SIZE ( max77693_muic_irqs ) ,
} ;
2014-09-17 20:40:24 +04:00
static const struct regmap_config max77693_regmap_haptic_config = {
. reg_bits = 8 ,
. val_bits = 8 ,
. max_register = MAX77693_HAPTIC_REG_END ,
} ;
2022-11-19 01:42:54 +03:00
static int max77693_i2c_probe ( struct i2c_client * i2c )
2012-05-15 00:50:39 +04:00
{
2022-11-19 01:42:54 +03:00
const struct i2c_device_id * id = i2c_client_get_device_id ( i2c ) ;
2012-05-15 00:50:39 +04:00
struct max77693_dev * max77693 ;
2014-05-21 10:52:47 +04:00
unsigned int reg_data ;
2012-05-15 00:50:39 +04:00
int ret = 0 ;
max77693 = devm_kzalloc ( & i2c - > dev ,
sizeof ( struct max77693_dev ) , GFP_KERNEL ) ;
if ( max77693 = = NULL )
return - ENOMEM ;
i2c_set_clientdata ( i2c , max77693 ) ;
max77693 - > dev = & i2c - > dev ;
max77693 - > i2c = i2c ;
max77693 - > irq = i2c - > irq ;
max77693 - > type = id - > driver_data ;
2012-12-24 20:28:36 +04:00
max77693 - > regmap = devm_regmap_init_i2c ( i2c , & max77693_regmap_config ) ;
if ( IS_ERR ( max77693 - > regmap ) ) {
ret = PTR_ERR ( max77693 - > regmap ) ;
dev_err ( max77693 - > dev , " failed to allocate register map: %d \n " ,
ret ) ;
return ret ;
}
2012-05-15 00:50:39 +04:00
2014-05-21 10:52:47 +04:00
ret = regmap_read ( max77693 - > regmap , MAX77693_PMIC_REG_PMIC_ID2 ,
2012-12-24 20:28:36 +04:00
& reg_data ) ;
if ( ret < 0 ) {
2012-05-15 00:50:39 +04:00
dev_err ( max77693 - > dev , " device not found on this channel \n " ) ;
2012-12-24 20:28:36 +04:00
return ret ;
2012-05-15 00:50:39 +04:00
} else
dev_info ( max77693 - > dev , " device ID: 0x%x \n " , reg_data ) ;
2019-07-22 20:26:14 +03:00
max77693 - > i2c_muic = i2c_new_dummy_device ( i2c - > adapter , I2C_ADDR_MUIC ) ;
if ( IS_ERR ( max77693 - > i2c_muic ) ) {
2014-02-11 14:03:32 +04:00
dev_err ( max77693 - > dev , " Failed to allocate I2C device for MUIC \n " ) ;
2019-07-22 20:26:14 +03:00
return PTR_ERR ( max77693 - > i2c_muic ) ;
2014-02-11 14:03:32 +04:00
}
2015-07-15 15:59:50 +03:00
i2c_set_clientdata ( max77693 - > i2c_muic , max77693 ) ;
2012-05-15 00:50:39 +04:00
2019-07-22 20:26:14 +03:00
max77693 - > i2c_haptic = i2c_new_dummy_device ( i2c - > adapter , I2C_ADDR_HAPTIC ) ;
if ( IS_ERR ( max77693 - > i2c_haptic ) ) {
2014-02-11 14:03:32 +04:00
dev_err ( max77693 - > dev , " Failed to allocate I2C device for Haptic \n " ) ;
2019-07-22 20:26:14 +03:00
ret = PTR_ERR ( max77693 - > i2c_haptic ) ;
2014-02-11 14:03:32 +04:00
goto err_i2c_haptic ;
}
2015-07-15 15:59:50 +03:00
i2c_set_clientdata ( max77693 - > i2c_haptic , max77693 ) ;
2012-05-15 00:50:39 +04:00
2015-07-15 15:59:50 +03:00
max77693 - > regmap_haptic = devm_regmap_init_i2c ( max77693 - > i2c_haptic ,
2014-09-17 20:40:24 +04:00
& max77693_regmap_haptic_config ) ;
if ( IS_ERR ( max77693 - > regmap_haptic ) ) {
ret = PTR_ERR ( max77693 - > regmap_haptic ) ;
dev_err ( max77693 - > dev ,
" failed to initialize haptic register map: %d \n " , ret ) ;
goto err_regmap ;
}
2012-08-21 10:16:23 +04:00
/*
* Initialize register map for MUIC device because use regmap - muic
* instance of MUIC device when irq of max77693 is initialized
* before call max77693 - muic probe ( ) function .
*/
2015-07-15 15:59:50 +03:00
max77693 - > regmap_muic = devm_regmap_init_i2c ( max77693 - > i2c_muic ,
2013-12-20 13:35:08 +04:00
& max77693_regmap_muic_config ) ;
2012-08-21 10:16:23 +04:00
if ( IS_ERR ( max77693 - > regmap_muic ) ) {
ret = PTR_ERR ( max77693 - > regmap_muic ) ;
dev_err ( max77693 - > dev ,
" failed to allocate register map: %d \n " , ret ) ;
2014-09-17 20:40:24 +04:00
goto err_regmap ;
2012-08-21 10:16:23 +04:00
}
2014-05-21 10:52:48 +04:00
ret = regmap_add_irq_chip ( max77693 - > regmap , max77693 - > irq ,
2021-06-02 14:04:44 +03:00
IRQF_ONESHOT | IRQF_SHARED , 0 ,
2014-05-21 10:52:48 +04:00
& max77693_led_irq_chip ,
& max77693 - > irq_data_led ) ;
if ( ret ) {
dev_err ( max77693 - > dev , " failed to add irq chip: %d \n " , ret ) ;
2014-09-17 20:40:24 +04:00
goto err_regmap ;
2014-05-21 10:52:48 +04:00
}
ret = regmap_add_irq_chip ( max77693 - > regmap , max77693 - > irq ,
2021-06-02 14:04:44 +03:00
IRQF_ONESHOT | IRQF_SHARED , 0 ,
2014-05-21 10:52:48 +04:00
& max77693_topsys_irq_chip ,
& max77693 - > irq_data_topsys ) ;
if ( ret ) {
dev_err ( max77693 - > dev , " failed to add irq chip: %d \n " , ret ) ;
goto err_irq_topsys ;
}
ret = regmap_add_irq_chip ( max77693 - > regmap , max77693 - > irq ,
2021-06-02 14:04:44 +03:00
IRQF_ONESHOT | IRQF_SHARED , 0 ,
2014-05-21 10:52:48 +04:00
& max77693_charger_irq_chip ,
2015-07-15 15:59:50 +03:00
& max77693 - > irq_data_chg ) ;
2014-05-21 10:52:48 +04:00
if ( ret ) {
dev_err ( max77693 - > dev , " failed to add irq chip: %d \n " , ret ) ;
goto err_irq_charger ;
}
2014-10-10 12:22:01 +04:00
ret = regmap_add_irq_chip ( max77693 - > regmap_muic , max77693 - > irq ,
2021-06-02 14:04:44 +03:00
IRQF_ONESHOT | IRQF_SHARED , 0 ,
2014-05-21 10:52:48 +04:00
& max77693_muic_irq_chip ,
& max77693 - > irq_data_muic ) ;
if ( ret ) {
dev_err ( max77693 - > dev , " failed to add irq chip: %d \n " , ret ) ;
goto err_irq_muic ;
}
2012-05-15 00:54:20 +04:00
2014-10-10 14:48:35 +04:00
/* Unmask interrupts from all blocks in interrupt source register */
ret = regmap_update_bits ( max77693 - > regmap ,
MAX77693_PMIC_REG_INTSRC_MASK ,
SRC_IRQ_ALL , ( unsigned int ) ~ SRC_IRQ_ALL ) ;
if ( ret < 0 ) {
dev_err ( max77693 - > dev ,
" Could not unmask interrupts in INTSRC: %d \n " ,
ret ) ;
goto err_intsrc ;
}
2012-05-15 00:50:39 +04:00
pm_runtime_set_active ( max77693 - > dev ) ;
ret = mfd_add_devices ( max77693 - > dev , - 1 , max77693_devs ,
2012-09-11 11:16:36 +04:00
ARRAY_SIZE ( max77693_devs ) , NULL , 0 , NULL ) ;
2012-05-15 00:50:39 +04:00
if ( ret < 0 )
goto err_mfd ;
return ret ;
err_mfd :
2014-05-21 10:52:48 +04:00
mfd_remove_devices ( max77693 - > dev ) ;
2014-10-10 14:48:35 +04:00
err_intsrc :
2014-05-21 10:52:48 +04:00
regmap_del_irq_chip ( max77693 - > irq , max77693 - > irq_data_muic ) ;
err_irq_muic :
2015-07-15 15:59:50 +03:00
regmap_del_irq_chip ( max77693 - > irq , max77693 - > irq_data_chg ) ;
2014-05-21 10:52:48 +04:00
err_irq_charger :
regmap_del_irq_chip ( max77693 - > irq , max77693 - > irq_data_topsys ) ;
err_irq_topsys :
regmap_del_irq_chip ( max77693 - > irq , max77693 - > irq_data_led ) ;
2014-09-17 20:40:24 +04:00
err_regmap :
2015-07-15 15:59:50 +03:00
i2c_unregister_device ( max77693 - > i2c_haptic ) ;
2014-02-11 14:03:32 +04:00
err_i2c_haptic :
2015-07-15 15:59:50 +03:00
i2c_unregister_device ( max77693 - > i2c_muic ) ;
2012-05-15 00:50:39 +04:00
return ret ;
}
2022-08-15 11:02:30 +03:00
static void max77693_i2c_remove ( struct i2c_client * i2c )
2012-05-15 00:50:39 +04:00
{
struct max77693_dev * max77693 = i2c_get_clientdata ( i2c ) ;
mfd_remove_devices ( max77693 - > dev ) ;
2014-05-21 10:52:48 +04:00
regmap_del_irq_chip ( max77693 - > irq , max77693 - > irq_data_muic ) ;
2015-07-15 15:59:50 +03:00
regmap_del_irq_chip ( max77693 - > irq , max77693 - > irq_data_chg ) ;
2014-05-21 10:52:48 +04:00
regmap_del_irq_chip ( max77693 - > irq , max77693 - > irq_data_topsys ) ;
regmap_del_irq_chip ( max77693 - > irq , max77693 - > irq_data_led ) ;
2015-07-15 15:59:50 +03:00
i2c_unregister_device ( max77693 - > i2c_muic ) ;
i2c_unregister_device ( max77693 - > i2c_haptic ) ;
2012-05-15 00:50:39 +04:00
}
static const struct i2c_device_id max77693_i2c_id [ ] = {
{ " max77693 " , TYPE_MAX77693 } ,
{ }
} ;
MODULE_DEVICE_TABLE ( i2c , max77693_i2c_id ) ;
2012-05-15 00:54:20 +04:00
static int max77693_suspend ( struct device * dev )
{
2015-12-28 18:00:14 +03:00
struct i2c_client * i2c = to_i2c_client ( dev ) ;
2012-05-15 00:54:20 +04:00
struct max77693_dev * max77693 = i2c_get_clientdata ( i2c ) ;
2014-05-21 10:52:48 +04:00
if ( device_may_wakeup ( dev ) ) {
enable_irq_wake ( max77693 - > irq ) ;
disable_irq ( max77693 - > irq ) ;
}
2012-05-15 00:54:20 +04:00
return 0 ;
}
static int max77693_resume ( struct device * dev )
{
2015-12-28 18:00:14 +03:00
struct i2c_client * i2c = to_i2c_client ( dev ) ;
2012-05-15 00:54:20 +04:00
struct max77693_dev * max77693 = i2c_get_clientdata ( i2c ) ;
2014-05-21 10:52:48 +04:00
if ( device_may_wakeup ( dev ) ) {
disable_irq_wake ( max77693 - > irq ) ;
enable_irq ( max77693 - > irq ) ;
}
return 0 ;
2012-05-15 00:54:20 +04:00
}
2012-06-05 19:15:59 +04:00
static const struct dev_pm_ops max77693_pm = {
2012-05-15 00:54:20 +04:00
. suspend = max77693_suspend ,
. resume = max77693_resume ,
} ;
2013-09-27 11:27:46 +04:00
# ifdef CONFIG_OF
2014-05-13 14:58:52 +04:00
static const struct of_device_id max77693_dt_match [ ] = {
2013-09-27 11:27:46 +04:00
{ . compatible = " maxim,max77693 " } ,
{ } ,
} ;
2016-04-04 10:54:18 +03:00
MODULE_DEVICE_TABLE ( of , max77693_dt_match ) ;
2013-09-27 11:27:46 +04:00
# endif
2012-05-15 00:50:39 +04:00
static struct i2c_driver max77693_i2c_driver = {
. driver = {
. name = " max77693 " ,
2012-05-15 00:54:20 +04:00
. pm = & max77693_pm ,
2013-09-27 11:27:46 +04:00
. of_match_table = of_match_ptr ( max77693_dt_match ) ,
2012-05-15 00:50:39 +04:00
} ,
2022-11-19 01:42:54 +03:00
. probe_new = max77693_i2c_probe ,
2012-05-15 00:50:39 +04:00
. remove = max77693_i2c_remove ,
. id_table = max77693_i2c_id ,
} ;
2016-04-04 10:54:18 +03:00
module_i2c_driver ( max77693_i2c_driver ) ;
2012-05-15 00:50:39 +04:00
MODULE_DESCRIPTION ( " MAXIM 77693 multi-function core driver " ) ;
MODULE_AUTHOR ( " SangYoung, Son <hello.son@samsung.com> " ) ;
MODULE_LICENSE ( " GPL " ) ;