brintos

brintos / linux-shallow public Read only

0
0
Text · 8.2 KiB · 49bda5e Raw
369 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * XPower AXP288 PMIC operation region driver4 *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	0x5b17#define XPOWER_GPI1_CTRL	0x9218 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 const 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 = 0x00124	}, /* BUC6 */125	{126		.address = 0x4c,127		.reg = 0x92,128	}, /* GPI1 */129};130 131/* TMP0 - TMP5 are the same, all from GPADC */132static const struct pmic_table thermal_table[] = {133	{134		.address = 0x00,135		.reg = XPOWER_GPADC_LOW136	},137	{138		.address = 0x0c,139		.reg = XPOWER_GPADC_LOW140	},141	{142		.address = 0x18,143		.reg = XPOWER_GPADC_LOW144	},145	{146		.address = 0x24,147		.reg = XPOWER_GPADC_LOW148	},149	{150		.address = 0x30,151		.reg = XPOWER_GPADC_LOW152	},153	{154		.address = 0x3c,155		.reg = XPOWER_GPADC_LOW156	},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	else171		*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	else200		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 PMIC212 *213 * @regmap: regmap of the PMIC device214 * @reg: register to get the reading215 *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 shared225	 * with the GPADC. For proper fuel-gauge and charger operation the TS226	 * current-source needs to be permanently on. But to read the GPADC we227	 * need to temporary switch the TS current-source to ondemand, so that228	 * 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 necessary231	 * when the TS current-source is off (this happens on devices which232	 * 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, so241		 * this does to a single I2C-transfer, and thus there is no242		 * 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, sizeof(buf));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	struct device *dev = regmap_get_device(regmap);278	int ret;279 280	if (i2c_address != 0x34) {281		dev_err(dev, "Unexpected i2c-addr: 0x%02x (reg-addr 0x%x value 0x%x mask 0x%x)\n",282			i2c_address, reg_address, value, mask);283		return -ENXIO;284	}285 286	ret = iosf_mbi_block_punit_i2c_access();287	if (ret)288		return ret;289 290	ret = regmap_update_bits(regmap, reg_address, mask, value);291 292	iosf_mbi_unblock_punit_i2c_access();293 294	return ret;295}296 297static int intel_xpower_lpat_raw_to_temp(struct acpi_lpat_conversion_table *lpat_table,298					 int raw)299{300	struct acpi_lpat first = lpat_table->lpat[0];301	struct acpi_lpat last = lpat_table->lpat[lpat_table->lpat_count - 1];302 303	/*304	 * Some LPAT tables in the ACPI Device for the AXP288 PMIC for some305	 * reason only describe a small temperature range, e.g. 27° - 37°306	 * Celcius. Resulting in errors when the tablet is idle in a cool room.307	 *308	 * To avoid these errors clamp the raw value to be inside the LPAT.309	 */310	if (first.raw < last.raw)311		raw = clamp(raw, first.raw, last.raw);312	else313		raw = clamp(raw, last.raw, first.raw);314 315	return acpi_lpat_raw_to_temp(lpat_table, raw);316}317 318static const struct intel_pmic_opregion_data intel_xpower_pmic_opregion_data = {319	.get_power = intel_xpower_pmic_get_power,320	.update_power = intel_xpower_pmic_update_power,321	.get_raw_temp = intel_xpower_pmic_get_raw_temp,322	.exec_mipi_pmic_seq_element = intel_xpower_exec_mipi_pmic_seq_element,323	.lpat_raw_to_temp = intel_xpower_lpat_raw_to_temp,324	.power_table = power_table,325	.power_table_count = ARRAY_SIZE(power_table),326	.thermal_table = thermal_table,327	.thermal_table_count = ARRAY_SIZE(thermal_table),328	.pmic_i2c_address = 0x34,329};330 331static acpi_status intel_xpower_pmic_gpio_handler(u32 function,332		acpi_physical_address address, u32 bit_width, u64 *value,333		void *handler_context, void *region_context)334{335	return AE_OK;336}337 338static int intel_xpower_pmic_opregion_probe(struct platform_device *pdev)339{340	struct device *parent = pdev->dev.parent;341	struct axp20x_dev *axp20x = dev_get_drvdata(parent);342	acpi_status status;343	int result;344 345	status = acpi_install_address_space_handler(ACPI_HANDLE(parent),346			ACPI_ADR_SPACE_GPIO, intel_xpower_pmic_gpio_handler,347			NULL, NULL);348	if (ACPI_FAILURE(status))349		return -ENODEV;350 351	result = intel_pmic_install_opregion_handler(&pdev->dev,352					ACPI_HANDLE(parent), axp20x->regmap,353					&intel_xpower_pmic_opregion_data);354	if (result)355		acpi_remove_address_space_handler(ACPI_HANDLE(parent),356						  ACPI_ADR_SPACE_GPIO,357						  intel_xpower_pmic_gpio_handler);358 359	return result;360}361 362static struct platform_driver intel_xpower_pmic_opregion_driver = {363	.probe = intel_xpower_pmic_opregion_probe,364	.driver = {365		.name = "axp288_pmic_acpi",366	},367};368builtin_platform_driver(intel_xpower_pmic_opregion_driver);369