cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

intel_pmic_xpower.c (8305B)


      1// SPDX-License-Identifier: GPL-2.0
      2/*
      3 * XPower AXP288 PMIC operation region driver
      4 *
      5 * Copyright (C) 2014 Intel Corporation. All rights reserved.
      6 */
      7
      8#include <linux/acpi.h>
      9#include <linux/init.h>
     10#include <linux/mfd/axp20x.h>
     11#include <linux/regmap.h>
     12#include <linux/platform_device.h>
     13#include <asm/iosf_mbi.h>
     14#include "intel_pmic.h"
     15
     16#define XPOWER_GPADC_LOW	0x5b
     17#define XPOWER_GPI1_CTRL	0x92
     18
     19#define GPI1_LDO_MASK		GENMASK(2, 0)
     20#define GPI1_LDO_ON		(3 << 0)
     21#define GPI1_LDO_OFF		(4 << 0)
     22
     23#define AXP288_ADC_TS_CURRENT_ON_OFF_MASK		GENMASK(1, 0)
     24#define AXP288_ADC_TS_CURRENT_OFF			(0 << 0)
     25#define AXP288_ADC_TS_CURRENT_ON_WHEN_CHARGING		(1 << 0)
     26#define AXP288_ADC_TS_CURRENT_ON_ONDEMAND		(2 << 0)
     27#define AXP288_ADC_TS_CURRENT_ON			(3 << 0)
     28
     29static struct pmic_table power_table[] = {
     30	{
     31		.address = 0x00,
     32		.reg = 0x13,
     33		.bit = 0x05,
     34	}, /* ALD1 */
     35	{
     36		.address = 0x04,
     37		.reg = 0x13,
     38		.bit = 0x06,
     39	}, /* ALD2 */
     40	{
     41		.address = 0x08,
     42		.reg = 0x13,
     43		.bit = 0x07,
     44	}, /* ALD3 */
     45	{
     46		.address = 0x0c,
     47		.reg = 0x12,
     48		.bit = 0x03,
     49	}, /* DLD1 */
     50	{
     51		.address = 0x10,
     52		.reg = 0x12,
     53		.bit = 0x04,
     54	}, /* DLD2 */
     55	{
     56		.address = 0x14,
     57		.reg = 0x12,
     58		.bit = 0x05,
     59	}, /* DLD3 */
     60	{
     61		.address = 0x18,
     62		.reg = 0x12,
     63		.bit = 0x06,
     64	}, /* DLD4 */
     65	{
     66		.address = 0x1c,
     67		.reg = 0x12,
     68		.bit = 0x00,
     69	}, /* ELD1 */
     70	{
     71		.address = 0x20,
     72		.reg = 0x12,
     73		.bit = 0x01,
     74	}, /* ELD2 */
     75	{
     76		.address = 0x24,
     77		.reg = 0x12,
     78		.bit = 0x02,
     79	}, /* ELD3 */
     80	{
     81		.address = 0x28,
     82		.reg = 0x13,
     83		.bit = 0x02,
     84	}, /* FLD1 */
     85	{
     86		.address = 0x2c,
     87		.reg = 0x13,
     88		.bit = 0x03,
     89	}, /* FLD2 */
     90	{
     91		.address = 0x30,
     92		.reg = 0x13,
     93		.bit = 0x04,
     94	}, /* FLD3 */
     95	{
     96		.address = 0x34,
     97		.reg = 0x10,
     98		.bit = 0x03,
     99	}, /* BUC1 */
    100	{
    101		.address = 0x38,
    102		.reg = 0x10,
    103		.bit = 0x06,
    104	}, /* BUC2 */
    105	{
    106		.address = 0x3c,
    107		.reg = 0x10,
    108		.bit = 0x05,
    109	}, /* BUC3 */
    110	{
    111		.address = 0x40,
    112		.reg = 0x10,
    113		.bit = 0x04,
    114	}, /* BUC4 */
    115	{
    116		.address = 0x44,
    117		.reg = 0x10,
    118		.bit = 0x01,
    119	}, /* BUC5 */
    120	{
    121		.address = 0x48,
    122		.reg = 0x10,
    123		.bit = 0x00
    124	}, /* BUC6 */
    125	{
    126		.address = 0x4c,
    127		.reg = 0x92,
    128	}, /* GPI1 */
    129};
    130
    131/* TMP0 - TMP5 are the same, all from GPADC */
    132static struct pmic_table thermal_table[] = {
    133	{
    134		.address = 0x00,
    135		.reg = XPOWER_GPADC_LOW
    136	},
    137	{
    138		.address = 0x0c,
    139		.reg = XPOWER_GPADC_LOW
    140	},
    141	{
    142		.address = 0x18,
    143		.reg = XPOWER_GPADC_LOW
    144	},
    145	{
    146		.address = 0x24,
    147		.reg = XPOWER_GPADC_LOW
    148	},
    149	{
    150		.address = 0x30,
    151		.reg = XPOWER_GPADC_LOW
    152	},
    153	{
    154		.address = 0x3c,
    155		.reg = XPOWER_GPADC_LOW
    156	},
    157};
    158
    159static int intel_xpower_pmic_get_power(struct regmap *regmap, int reg,
    160				       int bit, u64 *value)
    161{
    162	int data;
    163
    164	if (regmap_read(regmap, reg, &data))
    165		return -EIO;
    166
    167	/* GPIO1 LDO regulator needs special handling */
    168	if (reg == XPOWER_GPI1_CTRL)
    169		*value = ((data & GPI1_LDO_MASK) == GPI1_LDO_ON);
    170	else
    171		*value = (data & BIT(bit)) ? 1 : 0;
    172
    173	return 0;
    174}
    175
    176static int intel_xpower_pmic_update_power(struct regmap *regmap, int reg,
    177					  int bit, bool on)
    178{
    179	int data, ret;
    180
    181	ret = iosf_mbi_block_punit_i2c_access();
    182	if (ret)
    183		return ret;
    184
    185	/* GPIO1 LDO regulator needs special handling */
    186	if (reg == XPOWER_GPI1_CTRL) {
    187		ret = regmap_update_bits(regmap, reg, GPI1_LDO_MASK,
    188					 on ? GPI1_LDO_ON : GPI1_LDO_OFF);
    189		goto out;
    190	}
    191
    192	if (regmap_read(regmap, reg, &data)) {
    193		ret = -EIO;
    194		goto out;
    195	}
    196
    197	if (on)
    198		data |= BIT(bit);
    199	else
    200		data &= ~BIT(bit);
    201
    202	if (regmap_write(regmap, reg, data))
    203		ret = -EIO;
    204out:
    205	iosf_mbi_unblock_punit_i2c_access();
    206
    207	return ret;
    208}
    209
    210/**
    211 * intel_xpower_pmic_get_raw_temp(): Get raw temperature reading from the PMIC
    212 *
    213 * @regmap: regmap of the PMIC device
    214 * @reg: register to get the reading
    215 *
    216 * Return a positive value on success, errno on failure.
    217 */
    218static int intel_xpower_pmic_get_raw_temp(struct regmap *regmap, int reg)
    219{
    220	int ret, adc_ts_pin_ctrl;
    221	u8 buf[2];
    222
    223	/*
    224	 * The current-source used for the battery temp-sensor (TS) is shared
    225	 * with the GPADC. For proper fuel-gauge and charger operation the TS
    226	 * current-source needs to be permanently on. But to read the GPADC we
    227	 * need to temporary switch the TS current-source to ondemand, so that
    228	 * the GPADC can use it, otherwise we will always read an all 0 value.
    229	 *
    230	 * Note that the switching from on to on-ondemand is not necessary
    231	 * when the TS current-source is off (this happens on devices which
    232	 * do not use the TS-pin).
    233	 */
    234	ret = regmap_read(regmap, AXP288_ADC_TS_PIN_CTRL, &adc_ts_pin_ctrl);
    235	if (ret)
    236		return ret;
    237
    238	if (adc_ts_pin_ctrl & AXP288_ADC_TS_CURRENT_ON_OFF_MASK) {
    239		/*
    240		 * AXP288_ADC_TS_PIN_CTRL reads are cached by the regmap, so
    241		 * this does to a single I2C-transfer, and thus there is no
    242		 * need to explicitly call iosf_mbi_block_punit_i2c_access().
    243		 */
    244		ret = regmap_update_bits(regmap, AXP288_ADC_TS_PIN_CTRL,
    245					 AXP288_ADC_TS_CURRENT_ON_OFF_MASK,
    246					 AXP288_ADC_TS_CURRENT_ON_ONDEMAND);
    247		if (ret)
    248			return ret;
    249
    250		/* Wait a bit after switching the current-source */
    251		usleep_range(6000, 10000);
    252	}
    253
    254	ret = iosf_mbi_block_punit_i2c_access();
    255	if (ret)
    256		return ret;
    257
    258	ret = regmap_bulk_read(regmap, AXP288_GP_ADC_H, buf, 2);
    259	if (ret == 0)
    260		ret = (buf[0] << 4) + ((buf[1] >> 4) & 0x0f);
    261
    262	if (adc_ts_pin_ctrl & AXP288_ADC_TS_CURRENT_ON_OFF_MASK) {
    263		regmap_update_bits(regmap, AXP288_ADC_TS_PIN_CTRL,
    264				   AXP288_ADC_TS_CURRENT_ON_OFF_MASK,
    265				   AXP288_ADC_TS_CURRENT_ON);
    266	}
    267
    268	iosf_mbi_unblock_punit_i2c_access();
    269
    270	return ret;
    271}
    272
    273static int intel_xpower_exec_mipi_pmic_seq_element(struct regmap *regmap,
    274						   u16 i2c_address, u32 reg_address,
    275						   u32 value, u32 mask)
    276{
    277	int ret;
    278
    279	if (i2c_address != 0x34) {
    280		pr_err("%s: Unexpected i2c-addr: 0x%02x (reg-addr 0x%x value 0x%x mask 0x%x)\n",
    281		       __func__, i2c_address, reg_address, value, mask);
    282		return -ENXIO;
    283	}
    284
    285	ret = iosf_mbi_block_punit_i2c_access();
    286	if (ret)
    287		return ret;
    288
    289	ret = regmap_update_bits(regmap, reg_address, mask, value);
    290
    291	iosf_mbi_unblock_punit_i2c_access();
    292
    293	return ret;
    294}
    295
    296static int intel_xpower_lpat_raw_to_temp(struct acpi_lpat_conversion_table *lpat_table,
    297					 int raw)
    298{
    299	struct acpi_lpat first = lpat_table->lpat[0];
    300	struct acpi_lpat last = lpat_table->lpat[lpat_table->lpat_count - 1];
    301
    302	/*
    303	 * Some LPAT tables in the ACPI Device for the AXP288 PMIC for some
    304	 * reason only describe a small temperature range, e.g. 27° - 37°
    305	 * Celcius. Resulting in errors when the tablet is idle in a cool room.
    306	 *
    307	 * To avoid these errors clamp the raw value to be inside the LPAT.
    308	 */
    309	if (first.raw < last.raw)
    310		raw = clamp(raw, first.raw, last.raw);
    311	else
    312		raw = clamp(raw, last.raw, first.raw);
    313
    314	return acpi_lpat_raw_to_temp(lpat_table, raw);
    315}
    316
    317static const struct intel_pmic_opregion_data intel_xpower_pmic_opregion_data = {
    318	.get_power = intel_xpower_pmic_get_power,
    319	.update_power = intel_xpower_pmic_update_power,
    320	.get_raw_temp = intel_xpower_pmic_get_raw_temp,
    321	.exec_mipi_pmic_seq_element = intel_xpower_exec_mipi_pmic_seq_element,
    322	.lpat_raw_to_temp = intel_xpower_lpat_raw_to_temp,
    323	.power_table = power_table,
    324	.power_table_count = ARRAY_SIZE(power_table),
    325	.thermal_table = thermal_table,
    326	.thermal_table_count = ARRAY_SIZE(thermal_table),
    327	.pmic_i2c_address = 0x34,
    328};
    329
    330static acpi_status intel_xpower_pmic_gpio_handler(u32 function,
    331		acpi_physical_address address, u32 bit_width, u64 *value,
    332		void *handler_context, void *region_context)
    333{
    334	return AE_OK;
    335}
    336
    337static int intel_xpower_pmic_opregion_probe(struct platform_device *pdev)
    338{
    339	struct device *parent = pdev->dev.parent;
    340	struct axp20x_dev *axp20x = dev_get_drvdata(parent);
    341	acpi_status status;
    342	int result;
    343
    344	status = acpi_install_address_space_handler(ACPI_HANDLE(parent),
    345			ACPI_ADR_SPACE_GPIO, intel_xpower_pmic_gpio_handler,
    346			NULL, NULL);
    347	if (ACPI_FAILURE(status))
    348		return -ENODEV;
    349
    350	result = intel_pmic_install_opregion_handler(&pdev->dev,
    351					ACPI_HANDLE(parent), axp20x->regmap,
    352					&intel_xpower_pmic_opregion_data);
    353	if (result)
    354		acpi_remove_address_space_handler(ACPI_HANDLE(parent),
    355						  ACPI_ADR_SPACE_GPIO,
    356						  intel_xpower_pmic_gpio_handler);
    357
    358	return result;
    359}
    360
    361static struct platform_driver intel_xpower_pmic_opregion_driver = {
    362	.probe = intel_xpower_pmic_opregion_probe,
    363	.driver = {
    364		.name = "axp288_pmic_acpi",
    365	},
    366};
    367builtin_platform_driver(intel_xpower_pmic_opregion_driver);