diff options
Diffstat (limited to 'drivers/acpi/pmic')
| -rw-r--r-- | drivers/acpi/pmic/intel_pmic.c | 354 | ||||
| -rw-r--r-- | drivers/acpi/pmic/intel_pmic.h | 25 | ||||
| -rw-r--r-- | drivers/acpi/pmic/intel_pmic_crc.c | 211 | ||||
| -rw-r--r-- | drivers/acpi/pmic/intel_pmic_xpower.c | 268 | 
4 files changed, 858 insertions, 0 deletions
diff --git a/drivers/acpi/pmic/intel_pmic.c b/drivers/acpi/pmic/intel_pmic.c new file mode 100644 index 000000000000..a732e5d7e322 --- /dev/null +++ b/drivers/acpi/pmic/intel_pmic.c @@ -0,0 +1,354 @@ +/* + * intel_pmic.c - Intel PMIC operation region driver + * + * Copyright (C) 2014 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version + * 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + */ + +#include <linux/module.h> +#include <linux/acpi.h> +#include <linux/regmap.h> +#include "intel_pmic.h" + +#define PMIC_POWER_OPREGION_ID		0x8d +#define PMIC_THERMAL_OPREGION_ID	0x8c + +struct acpi_lpat { +	int temp; +	int raw; +}; + +struct intel_pmic_opregion { +	struct mutex lock; +	struct acpi_lpat *lpat; +	int lpat_count; +	struct regmap *regmap; +	struct intel_pmic_opregion_data *data; +}; + +static int pmic_get_reg_bit(int address, struct pmic_table *table, +			    int count, int *reg, int *bit) +{ +	int i; + +	for (i = 0; i < count; i++) { +		if (table[i].address == address) { +			*reg = table[i].reg; +			if (bit) +				*bit = table[i].bit; +			return 0; +		} +	} +	return -ENOENT; +} + +/** + * raw_to_temp(): Return temperature from raw value through LPAT table + * + * @lpat: the temperature_raw mapping table + * @count: the count of the above mapping table + * @raw: the raw value, used as a key to get the temerature from the + *       above mapping table + * + * A positive value will be returned on success, a negative errno will + * be returned in error cases. + */ +static int raw_to_temp(struct acpi_lpat *lpat, int count, int raw) +{ +	int i, delta_temp, delta_raw, temp; + +	for (i = 0; i < count - 1; i++) { +		if ((raw >= lpat[i].raw && raw <= lpat[i+1].raw) || +		    (raw <= lpat[i].raw && raw >= lpat[i+1].raw)) +			break; +	} + +	if (i == count - 1) +		return -ENOENT; + +	delta_temp = lpat[i+1].temp - lpat[i].temp; +	delta_raw = lpat[i+1].raw - lpat[i].raw; +	temp = lpat[i].temp + (raw - lpat[i].raw) * delta_temp / delta_raw; + +	return temp; +} + +/** + * temp_to_raw(): Return raw value from temperature through LPAT table + * + * @lpat: the temperature_raw mapping table + * @count: the count of the above mapping table + * @temp: the temperature, used as a key to get the raw value from the + *        above mapping table + * + * A positive value will be returned on success, a negative errno will + * be returned in error cases. + */ +static int temp_to_raw(struct acpi_lpat *lpat, int count, int temp) +{ +	int i, delta_temp, delta_raw, raw; + +	for (i = 0; i < count - 1; i++) { +		if (temp >= lpat[i].temp && temp <= lpat[i+1].temp) +			break; +	} + +	if (i == count - 1) +		return -ENOENT; + +	delta_temp = lpat[i+1].temp - lpat[i].temp; +	delta_raw = lpat[i+1].raw - lpat[i].raw; +	raw = lpat[i].raw + (temp - lpat[i].temp) * delta_raw / delta_temp; + +	return raw; +} + +static void pmic_thermal_lpat(struct intel_pmic_opregion *opregion, +			      acpi_handle handle, struct device *dev) +{ +	struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; +	union acpi_object *obj_p, *obj_e; +	int *lpat, i; +	acpi_status status; + +	status = acpi_evaluate_object(handle, "LPAT", NULL, &buffer); +	if (ACPI_FAILURE(status)) +		return; + +	obj_p = (union acpi_object *)buffer.pointer; +	if (!obj_p || (obj_p->type != ACPI_TYPE_PACKAGE) || +	    (obj_p->package.count % 2) || (obj_p->package.count < 4)) +		goto out; + +	lpat = devm_kmalloc(dev, sizeof(int) * obj_p->package.count, +			    GFP_KERNEL); +	if (!lpat) +		goto out; + +	for (i = 0; i < obj_p->package.count; i++) { +		obj_e = &obj_p->package.elements[i]; +		if (obj_e->type != ACPI_TYPE_INTEGER) { +			devm_kfree(dev, lpat); +			goto out; +		} +		lpat[i] = (s64)obj_e->integer.value; +	} + +	opregion->lpat = (struct acpi_lpat *)lpat; +	opregion->lpat_count = obj_p->package.count / 2; + +out: +	kfree(buffer.pointer); +} + +static acpi_status intel_pmic_power_handler(u32 function, +		acpi_physical_address address, u32 bits, u64 *value64, +		void *handler_context, void *region_context) +{ +	struct intel_pmic_opregion *opregion = region_context; +	struct regmap *regmap = opregion->regmap; +	struct intel_pmic_opregion_data *d = opregion->data; +	int reg, bit, result; + +	if (bits != 32 || !value64) +		return AE_BAD_PARAMETER; + +	if (function == ACPI_WRITE && !(*value64 == 0 || *value64 == 1)) +		return AE_BAD_PARAMETER; + +	result = pmic_get_reg_bit(address, d->power_table, +				  d->power_table_count, ®, &bit); +	if (result == -ENOENT) +		return AE_BAD_PARAMETER; + +	mutex_lock(&opregion->lock); + +	result = function == ACPI_READ ? +		d->get_power(regmap, reg, bit, value64) : +		d->update_power(regmap, reg, bit, *value64 == 1); + +	mutex_unlock(&opregion->lock); + +	return result ? AE_ERROR : AE_OK; +} + +static int pmic_read_temp(struct intel_pmic_opregion *opregion, +			  int reg, u64 *value) +{ +	int raw_temp, temp; + +	if (!opregion->data->get_raw_temp) +		return -ENXIO; + +	raw_temp = opregion->data->get_raw_temp(opregion->regmap, reg); +	if (raw_temp < 0) +		return raw_temp; + +	if (!opregion->lpat) { +		*value = raw_temp; +		return 0; +	} + +	temp = raw_to_temp(opregion->lpat, opregion->lpat_count, raw_temp); +	if (temp < 0) +		return temp; + +	*value = temp; +	return 0; +} + +static int pmic_thermal_temp(struct intel_pmic_opregion *opregion, int reg, +			     u32 function, u64 *value) +{ +	return function == ACPI_READ ? +		pmic_read_temp(opregion, reg, value) : -EINVAL; +} + +static int pmic_thermal_aux(struct intel_pmic_opregion *opregion, int reg, +			    u32 function, u64 *value) +{ +	int raw_temp; + +	if (function == ACPI_READ) +		return pmic_read_temp(opregion, reg, value); + +	if (!opregion->data->update_aux) +		return -ENXIO; + +	if (opregion->lpat) { +		raw_temp = temp_to_raw(opregion->lpat, opregion->lpat_count, +				       *value); +		if (raw_temp < 0) +			return raw_temp; +	} else { +		raw_temp = *value; +	} + +	return opregion->data->update_aux(opregion->regmap, reg, raw_temp); +} + +static int pmic_thermal_pen(struct intel_pmic_opregion *opregion, int reg, +			    u32 function, u64 *value) +{ +	struct intel_pmic_opregion_data *d = opregion->data; +	struct regmap *regmap = opregion->regmap; + +	if (!d->get_policy || !d->update_policy) +		return -ENXIO; + +	if (function == ACPI_READ) +		return d->get_policy(regmap, reg, value); + +	if (*value != 0 && *value != 1) +		return -EINVAL; + +	return d->update_policy(regmap, reg, *value); +} + +static bool pmic_thermal_is_temp(int address) +{ +	return (address <= 0x3c) && !(address % 12); +} + +static bool pmic_thermal_is_aux(int address) +{ +	return (address >= 4 && address <= 0x40 && !((address - 4) % 12)) || +	       (address >= 8 && address <= 0x44 && !((address - 8) % 12)); +} + +static bool pmic_thermal_is_pen(int address) +{ +	return address >= 0x48 && address <= 0x5c; +} + +static acpi_status intel_pmic_thermal_handler(u32 function, +		acpi_physical_address address, u32 bits, u64 *value64, +		void *handler_context, void *region_context) +{ +	struct intel_pmic_opregion *opregion = region_context; +	struct intel_pmic_opregion_data *d = opregion->data; +	int reg, result; + +	if (bits != 32 || !value64) +		return AE_BAD_PARAMETER; + +	result = pmic_get_reg_bit(address, d->thermal_table, +				  d->thermal_table_count, ®, NULL); +	if (result == -ENOENT) +		return AE_BAD_PARAMETER; + +	mutex_lock(&opregion->lock); + +	if (pmic_thermal_is_temp(address)) +		result = pmic_thermal_temp(opregion, reg, function, value64); +	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); +	else +		result = -EINVAL; + +	mutex_unlock(&opregion->lock); + +	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) +{ +	acpi_status status; +	struct intel_pmic_opregion *opregion; + +	if (!dev || !regmap || !d) +		return -EINVAL; + +	if (!handle) +		return -ENODEV; + +	opregion = devm_kzalloc(dev, sizeof(*opregion), GFP_KERNEL); +	if (!opregion) +		return -ENOMEM; + +	mutex_init(&opregion->lock); +	opregion->regmap = regmap; +	pmic_thermal_lpat(opregion, handle, dev); + +	status = acpi_install_address_space_handler(handle, +						    PMIC_POWER_OPREGION_ID, +						    intel_pmic_power_handler, +						    NULL, opregion); +	if (ACPI_FAILURE(status)) +		return -ENODEV; + +	status = acpi_install_address_space_handler(handle, +						    PMIC_THERMAL_OPREGION_ID, +						    intel_pmic_thermal_handler, +						    NULL, opregion); +	if (ACPI_FAILURE(status)) { +		acpi_remove_address_space_handler(handle, PMIC_POWER_OPREGION_ID, +						  intel_pmic_power_handler); +		return -ENODEV; +	} + +	opregion->data = d; +	return 0; +} +EXPORT_SYMBOL_GPL(intel_pmic_install_opregion_handler); + +MODULE_LICENSE("GPL"); diff --git a/drivers/acpi/pmic/intel_pmic.h b/drivers/acpi/pmic/intel_pmic.h new file mode 100644 index 000000000000..d4e90af8f0dd --- /dev/null +++ b/drivers/acpi/pmic/intel_pmic.h @@ -0,0 +1,25 @@ +#ifndef __INTEL_PMIC_H +#define __INTEL_PMIC_H + +struct pmic_table { +	int address;	/* operation region address */ +	int reg;	/* corresponding thermal register */ +	int bit;	/* control bit for power */ +}; + +struct intel_pmic_opregion_data { +	int (*get_power)(struct regmap *r, int reg, int bit, u64 *value); +	int (*update_power)(struct regmap *r, int reg, int bit, bool on); +	int (*get_raw_temp)(struct regmap *r, int reg); +	int (*update_aux)(struct regmap *r, int reg, int raw_temp); +	int (*get_policy)(struct regmap *r, int reg, u64 *value); +	int (*update_policy)(struct regmap *r, int reg, int enable); +	struct pmic_table *power_table; +	int power_table_count; +	struct pmic_table *thermal_table; +	int thermal_table_count; +}; + +int intel_pmic_install_opregion_handler(struct device *dev, acpi_handle handle, struct regmap *regmap, struct intel_pmic_opregion_data *d); + +#endif diff --git a/drivers/acpi/pmic/intel_pmic_crc.c b/drivers/acpi/pmic/intel_pmic_crc.c new file mode 100644 index 000000000000..ef7d8ff95abe --- /dev/null +++ b/drivers/acpi/pmic/intel_pmic_crc.c @@ -0,0 +1,211 @@ +/* + * intel_pmic_crc.c - Intel CrystalCove PMIC operation region driver + * + * Copyright (C) 2014 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version + * 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + */ + +#include <linux/module.h> +#include <linux/acpi.h> +#include <linux/mfd/intel_soc_pmic.h> +#include <linux/regmap.h> +#include <linux/platform_device.h> +#include "intel_pmic.h" + +#define PWR_SOURCE_SELECT	BIT(1) + +#define PMIC_A0LOCK_REG		0xc5 + +static struct pmic_table power_table[] = { +	{ +		.address = 0x24, +		.reg = 0x66, +		.bit = 0x00, +	}, +	{ +		.address = 0x48, +		.reg = 0x5d, +		.bit = 0x00, +	}, +}; + +static struct pmic_table thermal_table[] = { +	{ +		.address = 0x00, +		.reg = 0x75 +	}, +	{ +		.address = 0x04, +		.reg = 0x95 +	}, +	{ +		.address = 0x08, +		.reg = 0x97 +	}, +	{ +		.address = 0x0c, +		.reg = 0x77 +	}, +	{ +		.address = 0x10, +		.reg = 0x9a +	}, +	{ +		.address = 0x14, +		.reg = 0x9c +	}, +	{ +		.address = 0x18, +		.reg = 0x79 +	}, +	{ +		.address = 0x1c, +		.reg = 0x9f +	}, +	{ +		.address = 0x20, +		.reg = 0xa1 +	}, +	{ +		.address = 0x48, +		.reg = 0x94 +	}, +	{ +		.address = 0x4c, +		.reg = 0x99 +	}, +	{ +		.address = 0x50, +		.reg = 0x9e +	}, +}; + +static int intel_crc_pmic_get_power(struct regmap *regmap, int reg, +				    int bit, u64 *value) +{ +	int data; + +	if (regmap_read(regmap, reg, &data)) +		return -EIO; + +	*value = (data & PWR_SOURCE_SELECT) && (data & BIT(bit)) ? 1 : 0; +	return 0; +} + +static int intel_crc_pmic_update_power(struct regmap *regmap, int reg, +				       int bit, bool on) +{ +	int data; + +	if (regmap_read(regmap, reg, &data)) +		return -EIO; + +	if (on) { +		data |= PWR_SOURCE_SELECT | BIT(bit); +	} else { +		data &= ~BIT(bit); +		data |= PWR_SOURCE_SELECT; +	} + +	if (regmap_write(regmap, reg, data)) +		return -EIO; +	return 0; +} + +static int intel_crc_pmic_get_raw_temp(struct regmap *regmap, int reg) +{ +	int temp_l, temp_h; + +	/* +	 * Raw temperature value is 10bits: 8bits in reg +	 * and 2bits in reg-1: bit0,1 +	 */ +	if (regmap_read(regmap, reg, &temp_l) || +	    regmap_read(regmap, reg - 1, &temp_h)) +		return -EIO; + +	return temp_l | (temp_h & 0x3) << 8; +} + +static int intel_crc_pmic_update_aux(struct regmap *regmap, int reg, int raw) +{ +	return regmap_write(regmap, reg, raw) || +		regmap_update_bits(regmap, reg - 1, 0x3, raw >> 8) ? -EIO : 0; +} + +static int intel_crc_pmic_get_policy(struct regmap *regmap, int reg, u64 *value) +{ +	int pen; + +	if (regmap_read(regmap, reg, &pen)) +		return -EIO; +	*value = pen >> 7; +	return 0; +} + +static int intel_crc_pmic_update_policy(struct regmap *regmap, +					int reg, int enable) +{ +	int alert0; + +	/* Update to policy enable bit requires unlocking a0lock */ +	if (regmap_read(regmap, PMIC_A0LOCK_REG, &alert0)) +		return -EIO; + +	if (regmap_update_bits(regmap, PMIC_A0LOCK_REG, 0x01, 0)) +		return -EIO; + +	if (regmap_update_bits(regmap, reg, 0x80, enable << 7)) +		return -EIO; + +	/* restore alert0 */ +	if (regmap_write(regmap, PMIC_A0LOCK_REG, alert0)) +		return -EIO; + +	return 0; +} + +static struct intel_pmic_opregion_data intel_crc_pmic_opregion_data = { +	.get_power	= intel_crc_pmic_get_power, +	.update_power	= intel_crc_pmic_update_power, +	.get_raw_temp	= intel_crc_pmic_get_raw_temp, +	.update_aux	= intel_crc_pmic_update_aux, +	.get_policy	= intel_crc_pmic_get_policy, +	.update_policy	= intel_crc_pmic_update_policy, +	.power_table	= power_table, +	.power_table_count= ARRAY_SIZE(power_table), +	.thermal_table	= thermal_table, +	.thermal_table_count = ARRAY_SIZE(thermal_table), +}; + +static int intel_crc_pmic_opregion_probe(struct platform_device *pdev) +{ +	struct intel_soc_pmic *pmic = dev_get_drvdata(pdev->dev.parent); +	return intel_pmic_install_opregion_handler(&pdev->dev, +			ACPI_HANDLE(pdev->dev.parent), pmic->regmap, +			&intel_crc_pmic_opregion_data); +} + +static struct platform_driver intel_crc_pmic_opregion_driver = { +	.probe = intel_crc_pmic_opregion_probe, +	.driver = { +		.name = "crystal_cove_pmic", +	}, +}; + +static int __init intel_crc_pmic_opregion_driver_init(void) +{ +	return platform_driver_register(&intel_crc_pmic_opregion_driver); +} +module_init(intel_crc_pmic_opregion_driver_init); + +MODULE_DESCRIPTION("CrystalCove ACPI opration region driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/acpi/pmic/intel_pmic_xpower.c b/drivers/acpi/pmic/intel_pmic_xpower.c new file mode 100644 index 000000000000..6a082d4de12c --- /dev/null +++ b/drivers/acpi/pmic/intel_pmic_xpower.c @@ -0,0 +1,268 @@ +/* + * intel_pmic_xpower.c - XPower AXP288 PMIC operation region driver + * + * Copyright (C) 2014 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version + * 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + */ + +#include <linux/module.h> +#include <linux/acpi.h> +#include <linux/mfd/axp20x.h> +#include <linux/regmap.h> +#include <linux/platform_device.h> +#include <linux/iio/consumer.h> +#include "intel_pmic.h" + +#define XPOWER_GPADC_LOW	0x5b + +static struct pmic_table power_table[] = { +	{ +		.address = 0x00, +		.reg = 0x13, +		.bit = 0x05, +	}, +	{ +		.address = 0x04, +		.reg = 0x13, +		.bit = 0x06, +	}, +	{ +		.address = 0x08, +		.reg = 0x13, +		.bit = 0x07, +	}, +	{ +		.address = 0x0c, +		.reg = 0x12, +		.bit = 0x03, +	}, +	{ +		.address = 0x10, +		.reg = 0x12, +		.bit = 0x04, +	}, +	{ +		.address = 0x14, +		.reg = 0x12, +		.bit = 0x05, +	}, +	{ +		.address = 0x18, +		.reg = 0x12, +		.bit = 0x06, +	}, +	{ +		.address = 0x1c, +		.reg = 0x12, +		.bit = 0x00, +	}, +	{ +		.address = 0x20, +		.reg = 0x12, +		.bit = 0x01, +	}, +	{ +		.address = 0x24, +		.reg = 0x12, +		.bit = 0x02, +	}, +	{ +		.address = 0x28, +		.reg = 0x13, +		.bit = 0x02, +	}, +	{ +		.address = 0x2c, +		.reg = 0x13, +		.bit = 0x03, +	}, +	{ +		.address = 0x30, +		.reg = 0x13, +		.bit = 0x04, +	}, +	{ +		.address = 0x38, +		.reg = 0x10, +		.bit = 0x03, +	}, +	{ +		.address = 0x3c, +		.reg = 0x10, +		.bit = 0x06, +	}, +	{ +		.address = 0x40, +		.reg = 0x10, +		.bit = 0x05, +	}, +	{ +		.address = 0x44, +		.reg = 0x10, +		.bit = 0x04, +	}, +	{ +		.address = 0x48, +		.reg = 0x10, +		.bit = 0x01, +	}, +	{ +		.address = 0x4c, +		.reg = 0x10, +		.bit = 0x00 +	}, +}; + +/* TMP0 - TMP5 are the same, all from GPADC */ +static struct pmic_table thermal_table[] = { +	{ +		.address = 0x00, +		.reg = XPOWER_GPADC_LOW +	}, +	{ +		.address = 0x0c, +		.reg = XPOWER_GPADC_LOW +	}, +	{ +		.address = 0x18, +		.reg = XPOWER_GPADC_LOW +	}, +	{ +		.address = 0x24, +		.reg = XPOWER_GPADC_LOW +	}, +	{ +		.address = 0x30, +		.reg = XPOWER_GPADC_LOW +	}, +	{ +		.address = 0x3c, +		.reg = XPOWER_GPADC_LOW +	}, +}; + +static int intel_xpower_pmic_get_power(struct regmap *regmap, int reg, +				       int bit, u64 *value) +{ +	int data; + +	if (regmap_read(regmap, reg, &data)) +		return -EIO; + +	*value = (data & BIT(bit)) ? 1 : 0; +	return 0; +} + +static int intel_xpower_pmic_update_power(struct regmap *regmap, int reg, +					  int bit, bool on) +{ +	int data; + +	if (regmap_read(regmap, reg, &data)) +		return -EIO; + +	if (on) +		data |= BIT(bit); +	else +		data &= ~BIT(bit); + +	if (regmap_write(regmap, reg, data)) +		return -EIO; + +	return 0; +} + +/** + * intel_xpower_pmic_get_raw_temp(): Get raw temperature reading from the PMIC + * + * @regmap: regmap of the PMIC device + * @reg: register to get the reading + * + * We could get the sensor value by manipulating the HW regs here, but since + * the axp288 IIO driver may also access the same regs at the same time, the + * APIs provided by IIO subsystem are used here instead to avoid problems. As + * a result, the two passed in params are of no actual use. + * + * Return a positive value on success, errno on failure. + */ +static int intel_xpower_pmic_get_raw_temp(struct regmap *regmap, int reg) +{ +	struct iio_channel *gpadc_chan; +	int ret, val; + +	gpadc_chan = iio_channel_get(NULL, "axp288-system-temp"); +	if (IS_ERR_OR_NULL(gpadc_chan)) +		return -EACCES; + +	ret = iio_read_channel_raw(gpadc_chan, &val); +	if (ret < 0) +		val = ret; + +	iio_channel_release(gpadc_chan); +	return val; +} + +static struct intel_pmic_opregion_data intel_xpower_pmic_opregion_data = { +	.get_power = intel_xpower_pmic_get_power, +	.update_power = intel_xpower_pmic_update_power, +	.get_raw_temp = intel_xpower_pmic_get_raw_temp, +	.power_table = power_table, +	.power_table_count = ARRAY_SIZE(power_table), +	.thermal_table = thermal_table, +	.thermal_table_count = ARRAY_SIZE(thermal_table), +}; + +static acpi_status intel_xpower_pmic_gpio_handler(u32 function, +		acpi_physical_address address, u32 bit_width, u64 *value, +		void *handler_context, void *region_context) +{ +	return AE_OK; +} + +static int intel_xpower_pmic_opregion_probe(struct platform_device *pdev) +{ +	struct device *parent = pdev->dev.parent; +	struct axp20x_dev *axp20x = dev_get_drvdata(parent); +	acpi_status status; +	int result; + +	status = acpi_install_address_space_handler(ACPI_HANDLE(parent), +			ACPI_ADR_SPACE_GPIO, intel_xpower_pmic_gpio_handler, +			NULL, NULL); +	if (ACPI_FAILURE(status)) +		return -ENODEV; + +	result = intel_pmic_install_opregion_handler(&pdev->dev, +					ACPI_HANDLE(parent), axp20x->regmap, +					&intel_xpower_pmic_opregion_data); +	if (result) +		acpi_remove_address_space_handler(ACPI_HANDLE(parent), +						  ACPI_ADR_SPACE_GPIO, +						  intel_xpower_pmic_gpio_handler); + +	return result; +} + +static struct platform_driver intel_xpower_pmic_opregion_driver = { +	.probe = intel_xpower_pmic_opregion_probe, +	.driver = { +		.name = "axp288_pmic_acpi", +	}, +}; + +static int __init intel_xpower_pmic_opregion_driver_init(void) +{ +	return platform_driver_register(&intel_xpower_pmic_opregion_driver); +} +module_init(intel_xpower_pmic_opregion_driver_init); + +MODULE_DESCRIPTION("XPower AXP288 ACPI operation region driver"); +MODULE_LICENSE("GPL");  | 

