2018-08-30 19:51:03 +03:00
// SPDX-License-Identifier: GPL-2.0
2014-11-24 17:24:47 +08:00
/*
2018-08-30 19:51:03 +03:00
* XPower AXP288 PMIC operation region driver
2014-11-24 17:24:47 +08:00
*
* Copyright ( C ) 2014 Intel Corporation . All rights reserved .
*/
# include <linux/acpi.h>
2018-08-30 19:51:02 +03:00
# include <linux/init.h>
2014-11-24 17:24:47 +08:00
# include <linux/mfd/axp20x.h>
2018-08-30 19:51:02 +03:00
# include <linux/regmap.h>
2018-10-11 16:29:10 +02:00
# include <linux/platform_device.h>
# include <asm/iosf_mbi.h>
2014-11-24 17:24:47 +08:00
# include "intel_pmic.h"
# define XPOWER_GPADC_LOW 0x5b
2017-05-14 23:35:39 +02:00
# define XPOWER_GPI1_CTRL 0x92
# define GPI1_LDO_MASK GENMASK(2, 0)
# define GPI1_LDO_ON (3 << 0)
# define GPI1_LDO_OFF (4 << 0)
2014-11-24 17:24:47 +08:00
2019-01-04 23:10:54 +01:00
# define AXP288_ADC_TS_CURRENT_ON_OFF_MASK GENMASK(1, 0)
# define AXP288_ADC_TS_CURRENT_OFF (0 << 0)
# define AXP288_ADC_TS_CURRENT_ON_WHEN_CHARGING (1 << 0)
# define AXP288_ADC_TS_CURRENT_ON_ONDEMAND (2 << 0)
# define AXP288_ADC_TS_CURRENT_ON (3 << 0)
2017-07-08 15:40:08 +02:00
2014-11-24 17:24:47 +08:00
static struct pmic_table power_table [ ] = {
{
. address = 0x00 ,
. reg = 0x13 ,
. bit = 0x05 ,
2017-04-21 13:48:08 +02:00
} , /* ALD1 */
2014-11-24 17:24:47 +08:00
{
. address = 0x04 ,
. reg = 0x13 ,
. bit = 0x06 ,
2017-04-21 13:48:08 +02:00
} , /* ALD2 */
2014-11-24 17:24:47 +08:00
{
. address = 0x08 ,
. reg = 0x13 ,
. bit = 0x07 ,
2017-04-21 13:48:08 +02:00
} , /* ALD3 */
2014-11-24 17:24:47 +08:00
{
. address = 0x0c ,
. reg = 0x12 ,
. bit = 0x03 ,
2017-04-21 13:48:08 +02:00
} , /* DLD1 */
2014-11-24 17:24:47 +08:00
{
. address = 0x10 ,
. reg = 0x12 ,
. bit = 0x04 ,
2017-04-21 13:48:08 +02:00
} , /* DLD2 */
2014-11-24 17:24:47 +08:00
{
. address = 0x14 ,
. reg = 0x12 ,
. bit = 0x05 ,
2017-04-21 13:48:08 +02:00
} , /* DLD3 */
2014-11-24 17:24:47 +08:00
{
. address = 0x18 ,
. reg = 0x12 ,
. bit = 0x06 ,
2017-04-21 13:48:08 +02:00
} , /* DLD4 */
2014-11-24 17:24:47 +08:00
{
. address = 0x1c ,
. reg = 0x12 ,
. bit = 0x00 ,
2017-04-21 13:48:08 +02:00
} , /* ELD1 */
2014-11-24 17:24:47 +08:00
{
. address = 0x20 ,
. reg = 0x12 ,
. bit = 0x01 ,
2017-04-21 13:48:08 +02:00
} , /* ELD2 */
2014-11-24 17:24:47 +08:00
{
. address = 0x24 ,
. reg = 0x12 ,
. bit = 0x02 ,
2017-04-21 13:48:08 +02:00
} , /* ELD3 */
2014-11-24 17:24:47 +08:00
{
. address = 0x28 ,
. reg = 0x13 ,
. bit = 0x02 ,
2017-04-21 13:48:08 +02:00
} , /* FLD1 */
2014-11-24 17:24:47 +08:00
{
. address = 0x2c ,
. reg = 0x13 ,
. bit = 0x03 ,
2017-04-21 13:48:08 +02:00
} , /* FLD2 */
2014-11-24 17:24:47 +08:00
{
. address = 0x30 ,
. reg = 0x13 ,
. bit = 0x04 ,
2017-04-21 13:48:08 +02:00
} , /* FLD3 */
2014-11-24 17:24:47 +08:00
{
2017-04-21 13:48:08 +02:00
. address = 0x34 ,
2014-11-24 17:24:47 +08:00
. reg = 0x10 ,
. bit = 0x03 ,
2017-04-21 13:48:08 +02:00
} , /* BUC1 */
2014-11-24 17:24:47 +08:00
{
2017-04-21 13:48:08 +02:00
. address = 0x38 ,
2014-11-24 17:24:47 +08:00
. reg = 0x10 ,
. bit = 0x06 ,
2017-04-21 13:48:08 +02:00
} , /* BUC2 */
2014-11-24 17:24:47 +08:00
{
2017-04-21 13:48:08 +02:00
. address = 0x3c ,
2014-11-24 17:24:47 +08:00
. reg = 0x10 ,
. bit = 0x05 ,
2017-04-21 13:48:08 +02:00
} , /* BUC3 */
2014-11-24 17:24:47 +08:00
{
2017-04-21 13:48:08 +02:00
. address = 0x40 ,
2014-11-24 17:24:47 +08:00
. reg = 0x10 ,
. bit = 0x04 ,
2017-04-21 13:48:08 +02:00
} , /* BUC4 */
2014-11-24 17:24:47 +08:00
{
2017-04-21 13:48:08 +02:00
. address = 0x44 ,
2014-11-24 17:24:47 +08:00
. reg = 0x10 ,
. bit = 0x01 ,
2017-04-21 13:48:08 +02:00
} , /* BUC5 */
2014-11-24 17:24:47 +08:00
{
2017-04-21 13:48:08 +02:00
. address = 0x48 ,
2014-11-24 17:24:47 +08:00
. reg = 0x10 ,
. bit = 0x00
2017-04-21 13:48:08 +02:00
} , /* BUC6 */
2017-05-14 23:35:39 +02:00
{
. address = 0x4c ,
. reg = 0x92 ,
} , /* GPI1 */
2014-11-24 17:24:47 +08:00
} ;
/* TMP0 - TMP5 are the same, all from GPADC */
static struct pmic_table thermal_table [ ] = {
{
. address = 0x00 ,
. reg = XPOWER_GPADC_LOW
} ,
{
. address = 0x0c ,
. reg = XPOWER_GPADC_LOW
} ,
{
. address = 0x18 ,
. reg = XPOWER_GPADC_LOW
} ,
{
. address = 0x24 ,
. reg = XPOWER_GPADC_LOW
} ,
{
. address = 0x30 ,
. reg = XPOWER_GPADC_LOW
} ,
{
. address = 0x3c ,
. reg = XPOWER_GPADC_LOW
} ,
} ;
static int intel_xpower_pmic_get_power ( struct regmap * regmap , int reg ,
int bit , u64 * value )
{
int data ;
if ( regmap_read ( regmap , reg , & data ) )
return - EIO ;
2017-05-14 23:35:39 +02:00
/* GPIO1 LDO regulator needs special handling */
if ( reg = = XPOWER_GPI1_CTRL )
* value = ( ( data & GPI1_LDO_MASK ) = = GPI1_LDO_ON ) ;
else
* value = ( data & BIT ( bit ) ) ? 1 : 0 ;
2014-11-24 17:24:47 +08:00
return 0 ;
}
static int intel_xpower_pmic_update_power ( struct regmap * regmap , int reg ,
int bit , bool on )
{
2018-10-11 16:29:10 +02:00
int data , ret ;
2014-11-24 17:24:47 +08:00
2018-10-11 16:29:10 +02:00
ret = iosf_mbi_block_punit_i2c_access ( ) ;
if ( ret )
return ret ;
2021-07-06 18:09:22 +02:00
/* GPIO1 LDO regulator needs special handling */
if ( reg = = XPOWER_GPI1_CTRL ) {
ret = regmap_update_bits ( regmap , reg , GPI1_LDO_MASK ,
on ? GPI1_LDO_ON : GPI1_LDO_OFF ) ;
goto out ;
}
2018-10-11 16:29:10 +02:00
if ( regmap_read ( regmap , reg , & data ) ) {
ret = - EIO ;
goto out ;
}
2014-11-24 17:24:47 +08:00
if ( on )
data | = BIT ( bit ) ;
else
data & = ~ BIT ( bit ) ;
if ( regmap_write ( regmap , reg , data ) )
2018-10-11 16:29:10 +02:00
ret = - EIO ;
out :
iosf_mbi_unblock_punit_i2c_access ( ) ;
2014-11-24 17:24:47 +08:00
2018-10-11 16:29:10 +02:00
return ret ;
2014-11-24 17:24:47 +08:00
}
/**
* intel_xpower_pmic_get_raw_temp ( ) : Get raw temperature reading from the PMIC
*
* @ regmap : regmap of the PMIC device
* @ reg : register to get the reading
*
* Return a positive value on success , errno on failure .
*/
static int intel_xpower_pmic_get_raw_temp ( struct regmap * regmap , int reg )
{
2019-01-04 23:10:54 +01:00
int ret , adc_ts_pin_ctrl ;
2017-04-19 15:07:00 +02:00
u8 buf [ 2 ] ;
2014-11-24 17:24:47 +08:00
2019-01-04 23:10:54 +01:00
/*
* The current - source used for the battery temp - sensor ( TS ) is shared
* with the GPADC . For proper fuel - gauge and charger operation the TS
* current - source needs to be permanently on . But to read the GPADC we
* need to temporary switch the TS current - source to ondemand , so that
* the GPADC can use it , otherwise we will always read an all 0 value .
*
* Note that the switching from on to on - ondemand is not necessary
* when the TS current - source is off ( this happens on devices which
* do not use the TS - pin ) .
*/
ret = regmap_read ( regmap , AXP288_ADC_TS_PIN_CTRL , & adc_ts_pin_ctrl ) ;
2017-07-08 15:40:08 +02:00
if ( ret )
return ret ;
2019-01-04 23:10:54 +01:00
if ( adc_ts_pin_ctrl & AXP288_ADC_TS_CURRENT_ON_OFF_MASK ) {
2021-07-06 18:09:22 +02:00
/*
* AXP288_ADC_TS_PIN_CTRL reads are cached by the regmap , so
* this does to a single I2C - transfer , and thus there is no
* need to explicitly call iosf_mbi_block_punit_i2c_access ( ) .
*/
2019-01-04 23:10:54 +01:00
ret = regmap_update_bits ( regmap , AXP288_ADC_TS_PIN_CTRL ,
AXP288_ADC_TS_CURRENT_ON_OFF_MASK ,
AXP288_ADC_TS_CURRENT_ON_ONDEMAND ) ;
if ( ret )
return ret ;
/* Wait a bit after switching the current-source */
usleep_range ( 6000 , 10000 ) ;
}
2017-07-08 15:40:08 +02:00
2021-07-06 18:09:22 +02:00
ret = iosf_mbi_block_punit_i2c_access ( ) ;
if ( ret )
return ret ;
2017-07-08 15:40:08 +02:00
ret = regmap_bulk_read ( regmap , AXP288_GP_ADC_H , buf , 2 ) ;
if ( ret = = 0 )
ret = ( buf [ 0 ] < < 4 ) + ( ( buf [ 1 ] > > 4 ) & 0x0f ) ;
2019-01-04 23:10:54 +01:00
if ( adc_ts_pin_ctrl & AXP288_ADC_TS_CURRENT_ON_OFF_MASK ) {
regmap_update_bits ( regmap , AXP288_ADC_TS_PIN_CTRL ,
AXP288_ADC_TS_CURRENT_ON_OFF_MASK ,
AXP288_ADC_TS_CURRENT_ON ) ;
}
2014-11-24 17:24:47 +08:00
2021-07-06 18:09:22 +02:00
iosf_mbi_unblock_punit_i2c_access ( ) ;
2017-07-08 15:40:08 +02:00
return ret ;
2014-11-24 17:24:47 +08:00
}
2021-07-06 18:09:23 +02:00
static int intel_xpower_exec_mipi_pmic_seq_element ( struct regmap * regmap ,
u16 i2c_address , u32 reg_address ,
u32 value , u32 mask )
{
int ret ;
if ( i2c_address ! = 0x34 ) {
pr_err ( " %s: Unexpected i2c-addr: 0x%02x (reg-addr 0x%x value 0x%x mask 0x%x) \n " ,
__func__ , i2c_address , reg_address , value , mask ) ;
return - ENXIO ;
}
ret = iosf_mbi_block_punit_i2c_access ( ) ;
if ( ret )
return ret ;
ret = regmap_update_bits ( regmap , reg_address , mask , value ) ;
iosf_mbi_unblock_punit_i2c_access ( ) ;
return ret ;
}
2021-11-26 16:21:09 +01:00
static int intel_xpower_lpat_raw_to_temp ( struct acpi_lpat_conversion_table * lpat_table ,
int raw )
{
struct acpi_lpat first = lpat_table - > lpat [ 0 ] ;
struct acpi_lpat last = lpat_table - > lpat [ lpat_table - > lpat_count - 1 ] ;
/*
* Some LPAT tables in the ACPI Device for the AXP288 PMIC for some
* reason only describe a small temperature range , e . g . 27 ° - 37 °
* Celcius . Resulting in errors when the tablet is idle in a cool room .
*
* To avoid these errors clamp the raw value to be inside the LPAT .
*/
if ( first . raw < last . raw )
raw = clamp ( raw , first . raw , last . raw ) ;
else
raw = clamp ( raw , last . raw , first . raw ) ;
return acpi_lpat_raw_to_temp ( lpat_table , raw ) ;
}
2021-11-26 16:21:07 +01:00
static const struct intel_pmic_opregion_data intel_xpower_pmic_opregion_data = {
2014-11-24 17:24:47 +08:00
. get_power = intel_xpower_pmic_get_power ,
. update_power = intel_xpower_pmic_update_power ,
. get_raw_temp = intel_xpower_pmic_get_raw_temp ,
2021-07-06 18:09:23 +02:00
. exec_mipi_pmic_seq_element = intel_xpower_exec_mipi_pmic_seq_element ,
2021-11-26 16:21:09 +01:00
. lpat_raw_to_temp = intel_xpower_lpat_raw_to_temp ,
2014-11-24 17:24:47 +08:00
. power_table = power_table ,
. power_table_count = ARRAY_SIZE ( power_table ) ,
. thermal_table = thermal_table ,
. thermal_table_count = ARRAY_SIZE ( thermal_table ) ,
2019-01-07 12:15:55 +01:00
. pmic_i2c_address = 0x34 ,
2014-11-24 17:24:47 +08:00
} ;
2014-11-24 17:32:33 +08:00
static acpi_status intel_xpower_pmic_gpio_handler ( u32 function ,
acpi_physical_address address , u32 bit_width , u64 * value ,
void * handler_context , void * region_context )
{
return AE_OK ;
}
2014-11-24 17:24:47 +08:00
static int intel_xpower_pmic_opregion_probe ( struct platform_device * pdev )
{
2014-11-24 17:32:33 +08:00
struct device * parent = pdev - > dev . parent ;
struct axp20x_dev * axp20x = dev_get_drvdata ( parent ) ;
acpi_status status ;
int result ;
status = acpi_install_address_space_handler ( ACPI_HANDLE ( parent ) ,
ACPI_ADR_SPACE_GPIO , intel_xpower_pmic_gpio_handler ,
NULL , NULL ) ;
if ( ACPI_FAILURE ( status ) )
return - ENODEV ;
result = intel_pmic_install_opregion_handler ( & pdev - > dev ,
ACPI_HANDLE ( parent ) , axp20x - > regmap ,
& intel_xpower_pmic_opregion_data ) ;
if ( result )
acpi_remove_address_space_handler ( ACPI_HANDLE ( parent ) ,
ACPI_ADR_SPACE_GPIO ,
intel_xpower_pmic_gpio_handler ) ;
return result ;
2014-11-24 17:24:47 +08:00
}
static struct platform_driver intel_xpower_pmic_opregion_driver = {
. probe = intel_xpower_pmic_opregion_probe ,
. driver = {
. name = " axp288_pmic_acpi " ,
} ,
} ;
2018-01-09 22:26:58 +02:00
builtin_platform_driver ( intel_xpower_pmic_opregion_driver ) ;