1b1eea857SAaron Lu /* 2b1eea857SAaron Lu * intel_pmic.c - Intel PMIC operation region driver 3b1eea857SAaron Lu * 4b1eea857SAaron Lu * Copyright (C) 2014 Intel Corporation. All rights reserved. 5b1eea857SAaron Lu * 6b1eea857SAaron Lu * This program is free software; you can redistribute it and/or 7b1eea857SAaron Lu * modify it under the terms of the GNU General Public License version 8b1eea857SAaron Lu * 2 as published by the Free Software Foundation. 9b1eea857SAaron Lu * 10b1eea857SAaron Lu * This program is distributed in the hope that it will be useful, 11b1eea857SAaron Lu * but WITHOUT ANY WARRANTY; without even the implied warranty of 12b1eea857SAaron Lu * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13b1eea857SAaron Lu * GNU General Public License for more details. 14b1eea857SAaron Lu */ 15b1eea857SAaron Lu 16b1eea857SAaron Lu #include <linux/module.h> 17b1eea857SAaron Lu #include <linux/acpi.h> 18b1eea857SAaron Lu #include <linux/regmap.h> 19b1eea857SAaron Lu #include "intel_pmic.h" 20b1eea857SAaron Lu 21b1eea857SAaron Lu #define PMIC_POWER_OPREGION_ID 0x8d 22b1eea857SAaron Lu #define PMIC_THERMAL_OPREGION_ID 0x8c 23b1eea857SAaron Lu 24b1eea857SAaron Lu struct acpi_lpat { 25b1eea857SAaron Lu int temp; 26b1eea857SAaron Lu int raw; 27b1eea857SAaron Lu }; 28b1eea857SAaron Lu 29b1eea857SAaron Lu struct intel_pmic_opregion { 30b1eea857SAaron Lu struct mutex lock; 31b1eea857SAaron Lu struct acpi_lpat *lpat; 32b1eea857SAaron Lu int lpat_count; 33b1eea857SAaron Lu struct regmap *regmap; 34b1eea857SAaron Lu struct intel_pmic_opregion_data *data; 35b1eea857SAaron Lu }; 36b1eea857SAaron Lu 37b1eea857SAaron Lu static int pmic_get_reg_bit(int address, struct pmic_table *table, 38b1eea857SAaron Lu int count, int *reg, int *bit) 39b1eea857SAaron Lu { 40b1eea857SAaron Lu int i; 41b1eea857SAaron Lu 42b1eea857SAaron Lu for (i = 0; i < count; i++) { 43b1eea857SAaron Lu if (table[i].address == address) { 44b1eea857SAaron Lu *reg = table[i].reg; 45b1eea857SAaron Lu if (bit) 46b1eea857SAaron Lu *bit = table[i].bit; 47b1eea857SAaron Lu return 0; 48b1eea857SAaron Lu } 49b1eea857SAaron Lu } 50b1eea857SAaron Lu return -ENOENT; 51b1eea857SAaron Lu } 52b1eea857SAaron Lu 53b1eea857SAaron Lu /** 54b1eea857SAaron Lu * raw_to_temp(): Return temperature from raw value through LPAT table 55b1eea857SAaron Lu * 56b1eea857SAaron Lu * @lpat: the temperature_raw mapping table 57b1eea857SAaron Lu * @count: the count of the above mapping table 58b1eea857SAaron Lu * @raw: the raw value, used as a key to get the temerature from the 59b1eea857SAaron Lu * above mapping table 60b1eea857SAaron Lu * 61b1eea857SAaron Lu * A positive value will be returned on success, a negative errno will 62b1eea857SAaron Lu * be returned in error cases. 63b1eea857SAaron Lu */ 64b1eea857SAaron Lu static int raw_to_temp(struct acpi_lpat *lpat, int count, int raw) 65b1eea857SAaron Lu { 66b1eea857SAaron Lu int i, delta_temp, delta_raw, temp; 67b1eea857SAaron Lu 68b1eea857SAaron Lu for (i = 0; i < count - 1; i++) { 69b1eea857SAaron Lu if ((raw >= lpat[i].raw && raw <= lpat[i+1].raw) || 70b1eea857SAaron Lu (raw <= lpat[i].raw && raw >= lpat[i+1].raw)) 71b1eea857SAaron Lu break; 72b1eea857SAaron Lu } 73b1eea857SAaron Lu 74b1eea857SAaron Lu if (i == count - 1) 75b1eea857SAaron Lu return -ENOENT; 76b1eea857SAaron Lu 77b1eea857SAaron Lu delta_temp = lpat[i+1].temp - lpat[i].temp; 78b1eea857SAaron Lu delta_raw = lpat[i+1].raw - lpat[i].raw; 79b1eea857SAaron Lu temp = lpat[i].temp + (raw - lpat[i].raw) * delta_temp / delta_raw; 80b1eea857SAaron Lu 81b1eea857SAaron Lu return temp; 82b1eea857SAaron Lu } 83b1eea857SAaron Lu 84b1eea857SAaron Lu /** 85b1eea857SAaron Lu * temp_to_raw(): Return raw value from temperature through LPAT table 86b1eea857SAaron Lu * 87b1eea857SAaron Lu * @lpat: the temperature_raw mapping table 88b1eea857SAaron Lu * @count: the count of the above mapping table 89b1eea857SAaron Lu * @temp: the temperature, used as a key to get the raw value from the 90b1eea857SAaron Lu * above mapping table 91b1eea857SAaron Lu * 92b1eea857SAaron Lu * A positive value will be returned on success, a negative errno will 93b1eea857SAaron Lu * be returned in error cases. 94b1eea857SAaron Lu */ 95b1eea857SAaron Lu static int temp_to_raw(struct acpi_lpat *lpat, int count, int temp) 96b1eea857SAaron Lu { 97b1eea857SAaron Lu int i, delta_temp, delta_raw, raw; 98b1eea857SAaron Lu 99b1eea857SAaron Lu for (i = 0; i < count - 1; i++) { 100b1eea857SAaron Lu if (temp >= lpat[i].temp && temp <= lpat[i+1].temp) 101b1eea857SAaron Lu break; 102b1eea857SAaron Lu } 103b1eea857SAaron Lu 104b1eea857SAaron Lu if (i == count - 1) 105b1eea857SAaron Lu return -ENOENT; 106b1eea857SAaron Lu 107b1eea857SAaron Lu delta_temp = lpat[i+1].temp - lpat[i].temp; 108b1eea857SAaron Lu delta_raw = lpat[i+1].raw - lpat[i].raw; 109b1eea857SAaron Lu raw = lpat[i].raw + (temp - lpat[i].temp) * delta_raw / delta_temp; 110b1eea857SAaron Lu 111b1eea857SAaron Lu return raw; 112b1eea857SAaron Lu } 113b1eea857SAaron Lu 114b1eea857SAaron Lu static void pmic_thermal_lpat(struct intel_pmic_opregion *opregion, 115b1eea857SAaron Lu acpi_handle handle, struct device *dev) 116b1eea857SAaron Lu { 117b1eea857SAaron Lu struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; 118b1eea857SAaron Lu union acpi_object *obj_p, *obj_e; 119b1eea857SAaron Lu int *lpat, i; 120b1eea857SAaron Lu acpi_status status; 121b1eea857SAaron Lu 122b1eea857SAaron Lu status = acpi_evaluate_object(handle, "LPAT", NULL, &buffer); 123b1eea857SAaron Lu if (ACPI_FAILURE(status)) 124b1eea857SAaron Lu return; 125b1eea857SAaron Lu 126b1eea857SAaron Lu obj_p = (union acpi_object *)buffer.pointer; 127b1eea857SAaron Lu if (!obj_p || (obj_p->type != ACPI_TYPE_PACKAGE) || 128b1eea857SAaron Lu (obj_p->package.count % 2) || (obj_p->package.count < 4)) 129b1eea857SAaron Lu goto out; 130b1eea857SAaron Lu 131b1eea857SAaron Lu lpat = devm_kmalloc(dev, sizeof(int) * obj_p->package.count, 132b1eea857SAaron Lu GFP_KERNEL); 133b1eea857SAaron Lu if (!lpat) 134b1eea857SAaron Lu goto out; 135b1eea857SAaron Lu 136b1eea857SAaron Lu for (i = 0; i < obj_p->package.count; i++) { 137b1eea857SAaron Lu obj_e = &obj_p->package.elements[i]; 138b1eea857SAaron Lu if (obj_e->type != ACPI_TYPE_INTEGER) { 139b1eea857SAaron Lu devm_kfree(dev, lpat); 140b1eea857SAaron Lu goto out; 141b1eea857SAaron Lu } 142b1eea857SAaron Lu lpat[i] = (s64)obj_e->integer.value; 143b1eea857SAaron Lu } 144b1eea857SAaron Lu 145b1eea857SAaron Lu opregion->lpat = (struct acpi_lpat *)lpat; 146b1eea857SAaron Lu opregion->lpat_count = obj_p->package.count / 2; 147b1eea857SAaron Lu 148b1eea857SAaron Lu out: 149b1eea857SAaron Lu kfree(buffer.pointer); 150b1eea857SAaron Lu } 151b1eea857SAaron Lu 152b1eea857SAaron Lu static acpi_status intel_pmic_power_handler(u32 function, 153b1eea857SAaron Lu acpi_physical_address address, u32 bits, u64 *value64, 154b1eea857SAaron Lu void *handler_context, void *region_context) 155b1eea857SAaron Lu { 156b1eea857SAaron Lu struct intel_pmic_opregion *opregion = region_context; 157b1eea857SAaron Lu struct regmap *regmap = opregion->regmap; 158b1eea857SAaron Lu struct intel_pmic_opregion_data *d = opregion->data; 159b1eea857SAaron Lu int reg, bit, result; 160b1eea857SAaron Lu 161b1eea857SAaron Lu if (bits != 32 || !value64) 162b1eea857SAaron Lu return AE_BAD_PARAMETER; 163b1eea857SAaron Lu 164b1eea857SAaron Lu if (function == ACPI_WRITE && !(*value64 == 0 || *value64 == 1)) 165b1eea857SAaron Lu return AE_BAD_PARAMETER; 166b1eea857SAaron Lu 167b1eea857SAaron Lu result = pmic_get_reg_bit(address, d->power_table, 168b1eea857SAaron Lu d->power_table_count, ®, &bit); 169b1eea857SAaron Lu if (result == -ENOENT) 170b1eea857SAaron Lu return AE_BAD_PARAMETER; 171b1eea857SAaron Lu 172b1eea857SAaron Lu mutex_lock(&opregion->lock); 173b1eea857SAaron Lu 174b1eea857SAaron Lu result = function == ACPI_READ ? 175b1eea857SAaron Lu d->get_power(regmap, reg, bit, value64) : 176b1eea857SAaron Lu d->update_power(regmap, reg, bit, *value64 == 1); 177b1eea857SAaron Lu 178b1eea857SAaron Lu mutex_unlock(&opregion->lock); 179b1eea857SAaron Lu 180b1eea857SAaron Lu return result ? AE_ERROR : AE_OK; 181b1eea857SAaron Lu } 182b1eea857SAaron Lu 183b1eea857SAaron Lu static int pmic_read_temp(struct intel_pmic_opregion *opregion, 184b1eea857SAaron Lu int reg, u64 *value) 185b1eea857SAaron Lu { 186b1eea857SAaron Lu int raw_temp, temp; 187b1eea857SAaron Lu 188b1eea857SAaron Lu if (!opregion->data->get_raw_temp) 189b1eea857SAaron Lu return -ENXIO; 190b1eea857SAaron Lu 191b1eea857SAaron Lu raw_temp = opregion->data->get_raw_temp(opregion->regmap, reg); 192b1eea857SAaron Lu if (raw_temp < 0) 193b1eea857SAaron Lu return raw_temp; 194b1eea857SAaron Lu 195b1eea857SAaron Lu if (!opregion->lpat) { 196b1eea857SAaron Lu *value = raw_temp; 197b1eea857SAaron Lu return 0; 198b1eea857SAaron Lu } 199b1eea857SAaron Lu 200b1eea857SAaron Lu temp = raw_to_temp(opregion->lpat, opregion->lpat_count, raw_temp); 201b1eea857SAaron Lu if (temp < 0) 202b1eea857SAaron Lu return temp; 203b1eea857SAaron Lu 204b1eea857SAaron Lu *value = temp; 205b1eea857SAaron Lu return 0; 206b1eea857SAaron Lu } 207b1eea857SAaron Lu 208b1eea857SAaron Lu static int pmic_thermal_temp(struct intel_pmic_opregion *opregion, int reg, 209b1eea857SAaron Lu u32 function, u64 *value) 210b1eea857SAaron Lu { 211b1eea857SAaron Lu return function == ACPI_READ ? 212b1eea857SAaron Lu pmic_read_temp(opregion, reg, value) : -EINVAL; 213b1eea857SAaron Lu } 214b1eea857SAaron Lu 215b1eea857SAaron Lu static int pmic_thermal_aux(struct intel_pmic_opregion *opregion, int reg, 216b1eea857SAaron Lu u32 function, u64 *value) 217b1eea857SAaron Lu { 218b1eea857SAaron Lu int raw_temp; 219b1eea857SAaron Lu 220b1eea857SAaron Lu if (function == ACPI_READ) 221b1eea857SAaron Lu return pmic_read_temp(opregion, reg, value); 222b1eea857SAaron Lu 223b1eea857SAaron Lu if (!opregion->data->update_aux) 224b1eea857SAaron Lu return -ENXIO; 225b1eea857SAaron Lu 226b1eea857SAaron Lu if (opregion->lpat) { 227b1eea857SAaron Lu raw_temp = temp_to_raw(opregion->lpat, opregion->lpat_count, 228b1eea857SAaron Lu *value); 229b1eea857SAaron Lu if (raw_temp < 0) 230b1eea857SAaron Lu return raw_temp; 231b1eea857SAaron Lu } else { 232b1eea857SAaron Lu raw_temp = *value; 233b1eea857SAaron Lu } 234b1eea857SAaron Lu 235b1eea857SAaron Lu return opregion->data->update_aux(opregion->regmap, reg, raw_temp); 236b1eea857SAaron Lu } 237b1eea857SAaron Lu 238b1eea857SAaron Lu static int pmic_thermal_pen(struct intel_pmic_opregion *opregion, int reg, 239b1eea857SAaron Lu u32 function, u64 *value) 240b1eea857SAaron Lu { 241b1eea857SAaron Lu struct intel_pmic_opregion_data *d = opregion->data; 242b1eea857SAaron Lu struct regmap *regmap = opregion->regmap; 243b1eea857SAaron Lu 244b1eea857SAaron Lu if (!d->get_policy || !d->update_policy) 245b1eea857SAaron Lu return -ENXIO; 246b1eea857SAaron Lu 247b1eea857SAaron Lu if (function == ACPI_READ) 248b1eea857SAaron Lu return d->get_policy(regmap, reg, value); 249b1eea857SAaron Lu 250b1eea857SAaron Lu if (*value != 0 && *value != 1) 251b1eea857SAaron Lu return -EINVAL; 252b1eea857SAaron Lu 253b1eea857SAaron Lu return d->update_policy(regmap, reg, *value); 254b1eea857SAaron Lu } 255b1eea857SAaron Lu 256b1eea857SAaron Lu static bool pmic_thermal_is_temp(int address) 257b1eea857SAaron Lu { 258b1eea857SAaron Lu return (address <= 0x3c) && !(address % 12); 259b1eea857SAaron Lu } 260b1eea857SAaron Lu 261b1eea857SAaron Lu static bool pmic_thermal_is_aux(int address) 262b1eea857SAaron Lu { 263b1eea857SAaron Lu return (address >= 4 && address <= 0x40 && !((address - 4) % 12)) || 264b1eea857SAaron Lu (address >= 8 && address <= 0x44 && !((address - 8) % 12)); 265b1eea857SAaron Lu } 266b1eea857SAaron Lu 267b1eea857SAaron Lu static bool pmic_thermal_is_pen(int address) 268b1eea857SAaron Lu { 269b1eea857SAaron Lu return address >= 0x48 && address <= 0x5c; 270b1eea857SAaron Lu } 271b1eea857SAaron Lu 272b1eea857SAaron Lu static acpi_status intel_pmic_thermal_handler(u32 function, 273b1eea857SAaron Lu acpi_physical_address address, u32 bits, u64 *value64, 274b1eea857SAaron Lu void *handler_context, void *region_context) 275b1eea857SAaron Lu { 276b1eea857SAaron Lu struct intel_pmic_opregion *opregion = region_context; 277b1eea857SAaron Lu struct intel_pmic_opregion_data *d = opregion->data; 278b1eea857SAaron Lu int reg, result; 279b1eea857SAaron Lu 280b1eea857SAaron Lu if (bits != 32 || !value64) 281b1eea857SAaron Lu return AE_BAD_PARAMETER; 282b1eea857SAaron Lu 283b1eea857SAaron Lu result = pmic_get_reg_bit(address, d->thermal_table, 284b1eea857SAaron Lu d->thermal_table_count, ®, NULL); 285b1eea857SAaron Lu if (result == -ENOENT) 286b1eea857SAaron Lu return AE_BAD_PARAMETER; 287b1eea857SAaron Lu 288b1eea857SAaron Lu mutex_lock(&opregion->lock); 289b1eea857SAaron Lu 290b1eea857SAaron Lu if (pmic_thermal_is_temp(address)) 291b1eea857SAaron Lu result = pmic_thermal_temp(opregion, reg, function, value64); 292b1eea857SAaron Lu else if (pmic_thermal_is_aux(address)) 293b1eea857SAaron Lu result = pmic_thermal_aux(opregion, reg, function, value64); 294b1eea857SAaron Lu else if (pmic_thermal_is_pen(address)) 295b1eea857SAaron Lu result = pmic_thermal_pen(opregion, reg, function, value64); 296b1eea857SAaron Lu else 297b1eea857SAaron Lu result = -EINVAL; 298b1eea857SAaron Lu 299b1eea857SAaron Lu mutex_unlock(&opregion->lock); 300b1eea857SAaron Lu 301b1eea857SAaron Lu if (result < 0) { 302b1eea857SAaron Lu if (result == -EINVAL) 303b1eea857SAaron Lu return AE_BAD_PARAMETER; 304b1eea857SAaron Lu else 305b1eea857SAaron Lu return AE_ERROR; 306b1eea857SAaron Lu } 307b1eea857SAaron Lu 308b1eea857SAaron Lu return AE_OK; 309b1eea857SAaron Lu } 310b1eea857SAaron Lu 311b1eea857SAaron Lu int intel_pmic_install_opregion_handler(struct device *dev, acpi_handle handle, 312b1eea857SAaron Lu struct regmap *regmap, 313b1eea857SAaron Lu struct intel_pmic_opregion_data *d) 314b1eea857SAaron Lu { 315b1eea857SAaron Lu acpi_status status; 316b1eea857SAaron Lu struct intel_pmic_opregion *opregion; 317b1eea857SAaron Lu 318b1eea857SAaron Lu if (!dev || !regmap || !d) 319b1eea857SAaron Lu return -EINVAL; 320b1eea857SAaron Lu 321b1eea857SAaron Lu if (!handle) 322b1eea857SAaron Lu return -ENODEV; 323b1eea857SAaron Lu 324b1eea857SAaron Lu opregion = devm_kzalloc(dev, sizeof(*opregion), GFP_KERNEL); 325b1eea857SAaron Lu if (!opregion) 326b1eea857SAaron Lu return -ENOMEM; 327b1eea857SAaron Lu 328b1eea857SAaron Lu mutex_init(&opregion->lock); 329b1eea857SAaron Lu opregion->regmap = regmap; 330b1eea857SAaron Lu pmic_thermal_lpat(opregion, handle, dev); 331b1eea857SAaron Lu 332b1eea857SAaron Lu status = acpi_install_address_space_handler(handle, 333b1eea857SAaron Lu PMIC_POWER_OPREGION_ID, 334b1eea857SAaron Lu intel_pmic_power_handler, 335b1eea857SAaron Lu NULL, opregion); 336b1eea857SAaron Lu if (ACPI_FAILURE(status)) 337b1eea857SAaron Lu return -ENODEV; 338b1eea857SAaron Lu 339b1eea857SAaron Lu status = acpi_install_address_space_handler(handle, 340b1eea857SAaron Lu PMIC_THERMAL_OPREGION_ID, 341b1eea857SAaron Lu intel_pmic_thermal_handler, 342b1eea857SAaron Lu NULL, opregion); 343b1eea857SAaron Lu if (ACPI_FAILURE(status)) { 344b1eea857SAaron Lu acpi_remove_address_space_handler(handle, PMIC_POWER_OPREGION_ID, 345b1eea857SAaron Lu intel_pmic_power_handler); 346b1eea857SAaron Lu return -ENODEV; 347b1eea857SAaron Lu } 348b1eea857SAaron Lu 349b1eea857SAaron Lu opregion->data = d; 350b1eea857SAaron Lu return 0; 351b1eea857SAaron Lu } 352b1eea857SAaron Lu EXPORT_SYMBOL_GPL(intel_pmic_install_opregion_handler); 353b1eea857SAaron Lu 354b1eea857SAaron Lu MODULE_LICENSE("GPL"); 355