384 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * intel_pmic.c - Intel PMIC operation region driver4 *5 * Copyright (C) 2014 Intel Corporation. All rights reserved.6 */7 8#include <linux/export.h>9#include <linux/acpi.h>10#include <linux/mfd/intel_soc_pmic.h>11#include <linux/regmap.h>12#include <acpi/acpi_lpat.h>13#include "intel_pmic.h"14 15#define PMIC_POWER_OPREGION_ID 0x8d16#define PMIC_THERMAL_OPREGION_ID 0x8c17#define PMIC_REGS_OPREGION_ID 0x8f18 19struct intel_pmic_regs_handler_ctx {20 unsigned int val;21 u16 addr;22};23 24struct intel_pmic_opregion {25 struct mutex lock;26 struct acpi_lpat_conversion_table *lpat_table;27 struct regmap *regmap;28 const struct intel_pmic_opregion_data *data;29 struct intel_pmic_regs_handler_ctx ctx;30};31 32static struct intel_pmic_opregion *intel_pmic_opregion;33 34static int pmic_get_reg_bit(int address, const struct pmic_table *table,35 int count, int *reg, int *bit)36{37 int i;38 39 for (i = 0; i < count; i++) {40 if (table[i].address == address) {41 *reg = table[i].reg;42 if (bit)43 *bit = table[i].bit;44 return 0;45 }46 }47 return -ENOENT;48}49 50static acpi_status intel_pmic_power_handler(u32 function,51 acpi_physical_address address, u32 bits, u64 *value64,52 void *handler_context, void *region_context)53{54 struct intel_pmic_opregion *opregion = region_context;55 struct regmap *regmap = opregion->regmap;56 const struct intel_pmic_opregion_data *d = opregion->data;57 int reg, bit, result;58 59 if (bits != 32 || !value64)60 return AE_BAD_PARAMETER;61 62 if (function == ACPI_WRITE && !(*value64 == 0 || *value64 == 1))63 return AE_BAD_PARAMETER;64 65 result = pmic_get_reg_bit(address, d->power_table,66 d->power_table_count, ®, &bit);67 if (result == -ENOENT)68 return AE_BAD_PARAMETER;69 70 mutex_lock(&opregion->lock);71 72 result = function == ACPI_READ ?73 d->get_power(regmap, reg, bit, value64) :74 d->update_power(regmap, reg, bit, *value64 == 1);75 76 mutex_unlock(&opregion->lock);77 78 return result ? AE_ERROR : AE_OK;79}80 81static int pmic_read_temp(struct intel_pmic_opregion *opregion,82 int reg, u64 *value)83{84 int raw_temp, temp;85 86 if (!opregion->data->get_raw_temp)87 return -ENXIO;88 89 raw_temp = opregion->data->get_raw_temp(opregion->regmap, reg);90 if (raw_temp < 0)91 return raw_temp;92 93 if (!opregion->lpat_table) {94 *value = raw_temp;95 return 0;96 }97 98 temp = opregion->data->lpat_raw_to_temp(opregion->lpat_table, raw_temp);99 if (temp < 0)100 return temp;101 102 *value = temp;103 return 0;104}105 106static int pmic_thermal_temp(struct intel_pmic_opregion *opregion, int reg,107 u32 function, u64 *value)108{109 return function == ACPI_READ ?110 pmic_read_temp(opregion, reg, value) : -EINVAL;111}112 113static int pmic_thermal_aux(struct intel_pmic_opregion *opregion, int reg,114 u32 function, u64 *value)115{116 int raw_temp;117 118 if (function == ACPI_READ)119 return pmic_read_temp(opregion, reg, value);120 121 if (!opregion->data->update_aux)122 return -ENXIO;123 124 if (opregion->lpat_table) {125 raw_temp = acpi_lpat_temp_to_raw(opregion->lpat_table, *value);126 if (raw_temp < 0)127 return raw_temp;128 } else {129 raw_temp = *value;130 }131 132 return opregion->data->update_aux(opregion->regmap, reg, raw_temp);133}134 135static int pmic_thermal_pen(struct intel_pmic_opregion *opregion, int reg,136 int bit, u32 function, u64 *value)137{138 const struct intel_pmic_opregion_data *d = opregion->data;139 struct regmap *regmap = opregion->regmap;140 141 if (!d->get_policy || !d->update_policy)142 return -ENXIO;143 144 if (function == ACPI_READ)145 return d->get_policy(regmap, reg, bit, value);146 147 if (*value != 0 && *value != 1)148 return -EINVAL;149 150 return d->update_policy(regmap, reg, bit, *value);151}152 153static bool pmic_thermal_is_temp(int address)154{155 return (address <= 0x3c) && !(address % 12);156}157 158static bool pmic_thermal_is_aux(int address)159{160 return (address >= 4 && address <= 0x40 && !((address - 4) % 12)) ||161 (address >= 8 && address <= 0x44 && !((address - 8) % 12));162}163 164static bool pmic_thermal_is_pen(int address)165{166 return address >= 0x48 && address <= 0x5c;167}168 169static acpi_status intel_pmic_thermal_handler(u32 function,170 acpi_physical_address address, u32 bits, u64 *value64,171 void *handler_context, void *region_context)172{173 struct intel_pmic_opregion *opregion = region_context;174 const struct intel_pmic_opregion_data *d = opregion->data;175 int reg, bit, result;176 177 if (bits != 32 || !value64)178 return AE_BAD_PARAMETER;179 180 result = pmic_get_reg_bit(address, d->thermal_table,181 d->thermal_table_count, ®, &bit);182 if (result == -ENOENT)183 return AE_BAD_PARAMETER;184 185 mutex_lock(&opregion->lock);186 187 if (pmic_thermal_is_temp(address))188 result = pmic_thermal_temp(opregion, reg, function, value64);189 else if (pmic_thermal_is_aux(address))190 result = pmic_thermal_aux(opregion, reg, function, value64);191 else if (pmic_thermal_is_pen(address))192 result = pmic_thermal_pen(opregion, reg, bit,193 function, value64);194 else195 result = -EINVAL;196 197 mutex_unlock(&opregion->lock);198 199 if (result < 0) {200 if (result == -EINVAL)201 return AE_BAD_PARAMETER;202 else203 return AE_ERROR;204 }205 206 return AE_OK;207}208 209static acpi_status intel_pmic_regs_handler(u32 function,210 acpi_physical_address address, u32 bits, u64 *value64,211 void *handler_context, void *region_context)212{213 struct intel_pmic_opregion *opregion = region_context;214 int result = -EINVAL;215 216 if (function == ACPI_WRITE) {217 switch (address) {218 case 0:219 return AE_OK;220 case 1:221 opregion->ctx.addr |= (*value64 & 0xff) << 8;222 return AE_OK;223 case 2:224 opregion->ctx.addr |= *value64 & 0xff;225 return AE_OK;226 case 3:227 opregion->ctx.val = *value64 & 0xff;228 return AE_OK;229 case 4:230 if (*value64) {231 result = regmap_write(opregion->regmap, opregion->ctx.addr,232 opregion->ctx.val);233 } else {234 result = regmap_read(opregion->regmap, opregion->ctx.addr,235 &opregion->ctx.val);236 }237 opregion->ctx.addr = 0;238 }239 }240 241 if (function == ACPI_READ && address == 3) {242 *value64 = opregion->ctx.val;243 return AE_OK;244 }245 246 if (result < 0) {247 if (result == -EINVAL)248 return AE_BAD_PARAMETER;249 else250 return AE_ERROR;251 }252 253 return AE_OK;254}255 256int intel_pmic_install_opregion_handler(struct device *dev, acpi_handle handle,257 struct regmap *regmap,258 const struct intel_pmic_opregion_data *d)259{260 acpi_status status = AE_OK;261 struct intel_pmic_opregion *opregion;262 int ret;263 264 if (!dev || !regmap || !d)265 return -EINVAL;266 267 if (!handle)268 return -ENODEV;269 270 opregion = devm_kzalloc(dev, sizeof(*opregion), GFP_KERNEL);271 if (!opregion)272 return -ENOMEM;273 274 mutex_init(&opregion->lock);275 opregion->regmap = regmap;276 opregion->lpat_table = acpi_lpat_get_conversion_table(handle);277 278 if (d->power_table_count)279 status = acpi_install_address_space_handler(handle,280 PMIC_POWER_OPREGION_ID,281 intel_pmic_power_handler,282 NULL, opregion);283 if (ACPI_FAILURE(status)) {284 ret = -ENODEV;285 goto out_error;286 }287 288 if (d->thermal_table_count)289 status = acpi_install_address_space_handler(handle,290 PMIC_THERMAL_OPREGION_ID,291 intel_pmic_thermal_handler,292 NULL, opregion);293 if (ACPI_FAILURE(status)) {294 ret = -ENODEV;295 goto out_remove_power_handler;296 }297 298 status = acpi_install_address_space_handler(handle,299 PMIC_REGS_OPREGION_ID, intel_pmic_regs_handler, NULL,300 opregion);301 if (ACPI_FAILURE(status)) {302 ret = -ENODEV;303 goto out_remove_thermal_handler;304 }305 306 opregion->data = d;307 intel_pmic_opregion = opregion;308 return 0;309 310out_remove_thermal_handler:311 if (d->thermal_table_count)312 acpi_remove_address_space_handler(handle,313 PMIC_THERMAL_OPREGION_ID,314 intel_pmic_thermal_handler);315 316out_remove_power_handler:317 if (d->power_table_count)318 acpi_remove_address_space_handler(handle,319 PMIC_POWER_OPREGION_ID,320 intel_pmic_power_handler);321 322out_error:323 acpi_lpat_free_conversion_table(opregion->lpat_table);324 return ret;325}326EXPORT_SYMBOL_GPL(intel_pmic_install_opregion_handler);327 328/**329 * intel_soc_pmic_exec_mipi_pmic_seq_element - Execute PMIC MIPI sequence330 * @i2c_address: I2C client address for the PMIC331 * @reg_address: PMIC register address332 * @value: New value for the register bits to change333 * @mask: Mask indicating which register bits to change334 *335 * DSI LCD panels describe an initialization sequence in the i915 VBT (Video336 * BIOS Tables) using so called MIPI sequences. One possible element in these337 * sequences is a PMIC specific element of 15 bytes.338 *339 * This function executes these PMIC specific elements sending the embedded340 * commands to the PMIC.341 *342 * Return 0 on success, < 0 on failure.343 */344int intel_soc_pmic_exec_mipi_pmic_seq_element(u16 i2c_address, u32 reg_address,345 u32 value, u32 mask)346{347 const struct intel_pmic_opregion_data *d;348 int ret;349 350 if (!intel_pmic_opregion) {351 pr_warn("%s: No PMIC registered\n", __func__);352 return -ENXIO;353 }354 355 d = intel_pmic_opregion->data;356 357 mutex_lock(&intel_pmic_opregion->lock);358 359 if (d->exec_mipi_pmic_seq_element) {360 ret = d->exec_mipi_pmic_seq_element(intel_pmic_opregion->regmap,361 i2c_address, reg_address,362 value, mask);363 } else if (d->pmic_i2c_address) {364 if (i2c_address == d->pmic_i2c_address) {365 ret = regmap_update_bits(intel_pmic_opregion->regmap,366 reg_address, mask, value);367 } else {368 pr_err("%s: Unexpected i2c-addr: 0x%02x (reg-addr 0x%x value 0x%x mask 0x%x)\n",369 __func__, i2c_address, reg_address, value, mask);370 ret = -ENXIO;371 }372 } else {373 pr_warn("%s: Not implemented\n", __func__);374 pr_warn("%s: i2c-addr: 0x%x reg-addr 0x%x value 0x%x mask 0x%x\n",375 __func__, i2c_address, reg_address, value, mask);376 ret = -EOPNOTSUPP;377 }378 379 mutex_unlock(&intel_pmic_opregion->lock);380 381 return ret;382}383EXPORT_SYMBOL_GPL(intel_soc_pmic_exec_mipi_pmic_seq_element);384