diff options
Diffstat (limited to 'drivers/acpi/pmic/intel_pmic.c')
-rw-r--r-- | drivers/acpi/pmic/intel_pmic.c | 84 |
1 files changed, 74 insertions, 10 deletions
diff --git a/drivers/acpi/pmic/intel_pmic.c b/drivers/acpi/pmic/intel_pmic.c index bd772cd56494..ca18e0d23df9 100644 --- a/drivers/acpi/pmic/intel_pmic.c +++ b/drivers/acpi/pmic/intel_pmic.c @@ -13,7 +13,7 @@ * GNU General Public License for more details. */ -#include <linux/module.h> +#include <linux/export.h> #include <linux/acpi.h> #include <linux/regmap.h> #include <acpi/acpi_lpat.h> @@ -21,12 +21,19 @@ #define PMIC_POWER_OPREGION_ID 0x8d #define PMIC_THERMAL_OPREGION_ID 0x8c +#define PMIC_REGS_OPREGION_ID 0x8f + +struct intel_pmic_regs_handler_ctx { + unsigned int val; + u16 addr; +}; struct intel_pmic_opregion { struct mutex lock; struct acpi_lpat_conversion_table *lpat_table; struct regmap *regmap; struct intel_pmic_opregion_data *data; + struct intel_pmic_regs_handler_ctx ctx; }; static int pmic_get_reg_bit(int address, struct pmic_table *table, @@ -131,7 +138,7 @@ static int pmic_thermal_aux(struct intel_pmic_opregion *opregion, int reg, } static int pmic_thermal_pen(struct intel_pmic_opregion *opregion, int reg, - u32 function, u64 *value) + int bit, u32 function, u64 *value) { struct intel_pmic_opregion_data *d = opregion->data; struct regmap *regmap = opregion->regmap; @@ -140,12 +147,12 @@ static int pmic_thermal_pen(struct intel_pmic_opregion *opregion, int reg, return -ENXIO; if (function == ACPI_READ) - return d->get_policy(regmap, reg, value); + return d->get_policy(regmap, reg, bit, value); if (*value != 0 && *value != 1) return -EINVAL; - return d->update_policy(regmap, reg, *value); + return d->update_policy(regmap, reg, bit, *value); } static bool pmic_thermal_is_temp(int address) @@ -170,13 +177,13 @@ static acpi_status intel_pmic_thermal_handler(u32 function, { struct intel_pmic_opregion *opregion = region_context; struct intel_pmic_opregion_data *d = opregion->data; - int reg, result; + int reg, bit, result; if (bits != 32 || !value64) return AE_BAD_PARAMETER; result = pmic_get_reg_bit(address, d->thermal_table, - d->thermal_table_count, ®, NULL); + d->thermal_table_count, ®, &bit); if (result == -ENOENT) return AE_BAD_PARAMETER; @@ -187,7 +194,8 @@ static acpi_status intel_pmic_thermal_handler(u32 function, else if (pmic_thermal_is_aux(address)) result = pmic_thermal_aux(opregion, reg, function, value64); else if (pmic_thermal_is_pen(address)) - result = pmic_thermal_pen(opregion, reg, function, value64); + result = pmic_thermal_pen(opregion, reg, bit, + function, value64); else result = -EINVAL; @@ -203,6 +211,48 @@ static acpi_status intel_pmic_thermal_handler(u32 function, return AE_OK; } +static acpi_status intel_pmic_regs_handler(u32 function, + acpi_physical_address address, u32 bits, u64 *value64, + void *handler_context, void *region_context) +{ + struct intel_pmic_opregion *opregion = region_context; + int result = 0; + + switch (address) { + case 0: + return AE_OK; + case 1: + opregion->ctx.addr |= (*value64 & 0xff) << 8; + return AE_OK; + case 2: + opregion->ctx.addr |= *value64 & 0xff; + return AE_OK; + case 3: + opregion->ctx.val = *value64 & 0xff; + return AE_OK; + case 4: + if (*value64) { + result = regmap_write(opregion->regmap, opregion->ctx.addr, + opregion->ctx.val); + } else { + result = regmap_read(opregion->regmap, opregion->ctx.addr, + &opregion->ctx.val); + if (result == 0) + *value64 = opregion->ctx.val; + } + memset(&opregion->ctx, 0x00, sizeof(opregion->ctx)); + } + + if (result < 0) { + if (result == -EINVAL) + return AE_BAD_PARAMETER; + else + return AE_ERROR; + } + + return AE_OK; +} + int intel_pmic_install_opregion_handler(struct device *dev, acpi_handle handle, struct regmap *regmap, struct intel_pmic_opregion_data *d) @@ -242,16 +292,30 @@ int intel_pmic_install_opregion_handler(struct device *dev, acpi_handle handle, acpi_remove_address_space_handler(handle, PMIC_POWER_OPREGION_ID, intel_pmic_power_handler); ret = -ENODEV; - goto out_error; + goto out_remove_power_handler; + } + + status = acpi_install_address_space_handler(handle, + PMIC_REGS_OPREGION_ID, intel_pmic_regs_handler, NULL, + opregion); + if (ACPI_FAILURE(status)) { + ret = -ENODEV; + goto out_remove_thermal_handler; } opregion->data = d; return 0; +out_remove_thermal_handler: + acpi_remove_address_space_handler(handle, PMIC_THERMAL_OPREGION_ID, + intel_pmic_thermal_handler); + +out_remove_power_handler: + acpi_remove_address_space_handler(handle, PMIC_POWER_OPREGION_ID, + intel_pmic_power_handler); + out_error: acpi_lpat_free_conversion_table(opregion->lpat_table); return ret; } EXPORT_SYMBOL_GPL(intel_pmic_install_opregion_handler); - -MODULE_LICENSE("GPL"); |