| .. | .. |
|---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-only |
|---|
| 1 | 2 | /* |
|---|
| 2 | 3 | * intel_pmic.c - Intel PMIC operation region driver |
|---|
| 3 | 4 | * |
|---|
| 4 | 5 | * 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 | 6 | */ |
|---|
| 15 | 7 | |
|---|
| 16 | 8 | #include <linux/export.h> |
|---|
| 17 | 9 | #include <linux/acpi.h> |
|---|
| 10 | +#include <linux/mfd/intel_soc_pmic.h> |
|---|
| 18 | 11 | #include <linux/regmap.h> |
|---|
| 19 | 12 | #include <acpi/acpi_lpat.h> |
|---|
| 20 | 13 | #include "intel_pmic.h" |
|---|
| .. | .. |
|---|
| 35 | 28 | struct intel_pmic_opregion_data *data; |
|---|
| 36 | 29 | struct intel_pmic_regs_handler_ctx ctx; |
|---|
| 37 | 30 | }; |
|---|
| 31 | + |
|---|
| 32 | +static struct intel_pmic_opregion *intel_pmic_opregion; |
|---|
| 38 | 33 | |
|---|
| 39 | 34 | static int pmic_get_reg_bit(int address, struct pmic_table *table, |
|---|
| 40 | 35 | int count, int *reg, int *bit) |
|---|
| .. | .. |
|---|
| 262 | 257 | struct regmap *regmap, |
|---|
| 263 | 258 | struct intel_pmic_opregion_data *d) |
|---|
| 264 | 259 | { |
|---|
| 265 | | - acpi_status status; |
|---|
| 260 | + acpi_status status = AE_OK; |
|---|
| 266 | 261 | struct intel_pmic_opregion *opregion; |
|---|
| 267 | 262 | int ret; |
|---|
| 268 | 263 | |
|---|
| .. | .. |
|---|
| 280 | 275 | opregion->regmap = regmap; |
|---|
| 281 | 276 | opregion->lpat_table = acpi_lpat_get_conversion_table(handle); |
|---|
| 282 | 277 | |
|---|
| 283 | | - status = acpi_install_address_space_handler(handle, |
|---|
| 278 | + if (d->power_table_count) |
|---|
| 279 | + status = acpi_install_address_space_handler(handle, |
|---|
| 284 | 280 | PMIC_POWER_OPREGION_ID, |
|---|
| 285 | 281 | intel_pmic_power_handler, |
|---|
| 286 | 282 | NULL, opregion); |
|---|
| .. | .. |
|---|
| 289 | 285 | goto out_error; |
|---|
| 290 | 286 | } |
|---|
| 291 | 287 | |
|---|
| 292 | | - status = acpi_install_address_space_handler(handle, |
|---|
| 288 | + if (d->thermal_table_count) |
|---|
| 289 | + status = acpi_install_address_space_handler(handle, |
|---|
| 293 | 290 | PMIC_THERMAL_OPREGION_ID, |
|---|
| 294 | 291 | intel_pmic_thermal_handler, |
|---|
| 295 | 292 | NULL, opregion); |
|---|
| 296 | 293 | if (ACPI_FAILURE(status)) { |
|---|
| 297 | | - acpi_remove_address_space_handler(handle, PMIC_POWER_OPREGION_ID, |
|---|
| 298 | | - intel_pmic_power_handler); |
|---|
| 299 | 294 | ret = -ENODEV; |
|---|
| 300 | 295 | goto out_remove_power_handler; |
|---|
| 301 | 296 | } |
|---|
| .. | .. |
|---|
| 309 | 304 | } |
|---|
| 310 | 305 | |
|---|
| 311 | 306 | opregion->data = d; |
|---|
| 307 | + intel_pmic_opregion = opregion; |
|---|
| 312 | 308 | return 0; |
|---|
| 313 | 309 | |
|---|
| 314 | 310 | out_remove_thermal_handler: |
|---|
| 315 | | - acpi_remove_address_space_handler(handle, PMIC_THERMAL_OPREGION_ID, |
|---|
| 316 | | - intel_pmic_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); |
|---|
| 317 | 315 | |
|---|
| 318 | 316 | out_remove_power_handler: |
|---|
| 319 | | - acpi_remove_address_space_handler(handle, PMIC_POWER_OPREGION_ID, |
|---|
| 320 | | - intel_pmic_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 | 321 | |
|---|
| 322 | 322 | out_error: |
|---|
| 323 | 323 | acpi_lpat_free_conversion_table(opregion->lpat_table); |
|---|
| 324 | 324 | return ret; |
|---|
| 325 | 325 | } |
|---|
| 326 | 326 | EXPORT_SYMBOL_GPL(intel_pmic_install_opregion_handler); |
|---|
| 327 | + |
|---|
| 328 | +/** |
|---|
| 329 | + * intel_soc_pmic_exec_mipi_pmic_seq_element - Execute PMIC MIPI sequence |
|---|
| 330 | + * @i2c_address: I2C client address for the PMIC |
|---|
| 331 | + * @reg_address: PMIC register address |
|---|
| 332 | + * @value: New value for the register bits to change |
|---|
| 333 | + * @mask: Mask indicating which register bits to change |
|---|
| 334 | + * |
|---|
| 335 | + * DSI LCD panels describe an initialization sequence in the i915 VBT (Video |
|---|
| 336 | + * BIOS Tables) using so called MIPI sequences. One possible element in these |
|---|
| 337 | + * sequences is a PMIC specific element of 15 bytes. |
|---|
| 338 | + * |
|---|
| 339 | + * This function executes these PMIC specific elements sending the embedded |
|---|
| 340 | + * commands to the PMIC. |
|---|
| 341 | + * |
|---|
| 342 | + * Return 0 on success, < 0 on failure. |
|---|
| 343 | + */ |
|---|
| 344 | +int intel_soc_pmic_exec_mipi_pmic_seq_element(u16 i2c_address, u32 reg_address, |
|---|
| 345 | + u32 value, u32 mask) |
|---|
| 346 | +{ |
|---|
| 347 | + 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 | +} |
|---|
| 383 | +EXPORT_SYMBOL_GPL(intel_soc_pmic_exec_mipi_pmic_seq_element); |
|---|