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