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