All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v3 0/3] Support PMIC operation region for CrystalCove and XPower
@ 2014-11-21  7:11 ` Aaron Lu
  0 siblings, 0 replies; 34+ messages in thread
From: Aaron Lu @ 2014-11-21  7:11 UTC (permalink / raw)
  To: Rafael J. Wysocki
  Cc: Lee Jones, Jacob Pan, Yegnesh Iyer,
	linux-acpi-u79uwXL29TY76Z2rM5mHXA,
	linux-iio-u79uwXL29TY76Z2rM5mHXA,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA

v3:
Only some function/variable name changes, no functiona changes:
- Replace the dptf/DPTF word originate from the BIOS ACPI table with more
  meaningful word thermal/THERMAL in all places;
- Eliminate the soc part in various structure and function names to make
  them shorter:
  intel_soc_pmic_opregion -> intel_pmic_opregion
  intel_soc_pmic_pmop_handler -> intel_pmic_pmop_handler
  intel_soc_pmic_install_opregion_handler -> intel_pmic_install_opregion_handler
  etc.


v2:
Place PMIC operation files under drivers/acpi/pmic instead of
drivers/acpi/pmic_opregion as suggested by Rafael;
Rename PMIC operation files to make them shorter as suggested by Rafael.

v1:
On Intel Baytrail-T and Baytrail-T-CR platforms, there are two customized
ACPI operation regions defined for the Power Management Integrated Circuit
device, one is for power resource handling and one is for thermal: sensor
temperature reporting, trip point setting, etc. There are different PMIC
chips used on those platforms and though each has the same two operation
regions and functionality, their implementation is different so every PMIC
will need a driver. But since their functionality is similar, some common
code is abstracted into the intel_soc_pmic_opregion.c.

The last version is posted here:
https://lkml.org/lkml/2014/9/8/801

Changes since then:
1 Move to drivers/acpi as discussed on the above thread;
2 Added support for XPower AXP288 PMIC operation region support;
3 Since operation region handler can not be removed(at the moment at least),
  use bool for the two operation region driver configs instead of tristate;
  Another reason to do this is that, with Mika's MFD ACPI support patch, all
  those MFD cell devices created will have the same modalias as their parent's
  so it doesn't make much sense to compile these drivers into modules.

Patch 1 applies on top of Rafael's pm-next branch, and then patch 2 and
patch 3 needs merge of Lee's mfd/ib-mfd-iio-3.19 branch where the PMIC
driver XPower AXP288 and iio driver axp288_adc is located.


Aaron Lu (3):
  ACPI / PMIC: support PMIC operation region for CrystalCove
  ACPI / PMIC: support PMIC operation region for XPower AXP288
  ACPI / PMIC: AXP288: support virtual GPIO in ACPI table

^ permalink raw reply	[flat|nested] 34+ messages in thread

* [PATCH v3 0/3] Support PMIC operation region for CrystalCove and XPower
@ 2014-11-21  7:11 ` Aaron Lu
  0 siblings, 0 replies; 34+ messages in thread
From: Aaron Lu @ 2014-11-21  7:11 UTC (permalink / raw)
  To: Rafael J. Wysocki
  Cc: Lee Jones, Jacob Pan, Yegnesh Iyer, linux-acpi, linux-iio, linux-kernel

v3:
Only some function/variable name changes, no functiona changes:
- Replace the dptf/DPTF word originate from the BIOS ACPI table with more
  meaningful word thermal/THERMAL in all places;
- Eliminate the soc part in various structure and function names to make
  them shorter:
  intel_soc_pmic_opregion -> intel_pmic_opregion
  intel_soc_pmic_pmop_handler -> intel_pmic_pmop_handler
  intel_soc_pmic_install_opregion_handler -> intel_pmic_install_opregion_handler
  etc.


v2:
Place PMIC operation files under drivers/acpi/pmic instead of
drivers/acpi/pmic_opregion as suggested by Rafael;
Rename PMIC operation files to make them shorter as suggested by Rafael.

v1:
On Intel Baytrail-T and Baytrail-T-CR platforms, there are two customized
ACPI operation regions defined for the Power Management Integrated Circuit
device, one is for power resource handling and one is for thermal: sensor
temperature reporting, trip point setting, etc. There are different PMIC
chips used on those platforms and though each has the same two operation
regions and functionality, their implementation is different so every PMIC
will need a driver. But since their functionality is similar, some common
code is abstracted into the intel_soc_pmic_opregion.c.

The last version is posted here:
https://lkml.org/lkml/2014/9/8/801

Changes since then:
1 Move to drivers/acpi as discussed on the above thread;
2 Added support for XPower AXP288 PMIC operation region support;
3 Since operation region handler can not be removed(at the moment at least),
  use bool for the two operation region driver configs instead of tristate;
  Another reason to do this is that, with Mika's MFD ACPI support patch, all
  those MFD cell devices created will have the same modalias as their parent's
  so it doesn't make much sense to compile these drivers into modules.

Patch 1 applies on top of Rafael's pm-next branch, and then patch 2 and
patch 3 needs merge of Lee's mfd/ib-mfd-iio-3.19 branch where the PMIC
driver XPower AXP288 and iio driver axp288_adc is located.


Aaron Lu (3):
  ACPI / PMIC: support PMIC operation region for CrystalCove
  ACPI / PMIC: support PMIC operation region for XPower AXP288
  ACPI / PMIC: AXP288: support virtual GPIO in ACPI table


^ permalink raw reply	[flat|nested] 34+ messages in thread

* [PATCH v3 1/3] ACPI / PMIC: support PMIC operation region for CrystalCove
  2014-11-21  7:11 ` Aaron Lu
@ 2014-11-21  7:11     ` Aaron Lu
  -1 siblings, 0 replies; 34+ messages in thread
From: Aaron Lu @ 2014-11-21  7:11 UTC (permalink / raw)
  To: Rafael J. Wysocki
  Cc: Lee Jones, Jacob Pan, Yegnesh Iyer,
	linux-acpi-u79uwXL29TY76Z2rM5mHXA,
	linux-iio-u79uwXL29TY76Z2rM5mHXA,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA

The Baytrail-T platform firmware has defined two customized operation
regions for PMIC chip Crystal Cove - one is for power resource handling
and one is for thermal: sensor temperature reporting, trip point setting,
etc. This patch adds support for them on top of the existing Crystal Cove
PMIC driver.

The reason to split code into a separate file intel_pmic.c is that there
are more PMIC drivers with ACPI operation region support coming and we can
re-use those code. The intel_pmic_opregion_data structure is created also
for this purpose: when we need to support a new PMIC's operation region,
we just need to fill those callbacks and the two register mapping tables.

Signed-off-by: Aaron Lu <aaron.lu-ral2JQCrhuEAvxtiuMwx3w@public.gmane.org>
Acked-by: Lee Jones <lee.jones-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org> for the MFD part
---
 drivers/acpi/Kconfig               |  17 ++
 drivers/acpi/Makefile              |   3 +
 drivers/acpi/pmic/intel_pmic.c     | 339 +++++++++++++++++++++++++++++++++++++
 drivers/acpi/pmic/intel_pmic.h     |  34 ++++
 drivers/acpi/pmic/intel_pmic_crc.c | 216 +++++++++++++++++++++++
 drivers/mfd/intel_soc_pmic_crc.c   |   3 +
 6 files changed, 612 insertions(+)
 create mode 100644 drivers/acpi/pmic/intel_pmic.c
 create mode 100644 drivers/acpi/pmic/intel_pmic.h
 create mode 100644 drivers/acpi/pmic/intel_pmic_crc.c

diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig
index 79078b8f5697..3e5f2056f946 100644
--- a/drivers/acpi/Kconfig
+++ b/drivers/acpi/Kconfig
@@ -393,4 +393,21 @@ config ACPI_EXTLOG
 	  driver adds support for that functionality with corresponding
 	  tracepoint which carries that information to userspace.
 
+menuconfig PMIC_OPREGION
+	bool "PMIC (Power Management Integrated Circuit) operation region support"
+	help
+	  Select this option to enable support for ACPI operation
+	  region of the PMIC chip. The operation region can be used
+	  to control power rails and sensor reading/writing on the
+	  PMIC chip.
+
+if PMIC_OPREGION
+config CRC_PMIC_OPREGION
+	bool "ACPI operation region support for CrystalCove PMIC"
+	depends on INTEL_SOC_PMIC
+	help
+	  This config adds ACPI operation region support for CrystalCove PMIC.
+
+endif
+
 endif	# ACPI
diff --git a/drivers/acpi/Makefile b/drivers/acpi/Makefile
index 6d11522f0e48..f5938399ac14 100644
--- a/drivers/acpi/Makefile
+++ b/drivers/acpi/Makefile
@@ -88,3 +88,6 @@ obj-$(CONFIG_ACPI_PROCESSOR_AGGREGATOR) += acpi_pad.o
 obj-$(CONFIG_ACPI_APEI)		+= apei/
 
 obj-$(CONFIG_ACPI_EXTLOG)	+= acpi_extlog.o
+
+obj-$(CONFIG_PMIC_OPREGION)	+= pmic/intel_pmic.o
+obj-$(CONFIG_CRC_PMIC_OPREGION) += pmic/intel_pmic_crc.o
diff --git a/drivers/acpi/pmic/intel_pmic.c b/drivers/acpi/pmic/intel_pmic.c
new file mode 100644
index 000000000000..5dbc0fb4d536
--- /dev/null
+++ b/drivers/acpi/pmic/intel_pmic.c
@@ -0,0 +1,339 @@
+/*
+ * 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_PMOP_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 struct pmic_pwr_reg *
+pmic_get_pwr_reg(int address, struct pmic_pwr_table *table, int count)
+{
+	int i;
+
+	for (i = 0; i < count; i++) {
+		if (table[i].address == address)
+			return &table[i].pwr_reg;
+	}
+	return NULL;
+}
+
+static int
+pmic_get_thermal_reg(int address, struct pmic_thermal_table *table, int count)
+{
+	int i;
+
+	for (i = 0; i < count; i++) {
+		if (table[i].address == address)
+			return table[i].reg;
+	}
+	return -ENOENT;
+}
+
+/* Return temperature from raw value through LPAT table */
+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;
+}
+
+/* Return raw value from temperature through LPAT table */
+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(*lpat) * 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)
+			goto out;
+		lpat[i] = 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_pmop_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;
+	struct pmic_pwr_reg *preg;
+	int result;
+
+	if (bits != 32 || !value64)
+		return AE_BAD_PARAMETER;
+
+	if (function == ACPI_WRITE && !(*value64 == 0 || *value64 == 1))
+		return AE_BAD_PARAMETER;
+
+	preg = pmic_get_pwr_reg(address, d->pwr_table, d->pwr_table_count);
+	if (!preg)
+		return AE_BAD_PARAMETER;
+
+	mutex_lock(&opregion->lock);
+
+	if (function == ACPI_READ)
+		result = d->get_power(regmap, preg, value64);
+	else
+		result = d->update_power(regmap, preg, *value64 == 1);
+
+	mutex_unlock(&opregion->lock);
+
+	return result ? AE_ERROR : AE_OK;
+}
+
+static acpi_status pmic_read_temp(struct intel_pmic_opregion *opregion,
+				  int reg, u64 *value)
+{
+	int raw_temp, temp;
+
+	if (!opregion->data->get_raw_temp)
+		return AE_BAD_PARAMETER;
+
+	raw_temp = opregion->data->get_raw_temp(opregion->regmap, reg);
+	if (raw_temp < 0)
+		return AE_ERROR;
+
+	if (!opregion->lpat) {
+		*value = raw_temp;
+		return AE_OK;
+	}
+
+	temp = raw_to_temp(opregion->lpat, opregion->lpat_count, raw_temp);
+	if (temp < 0)
+		return AE_ERROR;
+
+	*value = temp;
+	return AE_OK;
+}
+
+static acpi_status pmic_thermal_temp(struct intel_pmic_opregion *opregion,
+				     int reg, u32 function, u64 *value)
+{
+	if (function != ACPI_READ)
+		return AE_BAD_PARAMETER;
+
+	return pmic_read_temp(opregion, reg, value);
+}
+
+static acpi_status 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 AE_BAD_PARAMETER;
+
+	if (opregion->lpat) {
+		raw_temp = temp_to_raw(opregion->lpat, opregion->lpat_count,
+					 *value);
+		if (raw_temp < 0)
+			return AE_ERROR;
+	} else {
+		raw_temp = *value;
+	}
+
+	return opregion->data->update_aux(opregion->regmap, reg, raw_temp) ?
+		AE_ERROR : AE_OK;
+}
+
+static acpi_status 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 AE_BAD_PARAMETER;
+
+	if (function == ACPI_READ)
+		return d->get_policy(regmap, reg, value) ? AE_ERROR : AE_OK;
+
+	if (*value != 0 || *value != 1)
+		return AE_BAD_PARAMETER;
+
+	return d->update_policy(regmap, reg, *value) ? AE_ERROR : AE_OK;
+}
+
+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;
+	int reg;
+	int result;
+
+	if (bits != 32 || !value64)
+		return AE_BAD_PARAMETER;
+
+	reg = pmic_get_thermal_reg(address, opregion->data->thermal_table,
+				   opregion->data->thermal_table_count);
+	if (!reg)
+		return AE_BAD_PARAMETER;
+
+	mutex_lock(&opregion->lock);
+
+	result = AE_BAD_PARAMETER;
+	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);
+
+	mutex_unlock(&opregion->lock);
+
+	return result;
+}
+
+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_PMOP_OPREGION_ID,
+						    intel_pmic_pmop_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_PMOP_OPREGION_ID,
+						  intel_pmic_pmop_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..18b9bb80f8b6
--- /dev/null
+++ b/drivers/acpi/pmic/intel_pmic.h
@@ -0,0 +1,34 @@
+#ifndef __INTEL_PMIC_H
+#define __INTEL_PMIC_H
+
+struct pmic_pwr_reg {
+	int reg;	/* corresponding PMIC register */
+	int bit;	/* control bit for power */
+};
+
+struct pmic_pwr_table {
+	int address;	/* operation region address */
+	struct pmic_pwr_reg pwr_reg;
+};
+
+struct pmic_thermal_table {
+	int address;	/* operation region address */
+	int reg;	/* corresponding thermal register */
+};
+
+struct intel_pmic_opregion_data {
+	int (*get_power)(struct regmap *r, struct pmic_pwr_reg *preg, u64 *value);
+	int (*update_power)(struct regmap *r, struct pmic_pwr_reg *preg, 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_pwr_table *pwr_table;
+	int pwr_table_count;
+	struct pmic_thermal_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..7629f16d1526
--- /dev/null
+++ b/drivers/acpi/pmic/intel_pmic_crc.c
@@ -0,0 +1,216 @@
+/*
+ * 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_pwr_table pwr_table[] = {
+	{
+		.address = 0x24,
+		.pwr_reg = {
+			.reg = 0x66,
+			.bit = 0x00,
+		},
+	},	/* X285 -> V2P85SX, camara */
+	{
+		.address = 0x48,
+		.pwr_reg = {
+			.reg = 0x5d,
+			.bit = 0x00,
+		},
+	},	/* V18X -> V1P8SX, eMMC/camara/audio */
+};
+
+static struct pmic_thermal_table thermal_table[] = {
+	{
+		.address = 0x00,
+		.reg = 0x75
+	},	/* TMP0 -> SYS0_THRM_RSLT_L */
+	{
+		.address = 0x04,
+		.reg = 0x95
+	},	/* AX00 -> SYS0_THRMALRT0_L */
+	{
+		.address = 0x08,
+		.reg = 0x97
+	},	/* AX01 -> SYS0_THRMALRT1_L */
+	{
+		.address = 0x0c,
+		.reg = 0x77
+	},	/* TMP1 -> SYS1_THRM_RSLT_L */
+	{
+		.address = 0x10,
+		.reg = 0x9a
+	},	/* AX10 -> SYS1_THRMALRT0_L */
+	{
+		.address = 0x14,
+		.reg = 0x9c
+	},	/* AX11 -> SYS1_THRMALRT1_L */
+	{
+		.address = 0x18,
+		.reg = 0x79
+	},	/* TMP2 -> SYS2_THRM_RSLT_L */
+	{
+		.address = 0x1c,
+		.reg = 0x9f
+	},	/* AX20 -> SYS2_THRMALRT0_L */
+	{
+		.address = 0x20,
+		.reg = 0xa1
+	},	/* AX21 -> SYS2_THRMALRT1_L */
+	{
+		.address = 0x48,
+		.reg = 0x94
+	},	/* PEN0 -> SYS0_THRMALRT0_H */
+	{
+		.address = 0x4c,
+		.reg = 0x99
+	},	/* PEN1 -> SYS1_THRMALRT1_H */
+	{
+		.address = 0x50,
+		.reg = 0x9e
+	},	/* PEN2 -> SYS2_THRMALRT2_H */
+};
+
+static int intel_crc_pmic_get_power(struct regmap *regmap,
+				    struct pmic_pwr_reg *preg, u64 *value)
+{
+	int data;
+
+	if (regmap_read(regmap, preg->reg, &data))
+		return -EIO;
+
+	*value = (data & PWR_SOURCE_SELECT) && (data & BIT(preg->bit)) ? 1 : 0;
+	return 0;
+}
+
+static int intel_crc_pmic_update_power(struct regmap *regmap,
+				       struct pmic_pwr_reg *preg, bool on)
+{
+	int data;
+
+	if (regmap_read(regmap, preg->reg, &data))
+		return -EIO;
+
+	if (on) {
+		data |= PWR_SOURCE_SELECT | BIT(preg->bit);
+	} else {
+		data &= ~BIT(preg->bit);
+		data |= PWR_SOURCE_SELECT;
+	}
+
+	if (regmap_write(regmap, preg->reg, data))
+		return -EIO;
+	return 0;
+}
+
+/* Raw temperature value is 10bits: 8bits in reg and 2bits in reg-1 bit0,1 */
+static int intel_crc_pmic_get_raw_temp(struct regmap *regmap, int reg)
+{
+	int temp_l, temp_h;
+
+	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)
+{
+	if (regmap_write(regmap, reg, raw) ||
+	    regmap_update_bits(regmap, reg - 1, 0x3, raw >> 8))
+		return -EIO;
+
+	return 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,
+	.pwr_table	= pwr_table,
+	.pwr_table_count= ARRAY_SIZE(pwr_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_region",
+	},
+};
+
+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/mfd/intel_soc_pmic_crc.c b/drivers/mfd/intel_soc_pmic_crc.c
index 7107cab832e6..48845d636bba 100644
--- a/drivers/mfd/intel_soc_pmic_crc.c
+++ b/drivers/mfd/intel_soc_pmic_crc.c
@@ -106,6 +106,9 @@ static struct mfd_cell crystal_cove_dev[] = {
 		.num_resources = ARRAY_SIZE(gpio_resources),
 		.resources = gpio_resources,
 	},
+	{
+		.name = "crystal_cove_region",
+	},
 };
 
 static struct regmap_config crystal_cove_regmap_config = {
-- 
1.9.3

^ permalink raw reply related	[flat|nested] 34+ messages in thread

* [PATCH v3 1/3] ACPI / PMIC: support PMIC operation region for CrystalCove
@ 2014-11-21  7:11     ` Aaron Lu
  0 siblings, 0 replies; 34+ messages in thread
From: Aaron Lu @ 2014-11-21  7:11 UTC (permalink / raw)
  To: Rafael J. Wysocki
  Cc: Lee Jones, Jacob Pan, Yegnesh Iyer, linux-acpi, linux-iio, linux-kernel

The Baytrail-T platform firmware has defined two customized operation
regions for PMIC chip Crystal Cove - one is for power resource handling
and one is for thermal: sensor temperature reporting, trip point setting,
etc. This patch adds support for them on top of the existing Crystal Cove
PMIC driver.

The reason to split code into a separate file intel_pmic.c is that there
are more PMIC drivers with ACPI operation region support coming and we can
re-use those code. The intel_pmic_opregion_data structure is created also
for this purpose: when we need to support a new PMIC's operation region,
we just need to fill those callbacks and the two register mapping tables.

Signed-off-by: Aaron Lu <aaron.lu@intel.com>
Acked-by: Lee Jones <lee.jones@linaro.org> for the MFD part
---
 drivers/acpi/Kconfig               |  17 ++
 drivers/acpi/Makefile              |   3 +
 drivers/acpi/pmic/intel_pmic.c     | 339 +++++++++++++++++++++++++++++++++++++
 drivers/acpi/pmic/intel_pmic.h     |  34 ++++
 drivers/acpi/pmic/intel_pmic_crc.c | 216 +++++++++++++++++++++++
 drivers/mfd/intel_soc_pmic_crc.c   |   3 +
 6 files changed, 612 insertions(+)
 create mode 100644 drivers/acpi/pmic/intel_pmic.c
 create mode 100644 drivers/acpi/pmic/intel_pmic.h
 create mode 100644 drivers/acpi/pmic/intel_pmic_crc.c

diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig
index 79078b8f5697..3e5f2056f946 100644
--- a/drivers/acpi/Kconfig
+++ b/drivers/acpi/Kconfig
@@ -393,4 +393,21 @@ config ACPI_EXTLOG
 	  driver adds support for that functionality with corresponding
 	  tracepoint which carries that information to userspace.
 
+menuconfig PMIC_OPREGION
+	bool "PMIC (Power Management Integrated Circuit) operation region support"
+	help
+	  Select this option to enable support for ACPI operation
+	  region of the PMIC chip. The operation region can be used
+	  to control power rails and sensor reading/writing on the
+	  PMIC chip.
+
+if PMIC_OPREGION
+config CRC_PMIC_OPREGION
+	bool "ACPI operation region support for CrystalCove PMIC"
+	depends on INTEL_SOC_PMIC
+	help
+	  This config adds ACPI operation region support for CrystalCove PMIC.
+
+endif
+
 endif	# ACPI
diff --git a/drivers/acpi/Makefile b/drivers/acpi/Makefile
index 6d11522f0e48..f5938399ac14 100644
--- a/drivers/acpi/Makefile
+++ b/drivers/acpi/Makefile
@@ -88,3 +88,6 @@ obj-$(CONFIG_ACPI_PROCESSOR_AGGREGATOR) += acpi_pad.o
 obj-$(CONFIG_ACPI_APEI)		+= apei/
 
 obj-$(CONFIG_ACPI_EXTLOG)	+= acpi_extlog.o
+
+obj-$(CONFIG_PMIC_OPREGION)	+= pmic/intel_pmic.o
+obj-$(CONFIG_CRC_PMIC_OPREGION) += pmic/intel_pmic_crc.o
diff --git a/drivers/acpi/pmic/intel_pmic.c b/drivers/acpi/pmic/intel_pmic.c
new file mode 100644
index 000000000000..5dbc0fb4d536
--- /dev/null
+++ b/drivers/acpi/pmic/intel_pmic.c
@@ -0,0 +1,339 @@
+/*
+ * 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_PMOP_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 struct pmic_pwr_reg *
+pmic_get_pwr_reg(int address, struct pmic_pwr_table *table, int count)
+{
+	int i;
+
+	for (i = 0; i < count; i++) {
+		if (table[i].address == address)
+			return &table[i].pwr_reg;
+	}
+	return NULL;
+}
+
+static int
+pmic_get_thermal_reg(int address, struct pmic_thermal_table *table, int count)
+{
+	int i;
+
+	for (i = 0; i < count; i++) {
+		if (table[i].address == address)
+			return table[i].reg;
+	}
+	return -ENOENT;
+}
+
+/* Return temperature from raw value through LPAT table */
+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;
+}
+
+/* Return raw value from temperature through LPAT table */
+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(*lpat) * 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)
+			goto out;
+		lpat[i] = 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_pmop_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;
+	struct pmic_pwr_reg *preg;
+	int result;
+
+	if (bits != 32 || !value64)
+		return AE_BAD_PARAMETER;
+
+	if (function == ACPI_WRITE && !(*value64 == 0 || *value64 == 1))
+		return AE_BAD_PARAMETER;
+
+	preg = pmic_get_pwr_reg(address, d->pwr_table, d->pwr_table_count);
+	if (!preg)
+		return AE_BAD_PARAMETER;
+
+	mutex_lock(&opregion->lock);
+
+	if (function == ACPI_READ)
+		result = d->get_power(regmap, preg, value64);
+	else
+		result = d->update_power(regmap, preg, *value64 == 1);
+
+	mutex_unlock(&opregion->lock);
+
+	return result ? AE_ERROR : AE_OK;
+}
+
+static acpi_status pmic_read_temp(struct intel_pmic_opregion *opregion,
+				  int reg, u64 *value)
+{
+	int raw_temp, temp;
+
+	if (!opregion->data->get_raw_temp)
+		return AE_BAD_PARAMETER;
+
+	raw_temp = opregion->data->get_raw_temp(opregion->regmap, reg);
+	if (raw_temp < 0)
+		return AE_ERROR;
+
+	if (!opregion->lpat) {
+		*value = raw_temp;
+		return AE_OK;
+	}
+
+	temp = raw_to_temp(opregion->lpat, opregion->lpat_count, raw_temp);
+	if (temp < 0)
+		return AE_ERROR;
+
+	*value = temp;
+	return AE_OK;
+}
+
+static acpi_status pmic_thermal_temp(struct intel_pmic_opregion *opregion,
+				     int reg, u32 function, u64 *value)
+{
+	if (function != ACPI_READ)
+		return AE_BAD_PARAMETER;
+
+	return pmic_read_temp(opregion, reg, value);
+}
+
+static acpi_status 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 AE_BAD_PARAMETER;
+
+	if (opregion->lpat) {
+		raw_temp = temp_to_raw(opregion->lpat, opregion->lpat_count,
+					 *value);
+		if (raw_temp < 0)
+			return AE_ERROR;
+	} else {
+		raw_temp = *value;
+	}
+
+	return opregion->data->update_aux(opregion->regmap, reg, raw_temp) ?
+		AE_ERROR : AE_OK;
+}
+
+static acpi_status 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 AE_BAD_PARAMETER;
+
+	if (function == ACPI_READ)
+		return d->get_policy(regmap, reg, value) ? AE_ERROR : AE_OK;
+
+	if (*value != 0 || *value != 1)
+		return AE_BAD_PARAMETER;
+
+	return d->update_policy(regmap, reg, *value) ? AE_ERROR : AE_OK;
+}
+
+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;
+	int reg;
+	int result;
+
+	if (bits != 32 || !value64)
+		return AE_BAD_PARAMETER;
+
+	reg = pmic_get_thermal_reg(address, opregion->data->thermal_table,
+				   opregion->data->thermal_table_count);
+	if (!reg)
+		return AE_BAD_PARAMETER;
+
+	mutex_lock(&opregion->lock);
+
+	result = AE_BAD_PARAMETER;
+	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);
+
+	mutex_unlock(&opregion->lock);
+
+	return result;
+}
+
+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_PMOP_OPREGION_ID,
+						    intel_pmic_pmop_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_PMOP_OPREGION_ID,
+						  intel_pmic_pmop_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..18b9bb80f8b6
--- /dev/null
+++ b/drivers/acpi/pmic/intel_pmic.h
@@ -0,0 +1,34 @@
+#ifndef __INTEL_PMIC_H
+#define __INTEL_PMIC_H
+
+struct pmic_pwr_reg {
+	int reg;	/* corresponding PMIC register */
+	int bit;	/* control bit for power */
+};
+
+struct pmic_pwr_table {
+	int address;	/* operation region address */
+	struct pmic_pwr_reg pwr_reg;
+};
+
+struct pmic_thermal_table {
+	int address;	/* operation region address */
+	int reg;	/* corresponding thermal register */
+};
+
+struct intel_pmic_opregion_data {
+	int (*get_power)(struct regmap *r, struct pmic_pwr_reg *preg, u64 *value);
+	int (*update_power)(struct regmap *r, struct pmic_pwr_reg *preg, 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_pwr_table *pwr_table;
+	int pwr_table_count;
+	struct pmic_thermal_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..7629f16d1526
--- /dev/null
+++ b/drivers/acpi/pmic/intel_pmic_crc.c
@@ -0,0 +1,216 @@
+/*
+ * 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_pwr_table pwr_table[] = {
+	{
+		.address = 0x24,
+		.pwr_reg = {
+			.reg = 0x66,
+			.bit = 0x00,
+		},
+	},	/* X285 -> V2P85SX, camara */
+	{
+		.address = 0x48,
+		.pwr_reg = {
+			.reg = 0x5d,
+			.bit = 0x00,
+		},
+	},	/* V18X -> V1P8SX, eMMC/camara/audio */
+};
+
+static struct pmic_thermal_table thermal_table[] = {
+	{
+		.address = 0x00,
+		.reg = 0x75
+	},	/* TMP0 -> SYS0_THRM_RSLT_L */
+	{
+		.address = 0x04,
+		.reg = 0x95
+	},	/* AX00 -> SYS0_THRMALRT0_L */
+	{
+		.address = 0x08,
+		.reg = 0x97
+	},	/* AX01 -> SYS0_THRMALRT1_L */
+	{
+		.address = 0x0c,
+		.reg = 0x77
+	},	/* TMP1 -> SYS1_THRM_RSLT_L */
+	{
+		.address = 0x10,
+		.reg = 0x9a
+	},	/* AX10 -> SYS1_THRMALRT0_L */
+	{
+		.address = 0x14,
+		.reg = 0x9c
+	},	/* AX11 -> SYS1_THRMALRT1_L */
+	{
+		.address = 0x18,
+		.reg = 0x79
+	},	/* TMP2 -> SYS2_THRM_RSLT_L */
+	{
+		.address = 0x1c,
+		.reg = 0x9f
+	},	/* AX20 -> SYS2_THRMALRT0_L */
+	{
+		.address = 0x20,
+		.reg = 0xa1
+	},	/* AX21 -> SYS2_THRMALRT1_L */
+	{
+		.address = 0x48,
+		.reg = 0x94
+	},	/* PEN0 -> SYS0_THRMALRT0_H */
+	{
+		.address = 0x4c,
+		.reg = 0x99
+	},	/* PEN1 -> SYS1_THRMALRT1_H */
+	{
+		.address = 0x50,
+		.reg = 0x9e
+	},	/* PEN2 -> SYS2_THRMALRT2_H */
+};
+
+static int intel_crc_pmic_get_power(struct regmap *regmap,
+				    struct pmic_pwr_reg *preg, u64 *value)
+{
+	int data;
+
+	if (regmap_read(regmap, preg->reg, &data))
+		return -EIO;
+
+	*value = (data & PWR_SOURCE_SELECT) && (data & BIT(preg->bit)) ? 1 : 0;
+	return 0;
+}
+
+static int intel_crc_pmic_update_power(struct regmap *regmap,
+				       struct pmic_pwr_reg *preg, bool on)
+{
+	int data;
+
+	if (regmap_read(regmap, preg->reg, &data))
+		return -EIO;
+
+	if (on) {
+		data |= PWR_SOURCE_SELECT | BIT(preg->bit);
+	} else {
+		data &= ~BIT(preg->bit);
+		data |= PWR_SOURCE_SELECT;
+	}
+
+	if (regmap_write(regmap, preg->reg, data))
+		return -EIO;
+	return 0;
+}
+
+/* Raw temperature value is 10bits: 8bits in reg and 2bits in reg-1 bit0,1 */
+static int intel_crc_pmic_get_raw_temp(struct regmap *regmap, int reg)
+{
+	int temp_l, temp_h;
+
+	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)
+{
+	if (regmap_write(regmap, reg, raw) ||
+	    regmap_update_bits(regmap, reg - 1, 0x3, raw >> 8))
+		return -EIO;
+
+	return 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,
+	.pwr_table	= pwr_table,
+	.pwr_table_count= ARRAY_SIZE(pwr_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_region",
+	},
+};
+
+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/mfd/intel_soc_pmic_crc.c b/drivers/mfd/intel_soc_pmic_crc.c
index 7107cab832e6..48845d636bba 100644
--- a/drivers/mfd/intel_soc_pmic_crc.c
+++ b/drivers/mfd/intel_soc_pmic_crc.c
@@ -106,6 +106,9 @@ static struct mfd_cell crystal_cove_dev[] = {
 		.num_resources = ARRAY_SIZE(gpio_resources),
 		.resources = gpio_resources,
 	},
+	{
+		.name = "crystal_cove_region",
+	},
 };
 
 static struct regmap_config crystal_cove_regmap_config = {
-- 
1.9.3


^ permalink raw reply related	[flat|nested] 34+ messages in thread

* [PATCH v3 2/3] ACPI / PMIC: support PMIC operation region for XPower AXP288
  2014-11-21  7:11 ` Aaron Lu
  (?)
@ 2014-11-21  7:11 ` Aaron Lu
  2014-11-24  1:04   ` Rafael J. Wysocki
  -1 siblings, 1 reply; 34+ messages in thread
From: Aaron Lu @ 2014-11-21  7:11 UTC (permalink / raw)
  To: Rafael J. Wysocki
  Cc: Lee Jones, Jacob Pan, Yegnesh Iyer, linux-acpi, linux-iio, linux-kernel

The Baytrail-T-CR platform firmware has defined two customized operation
regions for PMIC chip Dollar Cove XPower - one is for power resource
handling and one is for thermal just like the CrystalCove one. This patch
adds support for them on top of the common PMIC opregion region code.

Signed-off-by: Aaron Lu <aaron.lu@intel.com>
Acked-by: Lee Jones <lee.jones@linaro.org> for the MFD part
---
 drivers/acpi/Kconfig                  |   6 +
 drivers/acpi/Makefile                 |   1 +
 drivers/acpi/pmic/intel_pmic_xpower.c | 277 ++++++++++++++++++++++++++++++++++
 drivers/mfd/axp20x.c                  |   3 +
 4 files changed, 287 insertions(+)
 create mode 100644 drivers/acpi/pmic/intel_pmic_xpower.c

diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig
index 3e5f2056f946..227f0692cbff 100644
--- a/drivers/acpi/Kconfig
+++ b/drivers/acpi/Kconfig
@@ -408,6 +408,12 @@ config CRC_PMIC_OPREGION
 	help
 	  This config adds ACPI operation region support for CrystalCove PMIC.
 
+config XPOWER_PMIC_OPREGION
+	bool "ACPI operation region support for XPower AXP288 PMIC"
+	depends on AXP288_ADC
+	help
+	  This config adds ACPI operation region support for XPower AXP288 PMIC.
+
 endif
 
 endif	# ACPI
diff --git a/drivers/acpi/Makefile b/drivers/acpi/Makefile
index f5938399ac14..f74317cc1ca9 100644
--- a/drivers/acpi/Makefile
+++ b/drivers/acpi/Makefile
@@ -91,3 +91,4 @@ obj-$(CONFIG_ACPI_EXTLOG)	+= acpi_extlog.o
 
 obj-$(CONFIG_PMIC_OPREGION)	+= pmic/intel_pmic.o
 obj-$(CONFIG_CRC_PMIC_OPREGION) += pmic/intel_pmic_crc.o
+obj-$(CONFIG_XPOWER_PMIC_OPREGION) += pmic/intel_pmic_xpower.o
diff --git a/drivers/acpi/pmic/intel_pmic_xpower.c b/drivers/acpi/pmic/intel_pmic_xpower.c
new file mode 100644
index 000000000000..6c4d6ce0cff1
--- /dev/null
+++ b/drivers/acpi/pmic/intel_pmic_xpower.c
@@ -0,0 +1,277 @@
+/*
+ * 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_pwr_table pwr_table[] = {
+	{
+		.address = 0x00,
+		.pwr_reg = {
+			.reg = 0x13,
+			.bit = 0x05,
+		}
+	}, /* ALD1, 0x13, bit5 */
+	{
+		.address = 0x04,
+		.pwr_reg = {
+			.reg = 0x13,
+			.bit = 0x06,
+		}
+	}, /* ALD2, 0x13, bit6 */
+	{
+		.address = 0x08,
+		.pwr_reg = {
+			.reg = 0x13,
+			.bit = 0x07,
+		}
+	}, /* ALD3, 0x13, bit7 */
+	{
+		.address = 0x0c,
+		.pwr_reg = {
+			.reg = 0x12,
+			.bit = 0x03,
+		}
+	}, /* DLD1, 0x12, bit3 */
+	{
+		.address = 0x10,
+		.pwr_reg = {
+			.reg = 0x12,
+			.bit = 0x04,
+		}
+	}, /* DLD2, 0x12, bit4 */
+	{
+		.address = 0x14,
+		.pwr_reg = {
+			.reg = 0x12,
+			.bit = 0x05,
+		}
+	}, /* DLD3, 0x12, bit5 */
+	{
+		.address = 0x18,
+		.pwr_reg = {
+			.reg = 0x12,
+			.bit = 0x06,
+		}
+	}, /* DLD4, 0x12, bit6 */
+	{
+		.address = 0x1c,
+		.pwr_reg = {
+			.reg = 0x12,
+			.bit = 0x00,
+		}
+	}, /* ELD1, 0x12, bit0 */
+	{
+		.address = 0x20,
+		.pwr_reg = {
+			.reg = 0x12,
+			.bit = 0x01,
+		}
+	}, /* ELD2, 0x12, bit1 */
+	{
+		.address = 0x24,
+		.pwr_reg = {
+			.reg = 0x12,
+			.bit = 0x02,
+		}
+	}, /* ELD3, 0x12, bit2 */
+	{
+		.address = 0x28,
+		.pwr_reg = {
+			.reg = 0x13,
+			.bit = 0x02,
+		}
+	}, /* FLD1, 0x13, bit2 */
+	{
+		.address = 0x2c,
+		.pwr_reg = {
+			.reg = 0x13,
+			.bit = 0x03,
+		}
+	}, /* FLD2, 0x13, bit3 */
+	{
+		.address = 0x30,
+		.pwr_reg = {
+			.reg = 0x13,
+			.bit = 0x04,
+		}
+	}, /* FLD3, 0x13, bit4 */
+	{
+		.address = 0x38,
+		.pwr_reg = {
+			.reg = 0x10,
+			.bit = 0x03,
+		}
+	}, /* BUC1, 0x10, bit3 */
+	{
+		.address = 0x3c,
+		.pwr_reg = {
+			.reg = 0x10,
+			.bit = 0x06,
+		}
+	}, /* BUC2, 0x10, bit6 */
+	{
+		.address = 0x40,
+		.pwr_reg = {
+			.reg = 0x10,
+			.bit = 0x05,
+		}
+	}, /* BUC3, 0x10, bit5 */
+	{
+		.address = 0x44,
+		.pwr_reg = {
+			.reg = 0x10,
+			.bit = 0x04,
+		}
+	}, /* BUC4, 0x10, bit4 */
+	{
+		.address = 0x48,
+		.pwr_reg = {
+			.reg = 0x10,
+			.bit = 0x01,
+		}
+	}, /* BUC5, 0x10, bit1 */
+	{
+		.address = 0x4c,
+		.pwr_reg = {
+			.reg = 0x10,
+			.bit = 0x00
+		}
+	}, /* BUC6, 0x10, bit0 */
+};
+
+/* TMP0 - TMP5 are the same, all from GPADC */
+static struct pmic_thermal_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,
+				       struct pmic_pwr_reg *preg, u64 *value)
+{
+	int data;
+
+	if (regmap_read(regmap, preg->reg, &data))
+		return -EIO;
+
+	*value = (data & BIT(preg->bit)) ? 1 : 0;
+	return 0;
+}
+
+static int intel_xpower_pmic_update_power(struct regmap *regmap,
+					  struct pmic_pwr_reg *preg, bool on)
+{
+	int data;
+
+	if (regmap_read(regmap, preg->reg, &data))
+		return -EIO;
+
+	if (on)
+		data |= BIT(preg->bit);
+	else
+		data &= ~BIT(preg->bit);
+
+	if (regmap_write(regmap, preg->reg, data))
+		return -EIO;
+
+	return 0;
+}
+
+/*
+ * 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.
+ */
+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,
+	.pwr_table = pwr_table,
+	.pwr_table_count = ARRAY_SIZE(pwr_table),
+	.thermal_table = thermal_table,
+	.thermal_table_count = ARRAY_SIZE(thermal_table),
+};
+
+
+static int intel_xpower_pmic_opregion_probe(struct platform_device *pdev)
+{
+	struct axp20x_dev *axp20x = dev_get_drvdata(pdev->dev.parent);
+	return intel_pmic_install_opregion_handler(&pdev->dev,
+			ACPI_HANDLE(pdev->dev.parent), axp20x->regmap,
+			&intel_xpower_pmic_opregion_data);
+}
+
+static struct platform_driver intel_xpower_pmic_opregion_driver = {
+	.probe = intel_xpower_pmic_opregion_probe,
+	.driver = {
+		.name = "axp288_opregion",
+	},
+};
+
+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");
diff --git a/drivers/mfd/axp20x.c b/drivers/mfd/axp20x.c
index acbb4c3bf98c..daf3c8d33b4d 100644
--- a/drivers/mfd/axp20x.c
+++ b/drivers/mfd/axp20x.c
@@ -354,6 +354,9 @@ static struct mfd_cell axp288_cells[] = {
 		.num_resources = ARRAY_SIZE(axp288_battery_resources),
 		.resources = axp288_battery_resources,
 	},
+	{
+		.name = "axp288_opregion",
+	},
 };
 
 static struct axp20x_dev *axp20x_pm_power_off;
-- 
1.9.3

^ permalink raw reply related	[flat|nested] 34+ messages in thread

* [PATCH v3 3/3] ACPI / PMIC: AXP288: support virtual GPIO in ACPI table
  2014-11-21  7:11 ` Aaron Lu
  (?)
  (?)
@ 2014-11-21  7:11 ` Aaron Lu
  2014-11-24  1:06   ` Rafael J. Wysocki
  -1 siblings, 1 reply; 34+ messages in thread
From: Aaron Lu @ 2014-11-21  7:11 UTC (permalink / raw)
  To: Rafael J. Wysocki
  Cc: Lee Jones, Jacob Pan, Yegnesh Iyer, linux-acpi, linux-iio, linux-kernel

The same virtual GPIO strategy is also used for the AXP288 PMIC in that
various control methods that are used to do power rail handling and
sensor reading/setting will touch GPIO fields defined under the PMIC
device. The GPIO fileds are only defined by the ACPI code while the
actual hardware doesn't really have a GPIO controller, but to make those
control method execution succeed, we have to install a GPIO handler for
the PMIC device handle. Since we do not need the virtual GPIO strategy,
we can simply do nothing in that handler.

Signed-off-by: Aaron Lu <aaron.lu@intel.com>
---
 drivers/acpi/pmic/intel_pmic_xpower.c | 27 +++++++++++++++++++++++----
 1 file changed, 23 insertions(+), 4 deletions(-)

diff --git a/drivers/acpi/pmic/intel_pmic_xpower.c b/drivers/acpi/pmic/intel_pmic_xpower.c
index 6c4d6ce0cff1..480c41c36444 100644
--- a/drivers/acpi/pmic/intel_pmic_xpower.c
+++ b/drivers/acpi/pmic/intel_pmic_xpower.c
@@ -251,13 +251,32 @@ static struct intel_pmic_opregion_data intel_xpower_pmic_opregion_data = {
 	.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 axp20x_dev *axp20x = dev_get_drvdata(pdev->dev.parent);
-	return intel_pmic_install_opregion_handler(&pdev->dev,
-			ACPI_HANDLE(pdev->dev.parent), axp20x->regmap,
-			&intel_xpower_pmic_opregion_data);
+	struct device *parent = pdev->dev.parent;
+	struct axp20x_dev *axp20x = dev_get_drvdata(parent);
+	acpi_status status;
+	int result;
+
+	result = intel_pmic_install_opregion_handler(&pdev->dev,
+					ACPI_HANDLE(parent), axp20x->regmap,
+					&intel_xpower_pmic_opregion_data);
+	if (!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))
+			result = -ENODEV;
+	}
+
+	return result;
 }
 
 static struct platform_driver intel_xpower_pmic_opregion_driver = {
-- 
1.9.3

^ permalink raw reply related	[flat|nested] 34+ messages in thread

* Re: [PATCH v3 1/3] ACPI / PMIC: support PMIC operation region for CrystalCove
  2014-11-21  7:11     ` Aaron Lu
  (?)
@ 2014-11-24  0:59     ` Rafael J. Wysocki
  2014-11-24  5:55       ` Aaron Lu
  2014-11-24  9:21       ` [PATCH v3 1/3 updated] " Aaron Lu
  -1 siblings, 2 replies; 34+ messages in thread
From: Rafael J. Wysocki @ 2014-11-24  0:59 UTC (permalink / raw)
  To: Aaron Lu
  Cc: Lee Jones, Jacob Pan, Yegnesh Iyer, linux-acpi, linux-iio, linux-kernel

On Friday, November 21, 2014 03:11:49 PM Aaron Lu wrote:
> The Baytrail-T platform firmware has defined two customized operation
> regions for PMIC chip Crystal Cove - one is for power resource handling
> and one is for thermal: sensor temperature reporting, trip point setting,
> etc. This patch adds support for them on top of the existing Crystal Cove
> PMIC driver.
> 
> The reason to split code into a separate file intel_pmic.c is that there
> are more PMIC drivers with ACPI operation region support coming and we can
> re-use those code. The intel_pmic_opregion_data structure is created also
> for this purpose: when we need to support a new PMIC's operation region,
> we just need to fill those callbacks and the two register mapping tables.
> 
> Signed-off-by: Aaron Lu <aaron.lu@intel.com>
> Acked-by: Lee Jones <lee.jones@linaro.org> for the MFD part

Thanks for resending, looks better to me.

Some nitpicking below.

> ---
>  drivers/acpi/Kconfig               |  17 ++
>  drivers/acpi/Makefile              |   3 +
>  drivers/acpi/pmic/intel_pmic.c     | 339 +++++++++++++++++++++++++++++++++++++
>  drivers/acpi/pmic/intel_pmic.h     |  34 ++++
>  drivers/acpi/pmic/intel_pmic_crc.c | 216 +++++++++++++++++++++++
>  drivers/mfd/intel_soc_pmic_crc.c   |   3 +
>  6 files changed, 612 insertions(+)
>  create mode 100644 drivers/acpi/pmic/intel_pmic.c
>  create mode 100644 drivers/acpi/pmic/intel_pmic.h
>  create mode 100644 drivers/acpi/pmic/intel_pmic_crc.c
> 
> diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig
> index 79078b8f5697..3e5f2056f946 100644
> --- a/drivers/acpi/Kconfig
> +++ b/drivers/acpi/Kconfig
> @@ -393,4 +393,21 @@ config ACPI_EXTLOG
>  	  driver adds support for that functionality with corresponding
>  	  tracepoint which carries that information to userspace.
>  
> +menuconfig PMIC_OPREGION
> +	bool "PMIC (Power Management Integrated Circuit) operation region support"
> +	help
> +	  Select this option to enable support for ACPI operation
> +	  region of the PMIC chip. The operation region can be used
> +	  to control power rails and sensor reading/writing on the
> +	  PMIC chip.
> +
> +if PMIC_OPREGION
> +config CRC_PMIC_OPREGION

If that is the only possible choice for PMIC_OPREGION, it should be selected
automatically.  Alternatively, PMIC_OPREGION should be selected automatically
if CRC_PMIC_OPREGION is set.

> +	bool "ACPI operation region support for CrystalCove PMIC"
> +	depends on INTEL_SOC_PMIC
> +	help
> +	  This config adds ACPI operation region support for CrystalCove PMIC.
> +
> +endif
> +
>  endif	# ACPI
> diff --git a/drivers/acpi/Makefile b/drivers/acpi/Makefile
> index 6d11522f0e48..f5938399ac14 100644
> --- a/drivers/acpi/Makefile
> +++ b/drivers/acpi/Makefile
> @@ -88,3 +88,6 @@ obj-$(CONFIG_ACPI_PROCESSOR_AGGREGATOR) += acpi_pad.o
>  obj-$(CONFIG_ACPI_APEI)		+= apei/
>  
>  obj-$(CONFIG_ACPI_EXTLOG)	+= acpi_extlog.o
> +
> +obj-$(CONFIG_PMIC_OPREGION)	+= pmic/intel_pmic.o
> +obj-$(CONFIG_CRC_PMIC_OPREGION) += pmic/intel_pmic_crc.o
> diff --git a/drivers/acpi/pmic/intel_pmic.c b/drivers/acpi/pmic/intel_pmic.c
> new file mode 100644
> index 000000000000..5dbc0fb4d536
> --- /dev/null
> +++ b/drivers/acpi/pmic/intel_pmic.c
> @@ -0,0 +1,339 @@
> +/*
> + * 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_PMOP_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 struct pmic_pwr_reg *
> +pmic_get_pwr_reg(int address, struct pmic_pwr_table *table, int count)
> +{
> +	int i;
> +
> +	for (i = 0; i < count; i++) {
> +		if (table[i].address == address)
> +			return &table[i].pwr_reg;
> +	}
> +	return NULL;
> +}
> +
> +static int
> +pmic_get_thermal_reg(int address, struct pmic_thermal_table *table, int count)
> +{
> +	int i;
> +
> +	for (i = 0; i < count; i++) {
> +		if (table[i].address == address)
> +			return table[i].reg;
> +	}
> +	return -ENOENT;
> +}

This is slightly inconsistent.  While pmic_get_pwr_reg() returns a pointer
to struct pmic_pwr_reg, this one returns an int.

I see that this is because the definitions of struct pmic_thermal_table
and struct pmic_pwr_table are inconsistent, but is that really necessary?

You could define

struct pmic_table {
	int address;	/* operation region address */
	int reg;	/* corresponding PMIC register */
	int bit;	/* control bit for power */
};

and use it for both power and thermal.  [The latter will not use the bit field,
but is that really a problem?]

It looks like some code duplication might be reduced this way.

Besides, "power" looks better than "pwr", especially that you use "thermal"
instead of "thrm" (for example).

> +
> +/* Return temperature from raw value through LPAT table */
> +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;
> +}
> +
> +/* Return raw value from temperature through LPAT table */
> +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(*lpat) * obj_p->package.count,
> +			    GFP_KERNEL);

This looks fishy.

Of course, sizeof(*lpat) is the same as sizeof(int), but is more obfuscated
and you're allocating memory for an array of integers.

> +	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)

lpat[] has to be freed here.

> +			goto out;
> +		lpat[i] = obj_e->integer.value;

Here, integer.value is generally u64, so I'd use an explicit cast to s64 before
casting that to int.  Otherwise it looks like you've forgotten about possible
overflows, which I assume is not the case.

> +	}
> +
> +	opregion->lpat = (struct acpi_lpat *)lpat;
> +	opregion->lpat_count = obj_p->package.count / 2;
> +
> +out:
> +	kfree(buffer.pointer);
> +}
> +
> +static acpi_status
> +intel_pmic_pmop_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;
> +	struct pmic_pwr_reg *preg;
> +	int result;
> +
> +	if (bits != 32 || !value64)
> +		return AE_BAD_PARAMETER;
> +
> +	if (function == ACPI_WRITE && !(*value64 == 0 || *value64 == 1))
> +		return AE_BAD_PARAMETER;
> +
> +	preg = pmic_get_pwr_reg(address, d->pwr_table, d->pwr_table_count);
> +	if (!preg)
> +		return AE_BAD_PARAMETER;
> +
> +	mutex_lock(&opregion->lock);
> +
> +	if (function == ACPI_READ)
> +		result = d->get_power(regmap, preg, value64);
> +	else
> +		result = d->update_power(regmap, preg, *value64 == 1);

I'd write that as

	retult = function == ACPI_READ ?
		d->get_power(regmap, preg, value64) :
		d->update_power(regmap, preg, *value64 == 1);

which will be consistent with the "return" statement below.

> +
> +	mutex_unlock(&opregion->lock);
> +
> +	return result ? AE_ERROR : AE_OK;
> +}
> +
> +static acpi_status pmic_read_temp(struct intel_pmic_opregion *opregion,
> +				  int reg, u64 *value)
> +{
> +	int raw_temp, temp;
> +
> +	if (!opregion->data->get_raw_temp)
> +		return AE_BAD_PARAMETER;
> +
> +	raw_temp = opregion->data->get_raw_temp(opregion->regmap, reg);
> +	if (raw_temp < 0)
> +		return AE_ERROR;
> +
> +	if (!opregion->lpat) {
> +		*value = raw_temp;
> +		return AE_OK;
> +	}
> +
> +	temp = raw_to_temp(opregion->lpat, opregion->lpat_count, raw_temp);
> +	if (temp < 0)
> +		return AE_ERROR;
> +
> +	*value = temp;
> +	return AE_OK;
> +}
> +
> +static acpi_status pmic_thermal_temp(struct intel_pmic_opregion *opregion,
> +				     int reg, u32 function, u64 *value)
> +{
> +	if (function != ACPI_READ)
> +		return AE_BAD_PARAMETER;
> +
> +	return pmic_read_temp(opregion, reg, value);

What about

	return function == ACPI_READ ?
		pmic_read_temp(opregion, reg, value) : AE_BAD_PARAMETER;

?

> +}
> +
> +static acpi_status 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 AE_BAD_PARAMETER;
> +
> +	if (opregion->lpat) {
> +		raw_temp = temp_to_raw(opregion->lpat, opregion->lpat_count,
> +					 *value);
> +		if (raw_temp < 0)
> +			return AE_ERROR;
> +	} else {
> +		raw_temp = *value;
> +	}
> +
> +	return opregion->data->update_aux(opregion->regmap, reg, raw_temp) ?
> +		AE_ERROR : AE_OK;

You seem to be casting all error codes into AE_ERROR here.  Should the function
simply return int and pass the original error code to the caller instead?

> +}
> +
> +static acpi_status 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 AE_BAD_PARAMETER;
> +
> +	if (function == ACPI_READ)
> +		return d->get_policy(regmap, reg, value) ? AE_ERROR : AE_OK;
> +
> +	if (*value != 0 || *value != 1)
> +		return AE_BAD_PARAMETER;
> +
> +	return d->update_policy(regmap, reg, *value) ? AE_ERROR : AE_OK;

Well, same here.

> +}
> +
> +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;
> +	int reg;
> +	int result;
> +
> +	if (bits != 32 || !value64)
> +		return AE_BAD_PARAMETER;
> +
> +	reg = pmic_get_thermal_reg(address, opregion->data->thermal_table,
> +				   opregion->data->thermal_table_count);
> +	if (!reg)
> +		return AE_BAD_PARAMETER;
> +
> +	mutex_lock(&opregion->lock);
> +
> +	result = AE_BAD_PARAMETER;
> +	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);
> +
> +	mutex_unlock(&opregion->lock);
> +
> +	return result;
> +}
> +
> +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_PMOP_OPREGION_ID,
> +						    intel_pmic_pmop_handler,
> +						    NULL, opregion);
> +	if (ACPI_FAILURE(status))
> +		return -ENODEV;

And you return a int from here.

Would it make sense for the majority of functions in this file to return ints
rather than acpi_status values?

> +
> +	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_PMOP_OPREGION_ID,
> +						  intel_pmic_pmop_handler);
> +		return -ENODEV;
> +	}
> +
> +	opregion->data = d;

I guess the opregion will never be removed, right?

> +	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..18b9bb80f8b6
> --- /dev/null
> +++ b/drivers/acpi/pmic/intel_pmic.h
> @@ -0,0 +1,34 @@
> +#ifndef __INTEL_PMIC_H
> +#define __INTEL_PMIC_H
> +
> +struct pmic_pwr_reg {
> +	int reg;	/* corresponding PMIC register */
> +	int bit;	/* control bit for power */
> +};
> +
> +struct pmic_pwr_table {
> +	int address;	/* operation region address */
> +	struct pmic_pwr_reg pwr_reg;
> +};
> +
> +struct pmic_thermal_table {
> +	int address;	/* operation region address */
> +	int reg;	/* corresponding thermal register */
> +};
> +
> +struct intel_pmic_opregion_data {
> +	int (*get_power)(struct regmap *r, struct pmic_pwr_reg *preg, u64 *value);
> +	int (*update_power)(struct regmap *r, struct pmic_pwr_reg *preg, 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_pwr_table *pwr_table;
> +	int pwr_table_count;
> +	struct pmic_thermal_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..7629f16d1526
> --- /dev/null
> +++ b/drivers/acpi/pmic/intel_pmic_crc.c
> @@ -0,0 +1,216 @@
> +/*
> + * 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_pwr_table pwr_table[] = {
> +	{
> +		.address = 0x24,
> +		.pwr_reg = {
> +			.reg = 0x66,
> +			.bit = 0x00,
> +		},
> +	},	/* X285 -> V2P85SX, camara */
> +	{
> +		.address = 0x48,
> +		.pwr_reg = {
> +			.reg = 0x5d,
> +			.bit = 0x00,
> +		},
> +	},	/* V18X -> V1P8SX, eMMC/camara/audio */
> +};
> +
> +static struct pmic_thermal_table thermal_table[] = {
> +	{
> +		.address = 0x00,
> +		.reg = 0x75
> +	},	/* TMP0 -> SYS0_THRM_RSLT_L */
> +	{
> +		.address = 0x04,
> +		.reg = 0x95
> +	},	/* AX00 -> SYS0_THRMALRT0_L */
> +	{
> +		.address = 0x08,
> +		.reg = 0x97
> +	},	/* AX01 -> SYS0_THRMALRT1_L */
> +	{
> +		.address = 0x0c,
> +		.reg = 0x77
> +	},	/* TMP1 -> SYS1_THRM_RSLT_L */
> +	{
> +		.address = 0x10,
> +		.reg = 0x9a
> +	},	/* AX10 -> SYS1_THRMALRT0_L */
> +	{
> +		.address = 0x14,
> +		.reg = 0x9c
> +	},	/* AX11 -> SYS1_THRMALRT1_L */
> +	{
> +		.address = 0x18,
> +		.reg = 0x79
> +	},	/* TMP2 -> SYS2_THRM_RSLT_L */
> +	{
> +		.address = 0x1c,
> +		.reg = 0x9f
> +	},	/* AX20 -> SYS2_THRMALRT0_L */
> +	{
> +		.address = 0x20,
> +		.reg = 0xa1
> +	},	/* AX21 -> SYS2_THRMALRT1_L */
> +	{
> +		.address = 0x48,
> +		.reg = 0x94
> +	},	/* PEN0 -> SYS0_THRMALRT0_H */
> +	{
> +		.address = 0x4c,
> +		.reg = 0x99
> +	},	/* PEN1 -> SYS1_THRMALRT1_H */
> +	{
> +		.address = 0x50,
> +		.reg = 0x9e
> +	},	/* PEN2 -> SYS2_THRMALRT2_H */
> +};
> +
> +static int intel_crc_pmic_get_power(struct regmap *regmap,
> +				    struct pmic_pwr_reg *preg, u64 *value)
> +{
> +	int data;
> +
> +	if (regmap_read(regmap, preg->reg, &data))
> +		return -EIO;
> +
> +	*value = (data & PWR_SOURCE_SELECT) && (data & BIT(preg->bit)) ? 1 : 0;
> +	return 0;
> +}
> +
> +static int intel_crc_pmic_update_power(struct regmap *regmap,
> +				       struct pmic_pwr_reg *preg, bool on)
> +{
> +	int data;
> +
> +	if (regmap_read(regmap, preg->reg, &data))
> +		return -EIO;
> +
> +	if (on) {
> +		data |= PWR_SOURCE_SELECT | BIT(preg->bit);
> +	} else {
> +		data &= ~BIT(preg->bit);
> +		data |= PWR_SOURCE_SELECT;
> +	}
> +
> +	if (regmap_write(regmap, preg->reg, data))
> +		return -EIO;
> +	return 0;
> +}
> +
> +/* Raw temperature value is 10bits: 8bits in reg and 2bits in reg-1 bit0,1 */

Proper kerneldoc, please.  Here and elsewhere where it makes sense.

All functions that aren't static need to have kerneldoc comments.

> +static int intel_crc_pmic_get_raw_temp(struct regmap *regmap, int reg)
> +{
> +	int temp_l, temp_h;
> +
> +	if (regmap_read(regmap, reg, &temp_l) ||
> +	    regmap_read(regmap, reg - 1, &temp_h))
> +		return -EIO;
> +
> +	return (temp_l | ((temp_h & 0x3) << 8));

At least one paren is not necessary here.

> +}
> +
> +static int
> +intel_crc_pmic_update_aux(struct regmap *regmap, int reg, int raw)
> +{
> +	if (regmap_write(regmap, reg, raw) ||
> +	    regmap_update_bits(regmap, reg - 1, 0x3, raw >> 8))
> +		return -EIO;
> +
> +	return 0;

What about

	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;

Empty line here?

> +	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,
> +	.pwr_table	= pwr_table,
> +	.pwr_table_count= ARRAY_SIZE(pwr_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_region",
> +	},
> +};
> +
> +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/mfd/intel_soc_pmic_crc.c b/drivers/mfd/intel_soc_pmic_crc.c
> index 7107cab832e6..48845d636bba 100644
> --- a/drivers/mfd/intel_soc_pmic_crc.c
> +++ b/drivers/mfd/intel_soc_pmic_crc.c
> @@ -106,6 +106,9 @@ static struct mfd_cell crystal_cove_dev[] = {
>  		.num_resources = ARRAY_SIZE(gpio_resources),
>  		.resources = gpio_resources,
>  	},
> +	{
> +		.name = "crystal_cove_region",
> +	},
>  };
>  
>  static struct regmap_config crystal_cove_regmap_config = {
> 

-- 
I speak only for myself.
Rafael J. Wysocki, Intel Open Source Technology Center.

^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [PATCH v3 2/3] ACPI / PMIC: support PMIC operation region for XPower AXP288
  2014-11-21  7:11 ` [PATCH v3 2/3] ACPI / PMIC: support PMIC operation region for XPower AXP288 Aaron Lu
@ 2014-11-24  1:04   ` Rafael J. Wysocki
  2014-11-24  9:24     ` [PATCH v3 updated " Aaron Lu
  0 siblings, 1 reply; 34+ messages in thread
From: Rafael J. Wysocki @ 2014-11-24  1:04 UTC (permalink / raw)
  To: Aaron Lu
  Cc: Lee Jones, Jacob Pan, Yegnesh Iyer, linux-acpi, linux-iio, linux-kernel

On Friday, November 21, 2014 03:11:50 PM Aaron Lu wrote:
> The Baytrail-T-CR platform firmware has defined two customized operation
> regions for PMIC chip Dollar Cove XPower - one is for power resource
> handling and one is for thermal just like the CrystalCove one. This patch
> adds support for them on top of the common PMIC opregion region code.
> 
> Signed-off-by: Aaron Lu <aaron.lu@intel.com>
> Acked-by: Lee Jones <lee.jones@linaro.org> for the MFD part
> ---
>  drivers/acpi/Kconfig                  |   6 +
>  drivers/acpi/Makefile                 |   1 +
>  drivers/acpi/pmic/intel_pmic_xpower.c | 277 ++++++++++++++++++++++++++++++++++
>  drivers/mfd/axp20x.c                  |   3 +
>  4 files changed, 287 insertions(+)
>  create mode 100644 drivers/acpi/pmic/intel_pmic_xpower.c
> 
> diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig
> index 3e5f2056f946..227f0692cbff 100644
> --- a/drivers/acpi/Kconfig
> +++ b/drivers/acpi/Kconfig
> @@ -408,6 +408,12 @@ config CRC_PMIC_OPREGION
>  	help
>  	  This config adds ACPI operation region support for CrystalCove PMIC.
>  
> +config XPOWER_PMIC_OPREGION
> +	bool "ACPI operation region support for XPower AXP288 PMIC"
> +	depends on AXP288_ADC
> +	help
> +	  This config adds ACPI operation region support for XPower AXP288 PMIC.
> +
>  endif
>  
>  endif	# ACPI
> diff --git a/drivers/acpi/Makefile b/drivers/acpi/Makefile
> index f5938399ac14..f74317cc1ca9 100644
> --- a/drivers/acpi/Makefile
> +++ b/drivers/acpi/Makefile
> @@ -91,3 +91,4 @@ obj-$(CONFIG_ACPI_EXTLOG)	+= acpi_extlog.o
>  
>  obj-$(CONFIG_PMIC_OPREGION)	+= pmic/intel_pmic.o
>  obj-$(CONFIG_CRC_PMIC_OPREGION) += pmic/intel_pmic_crc.o
> +obj-$(CONFIG_XPOWER_PMIC_OPREGION) += pmic/intel_pmic_xpower.o
> diff --git a/drivers/acpi/pmic/intel_pmic_xpower.c b/drivers/acpi/pmic/intel_pmic_xpower.c
> new file mode 100644
> index 000000000000..6c4d6ce0cff1
> --- /dev/null
> +++ b/drivers/acpi/pmic/intel_pmic_xpower.c
> @@ -0,0 +1,277 @@
> +/*
> + * 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_pwr_table pwr_table[] = {
> +	{
> +		.address = 0x00,
> +		.pwr_reg = {
> +			.reg = 0x13,
> +			.bit = 0x05,
> +		}
> +	}, /* ALD1, 0x13, bit5 */

It is not necessary to repeat the reg numbers and bits in the comments.

> +	{
> +		.address = 0x04,
> +		.pwr_reg = {
> +			.reg = 0x13,
> +			.bit = 0x06,
> +		}
> +	}, /* ALD2, 0x13, bit6 */
> +	{
> +		.address = 0x08,
> +		.pwr_reg = {
> +			.reg = 0x13,
> +			.bit = 0x07,
> +		}
> +	}, /* ALD3, 0x13, bit7 */
> +	{
> +		.address = 0x0c,
> +		.pwr_reg = {
> +			.reg = 0x12,
> +			.bit = 0x03,
> +		}
> +	}, /* DLD1, 0x12, bit3 */
> +	{
> +		.address = 0x10,
> +		.pwr_reg = {
> +			.reg = 0x12,
> +			.bit = 0x04,
> +		}
> +	}, /* DLD2, 0x12, bit4 */
> +	{
> +		.address = 0x14,
> +		.pwr_reg = {
> +			.reg = 0x12,
> +			.bit = 0x05,
> +		}
> +	}, /* DLD3, 0x12, bit5 */
> +	{
> +		.address = 0x18,
> +		.pwr_reg = {
> +			.reg = 0x12,
> +			.bit = 0x06,
> +		}
> +	}, /* DLD4, 0x12, bit6 */
> +	{
> +		.address = 0x1c,
> +		.pwr_reg = {
> +			.reg = 0x12,
> +			.bit = 0x00,
> +		}
> +	}, /* ELD1, 0x12, bit0 */
> +	{
> +		.address = 0x20,
> +		.pwr_reg = {
> +			.reg = 0x12,
> +			.bit = 0x01,
> +		}
> +	}, /* ELD2, 0x12, bit1 */
> +	{
> +		.address = 0x24,
> +		.pwr_reg = {
> +			.reg = 0x12,
> +			.bit = 0x02,
> +		}
> +	}, /* ELD3, 0x12, bit2 */
> +	{
> +		.address = 0x28,
> +		.pwr_reg = {
> +			.reg = 0x13,
> +			.bit = 0x02,
> +		}
> +	}, /* FLD1, 0x13, bit2 */
> +	{
> +		.address = 0x2c,
> +		.pwr_reg = {
> +			.reg = 0x13,
> +			.bit = 0x03,
> +		}
> +	}, /* FLD2, 0x13, bit3 */
> +	{
> +		.address = 0x30,
> +		.pwr_reg = {
> +			.reg = 0x13,
> +			.bit = 0x04,
> +		}
> +	}, /* FLD3, 0x13, bit4 */
> +	{
> +		.address = 0x38,
> +		.pwr_reg = {
> +			.reg = 0x10,
> +			.bit = 0x03,
> +		}
> +	}, /* BUC1, 0x10, bit3 */
> +	{
> +		.address = 0x3c,
> +		.pwr_reg = {
> +			.reg = 0x10,
> +			.bit = 0x06,
> +		}
> +	}, /* BUC2, 0x10, bit6 */
> +	{
> +		.address = 0x40,
> +		.pwr_reg = {
> +			.reg = 0x10,
> +			.bit = 0x05,
> +		}
> +	}, /* BUC3, 0x10, bit5 */
> +	{
> +		.address = 0x44,
> +		.pwr_reg = {
> +			.reg = 0x10,
> +			.bit = 0x04,
> +		}
> +	}, /* BUC4, 0x10, bit4 */
> +	{
> +		.address = 0x48,
> +		.pwr_reg = {
> +			.reg = 0x10,
> +			.bit = 0x01,
> +		}
> +	}, /* BUC5, 0x10, bit1 */
> +	{
> +		.address = 0x4c,
> +		.pwr_reg = {
> +			.reg = 0x10,
> +			.bit = 0x00
> +		}
> +	}, /* BUC6, 0x10, bit0 */
> +};
> +
> +/* TMP0 - TMP5 are the same, all from GPADC */
> +static struct pmic_thermal_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,
> +				       struct pmic_pwr_reg *preg, u64 *value)
> +{
> +	int data;
> +
> +	if (regmap_read(regmap, preg->reg, &data))
> +		return -EIO;
> +
> +	*value = (data & BIT(preg->bit)) ? 1 : 0;
> +	return 0;
> +}
> +
> +static int intel_xpower_pmic_update_power(struct regmap *regmap,
> +					  struct pmic_pwr_reg *preg, bool on)
> +{
> +	int data;
> +
> +	if (regmap_read(regmap, preg->reg, &data))
> +		return -EIO;
> +
> +	if (on)
> +		data |= BIT(preg->bit);
> +	else
> +		data &= ~BIT(preg->bit);
> +
> +	if (regmap_write(regmap, preg->reg, data))
> +		return -EIO;
> +
> +	return 0;
> +}
> +
> +/*
> + * 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.
> + */
> +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,
> +	.pwr_table = pwr_table,
> +	.pwr_table_count = ARRAY_SIZE(pwr_table),
> +	.thermal_table = thermal_table,
> +	.thermal_table_count = ARRAY_SIZE(thermal_table),
> +};
> +
> +
> +static int intel_xpower_pmic_opregion_probe(struct platform_device *pdev)
> +{
> +	struct axp20x_dev *axp20x = dev_get_drvdata(pdev->dev.parent);
> +	return intel_pmic_install_opregion_handler(&pdev->dev,
> +			ACPI_HANDLE(pdev->dev.parent), axp20x->regmap,
> +			&intel_xpower_pmic_opregion_data);
> +}
> +
> +static struct platform_driver intel_xpower_pmic_opregion_driver = {
> +	.probe = intel_xpower_pmic_opregion_probe,
> +	.driver = {
> +		.name = "axp288_opregion",
> +	},
> +};
> +
> +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");
> diff --git a/drivers/mfd/axp20x.c b/drivers/mfd/axp20x.c
> index acbb4c3bf98c..daf3c8d33b4d 100644
> --- a/drivers/mfd/axp20x.c
> +++ b/drivers/mfd/axp20x.c
> @@ -354,6 +354,9 @@ static struct mfd_cell axp288_cells[] = {
>  		.num_resources = ARRAY_SIZE(axp288_battery_resources),
>  		.resources = axp288_battery_resources,
>  	},
> +	{
> +		.name = "axp288_opregion",
> +	},
>  };
>  
>  static struct axp20x_dev *axp20x_pm_power_off;
> 

The rest looks OK to me.

-- 
I speak only for myself.
Rafael J. Wysocki, Intel Open Source Technology Center.

^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [PATCH v3 3/3] ACPI / PMIC: AXP288: support virtual GPIO in ACPI table
  2014-11-21  7:11 ` [PATCH v3 3/3] ACPI / PMIC: AXP288: support virtual GPIO in ACPI table Aaron Lu
@ 2014-11-24  1:06   ` Rafael J. Wysocki
  2014-11-24  6:01     ` Aaron Lu
       [not found]     ` <1648486.coeVgHKNdn-sKB8Sp2ER+y1GS7QM15AGw@public.gmane.org>
  0 siblings, 2 replies; 34+ messages in thread
From: Rafael J. Wysocki @ 2014-11-24  1:06 UTC (permalink / raw)
  To: Aaron Lu
  Cc: Lee Jones, Jacob Pan, Yegnesh Iyer, linux-acpi, linux-iio, linux-kernel

On Friday, November 21, 2014 03:11:51 PM Aaron Lu wrote:
> The same virtual GPIO strategy is also used for the AXP288 PMIC in that
> various control methods that are used to do power rail handling and
> sensor reading/setting will touch GPIO fields defined under the PMIC
> device. The GPIO fileds are only defined by the ACPI code while the
> actual hardware doesn't really have a GPIO controller, but to make those
> control method execution succeed, we have to install a GPIO handler for
> the PMIC device handle. Since we do not need the virtual GPIO strategy,
> we can simply do nothing in that handler.
> 
> Signed-off-by: Aaron Lu <aaron.lu@intel.com>
> ---
>  drivers/acpi/pmic/intel_pmic_xpower.c | 27 +++++++++++++++++++++++----
>  1 file changed, 23 insertions(+), 4 deletions(-)
> 
> diff --git a/drivers/acpi/pmic/intel_pmic_xpower.c b/drivers/acpi/pmic/intel_pmic_xpower.c
> index 6c4d6ce0cff1..480c41c36444 100644
> --- a/drivers/acpi/pmic/intel_pmic_xpower.c
> +++ b/drivers/acpi/pmic/intel_pmic_xpower.c
> @@ -251,13 +251,32 @@ static struct intel_pmic_opregion_data intel_xpower_pmic_opregion_data = {
>  	.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 axp20x_dev *axp20x = dev_get_drvdata(pdev->dev.parent);
> -	return intel_pmic_install_opregion_handler(&pdev->dev,
> -			ACPI_HANDLE(pdev->dev.parent), axp20x->regmap,
> -			&intel_xpower_pmic_opregion_data);
> +	struct device *parent = pdev->dev.parent;
> +	struct axp20x_dev *axp20x = dev_get_drvdata(parent);
> +	acpi_status status;
> +	int result;
> +
> +	result = intel_pmic_install_opregion_handler(&pdev->dev,
> +					ACPI_HANDLE(parent), axp20x->regmap,
> +					&intel_xpower_pmic_opregion_data);
> +	if (!result) {
> +		status = acpi_install_address_space_handler(
> +				ACPI_HANDLE(parent), ACPI_ADR_SPACE_GPIO,
> +				intel_xpower_pmic_gpio_handler, NULL, NULL);

So here we have a problem, because we can't unregister the opregion handler
registered above if this fails.  Not nice.

> +		if (ACPI_FAILURE(status))
> +			result = -ENODEV;
> +	}
> +
> +	return result;
>  }
>  
>  static struct platform_driver intel_xpower_pmic_opregion_driver = {
> 

-- 
I speak only for myself.
Rafael J. Wysocki, Intel Open Source Technology Center.

^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [PATCH v3 1/3] ACPI / PMIC: support PMIC operation region for CrystalCove
  2014-11-24  0:59     ` Rafael J. Wysocki
@ 2014-11-24  5:55       ` Aaron Lu
  2014-11-24  9:21       ` [PATCH v3 1/3 updated] " Aaron Lu
  1 sibling, 0 replies; 34+ messages in thread
From: Aaron Lu @ 2014-11-24  5:55 UTC (permalink / raw)
  To: Rafael J. Wysocki
  Cc: Lee Jones, Jacob Pan, Yegnesh Iyer, linux-acpi, linux-iio, linux-kernel

On 11/24/2014 08:59 AM, Rafael J. Wysocki wrote:
> On Friday, November 21, 2014 03:11:49 PM Aaron Lu wrote:
>> The Baytrail-T platform firmware has defined two customized operation
>> regions for PMIC chip Crystal Cove - one is for power resource handling
>> and one is for thermal: sensor temperature reporting, trip point setting,
>> etc. This patch adds support for them on top of the existing Crystal Cove
>> PMIC driver.
>>
>> The reason to split code into a separate file intel_pmic.c is that there
>> are more PMIC drivers with ACPI operation region support coming and we can
>> re-use those code. The intel_pmic_opregion_data structure is created also
>> for this purpose: when we need to support a new PMIC's operation region,
>> we just need to fill those callbacks and the two register mapping tables.
>>
>> Signed-off-by: Aaron Lu <aaron.lu@intel.com>
>> Acked-by: Lee Jones <lee.jones@linaro.org> for the MFD part
> 
> Thanks for resending, looks better to me.
> 
> Some nitpicking below.

Thaks for taking a look at them, some response below.

> 
>> ---
>>  drivers/acpi/Kconfig               |  17 ++
>>  drivers/acpi/Makefile              |   3 +
>>  drivers/acpi/pmic/intel_pmic.c     | 339 +++++++++++++++++++++++++++++++++++++
>>  drivers/acpi/pmic/intel_pmic.h     |  34 ++++
>>  drivers/acpi/pmic/intel_pmic_crc.c | 216 +++++++++++++++++++++++
>>  drivers/mfd/intel_soc_pmic_crc.c   |   3 +
>>  6 files changed, 612 insertions(+)
>>  create mode 100644 drivers/acpi/pmic/intel_pmic.c
>>  create mode 100644 drivers/acpi/pmic/intel_pmic.h
>>  create mode 100644 drivers/acpi/pmic/intel_pmic_crc.c
>>
>> diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig
>> index 79078b8f5697..3e5f2056f946 100644
>> --- a/drivers/acpi/Kconfig
>> +++ b/drivers/acpi/Kconfig
>> @@ -393,4 +393,21 @@ config ACPI_EXTLOG
>>  	  driver adds support for that functionality with corresponding
>>  	  tracepoint which carries that information to userspace.
>>  
>> +menuconfig PMIC_OPREGION
>> +	bool "PMIC (Power Management Integrated Circuit) operation region support"
>> +	help
>> +	  Select this option to enable support for ACPI operation
>> +	  region of the PMIC chip. The operation region can be used
>> +	  to control power rails and sensor reading/writing on the
>> +	  PMIC chip.
>> +
>> +if PMIC_OPREGION
>> +config CRC_PMIC_OPREGION
> 
> If that is the only possible choice for PMIC_OPREGION, it should be selected
> automatically.  Alternatively, PMIC_OPREGION should be selected automatically
> if CRC_PMIC_OPREGION is set.

It is not the only possible choice, currently we have two(see patch 2/3):
CRC_PMIC_OPREGION and XPOWER_PMIC_OPREGION. I would assume this is a
increasing list with more and more PMIC opregion support added. I can
use select for PMIC_OPREGION for all those PMIC operation region drivers,
but it seems easier to use a "if PMIC_OPREGION ... endif" between them.
Please let me know if this is OK?

> 
>> +	bool "ACPI operation region support for CrystalCove PMIC"
>> +	depends on INTEL_SOC_PMIC
>> +	help
>> +	  This config adds ACPI operation region support for CrystalCove PMIC.
>> +
>> +endif
>> +
>>  endif	# ACPI
>> diff --git a/drivers/acpi/Makefile b/drivers/acpi/Makefile
>> index 6d11522f0e48..f5938399ac14 100644
>> --- a/drivers/acpi/Makefile
>> +++ b/drivers/acpi/Makefile
>> @@ -88,3 +88,6 @@ obj-$(CONFIG_ACPI_PROCESSOR_AGGREGATOR) += acpi_pad.o
>>  obj-$(CONFIG_ACPI_APEI)		+= apei/
>>  
>>  obj-$(CONFIG_ACPI_EXTLOG)	+= acpi_extlog.o
>> +
>> +obj-$(CONFIG_PMIC_OPREGION)	+= pmic/intel_pmic.o
>> +obj-$(CONFIG_CRC_PMIC_OPREGION) += pmic/intel_pmic_crc.o
>> diff --git a/drivers/acpi/pmic/intel_pmic.c b/drivers/acpi/pmic/intel_pmic.c
>> new file mode 100644
>> index 000000000000..5dbc0fb4d536
>> --- /dev/null
>> +++ b/drivers/acpi/pmic/intel_pmic.c
>> @@ -0,0 +1,339 @@
>> +/*
>> + * 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_PMOP_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 struct pmic_pwr_reg *
>> +pmic_get_pwr_reg(int address, struct pmic_pwr_table *table, int count)
>> +{
>> +	int i;
>> +
>> +	for (i = 0; i < count; i++) {
>> +		if (table[i].address == address)
>> +			return &table[i].pwr_reg;
>> +	}
>> +	return NULL;
>> +}
>> +
>> +static int
>> +pmic_get_thermal_reg(int address, struct pmic_thermal_table *table, int count)
>> +{
>> +	int i;
>> +
>> +	for (i = 0; i < count; i++) {
>> +		if (table[i].address == address)
>> +			return table[i].reg;
>> +	}
>> +	return -ENOENT;
>> +}
> 
> This is slightly inconsistent.  While pmic_get_pwr_reg() returns a pointer
> to struct pmic_pwr_reg, this one returns an int.
> 
> I see that this is because the definitions of struct pmic_thermal_table
> and struct pmic_pwr_table are inconsistent, but is that really necessary?
> 
> You could define
> 
> struct pmic_table {
> 	int address;	/* operation region address */
> 	int reg;	/* corresponding PMIC register */
> 	int bit;	/* control bit for power */
> };
> 
> and use it for both power and thermal.  [The latter will not use the bit field,
> but is that really a problem?]
> 
> It looks like some code duplication might be reduced this way.

Yes.

> 
> Besides, "power" looks better than "pwr", especially that you use "thermal"
> instead of "thrm" (for example).

OK.

> 
>> +
>> +/* Return temperature from raw value through LPAT table */
>> +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;
>> +}
>> +
>> +/* Return raw value from temperature through LPAT table */
>> +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(*lpat) * obj_p->package.count,
>> +			    GFP_KERNEL);
> 
> This looks fishy.
> 
> Of course, sizeof(*lpat) is the same as sizeof(int), but is more obfuscated
> and you're allocating memory for an array of integers.

OK.

> 
>> +	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)
> 
> lpat[] has to be freed here.

Oh right.

> 
>> +			goto out;
>> +		lpat[i] = obj_e->integer.value;
> 
> Here, integer.value is generally u64, so I'd use an explicit cast to s64 before
> casting that to int.  Otherwise it looks like you've forgotten about possible
> overflows, which I assume is not the case.

OK.

> 
>> +	}
>> +
>> +	opregion->lpat = (struct acpi_lpat *)lpat;
>> +	opregion->lpat_count = obj_p->package.count / 2;
>> +
>> +out:
>> +	kfree(buffer.pointer);
>> +}
>> +
>> +static acpi_status
>> +intel_pmic_pmop_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;
>> +	struct pmic_pwr_reg *preg;
>> +	int result;
>> +
>> +	if (bits != 32 || !value64)
>> +		return AE_BAD_PARAMETER;
>> +
>> +	if (function == ACPI_WRITE && !(*value64 == 0 || *value64 == 1))
>> +		return AE_BAD_PARAMETER;
>> +
>> +	preg = pmic_get_pwr_reg(address, d->pwr_table, d->pwr_table_count);
>> +	if (!preg)
>> +		return AE_BAD_PARAMETER;
>> +
>> +	mutex_lock(&opregion->lock);
>> +
>> +	if (function == ACPI_READ)
>> +		result = d->get_power(regmap, preg, value64);
>> +	else
>> +		result = d->update_power(regmap, preg, *value64 == 1);
> 
> I'd write that as
> 
> 	retult = function == ACPI_READ ?
> 		d->get_power(regmap, preg, value64) :
> 		d->update_power(regmap, preg, *value64 == 1);
> 
> which will be consistent with the "return" statement below.

OK.

> 
>> +
>> +	mutex_unlock(&opregion->lock);
>> +
>> +	return result ? AE_ERROR : AE_OK;
>> +}
>> +
>> +static acpi_status pmic_read_temp(struct intel_pmic_opregion *opregion,
>> +				  int reg, u64 *value)
>> +{
>> +	int raw_temp, temp;
>> +
>> +	if (!opregion->data->get_raw_temp)
>> +		return AE_BAD_PARAMETER;
>> +
>> +	raw_temp = opregion->data->get_raw_temp(opregion->regmap, reg);
>> +	if (raw_temp < 0)
>> +		return AE_ERROR;
>> +
>> +	if (!opregion->lpat) {
>> +		*value = raw_temp;
>> +		return AE_OK;
>> +	}
>> +
>> +	temp = raw_to_temp(opregion->lpat, opregion->lpat_count, raw_temp);
>> +	if (temp < 0)
>> +		return AE_ERROR;
>> +
>> +	*value = temp;
>> +	return AE_OK;
>> +}
>> +
>> +static acpi_status pmic_thermal_temp(struct intel_pmic_opregion *opregion,
>> +				     int reg, u32 function, u64 *value)
>> +{
>> +	if (function != ACPI_READ)
>> +		return AE_BAD_PARAMETER;
>> +
>> +	return pmic_read_temp(opregion, reg, value);
> 
> What about
> 
> 	return function == ACPI_READ ?
> 		pmic_read_temp(opregion, reg, value) : AE_BAD_PARAMETER;
> 
> ?

OK.

> 
>> +}
>> +
>> +static acpi_status 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 AE_BAD_PARAMETER;
>> +
>> +	if (opregion->lpat) {
>> +		raw_temp = temp_to_raw(opregion->lpat, opregion->lpat_count,
>> +					 *value);
>> +		if (raw_temp < 0)
>> +			return AE_ERROR;
>> +	} else {
>> +		raw_temp = *value;
>> +	}
>> +
>> +	return opregion->data->update_aux(opregion->regmap, reg, raw_temp) ?
>> +		AE_ERROR : AE_OK;
> 
> You seem to be casting all error codes into AE_ERROR here.  Should the function
> simply return int and pass the original error code to the caller instead?

You mean pass the original error code to intel_pmic_thermal_handler?
Yes, I can do that. But since there isn't a 1-1 mapping between the
standard error code and ACPICA error values, I'm afriad I'll need to
cast them into AE_ERROR in intel_pmic_thermal_handler before return.

> 
>> +}
>> +
>> +static acpi_status 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 AE_BAD_PARAMETER;
>> +
>> +	if (function == ACPI_READ)
>> +		return d->get_policy(regmap, reg, value) ? AE_ERROR : AE_OK;
>> +
>> +	if (*value != 0 || *value != 1)
>> +		return AE_BAD_PARAMETER;
>> +
>> +	return d->update_policy(regmap, reg, *value) ? AE_ERROR : AE_OK;
> 
> Well, same here.
> 
>> +}
>> +
>> +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;
>> +	int reg;
>> +	int result;
>> +
>> +	if (bits != 32 || !value64)
>> +		return AE_BAD_PARAMETER;
>> +
>> +	reg = pmic_get_thermal_reg(address, opregion->data->thermal_table,
>> +				   opregion->data->thermal_table_count);
>> +	if (!reg)
>> +		return AE_BAD_PARAMETER;
>> +
>> +	mutex_lock(&opregion->lock);
>> +
>> +	result = AE_BAD_PARAMETER;
>> +	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);
>> +
>> +	mutex_unlock(&opregion->lock);
>> +
>> +	return result;
>> +}
>> +
>> +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_PMOP_OPREGION_ID,
>> +						    intel_pmic_pmop_handler,
>> +						    NULL, opregion);
>> +	if (ACPI_FAILURE(status))
>> +		return -ENODEV;
> 
> And you return a int from here.

I prefer to use int whenever possible, i.e. if the function is not
returning a value to a ACPICA function, I'll use int as the return value
instead of acpi_status.

> 
> Would it make sense for the majority of functions in this file to return ints
> rather than acpi_status values?

Yes, I think I can do that. Then I just need to do one cast in the
operation region handler function for those error return values.

> 
>> +
>> +	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_PMOP_OPREGION_ID,
>> +						  intel_pmic_pmop_handler);
>> +		return -ENODEV;
>> +	}
>> +
>> +	opregion->data = d;
> 
> I guess the opregion will never be removed, right?

Once installed properly, it will not be removed.

> 
>> +	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..18b9bb80f8b6
>> --- /dev/null
>> +++ b/drivers/acpi/pmic/intel_pmic.h
>> @@ -0,0 +1,34 @@
>> +#ifndef __INTEL_PMIC_H
>> +#define __INTEL_PMIC_H
>> +
>> +struct pmic_pwr_reg {
>> +	int reg;	/* corresponding PMIC register */
>> +	int bit;	/* control bit for power */
>> +};
>> +
>> +struct pmic_pwr_table {
>> +	int address;	/* operation region address */
>> +	struct pmic_pwr_reg pwr_reg;
>> +};
>> +
>> +struct pmic_thermal_table {
>> +	int address;	/* operation region address */
>> +	int reg;	/* corresponding thermal register */
>> +};
>> +
>> +struct intel_pmic_opregion_data {
>> +	int (*get_power)(struct regmap *r, struct pmic_pwr_reg *preg, u64 *value);
>> +	int (*update_power)(struct regmap *r, struct pmic_pwr_reg *preg, 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_pwr_table *pwr_table;
>> +	int pwr_table_count;
>> +	struct pmic_thermal_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..7629f16d1526
>> --- /dev/null
>> +++ b/drivers/acpi/pmic/intel_pmic_crc.c
>> @@ -0,0 +1,216 @@
>> +/*
>> + * 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_pwr_table pwr_table[] = {
>> +	{
>> +		.address = 0x24,
>> +		.pwr_reg = {
>> +			.reg = 0x66,
>> +			.bit = 0x00,
>> +		},
>> +	},	/* X285 -> V2P85SX, camara */
>> +	{
>> +		.address = 0x48,
>> +		.pwr_reg = {
>> +			.reg = 0x5d,
>> +			.bit = 0x00,
>> +		},
>> +	},	/* V18X -> V1P8SX, eMMC/camara/audio */
>> +};
>> +
>> +static struct pmic_thermal_table thermal_table[] = {
>> +	{
>> +		.address = 0x00,
>> +		.reg = 0x75
>> +	},	/* TMP0 -> SYS0_THRM_RSLT_L */
>> +	{
>> +		.address = 0x04,
>> +		.reg = 0x95
>> +	},	/* AX00 -> SYS0_THRMALRT0_L */
>> +	{
>> +		.address = 0x08,
>> +		.reg = 0x97
>> +	},	/* AX01 -> SYS0_THRMALRT1_L */
>> +	{
>> +		.address = 0x0c,
>> +		.reg = 0x77
>> +	},	/* TMP1 -> SYS1_THRM_RSLT_L */
>> +	{
>> +		.address = 0x10,
>> +		.reg = 0x9a
>> +	},	/* AX10 -> SYS1_THRMALRT0_L */
>> +	{
>> +		.address = 0x14,
>> +		.reg = 0x9c
>> +	},	/* AX11 -> SYS1_THRMALRT1_L */
>> +	{
>> +		.address = 0x18,
>> +		.reg = 0x79
>> +	},	/* TMP2 -> SYS2_THRM_RSLT_L */
>> +	{
>> +		.address = 0x1c,
>> +		.reg = 0x9f
>> +	},	/* AX20 -> SYS2_THRMALRT0_L */
>> +	{
>> +		.address = 0x20,
>> +		.reg = 0xa1
>> +	},	/* AX21 -> SYS2_THRMALRT1_L */
>> +	{
>> +		.address = 0x48,
>> +		.reg = 0x94
>> +	},	/* PEN0 -> SYS0_THRMALRT0_H */
>> +	{
>> +		.address = 0x4c,
>> +		.reg = 0x99
>> +	},	/* PEN1 -> SYS1_THRMALRT1_H */
>> +	{
>> +		.address = 0x50,
>> +		.reg = 0x9e
>> +	},	/* PEN2 -> SYS2_THRMALRT2_H */
>> +};
>> +
>> +static int intel_crc_pmic_get_power(struct regmap *regmap,
>> +				    struct pmic_pwr_reg *preg, u64 *value)
>> +{
>> +	int data;
>> +
>> +	if (regmap_read(regmap, preg->reg, &data))
>> +		return -EIO;
>> +
>> +	*value = (data & PWR_SOURCE_SELECT) && (data & BIT(preg->bit)) ? 1 : 0;
>> +	return 0;
>> +}
>> +
>> +static int intel_crc_pmic_update_power(struct regmap *regmap,
>> +				       struct pmic_pwr_reg *preg, bool on)
>> +{
>> +	int data;
>> +
>> +	if (regmap_read(regmap, preg->reg, &data))
>> +		return -EIO;
>> +
>> +	if (on) {
>> +		data |= PWR_SOURCE_SELECT | BIT(preg->bit);
>> +	} else {
>> +		data &= ~BIT(preg->bit);
>> +		data |= PWR_SOURCE_SELECT;
>> +	}
>> +
>> +	if (regmap_write(regmap, preg->reg, data))
>> +		return -EIO;
>> +	return 0;
>> +}
>> +
>> +/* Raw temperature value is 10bits: 8bits in reg and 2bits in reg-1 bit0,1 */
> 
> Proper kerneldoc, please.  Here and elsewhere where it makes sense.
> 
> All functions that aren't static need to have kerneldoc comments.

Will do that.

> 
>> +static int intel_crc_pmic_get_raw_temp(struct regmap *regmap, int reg)
>> +{
>> +	int temp_l, temp_h;
>> +
>> +	if (regmap_read(regmap, reg, &temp_l) ||
>> +	    regmap_read(regmap, reg - 1, &temp_h))
>> +		return -EIO;
>> +
>> +	return (temp_l | ((temp_h & 0x3) << 8));
> 
> At least one paren is not necessary here.
> 
>> +}
>> +
>> +static int
>> +intel_crc_pmic_update_aux(struct regmap *regmap, int reg, int raw)
>> +{
>> +	if (regmap_write(regmap, reg, raw) ||
>> +	    regmap_update_bits(regmap, reg - 1, 0x3, raw >> 8))
>> +		return -EIO;
>> +
>> +	return 0;
> 
> What about
> 
> 	return regmap_write(regmap, reg, raw) ||
> 		regmap_update_bits(regmap, reg - 1, 0x3, raw >> 8) ? -EIO : 0;

OK.

> 
>> +}
>> +
>> +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;
> 
> Empty line here?

OK.

Thanks a lot for the review.

-Aaron

> 
>> +	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,
>> +	.pwr_table	= pwr_table,
>> +	.pwr_table_count= ARRAY_SIZE(pwr_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_region",
>> +	},
>> +};
>> +
>> +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/mfd/intel_soc_pmic_crc.c b/drivers/mfd/intel_soc_pmic_crc.c
>> index 7107cab832e6..48845d636bba 100644
>> --- a/drivers/mfd/intel_soc_pmic_crc.c
>> +++ b/drivers/mfd/intel_soc_pmic_crc.c
>> @@ -106,6 +106,9 @@ static struct mfd_cell crystal_cove_dev[] = {
>>  		.num_resources = ARRAY_SIZE(gpio_resources),
>>  		.resources = gpio_resources,
>>  	},
>> +	{
>> +		.name = "crystal_cove_region",
>> +	},
>>  };
>>  
>>  static struct regmap_config crystal_cove_regmap_config = {
>>
> 


^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [PATCH v3 3/3] ACPI / PMIC: AXP288: support virtual GPIO in ACPI table
  2014-11-24  1:06   ` Rafael J. Wysocki
@ 2014-11-24  6:01     ` Aaron Lu
       [not found]     ` <1648486.coeVgHKNdn-sKB8Sp2ER+y1GS7QM15AGw@public.gmane.org>
  1 sibling, 0 replies; 34+ messages in thread
From: Aaron Lu @ 2014-11-24  6:01 UTC (permalink / raw)
  To: Rafael J. Wysocki
  Cc: Lee Jones, Jacob Pan, Yegnesh Iyer, linux-acpi, linux-iio, linux-kernel

On 11/24/2014 09:06 AM, Rafael J. Wysocki wrote:
> On Friday, November 21, 2014 03:11:51 PM Aaron Lu wrote:
>> The same virtual GPIO strategy is also used for the AXP288 PMIC in that
>> various control methods that are used to do power rail handling and
>> sensor reading/setting will touch GPIO fields defined under the PMIC
>> device. The GPIO fileds are only defined by the ACPI code while the
>> actual hardware doesn't really have a GPIO controller, but to make those
>> control method execution succeed, we have to install a GPIO handler for
>> the PMIC device handle. Since we do not need the virtual GPIO strategy,
>> we can simply do nothing in that handler.
>>
>> Signed-off-by: Aaron Lu <aaron.lu@intel.com>
>> ---
>>  drivers/acpi/pmic/intel_pmic_xpower.c | 27 +++++++++++++++++++++++----
>>  1 file changed, 23 insertions(+), 4 deletions(-)
>>
>> diff --git a/drivers/acpi/pmic/intel_pmic_xpower.c b/drivers/acpi/pmic/intel_pmic_xpower.c
>> index 6c4d6ce0cff1..480c41c36444 100644
>> --- a/drivers/acpi/pmic/intel_pmic_xpower.c
>> +++ b/drivers/acpi/pmic/intel_pmic_xpower.c
>> @@ -251,13 +251,32 @@ static struct intel_pmic_opregion_data intel_xpower_pmic_opregion_data = {
>>  	.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 axp20x_dev *axp20x = dev_get_drvdata(pdev->dev.parent);
>> -	return intel_pmic_install_opregion_handler(&pdev->dev,
>> -			ACPI_HANDLE(pdev->dev.parent), axp20x->regmap,
>> -			&intel_xpower_pmic_opregion_data);
>> +	struct device *parent = pdev->dev.parent;
>> +	struct axp20x_dev *axp20x = dev_get_drvdata(parent);
>> +	acpi_status status;
>> +	int result;
>> +
>> +	result = intel_pmic_install_opregion_handler(&pdev->dev,
>> +					ACPI_HANDLE(parent), axp20x->regmap,
>> +					&intel_xpower_pmic_opregion_data);
>> +	if (!result) {
>> +		status = acpi_install_address_space_handler(
>> +				ACPI_HANDLE(parent), ACPI_ADR_SPACE_GPIO,
>> +				intel_xpower_pmic_gpio_handler, NULL, NULL);
> 
> So here we have a problem, because we can't unregister the opregion handler
> registered above if this fails.  Not nice.

I'll add a remove_opregion_handler call if the above install failed, the
chance the remove_opregion_handler will trigger a problem during init time
is pretty low.

If that is not desired, I can install the operation region handler for
the virtual GPIO first and then the real PMIC operation region handler,
the cost of leaving a virtual GPIO operation region handler is essential
zero I think.

Thanks,
Aaron

> 
>> +		if (ACPI_FAILURE(status))
>> +			result = -ENODEV;
>> +	}
>> +
>> +	return result;
>>  }
>>  
>>  static struct platform_driver intel_xpower_pmic_opregion_driver = {
>>
> 


^ permalink raw reply	[flat|nested] 34+ messages in thread

* [PATCH v3 1/3 updated] ACPI / PMIC: support PMIC operation region for CrystalCove
  2014-11-24  0:59     ` Rafael J. Wysocki
  2014-11-24  5:55       ` Aaron Lu
@ 2014-11-24  9:21       ` Aaron Lu
  1 sibling, 0 replies; 34+ messages in thread
From: Aaron Lu @ 2014-11-24  9:21 UTC (permalink / raw)
  To: Rafael J. Wysocki
  Cc: Lee Jones, Jacob Pan, Yegnesh Iyer, linux-acpi, linux-iio, linux-kernel

On 11/24/2014 08:59 AM, Rafael J. Wysocki wrote:
> On Friday, November 21, 2014 03:11:49 PM Aaron Lu wrote:
>> The Baytrail-T platform firmware has defined two customized operation
>> regions for PMIC chip Crystal Cove - one is for power resource handling
>> and one is for thermal: sensor temperature reporting, trip point setting,
>> etc. This patch adds support for them on top of the existing Crystal Cove
>> PMIC driver.
>>
>> The reason to split code into a separate file intel_pmic.c is that there
>> are more PMIC drivers with ACPI operation region support coming and we can
>> re-use those code. The intel_pmic_opregion_data structure is created also
>> for this purpose: when we need to support a new PMIC's operation region,
>> we just need to fill those callbacks and the two register mapping tables.
>>
>> Signed-off-by: Aaron Lu <aaron.lu@intel.com>
>> Acked-by: Lee Jones <lee.jones@linaro.org> for the MFD part
> 
> Thanks for resending, looks better to me.
> 
> Some nitpicking below.

Updated patch here:

From: Aaron Lu <aaron.lu@intel.com>
Date: Mon, 20 Oct 2014 16:38:37 +0800
Subject: [PATCH 1/3] ACPI / PMIC: support PMIC operation region for
 CrystalCove

The Baytrail-T platform firmware has defined two customized operation
regions for PMIC chip Crystal Cove - one is for power resource handling
and one is for thermal: sensor temperature reporting, trip point setting,
etc. This patch adds support for them on top of the existing Crystal Cove
PMIC driver.

The reason to split code into a separate file intel_pmic.c is that there
are more PMIC drivers with ACPI operation region support coming and we can
re-use those code. The intel_pmic_opregion_data structure is created also
for this purpose: when we need to support a new PMIC's operation region,
we just need to fill those callbacks and the two register mapping tables.

Signed-off-by: Aaron Lu <aaron.lu@intel.com>
Acked-by: Lee Jones <lee.jones@linaro.org> for the MFD part
---
 drivers/acpi/Kconfig               |  17 ++
 drivers/acpi/Makefile              |   3 +
 drivers/acpi/pmic/intel_pmic.c     | 354 +++++++++++++++++++++++++++++++++++++
 drivers/acpi/pmic/intel_pmic.h     |  25 +++
 drivers/acpi/pmic/intel_pmic_crc.c | 211 ++++++++++++++++++++++
 drivers/mfd/intel_soc_pmic_crc.c   |   3 +
 6 files changed, 613 insertions(+)
 create mode 100644 drivers/acpi/pmic/intel_pmic.c
 create mode 100644 drivers/acpi/pmic/intel_pmic.h
 create mode 100644 drivers/acpi/pmic/intel_pmic_crc.c

diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig
index 79078b8f5697..3e5f2056f946 100644
--- a/drivers/acpi/Kconfig
+++ b/drivers/acpi/Kconfig
@@ -393,4 +393,21 @@ config ACPI_EXTLOG
 	  driver adds support for that functionality with corresponding
 	  tracepoint which carries that information to userspace.
 
+menuconfig PMIC_OPREGION
+	bool "PMIC (Power Management Integrated Circuit) operation region support"
+	help
+	  Select this option to enable support for ACPI operation
+	  region of the PMIC chip. The operation region can be used
+	  to control power rails and sensor reading/writing on the
+	  PMIC chip.
+
+if PMIC_OPREGION
+config CRC_PMIC_OPREGION
+	bool "ACPI operation region support for CrystalCove PMIC"
+	depends on INTEL_SOC_PMIC
+	help
+	  This config adds ACPI operation region support for CrystalCove PMIC.
+
+endif
+
 endif	# ACPI
diff --git a/drivers/acpi/Makefile b/drivers/acpi/Makefile
index 6d11522f0e48..f5938399ac14 100644
--- a/drivers/acpi/Makefile
+++ b/drivers/acpi/Makefile
@@ -88,3 +88,6 @@ obj-$(CONFIG_ACPI_PROCESSOR_AGGREGATOR) += acpi_pad.o
 obj-$(CONFIG_ACPI_APEI)		+= apei/
 
 obj-$(CONFIG_ACPI_EXTLOG)	+= acpi_extlog.o
+
+obj-$(CONFIG_PMIC_OPREGION)	+= pmic/intel_pmic.o
+obj-$(CONFIG_CRC_PMIC_OPREGION) += pmic/intel_pmic_crc.o
diff --git a/drivers/acpi/pmic/intel_pmic.c b/drivers/acpi/pmic/intel_pmic.c
new file mode 100644
index 000000000000..8da794664c7f
--- /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, &reg, &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, &reg, 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..8955e5b41195
--- /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_region",
+	},
+};
+
+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/mfd/intel_soc_pmic_crc.c b/drivers/mfd/intel_soc_pmic_crc.c
index 7107cab832e6..48845d636bba 100644
--- a/drivers/mfd/intel_soc_pmic_crc.c
+++ b/drivers/mfd/intel_soc_pmic_crc.c
@@ -106,6 +106,9 @@ static struct mfd_cell crystal_cove_dev[] = {
 		.num_resources = ARRAY_SIZE(gpio_resources),
 		.resources = gpio_resources,
 	},
+	{
+		.name = "crystal_cove_region",
+	},
 };
 
 static struct regmap_config crystal_cove_regmap_config = {
-- 
1.9.3


^ permalink raw reply related	[flat|nested] 34+ messages in thread

* [PATCH v3 updated 2/3] ACPI / PMIC: support PMIC operation region for XPower AXP288
  2014-11-24  1:04   ` Rafael J. Wysocki
@ 2014-11-24  9:24     ` Aaron Lu
  0 siblings, 0 replies; 34+ messages in thread
From: Aaron Lu @ 2014-11-24  9:24 UTC (permalink / raw)
  To: Rafael J. Wysocki
  Cc: Lee Jones, Jacob Pan, Yegnesh Iyer, linux-acpi, linux-iio, linux-kernel

On 11/24/2014 09:04 AM, Rafael J. Wysocki wrote:
> On Friday, November 21, 2014 03:11:50 PM Aaron Lu wrote:
>> +static struct pmic_pwr_table pwr_table[] = {
>> +	{
>> +		.address = 0x00,
>> +		.pwr_reg = {
>> +			.reg = 0x13,
>> +			.bit = 0x05,
>> +		}
>> +	}, /* ALD1, 0x13, bit5 */
> 
> It is not necessary to repeat the reg numbers and bits in the comments.

I've removed them all, it's only useful when developing the code.
Here is the updated version:

From: Aaron Lu <aaron.lu@intel.com>
Date: Mon, 20 Oct 2014 17:00:59 +0800
Subject: [PATCH 2/3] ACPI / PMIC: support PMIC operation region for XPower
 AXP288

The Baytrail-T-CR platform firmware has defined two customized operation
regions for PMIC chip Dollar Cove XPower - one is for power resource
handling and one is for thermal just like the CrystalCove one. This patch
adds support for them on top of the common PMIC opregion region code.

Signed-off-by: Aaron Lu <aaron.lu@intel.com>
Acked-by: Lee Jones <lee.jones@linaro.org> for the MFD part
---
 drivers/acpi/Kconfig                  |   6 +
 drivers/acpi/Makefile                 |   1 +
 drivers/acpi/pmic/intel_pmic_xpower.c | 246 ++++++++++++++++++++++++++++++++++
 drivers/mfd/axp20x.c                  |   3 +
 4 files changed, 256 insertions(+)
 create mode 100644 drivers/acpi/pmic/intel_pmic_xpower.c

diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig
index 3e5f2056f946..227f0692cbff 100644
--- a/drivers/acpi/Kconfig
+++ b/drivers/acpi/Kconfig
@@ -408,6 +408,12 @@ config CRC_PMIC_OPREGION
 	help
 	  This config adds ACPI operation region support for CrystalCove PMIC.
 
+config XPOWER_PMIC_OPREGION
+	bool "ACPI operation region support for XPower AXP288 PMIC"
+	depends on AXP288_ADC
+	help
+	  This config adds ACPI operation region support for XPower AXP288 PMIC.
+
 endif
 
 endif	# ACPI
diff --git a/drivers/acpi/Makefile b/drivers/acpi/Makefile
index f5938399ac14..f74317cc1ca9 100644
--- a/drivers/acpi/Makefile
+++ b/drivers/acpi/Makefile
@@ -91,3 +91,4 @@ obj-$(CONFIG_ACPI_EXTLOG)	+= acpi_extlog.o
 
 obj-$(CONFIG_PMIC_OPREGION)	+= pmic/intel_pmic.o
 obj-$(CONFIG_CRC_PMIC_OPREGION) += pmic/intel_pmic_crc.o
+obj-$(CONFIG_XPOWER_PMIC_OPREGION) += pmic/intel_pmic_xpower.o
diff --git a/drivers/acpi/pmic/intel_pmic_xpower.c b/drivers/acpi/pmic/intel_pmic_xpower.c
new file mode 100644
index 000000000000..795ca073db08
--- /dev/null
+++ b/drivers/acpi/pmic/intel_pmic_xpower.c
@@ -0,0 +1,246 @@
+/*
+ * 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 int intel_xpower_pmic_opregion_probe(struct platform_device *pdev)
+{
+	struct axp20x_dev *axp20x = dev_get_drvdata(pdev->dev.parent);
+	return intel_pmic_install_opregion_handler(&pdev->dev,
+			ACPI_HANDLE(pdev->dev.parent), axp20x->regmap,
+			&intel_xpower_pmic_opregion_data);
+}
+
+static struct platform_driver intel_xpower_pmic_opregion_driver = {
+	.probe = intel_xpower_pmic_opregion_probe,
+	.driver = {
+		.name = "axp288_opregion",
+	},
+};
+
+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");
diff --git a/drivers/mfd/axp20x.c b/drivers/mfd/axp20x.c
index acbb4c3bf98c..daf3c8d33b4d 100644
--- a/drivers/mfd/axp20x.c
+++ b/drivers/mfd/axp20x.c
@@ -354,6 +354,9 @@ static struct mfd_cell axp288_cells[] = {
 		.num_resources = ARRAY_SIZE(axp288_battery_resources),
 		.resources = axp288_battery_resources,
 	},
+	{
+		.name = "axp288_opregion",
+	},
 };
 
 static struct axp20x_dev *axp20x_pm_power_off;
-- 
1.9.3


^ permalink raw reply related	[flat|nested] 34+ messages in thread

* [PATCH v3 updated 3/3] ACPI / PMIC: AXP288: support virtual GPIO in ACPI table
  2014-11-24  1:06   ` Rafael J. Wysocki
@ 2014-11-24  9:32         ` Aaron Lu
       [not found]     ` <1648486.coeVgHKNdn-sKB8Sp2ER+y1GS7QM15AGw@public.gmane.org>
  1 sibling, 0 replies; 34+ messages in thread
From: Aaron Lu @ 2014-11-24  9:32 UTC (permalink / raw)
  To: Rafael J. Wysocki
  Cc: Lee Jones, Jacob Pan, Yegnesh Iyer,
	linux-acpi-u79uwXL29TY76Z2rM5mHXA,
	linux-iio-u79uwXL29TY76Z2rM5mHXA,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA

On 11/24/2014 09:06 AM, Rafael J. Wysocki wrote:
> On Friday, November 21, 2014 03:11:51 PM Aaron Lu wrote:
>> +	if (!result) {
>> +		status = acpi_install_address_space_handler(
>> +				ACPI_HANDLE(parent), ACPI_ADR_SPACE_GPIO,
>> +				intel_xpower_pmic_gpio_handler, NULL, NULL);
> 
> So here we have a problem, because we can't unregister the opregion handler
> registered above if this fails.  Not nice.

I'm not correct in the cover letter, the actual problem with operation
region remove is with module unload, it happens like this:

The acpi_remove_address_space_handler doesn't wait for the current
running handler to return, so if we call
acpi_remove_address_space_handler in a module's exit function, the
handler's memory will be freed and the running handler will access to
a no longer valid memory place.

So as long as we can make sure the handler will not go away from the
memory, we are safe.

With this said, the updated patch is here:

From: Aaron Lu <aaron.lu-ral2JQCrhuEAvxtiuMwx3w@public.gmane.org>
Date: Mon, 20 Oct 2014 17:06:20 +0800
Subject: [PATCH 3/3] ACPI / PMIC: AXP288: support virtual GPIO in ACPI table

The same virtual GPIO strategy is also used for the AXP288 PMIC in that
various control methods that are used to do power rail handling and
sensor reading/setting will touch GPIO fields defined under the PMIC
device. The GPIO fileds are only defined by the ACPI code while the
actual hardware doesn't really have a GPIO controller, but to make those
control method execution succeed, we have to install a GPIO handler for
the PMIC device handle. Since we do not need the virtual GPIO strategy,
we can simply do nothing in that handler.

Signed-off-by: Aaron Lu <aaron.lu-ral2JQCrhuEAvxtiuMwx3w@public.gmane.org>
---
 drivers/acpi/pmic/intel_pmic_xpower.c | 30 ++++++++++++++++++++++++++----
 1 file changed, 26 insertions(+), 4 deletions(-)

diff --git a/drivers/acpi/pmic/intel_pmic_xpower.c b/drivers/acpi/pmic/intel_pmic_xpower.c
index 795ca073db08..9ec57ebd81c9 100644
--- a/drivers/acpi/pmic/intel_pmic_xpower.c
+++ b/drivers/acpi/pmic/intel_pmic_xpower.c
@@ -220,13 +220,35 @@ static struct intel_pmic_opregion_data intel_xpower_pmic_opregion_data = {
 	.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 axp20x_dev *axp20x = dev_get_drvdata(pdev->dev.parent);
-	return intel_pmic_install_opregion_handler(&pdev->dev,
-			ACPI_HANDLE(pdev->dev.parent), axp20x->regmap,
-			&intel_xpower_pmic_opregion_data);
+	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 = {
-- 
1.9.3

^ permalink raw reply related	[flat|nested] 34+ messages in thread

* [PATCH v3 updated 3/3] ACPI / PMIC: AXP288: support virtual GPIO in ACPI table
@ 2014-11-24  9:32         ` Aaron Lu
  0 siblings, 0 replies; 34+ messages in thread
From: Aaron Lu @ 2014-11-24  9:32 UTC (permalink / raw)
  To: Rafael J. Wysocki
  Cc: Lee Jones, Jacob Pan, Yegnesh Iyer, linux-acpi, linux-iio, linux-kernel

On 11/24/2014 09:06 AM, Rafael J. Wysocki wrote:
> On Friday, November 21, 2014 03:11:51 PM Aaron Lu wrote:
>> +	if (!result) {
>> +		status = acpi_install_address_space_handler(
>> +				ACPI_HANDLE(parent), ACPI_ADR_SPACE_GPIO,
>> +				intel_xpower_pmic_gpio_handler, NULL, NULL);
> 
> So here we have a problem, because we can't unregister the opregion handler
> registered above if this fails.  Not nice.

I'm not correct in the cover letter, the actual problem with operation
region remove is with module unload, it happens like this:

The acpi_remove_address_space_handler doesn't wait for the current
running handler to return, so if we call
acpi_remove_address_space_handler in a module's exit function, the
handler's memory will be freed and the running handler will access to
a no longer valid memory place.

So as long as we can make sure the handler will not go away from the
memory, we are safe.

With this said, the updated patch is here:

From: Aaron Lu <aaron.lu@intel.com>
Date: Mon, 20 Oct 2014 17:06:20 +0800
Subject: [PATCH 3/3] ACPI / PMIC: AXP288: support virtual GPIO in ACPI table

The same virtual GPIO strategy is also used for the AXP288 PMIC in that
various control methods that are used to do power rail handling and
sensor reading/setting will touch GPIO fields defined under the PMIC
device. The GPIO fileds are only defined by the ACPI code while the
actual hardware doesn't really have a GPIO controller, but to make those
control method execution succeed, we have to install a GPIO handler for
the PMIC device handle. Since we do not need the virtual GPIO strategy,
we can simply do nothing in that handler.

Signed-off-by: Aaron Lu <aaron.lu@intel.com>
---
 drivers/acpi/pmic/intel_pmic_xpower.c | 30 ++++++++++++++++++++++++++----
 1 file changed, 26 insertions(+), 4 deletions(-)

diff --git a/drivers/acpi/pmic/intel_pmic_xpower.c b/drivers/acpi/pmic/intel_pmic_xpower.c
index 795ca073db08..9ec57ebd81c9 100644
--- a/drivers/acpi/pmic/intel_pmic_xpower.c
+++ b/drivers/acpi/pmic/intel_pmic_xpower.c
@@ -220,13 +220,35 @@ static struct intel_pmic_opregion_data intel_xpower_pmic_opregion_data = {
 	.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 axp20x_dev *axp20x = dev_get_drvdata(pdev->dev.parent);
-	return intel_pmic_install_opregion_handler(&pdev->dev,
-			ACPI_HANDLE(pdev->dev.parent), axp20x->regmap,
-			&intel_xpower_pmic_opregion_data);
+	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 = {
-- 
1.9.3


^ permalink raw reply related	[flat|nested] 34+ messages in thread

* Re: [PATCH v3 updated 3/3] ACPI / PMIC: AXP288: support virtual GPIO in ACPI table
  2014-11-24  9:32         ` Aaron Lu
@ 2014-11-24 15:19             ` Rafael J. Wysocki
  -1 siblings, 0 replies; 34+ messages in thread
From: Rafael J. Wysocki @ 2014-11-24 15:19 UTC (permalink / raw)
  To: Aaron Lu
  Cc: Lee Jones, Jacob Pan, Yegnesh Iyer,
	linux-acpi-u79uwXL29TY76Z2rM5mHXA,
	linux-iio-u79uwXL29TY76Z2rM5mHXA,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA, Lv Zheng

On Monday, November 24, 2014 05:32:33 PM Aaron Lu wrote:
> On 11/24/2014 09:06 AM, Rafael J. Wysocki wrote:
> > On Friday, November 21, 2014 03:11:51 PM Aaron Lu wrote:
> >> +	if (!result) {
> >> +		status = acpi_install_address_space_handler(
> >> +				ACPI_HANDLE(parent), ACPI_ADR_SPACE_GPIO,
> >> +				intel_xpower_pmic_gpio_handler, NULL, NULL);
> > 
> > So here we have a problem, because we can't unregister the opregion handler
> > registered above if this fails.  Not nice.
> 
> I'm not correct in the cover letter, the actual problem with operation
> region remove is with module unload, it happens like this:
> 
> The acpi_remove_address_space_handler doesn't wait for the current
> running handler to return, so if we call
> acpi_remove_address_space_handler in a module's exit function, the
> handler's memory will be freed and the running handler will access to
> a no longer valid memory place.
> 
> So as long as we can make sure the handler will not go away from the
> memory, we are safe.

This only means that address space handlers cannot be removed from kernel
modules.

Lv was trying to add a wrapper for that some time ago, maybe it's a good
idea to revive that?

> With this said, the updated patch is here:
> 
> From: Aaron Lu <aaron.lu-ral2JQCrhuEAvxtiuMwx3w@public.gmane.org>
> Date: Mon, 20 Oct 2014 17:06:20 +0800
> Subject: [PATCH 3/3] ACPI / PMIC: AXP288: support virtual GPIO in ACPI table
> 
> The same virtual GPIO strategy is also used for the AXP288 PMIC in that
> various control methods that are used to do power rail handling and
> sensor reading/setting will touch GPIO fields defined under the PMIC
> device. The GPIO fileds are only defined by the ACPI code while the
> actual hardware doesn't really have a GPIO controller, but to make those
> control method execution succeed, we have to install a GPIO handler for
> the PMIC device handle. Since we do not need the virtual GPIO strategy,
> we can simply do nothing in that handler.
> 
> Signed-off-by: Aaron Lu <aaron.lu-ral2JQCrhuEAvxtiuMwx3w@public.gmane.org>
> ---
>  drivers/acpi/pmic/intel_pmic_xpower.c | 30 ++++++++++++++++++++++++++----
>  1 file changed, 26 insertions(+), 4 deletions(-)
> 
> diff --git a/drivers/acpi/pmic/intel_pmic_xpower.c b/drivers/acpi/pmic/intel_pmic_xpower.c
> index 795ca073db08..9ec57ebd81c9 100644
> --- a/drivers/acpi/pmic/intel_pmic_xpower.c
> +++ b/drivers/acpi/pmic/intel_pmic_xpower.c
> @@ -220,13 +220,35 @@ static struct intel_pmic_opregion_data intel_xpower_pmic_opregion_data = {
>  	.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 axp20x_dev *axp20x = dev_get_drvdata(pdev->dev.parent);
> -	return intel_pmic_install_opregion_handler(&pdev->dev,
> -			ACPI_HANDLE(pdev->dev.parent), axp20x->regmap,
> -			&intel_xpower_pmic_opregion_data);
> +	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 = {
> 

-- 
I speak only for myself.
Rafael J. Wysocki, Intel Open Source Technology Center.

^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [PATCH v3 updated 3/3] ACPI / PMIC: AXP288: support virtual GPIO in ACPI table
@ 2014-11-24 15:19             ` Rafael J. Wysocki
  0 siblings, 0 replies; 34+ messages in thread
From: Rafael J. Wysocki @ 2014-11-24 15:19 UTC (permalink / raw)
  To: Aaron Lu
  Cc: Lee Jones, Jacob Pan, Yegnesh Iyer, linux-acpi, linux-iio,
	linux-kernel, Lv Zheng

On Monday, November 24, 2014 05:32:33 PM Aaron Lu wrote:
> On 11/24/2014 09:06 AM, Rafael J. Wysocki wrote:
> > On Friday, November 21, 2014 03:11:51 PM Aaron Lu wrote:
> >> +	if (!result) {
> >> +		status = acpi_install_address_space_handler(
> >> +				ACPI_HANDLE(parent), ACPI_ADR_SPACE_GPIO,
> >> +				intel_xpower_pmic_gpio_handler, NULL, NULL);
> > 
> > So here we have a problem, because we can't unregister the opregion handler
> > registered above if this fails.  Not nice.
> 
> I'm not correct in the cover letter, the actual problem with operation
> region remove is with module unload, it happens like this:
> 
> The acpi_remove_address_space_handler doesn't wait for the current
> running handler to return, so if we call
> acpi_remove_address_space_handler in a module's exit function, the
> handler's memory will be freed and the running handler will access to
> a no longer valid memory place.
> 
> So as long as we can make sure the handler will not go away from the
> memory, we are safe.

This only means that address space handlers cannot be removed from kernel
modules.

Lv was trying to add a wrapper for that some time ago, maybe it's a good
idea to revive that?

> With this said, the updated patch is here:
> 
> From: Aaron Lu <aaron.lu@intel.com>
> Date: Mon, 20 Oct 2014 17:06:20 +0800
> Subject: [PATCH 3/3] ACPI / PMIC: AXP288: support virtual GPIO in ACPI table
> 
> The same virtual GPIO strategy is also used for the AXP288 PMIC in that
> various control methods that are used to do power rail handling and
> sensor reading/setting will touch GPIO fields defined under the PMIC
> device. The GPIO fileds are only defined by the ACPI code while the
> actual hardware doesn't really have a GPIO controller, but to make those
> control method execution succeed, we have to install a GPIO handler for
> the PMIC device handle. Since we do not need the virtual GPIO strategy,
> we can simply do nothing in that handler.
> 
> Signed-off-by: Aaron Lu <aaron.lu@intel.com>
> ---
>  drivers/acpi/pmic/intel_pmic_xpower.c | 30 ++++++++++++++++++++++++++----
>  1 file changed, 26 insertions(+), 4 deletions(-)
> 
> diff --git a/drivers/acpi/pmic/intel_pmic_xpower.c b/drivers/acpi/pmic/intel_pmic_xpower.c
> index 795ca073db08..9ec57ebd81c9 100644
> --- a/drivers/acpi/pmic/intel_pmic_xpower.c
> +++ b/drivers/acpi/pmic/intel_pmic_xpower.c
> @@ -220,13 +220,35 @@ static struct intel_pmic_opregion_data intel_xpower_pmic_opregion_data = {
>  	.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 axp20x_dev *axp20x = dev_get_drvdata(pdev->dev.parent);
> -	return intel_pmic_install_opregion_handler(&pdev->dev,
> -			ACPI_HANDLE(pdev->dev.parent), axp20x->regmap,
> -			&intel_xpower_pmic_opregion_data);
> +	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 = {
> 

-- 
I speak only for myself.
Rafael J. Wysocki, Intel Open Source Technology Center.

^ permalink raw reply	[flat|nested] 34+ messages in thread

* RE: [PATCH v3 updated 3/3] ACPI / PMIC: AXP288: support virtual GPIO in ACPI table
  2014-11-24 15:19             ` Rafael J. Wysocki
  (?)
@ 2014-11-25  1:44                 ` Zheng, Lv
  -1 siblings, 0 replies; 34+ messages in thread
From: Zheng, Lv @ 2014-11-25  1:44 UTC (permalink / raw)
  To: Rafael J. Wysocki, Lu, Aaron
  Cc: Lee Jones, Jacob Pan, Iyer, Yegnesh S,
	linux-acpi-u79uwXL29TY76Z2rM5mHXA,
	linux-iio-u79uwXL29TY76Z2rM5mHXA,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA

Hi,

> From: Rafael J. Wysocki [mailto:rjw@rjwysocki.net]
> Sent: Monday, November 24, 2014 11:20 PM
> Lv
> Subject: Re: [PATCH v3 updated 3/3] ACPI / PMIC: AXP288: support virtual GPIO in ACPI table
> 
> On Monday, November 24, 2014 05:32:33 PM Aaron Lu wrote:
> > On 11/24/2014 09:06 AM, Rafael J. Wysocki wrote:
> > > On Friday, November 21, 2014 03:11:51 PM Aaron Lu wrote:
> > >> +	if (!result) {
> > >> +		status = acpi_install_address_space_handler(
> > >> +				ACPI_HANDLE(parent), ACPI_ADR_SPACE_GPIO,
> > >> +				intel_xpower_pmic_gpio_handler, NULL, NULL);
> > >
> > > So here we have a problem, because we can't unregister the opregion handler
> > > registered above if this fails.  Not nice.
> >
> > I'm not correct in the cover letter, the actual problem with operation
> > region remove is with module unload, it happens like this:
> >
> > The acpi_remove_address_space_handler doesn't wait for the current
> > running handler to return, so if we call
> > acpi_remove_address_space_handler in a module's exit function, the
> > handler's memory will be freed and the running handler will access to
> > a no longer valid memory place.
> >
> > So as long as we can make sure the handler will not go away from the
> > memory, we are safe.
> 
> This only means that address space handlers cannot be removed from kernel
> modules.
> 
> Lv was trying to add a wrapper for that some time ago, maybe it's a good
> idea to revive that?

I may fix the issue in ACPICA, but it looks like that it will cost time to discuss.
So I also think it is better to take the wrapper now.
I'll hand the patch to Aaron to let him do a test on it before posting it again.

Thanks and best regards
-Lv

> > With this said, the updated patch is here:
> >
> > From: Aaron Lu <aaron.lu@intel.com>
> > Date: Mon, 20 Oct 2014 17:06:20 +0800
> > Subject: [PATCH 3/3] ACPI / PMIC: AXP288: support virtual GPIO in ACPI table
> >
> > The same virtual GPIO strategy is also used for the AXP288 PMIC in that
> > various control methods that are used to do power rail handling and
> > sensor reading/setting will touch GPIO fields defined under the PMIC
> > device. The GPIO fileds are only defined by the ACPI code while the
> > actual hardware doesn't really have a GPIO controller, but to make those
> > control method execution succeed, we have to install a GPIO handler for
> > the PMIC device handle. Since we do not need the virtual GPIO strategy,
> > we can simply do nothing in that handler.
> >
> > Signed-off-by: Aaron Lu <aaron.lu@intel.com>
> > ---
> >  drivers/acpi/pmic/intel_pmic_xpower.c | 30 ++++++++++++++++++++++++++----
> >  1 file changed, 26 insertions(+), 4 deletions(-)
> >
> > diff --git a/drivers/acpi/pmic/intel_pmic_xpower.c b/drivers/acpi/pmic/intel_pmic_xpower.c
> > index 795ca073db08..9ec57ebd81c9 100644
> > --- a/drivers/acpi/pmic/intel_pmic_xpower.c
> > +++ b/drivers/acpi/pmic/intel_pmic_xpower.c
> > @@ -220,13 +220,35 @@ static struct intel_pmic_opregion_data intel_xpower_pmic_opregion_data = {
> >  	.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 axp20x_dev *axp20x = dev_get_drvdata(pdev->dev.parent);
> > -	return intel_pmic_install_opregion_handler(&pdev->dev,
> > -			ACPI_HANDLE(pdev->dev.parent), axp20x->regmap,
> > -			&intel_xpower_pmic_opregion_data);
> > +	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 = {
> >
> 
> --
> I speak only for myself.
> Rafael J. Wysocki, Intel Open Source Technology Center.

^ permalink raw reply	[flat|nested] 34+ messages in thread

* RE: [PATCH v3 updated 3/3] ACPI / PMIC: AXP288: support virtual GPIO in ACPI table
@ 2014-11-25  1:44                 ` Zheng, Lv
  0 siblings, 0 replies; 34+ messages in thread
From: Zheng, Lv @ 2014-11-25  1:44 UTC (permalink / raw)
  To: Rafael J. Wysocki, Lu, Aaron
  Cc: Lee Jones, Jacob Pan, Iyer, Yegnesh S, linux-acpi, linux-iio,
	linux-kernel

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #1: Type: text/plain; charset="utf-8", Size: 4671 bytes --]

Hi,

> From: Rafael J. Wysocki [mailto:rjw@rjwysocki.net]
> Sent: Monday, November 24, 2014 11:20 PM
> Lv
> Subject: Re: [PATCH v3 updated 3/3] ACPI / PMIC: AXP288: support virtual GPIO in ACPI table
> 
> On Monday, November 24, 2014 05:32:33 PM Aaron Lu wrote:
> > On 11/24/2014 09:06 AM, Rafael J. Wysocki wrote:
> > > On Friday, November 21, 2014 03:11:51 PM Aaron Lu wrote:
> > >> +	if (!result) {
> > >> +		status = acpi_install_address_space_handler(
> > >> +				ACPI_HANDLE(parent), ACPI_ADR_SPACE_GPIO,
> > >> +				intel_xpower_pmic_gpio_handler, NULL, NULL);
> > >
> > > So here we have a problem, because we can't unregister the opregion handler
> > > registered above if this fails.  Not nice.
> >
> > I'm not correct in the cover letter, the actual problem with operation
> > region remove is with module unload, it happens like this:
> >
> > The acpi_remove_address_space_handler doesn't wait for the current
> > running handler to return, so if we call
> > acpi_remove_address_space_handler in a module's exit function, the
> > handler's memory will be freed and the running handler will access to
> > a no longer valid memory place.
> >
> > So as long as we can make sure the handler will not go away from the
> > memory, we are safe.
> 
> This only means that address space handlers cannot be removed from kernel
> modules.
> 
> Lv was trying to add a wrapper for that some time ago, maybe it's a good
> idea to revive that?

I may fix the issue in ACPICA, but it looks like that it will cost time to discuss.
So I also think it is better to take the wrapper now.
I'll hand the patch to Aaron to let him do a test on it before posting it again.

Thanks and best regards
-Lv

> > With this said, the updated patch is here:
> >
> > From: Aaron Lu <aaron.lu@intel.com>
> > Date: Mon, 20 Oct 2014 17:06:20 +0800
> > Subject: [PATCH 3/3] ACPI / PMIC: AXP288: support virtual GPIO in ACPI table
> >
> > The same virtual GPIO strategy is also used for the AXP288 PMIC in that
> > various control methods that are used to do power rail handling and
> > sensor reading/setting will touch GPIO fields defined under the PMIC
> > device. The GPIO fileds are only defined by the ACPI code while the
> > actual hardware doesn't really have a GPIO controller, but to make those
> > control method execution succeed, we have to install a GPIO handler for
> > the PMIC device handle. Since we do not need the virtual GPIO strategy,
> > we can simply do nothing in that handler.
> >
> > Signed-off-by: Aaron Lu <aaron.lu@intel.com>
> > ---
> >  drivers/acpi/pmic/intel_pmic_xpower.c | 30 ++++++++++++++++++++++++++----
> >  1 file changed, 26 insertions(+), 4 deletions(-)
> >
> > diff --git a/drivers/acpi/pmic/intel_pmic_xpower.c b/drivers/acpi/pmic/intel_pmic_xpower.c
> > index 795ca073db08..9ec57ebd81c9 100644
> > --- a/drivers/acpi/pmic/intel_pmic_xpower.c
> > +++ b/drivers/acpi/pmic/intel_pmic_xpower.c
> > @@ -220,13 +220,35 @@ static struct intel_pmic_opregion_data intel_xpower_pmic_opregion_data = {
> >  	.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 axp20x_dev *axp20x = dev_get_drvdata(pdev->dev.parent);
> > -	return intel_pmic_install_opregion_handler(&pdev->dev,
> > -			ACPI_HANDLE(pdev->dev.parent), axp20x->regmap,
> > -			&intel_xpower_pmic_opregion_data);
> > +	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 = {
> >
> 
> --
> I speak only for myself.
> Rafael J. Wysocki, Intel Open Source Technology Center.
ÿôèº{.nÇ+‰·Ÿ®‰­†+%ŠËÿ±éݶ\x17¥Šwÿº{.nÇ+‰·¥Š{±þG«éÿŠ{ayº\x1dʇڙë,j\a­¢f£¢·hšïêÿ‘êçz_è®\x03(­éšŽŠÝ¢j"ú\x1a¶^[m§ÿÿ¾\a«þG«éÿ¢¸?™¨è­Ú&£ø§~á¶iO•æ¬z·švØ^\x14\x04\x1a¶^[m§ÿÿÃ\fÿ¶ìÿ¢¸?–I¥

^ permalink raw reply	[flat|nested] 34+ messages in thread

* RE: [PATCH v3 updated 3/3] ACPI / PMIC: AXP288: support virtual GPIO in ACPI table
@ 2014-11-25  1:44                 ` Zheng, Lv
  0 siblings, 0 replies; 34+ messages in thread
From: Zheng, Lv @ 2014-11-25  1:44 UTC (permalink / raw)
  To: Rafael J. Wysocki, Lu, Aaron
  Cc: Lee Jones, Jacob Pan, Iyer, Yegnesh S, linux-acpi, linux-iio,
	linux-kernel

SGksDQoNCj4gRnJvbTogUmFmYWVsIEouIFd5c29ja2kgW21haWx0bzpyandAcmp3eXNvY2tpLm5l
dF0NCj4gU2VudDogTW9uZGF5LCBOb3ZlbWJlciAyNCwgMjAxNCAxMToyMCBQTQ0KPiBMdg0KPiBT
dWJqZWN0OiBSZTogW1BBVENIIHYzIHVwZGF0ZWQgMy8zXSBBQ1BJIC8gUE1JQzogQVhQMjg4OiBz
dXBwb3J0IHZpcnR1YWwgR1BJTyBpbiBBQ1BJIHRhYmxlDQo+IA0KPiBPbiBNb25kYXksIE5vdmVt
YmVyIDI0LCAyMDE0IDA1OjMyOjMzIFBNIEFhcm9uIEx1IHdyb3RlOg0KPiA+IE9uIDExLzI0LzIw
MTQgMDk6MDYgQU0sIFJhZmFlbCBKLiBXeXNvY2tpIHdyb3RlOg0KPiA+ID4gT24gRnJpZGF5LCBO
b3ZlbWJlciAyMSwgMjAxNCAwMzoxMTo1MSBQTSBBYXJvbiBMdSB3cm90ZToNCj4gPiA+PiArCWlm
ICghcmVzdWx0KSB7DQo+ID4gPj4gKwkJc3RhdHVzID0gYWNwaV9pbnN0YWxsX2FkZHJlc3Nfc3Bh
Y2VfaGFuZGxlcigNCj4gPiA+PiArCQkJCUFDUElfSEFORExFKHBhcmVudCksIEFDUElfQURSX1NQ
QUNFX0dQSU8sDQo+ID4gPj4gKwkJCQlpbnRlbF94cG93ZXJfcG1pY19ncGlvX2hhbmRsZXIsIE5V
TEwsIE5VTEwpOw0KPiA+ID4NCj4gPiA+IFNvIGhlcmUgd2UgaGF2ZSBhIHByb2JsZW0sIGJlY2F1
c2Ugd2UgY2FuJ3QgdW5yZWdpc3RlciB0aGUgb3ByZWdpb24gaGFuZGxlcg0KPiA+ID4gcmVnaXN0
ZXJlZCBhYm92ZSBpZiB0aGlzIGZhaWxzLiAgTm90IG5pY2UuDQo+ID4NCj4gPiBJJ20gbm90IGNv
cnJlY3QgaW4gdGhlIGNvdmVyIGxldHRlciwgdGhlIGFjdHVhbCBwcm9ibGVtIHdpdGggb3BlcmF0
aW9uDQo+ID4gcmVnaW9uIHJlbW92ZSBpcyB3aXRoIG1vZHVsZSB1bmxvYWQsIGl0IGhhcHBlbnMg
bGlrZSB0aGlzOg0KPiA+DQo+ID4gVGhlIGFjcGlfcmVtb3ZlX2FkZHJlc3Nfc3BhY2VfaGFuZGxl
ciBkb2Vzbid0IHdhaXQgZm9yIHRoZSBjdXJyZW50DQo+ID4gcnVubmluZyBoYW5kbGVyIHRvIHJl
dHVybiwgc28gaWYgd2UgY2FsbA0KPiA+IGFjcGlfcmVtb3ZlX2FkZHJlc3Nfc3BhY2VfaGFuZGxl
ciBpbiBhIG1vZHVsZSdzIGV4aXQgZnVuY3Rpb24sIHRoZQ0KPiA+IGhhbmRsZXIncyBtZW1vcnkg
d2lsbCBiZSBmcmVlZCBhbmQgdGhlIHJ1bm5pbmcgaGFuZGxlciB3aWxsIGFjY2VzcyB0bw0KPiA+
IGEgbm8gbG9uZ2VyIHZhbGlkIG1lbW9yeSBwbGFjZS4NCj4gPg0KPiA+IFNvIGFzIGxvbmcgYXMg
d2UgY2FuIG1ha2Ugc3VyZSB0aGUgaGFuZGxlciB3aWxsIG5vdCBnbyBhd2F5IGZyb20gdGhlDQo+
ID4gbWVtb3J5LCB3ZSBhcmUgc2FmZS4NCj4gDQo+IFRoaXMgb25seSBtZWFucyB0aGF0IGFkZHJl
c3Mgc3BhY2UgaGFuZGxlcnMgY2Fubm90IGJlIHJlbW92ZWQgZnJvbSBrZXJuZWwNCj4gbW9kdWxl
cy4NCj4gDQo+IEx2IHdhcyB0cnlpbmcgdG8gYWRkIGEgd3JhcHBlciBmb3IgdGhhdCBzb21lIHRp
bWUgYWdvLCBtYXliZSBpdCdzIGEgZ29vZA0KPiBpZGVhIHRvIHJldml2ZSB0aGF0Pw0KDQpJIG1h
eSBmaXggdGhlIGlzc3VlIGluIEFDUElDQSwgYnV0IGl0IGxvb2tzIGxpa2UgdGhhdCBpdCB3aWxs
IGNvc3QgdGltZSB0byBkaXNjdXNzLg0KU28gSSBhbHNvIHRoaW5rIGl0IGlzIGJldHRlciB0byB0
YWtlIHRoZSB3cmFwcGVyIG5vdy4NCkknbGwgaGFuZCB0aGUgcGF0Y2ggdG8gQWFyb24gdG8gbGV0
IGhpbSBkbyBhIHRlc3Qgb24gaXQgYmVmb3JlIHBvc3RpbmcgaXQgYWdhaW4uDQoNClRoYW5rcyBh
bmQgYmVzdCByZWdhcmRzDQotTHYNCg0KPiA+IFdpdGggdGhpcyBzYWlkLCB0aGUgdXBkYXRlZCBw
YXRjaCBpcyBoZXJlOg0KPiA+DQo+ID4gRnJvbTogQWFyb24gTHUgPGFhcm9uLmx1QGludGVsLmNv
bT4NCj4gPiBEYXRlOiBNb24sIDIwIE9jdCAyMDE0IDE3OjA2OjIwICswODAwDQo+ID4gU3ViamVj
dDogW1BBVENIIDMvM10gQUNQSSAvIFBNSUM6IEFYUDI4ODogc3VwcG9ydCB2aXJ0dWFsIEdQSU8g
aW4gQUNQSSB0YWJsZQ0KPiA+DQo+ID4gVGhlIHNhbWUgdmlydHVhbCBHUElPIHN0cmF0ZWd5IGlz
IGFsc28gdXNlZCBmb3IgdGhlIEFYUDI4OCBQTUlDIGluIHRoYXQNCj4gPiB2YXJpb3VzIGNvbnRy
b2wgbWV0aG9kcyB0aGF0IGFyZSB1c2VkIHRvIGRvIHBvd2VyIHJhaWwgaGFuZGxpbmcgYW5kDQo+
ID4gc2Vuc29yIHJlYWRpbmcvc2V0dGluZyB3aWxsIHRvdWNoIEdQSU8gZmllbGRzIGRlZmluZWQg
dW5kZXIgdGhlIFBNSUMNCj4gPiBkZXZpY2UuIFRoZSBHUElPIGZpbGVkcyBhcmUgb25seSBkZWZp
bmVkIGJ5IHRoZSBBQ1BJIGNvZGUgd2hpbGUgdGhlDQo+ID4gYWN0dWFsIGhhcmR3YXJlIGRvZXNu
J3QgcmVhbGx5IGhhdmUgYSBHUElPIGNvbnRyb2xsZXIsIGJ1dCB0byBtYWtlIHRob3NlDQo+ID4g
Y29udHJvbCBtZXRob2QgZXhlY3V0aW9uIHN1Y2NlZWQsIHdlIGhhdmUgdG8gaW5zdGFsbCBhIEdQ
SU8gaGFuZGxlciBmb3INCj4gPiB0aGUgUE1JQyBkZXZpY2UgaGFuZGxlLiBTaW5jZSB3ZSBkbyBu
b3QgbmVlZCB0aGUgdmlydHVhbCBHUElPIHN0cmF0ZWd5LA0KPiA+IHdlIGNhbiBzaW1wbHkgZG8g
bm90aGluZyBpbiB0aGF0IGhhbmRsZXIuDQo+ID4NCj4gPiBTaWduZWQtb2ZmLWJ5OiBBYXJvbiBM
dSA8YWFyb24ubHVAaW50ZWwuY29tPg0KPiA+IC0tLQ0KPiA+ICBkcml2ZXJzL2FjcGkvcG1pYy9p
bnRlbF9wbWljX3hwb3dlci5jIHwgMzAgKysrKysrKysrKysrKysrKysrKysrKysrKystLS0tDQo+
ID4gIDEgZmlsZSBjaGFuZ2VkLCAyNiBpbnNlcnRpb25zKCspLCA0IGRlbGV0aW9ucygtKQ0KPiA+
DQo+ID4gZGlmZiAtLWdpdCBhL2RyaXZlcnMvYWNwaS9wbWljL2ludGVsX3BtaWNfeHBvd2VyLmMg
Yi9kcml2ZXJzL2FjcGkvcG1pYy9pbnRlbF9wbWljX3hwb3dlci5jDQo+ID4gaW5kZXggNzk1Y2Ew
NzNkYjA4Li45ZWM1N2ViZDgxYzkgMTAwNjQ0DQo+ID4gLS0tIGEvZHJpdmVycy9hY3BpL3BtaWMv
aW50ZWxfcG1pY194cG93ZXIuYw0KPiA+ICsrKyBiL2RyaXZlcnMvYWNwaS9wbWljL2ludGVsX3Bt
aWNfeHBvd2VyLmMNCj4gPiBAQCAtMjIwLDEzICsyMjAsMzUgQEAgc3RhdGljIHN0cnVjdCBpbnRl
bF9wbWljX29wcmVnaW9uX2RhdGEgaW50ZWxfeHBvd2VyX3BtaWNfb3ByZWdpb25fZGF0YSA9IHsN
Cj4gPiAgCS50aGVybWFsX3RhYmxlX2NvdW50ID0gQVJSQVlfU0laRSh0aGVybWFsX3RhYmxlKSwN
Cj4gPiAgfTsNCj4gPg0KPiA+ICtzdGF0aWMgYWNwaV9zdGF0dXMgaW50ZWxfeHBvd2VyX3BtaWNf
Z3Bpb19oYW5kbGVyKHUzMiBmdW5jdGlvbiwNCj4gPiArCQlhY3BpX3BoeXNpY2FsX2FkZHJlc3Mg
YWRkcmVzcywgdTMyIGJpdF93aWR0aCwgdTY0ICp2YWx1ZSwNCj4gPiArCQl2b2lkICpoYW5kbGVy
X2NvbnRleHQsIHZvaWQgKnJlZ2lvbl9jb250ZXh0KQ0KPiA+ICt7DQo+ID4gKwlyZXR1cm4gQUVf
T0s7DQo+ID4gK30NCj4gPg0KPiA+ICBzdGF0aWMgaW50IGludGVsX3hwb3dlcl9wbWljX29wcmVn
aW9uX3Byb2JlKHN0cnVjdCBwbGF0Zm9ybV9kZXZpY2UgKnBkZXYpDQo+ID4gIHsNCj4gPiAtCXN0
cnVjdCBheHAyMHhfZGV2ICpheHAyMHggPSBkZXZfZ2V0X2RydmRhdGEocGRldi0+ZGV2LnBhcmVu
dCk7DQo+ID4gLQlyZXR1cm4gaW50ZWxfcG1pY19pbnN0YWxsX29wcmVnaW9uX2hhbmRsZXIoJnBk
ZXYtPmRldiwNCj4gPiAtCQkJQUNQSV9IQU5ETEUocGRldi0+ZGV2LnBhcmVudCksIGF4cDIweC0+
cmVnbWFwLA0KPiA+IC0JCQkmaW50ZWxfeHBvd2VyX3BtaWNfb3ByZWdpb25fZGF0YSk7DQo+ID4g
KwlzdHJ1Y3QgZGV2aWNlICpwYXJlbnQgPSBwZGV2LT5kZXYucGFyZW50Ow0KPiA+ICsJc3RydWN0
IGF4cDIweF9kZXYgKmF4cDIweCA9IGRldl9nZXRfZHJ2ZGF0YShwYXJlbnQpOw0KPiA+ICsJYWNw
aV9zdGF0dXMgc3RhdHVzOw0KPiA+ICsJaW50IHJlc3VsdDsNCj4gPiArDQo+ID4gKwlzdGF0dXMg
PSBhY3BpX2luc3RhbGxfYWRkcmVzc19zcGFjZV9oYW5kbGVyKEFDUElfSEFORExFKHBhcmVudCks
DQo+ID4gKwkJCUFDUElfQURSX1NQQUNFX0dQSU8sIGludGVsX3hwb3dlcl9wbWljX2dwaW9faGFu
ZGxlciwNCj4gPiArCQkJTlVMTCwgTlVMTCk7DQo+ID4gKwlpZiAoQUNQSV9GQUlMVVJFKHN0YXR1
cykpDQo+ID4gKwkJcmV0dXJuIC1FTk9ERVY7DQo+ID4gKw0KPiA+ICsJcmVzdWx0ID0gaW50ZWxf
cG1pY19pbnN0YWxsX29wcmVnaW9uX2hhbmRsZXIoJnBkZXYtPmRldiwNCj4gPiArCQkJCQlBQ1BJ
X0hBTkRMRShwYXJlbnQpLCBheHAyMHgtPnJlZ21hcCwNCj4gPiArCQkJCQkmaW50ZWxfeHBvd2Vy
X3BtaWNfb3ByZWdpb25fZGF0YSk7DQo+ID4gKwlpZiAocmVzdWx0KQ0KPiA+ICsJCWFjcGlfcmVt
b3ZlX2FkZHJlc3Nfc3BhY2VfaGFuZGxlcihBQ1BJX0hBTkRMRShwYXJlbnQpLA0KPiA+ICsJCQkJ
CQkgIEFDUElfQURSX1NQQUNFX0dQSU8sDQo+ID4gKwkJCQkJCSAgaW50ZWxfeHBvd2VyX3BtaWNf
Z3Bpb19oYW5kbGVyKTsNCj4gPiArDQo+ID4gKwlyZXR1cm4gcmVzdWx0Ow0KPiA+ICB9DQo+ID4N
Cj4gPiAgc3RhdGljIHN0cnVjdCBwbGF0Zm9ybV9kcml2ZXIgaW50ZWxfeHBvd2VyX3BtaWNfb3By
ZWdpb25fZHJpdmVyID0gew0KPiA+DQo+IA0KPiAtLQ0KPiBJIHNwZWFrIG9ubHkgZm9yIG15c2Vs
Zi4NCj4gUmFmYWVsIEouIFd5c29ja2ksIEludGVsIE9wZW4gU291cmNlIFRlY2hub2xvZ3kgQ2Vu
dGVyLg0K

^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [PATCH v3 0/3] Support PMIC operation region for CrystalCove and XPower
  2014-11-21  7:11 ` Aaron Lu
@ 2014-11-25  1:47     ` Rafael J. Wysocki
  -1 siblings, 0 replies; 34+ messages in thread
From: Rafael J. Wysocki @ 2014-11-25  1:47 UTC (permalink / raw)
  To: Aaron Lu
  Cc: Lee Jones, Jacob Pan, Yegnesh Iyer,
	linux-acpi-u79uwXL29TY76Z2rM5mHXA,
	linux-iio-u79uwXL29TY76Z2rM5mHXA,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA

On Friday, November 21, 2014 03:11:48 PM Aaron Lu wrote:
> v3:
> Only some function/variable name changes, no functiona changes:
> - Replace the dptf/DPTF word originate from the BIOS ACPI table with more
>   meaningful word thermal/THERMAL in all places;
> - Eliminate the soc part in various structure and function names to make
>   them shorter:
>   intel_soc_pmic_opregion -> intel_pmic_opregion
>   intel_soc_pmic_pmop_handler -> intel_pmic_pmop_handler
>   intel_soc_pmic_install_opregion_handler -> intel_pmic_install_opregion_handler
>   etc.
> 
> 
> v2:
> Place PMIC operation files under drivers/acpi/pmic instead of
> drivers/acpi/pmic_opregion as suggested by Rafael;
> Rename PMIC operation files to make them shorter as suggested by Rafael.
> 
> v1:
> On Intel Baytrail-T and Baytrail-T-CR platforms, there are two customized
> ACPI operation regions defined for the Power Management Integrated Circuit
> device, one is for power resource handling and one is for thermal: sensor
> temperature reporting, trip point setting, etc. There are different PMIC
> chips used on those platforms and though each has the same two operation
> regions and functionality, their implementation is different so every PMIC
> will need a driver. But since their functionality is similar, some common
> code is abstracted into the intel_soc_pmic_opregion.c.
> 
> The last version is posted here:
> https://lkml.org/lkml/2014/9/8/801
> 
> Changes since then:
> 1 Move to drivers/acpi as discussed on the above thread;
> 2 Added support for XPower AXP288 PMIC operation region support;
> 3 Since operation region handler can not be removed(at the moment at least),
>   use bool for the two operation region driver configs instead of tristate;
>   Another reason to do this is that, with Mika's MFD ACPI support patch, all
>   those MFD cell devices created will have the same modalias as their parent's
>   so it doesn't make much sense to compile these drivers into modules.
> 
> Patch 1 applies on top of Rafael's pm-next branch, and then patch 2 and
> patch 3 needs merge of Lee's mfd/ib-mfd-iio-3.19 branch where the PMIC
> driver XPower AXP288 and iio driver axp288_adc is located.
> 
> 
> Aaron Lu (3):
>   ACPI / PMIC: support PMIC operation region for CrystalCove
>   ACPI / PMIC: support PMIC operation region for XPower AXP288
>   ACPI / PMIC: AXP288: support virtual GPIO in ACPI table

OK

I've pulled the Lee's 'mfd/ib-mfd-iio-3.19' branch and applied your updated
three on top of that.  Please check the bleeding-edge branch of linux-pm.git
for the result.

-- 
I speak only for myself.
Rafael J. Wysocki, Intel Open Source Technology Center.

^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [PATCH v3 0/3] Support PMIC operation region for CrystalCove and XPower
@ 2014-11-25  1:47     ` Rafael J. Wysocki
  0 siblings, 0 replies; 34+ messages in thread
From: Rafael J. Wysocki @ 2014-11-25  1:47 UTC (permalink / raw)
  To: Aaron Lu
  Cc: Lee Jones, Jacob Pan, Yegnesh Iyer, linux-acpi, linux-iio, linux-kernel

On Friday, November 21, 2014 03:11:48 PM Aaron Lu wrote:
> v3:
> Only some function/variable name changes, no functiona changes:
> - Replace the dptf/DPTF word originate from the BIOS ACPI table with more
>   meaningful word thermal/THERMAL in all places;
> - Eliminate the soc part in various structure and function names to make
>   them shorter:
>   intel_soc_pmic_opregion -> intel_pmic_opregion
>   intel_soc_pmic_pmop_handler -> intel_pmic_pmop_handler
>   intel_soc_pmic_install_opregion_handler -> intel_pmic_install_opregion_handler
>   etc.
> 
> 
> v2:
> Place PMIC operation files under drivers/acpi/pmic instead of
> drivers/acpi/pmic_opregion as suggested by Rafael;
> Rename PMIC operation files to make them shorter as suggested by Rafael.
> 
> v1:
> On Intel Baytrail-T and Baytrail-T-CR platforms, there are two customized
> ACPI operation regions defined for the Power Management Integrated Circuit
> device, one is for power resource handling and one is for thermal: sensor
> temperature reporting, trip point setting, etc. There are different PMIC
> chips used on those platforms and though each has the same two operation
> regions and functionality, their implementation is different so every PMIC
> will need a driver. But since their functionality is similar, some common
> code is abstracted into the intel_soc_pmic_opregion.c.
> 
> The last version is posted here:
> https://lkml.org/lkml/2014/9/8/801
> 
> Changes since then:
> 1 Move to drivers/acpi as discussed on the above thread;
> 2 Added support for XPower AXP288 PMIC operation region support;
> 3 Since operation region handler can not be removed(at the moment at least),
>   use bool for the two operation region driver configs instead of tristate;
>   Another reason to do this is that, with Mika's MFD ACPI support patch, all
>   those MFD cell devices created will have the same modalias as their parent's
>   so it doesn't make much sense to compile these drivers into modules.
> 
> Patch 1 applies on top of Rafael's pm-next branch, and then patch 2 and
> patch 3 needs merge of Lee's mfd/ib-mfd-iio-3.19 branch where the PMIC
> driver XPower AXP288 and iio driver axp288_adc is located.
> 
> 
> Aaron Lu (3):
>   ACPI / PMIC: support PMIC operation region for CrystalCove
>   ACPI / PMIC: support PMIC operation region for XPower AXP288
>   ACPI / PMIC: AXP288: support virtual GPIO in ACPI table

OK

I've pulled the Lee's 'mfd/ib-mfd-iio-3.19' branch and applied your updated
three on top of that.  Please check the bleeding-edge branch of linux-pm.git
for the result.

-- 
I speak only for myself.
Rafael J. Wysocki, Intel Open Source Technology Center.

^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [PATCH v3 0/3] Support PMIC operation region for CrystalCove and XPower
  2014-11-25  1:47     ` Rafael J. Wysocki
@ 2014-11-25 11:52         ` Aaron Lu
  -1 siblings, 0 replies; 34+ messages in thread
From: Aaron Lu @ 2014-11-25 11:52 UTC (permalink / raw)
  To: Rafael J. Wysocki
  Cc: Lee Jones, Jacob Pan, Yegnesh Iyer,
	linux-acpi-u79uwXL29TY76Z2rM5mHXA,
	linux-iio-u79uwXL29TY76Z2rM5mHXA,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA

On 11/25/2014 09:47 AM, Rafael J. Wysocki wrote:
> On Friday, November 21, 2014 03:11:48 PM Aaron Lu wrote:
>> v3:
>> Only some function/variable name changes, no functiona changes:
>> - Replace the dptf/DPTF word originate from the BIOS ACPI table with more
>>   meaningful word thermal/THERMAL in all places;
>> - Eliminate the soc part in various structure and function names to make
>>   them shorter:
>>   intel_soc_pmic_opregion -> intel_pmic_opregion
>>   intel_soc_pmic_pmop_handler -> intel_pmic_pmop_handler
>>   intel_soc_pmic_install_opregion_handler -> intel_pmic_install_opregion_handler
>>   etc.
>>
>>
>> v2:
>> Place PMIC operation files under drivers/acpi/pmic instead of
>> drivers/acpi/pmic_opregion as suggested by Rafael;
>> Rename PMIC operation files to make them shorter as suggested by Rafael.
>>
>> v1:
>> On Intel Baytrail-T and Baytrail-T-CR platforms, there are two customized
>> ACPI operation regions defined for the Power Management Integrated Circuit
>> device, one is for power resource handling and one is for thermal: sensor
>> temperature reporting, trip point setting, etc. There are different PMIC
>> chips used on those platforms and though each has the same two operation
>> regions and functionality, their implementation is different so every PMIC
>> will need a driver. But since their functionality is similar, some common
>> code is abstracted into the intel_soc_pmic_opregion.c.
>>
>> The last version is posted here:
>> https://lkml.org/lkml/2014/9/8/801
>>
>> Changes since then:
>> 1 Move to drivers/acpi as discussed on the above thread;
>> 2 Added support for XPower AXP288 PMIC operation region support;
>> 3 Since operation region handler can not be removed(at the moment at least),
>>   use bool for the two operation region driver configs instead of tristate;
>>   Another reason to do this is that, with Mika's MFD ACPI support patch, all
>>   those MFD cell devices created will have the same modalias as their parent's
>>   so it doesn't make much sense to compile these drivers into modules.
>>
>> Patch 1 applies on top of Rafael's pm-next branch, and then patch 2 and
>> patch 3 needs merge of Lee's mfd/ib-mfd-iio-3.19 branch where the PMIC
>> driver XPower AXP288 and iio driver axp288_adc is located.
>>
>>
>> Aaron Lu (3):
>>   ACPI / PMIC: support PMIC operation region for CrystalCove
>>   ACPI / PMIC: support PMIC operation region for XPower AXP288
>>   ACPI / PMIC: AXP288: support virtual GPIO in ACPI table
> 
> OK
> 
> I've pulled the Lee's 'mfd/ib-mfd-iio-3.19' branch and applied your updated
> three on top of that.  Please check the bleeding-edge branch of linux-pm.git
> for the result.

Thanks, and a fix patch is here:

From: Aaron Lu <aaron.lu-ral2JQCrhuEAvxtiuMwx3w@public.gmane.org>
Date: Tue, 25 Nov 2014 14:35:38 +0800
Subject: [PATCH] ACPI / PMIC: Make it possible to build PMIC driver as a module

This can solve a problem that when axp288_adc driver is built as a
module and the PMIC driver is builtin, following error would ocur:
 drivers/built-in.o: In function `intel_xpower_pmic_get_raw_temp':
 intel_pmic_xpower.c:(.text+0xdfaa7): undefined reference to `iio_channel_get'
 intel_pmic_xpower.c:(.text+0xdfb24): undefined reference to `iio_read_channel_raw'
 intel_pmic_xpower.c:(.text+0xdfb4e): undefined reference to `iio_channel_release'

Also, with the fix commit: 52870786ff5d ("ACPI: Use ACPI companion to
match only the first physical device"), the MFD cell device will have
its own platform modalias instead of its parent's ACPI modalias, this
made it possible for the module to be autoloaded.

Reported-by: kbuild test robot <fengguang.wu-ral2JQCrhuEAvxtiuMwx3w@public.gmane.org>
Signed-off-by: Aaron Lu <aaron.lu-ral2JQCrhuEAvxtiuMwx3w@public.gmane.org>
---
 drivers/acpi/Kconfig                  |  6 +++---
 drivers/acpi/pmic/intel_pmic_crc.c    | 12 +++++++++++-
 drivers/acpi/pmic/intel_pmic_xpower.c | 12 +++++++++++-
 3 files changed, 25 insertions(+), 5 deletions(-)

diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig
index 227f0692cbff..f9459ba4ce59 100644
--- a/drivers/acpi/Kconfig
+++ b/drivers/acpi/Kconfig
@@ -394,7 +394,7 @@ config ACPI_EXTLOG
 	  tracepoint which carries that information to userspace.
 
 menuconfig PMIC_OPREGION
-	bool "PMIC (Power Management Integrated Circuit) operation region support"
+	tristate "PMIC (Power Management Integrated Circuit) operation region support"
 	help
 	  Select this option to enable support for ACPI operation
 	  region of the PMIC chip. The operation region can be used
@@ -403,13 +403,13 @@ menuconfig PMIC_OPREGION
 
 if PMIC_OPREGION
 config CRC_PMIC_OPREGION
-	bool "ACPI operation region support for CrystalCove PMIC"
+	tristate "ACPI operation region support for CrystalCove PMIC"
 	depends on INTEL_SOC_PMIC
 	help
 	  This config adds ACPI operation region support for CrystalCove PMIC.
 
 config XPOWER_PMIC_OPREGION
-	bool "ACPI operation region support for XPower AXP288 PMIC"
+	tristate "ACPI operation region support for XPower AXP288 PMIC"
 	depends on AXP288_ADC
 	help
 	  This config adds ACPI operation region support for XPower AXP288 PMIC.
diff --git a/drivers/acpi/pmic/intel_pmic_crc.c b/drivers/acpi/pmic/intel_pmic_crc.c
index 8955e5b41195..8a193381b5ee 100644
--- a/drivers/acpi/pmic/intel_pmic_crc.c
+++ b/drivers/acpi/pmic/intel_pmic_crc.c
@@ -194,13 +194,23 @@ static int intel_crc_pmic_opregion_probe(struct platform_device *pdev)
 			&intel_crc_pmic_opregion_data);
 }
 
+#define DRV_NAME "crystal_cove_region"
+
+static struct platform_device_id crc_opregion_id_table[] = {
+	{ .name = DRV_NAME },
+	{},
+};
+
 static struct platform_driver intel_crc_pmic_opregion_driver = {
 	.probe = intel_crc_pmic_opregion_probe,
+	.id_table = crc_opregion_id_table,
 	.driver = {
-		.name = "crystal_cove_region",
+		.name = DRV_NAME,
 	},
 };
 
+MODULE_DEVICE_TABLE(platform, crc_opregion_id_table);
+
 static int __init intel_crc_pmic_opregion_driver_init(void)
 {
 	return platform_driver_register(&intel_crc_pmic_opregion_driver);
diff --git a/drivers/acpi/pmic/intel_pmic_xpower.c b/drivers/acpi/pmic/intel_pmic_xpower.c
index 9ec57ebd81c9..4debcbbd6285 100644
--- a/drivers/acpi/pmic/intel_pmic_xpower.c
+++ b/drivers/acpi/pmic/intel_pmic_xpower.c
@@ -251,13 +251,23 @@ static int intel_xpower_pmic_opregion_probe(struct platform_device *pdev)
 	return result;
 }
 
+#define DRV_NAME "axp288_opregion"
+
+static struct platform_device_id xpower_opregion_id_table[] = {
+	{ .name = DRV_NAME },
+	{},
+};
+
 static struct platform_driver intel_xpower_pmic_opregion_driver = {
 	.probe = intel_xpower_pmic_opregion_probe,
+	.id_table = xpower_opregion_id_table,
 	.driver = {
-		.name = "axp288_opregion",
+		.name = DRV_NAME,
 	},
 };
 
+MODULE_DEVICE_TABLE(platform, xpower_opregion_id_table);
+
 static int __init intel_xpower_pmic_opregion_driver_init(void)
 {
 	return platform_driver_register(&intel_xpower_pmic_opregion_driver);
-- 
1.9.3

^ permalink raw reply related	[flat|nested] 34+ messages in thread

* Re: [PATCH v3 0/3] Support PMIC operation region for CrystalCove and XPower
@ 2014-11-25 11:52         ` Aaron Lu
  0 siblings, 0 replies; 34+ messages in thread
From: Aaron Lu @ 2014-11-25 11:52 UTC (permalink / raw)
  To: Rafael J. Wysocki
  Cc: Lee Jones, Jacob Pan, Yegnesh Iyer, linux-acpi, linux-iio, linux-kernel

On 11/25/2014 09:47 AM, Rafael J. Wysocki wrote:
> On Friday, November 21, 2014 03:11:48 PM Aaron Lu wrote:
>> v3:
>> Only some function/variable name changes, no functiona changes:
>> - Replace the dptf/DPTF word originate from the BIOS ACPI table with more
>>   meaningful word thermal/THERMAL in all places;
>> - Eliminate the soc part in various structure and function names to make
>>   them shorter:
>>   intel_soc_pmic_opregion -> intel_pmic_opregion
>>   intel_soc_pmic_pmop_handler -> intel_pmic_pmop_handler
>>   intel_soc_pmic_install_opregion_handler -> intel_pmic_install_opregion_handler
>>   etc.
>>
>>
>> v2:
>> Place PMIC operation files under drivers/acpi/pmic instead of
>> drivers/acpi/pmic_opregion as suggested by Rafael;
>> Rename PMIC operation files to make them shorter as suggested by Rafael.
>>
>> v1:
>> On Intel Baytrail-T and Baytrail-T-CR platforms, there are two customized
>> ACPI operation regions defined for the Power Management Integrated Circuit
>> device, one is for power resource handling and one is for thermal: sensor
>> temperature reporting, trip point setting, etc. There are different PMIC
>> chips used on those platforms and though each has the same two operation
>> regions and functionality, their implementation is different so every PMIC
>> will need a driver. But since their functionality is similar, some common
>> code is abstracted into the intel_soc_pmic_opregion.c.
>>
>> The last version is posted here:
>> https://lkml.org/lkml/2014/9/8/801
>>
>> Changes since then:
>> 1 Move to drivers/acpi as discussed on the above thread;
>> 2 Added support for XPower AXP288 PMIC operation region support;
>> 3 Since operation region handler can not be removed(at the moment at least),
>>   use bool for the two operation region driver configs instead of tristate;
>>   Another reason to do this is that, with Mika's MFD ACPI support patch, all
>>   those MFD cell devices created will have the same modalias as their parent's
>>   so it doesn't make much sense to compile these drivers into modules.
>>
>> Patch 1 applies on top of Rafael's pm-next branch, and then patch 2 and
>> patch 3 needs merge of Lee's mfd/ib-mfd-iio-3.19 branch where the PMIC
>> driver XPower AXP288 and iio driver axp288_adc is located.
>>
>>
>> Aaron Lu (3):
>>   ACPI / PMIC: support PMIC operation region for CrystalCove
>>   ACPI / PMIC: support PMIC operation region for XPower AXP288
>>   ACPI / PMIC: AXP288: support virtual GPIO in ACPI table
> 
> OK
> 
> I've pulled the Lee's 'mfd/ib-mfd-iio-3.19' branch and applied your updated
> three on top of that.  Please check the bleeding-edge branch of linux-pm.git
> for the result.

Thanks, and a fix patch is here:

From: Aaron Lu <aaron.lu@intel.com>
Date: Tue, 25 Nov 2014 14:35:38 +0800
Subject: [PATCH] ACPI / PMIC: Make it possible to build PMIC driver as a module

This can solve a problem that when axp288_adc driver is built as a
module and the PMIC driver is builtin, following error would ocur:
 drivers/built-in.o: In function `intel_xpower_pmic_get_raw_temp':
 intel_pmic_xpower.c:(.text+0xdfaa7): undefined reference to `iio_channel_get'
 intel_pmic_xpower.c:(.text+0xdfb24): undefined reference to `iio_read_channel_raw'
 intel_pmic_xpower.c:(.text+0xdfb4e): undefined reference to `iio_channel_release'

Also, with the fix commit: 52870786ff5d ("ACPI: Use ACPI companion to
match only the first physical device"), the MFD cell device will have
its own platform modalias instead of its parent's ACPI modalias, this
made it possible for the module to be autoloaded.

Reported-by: kbuild test robot <fengguang.wu@intel.com>
Signed-off-by: Aaron Lu <aaron.lu@intel.com>
---
 drivers/acpi/Kconfig                  |  6 +++---
 drivers/acpi/pmic/intel_pmic_crc.c    | 12 +++++++++++-
 drivers/acpi/pmic/intel_pmic_xpower.c | 12 +++++++++++-
 3 files changed, 25 insertions(+), 5 deletions(-)

diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig
index 227f0692cbff..f9459ba4ce59 100644
--- a/drivers/acpi/Kconfig
+++ b/drivers/acpi/Kconfig
@@ -394,7 +394,7 @@ config ACPI_EXTLOG
 	  tracepoint which carries that information to userspace.
 
 menuconfig PMIC_OPREGION
-	bool "PMIC (Power Management Integrated Circuit) operation region support"
+	tristate "PMIC (Power Management Integrated Circuit) operation region support"
 	help
 	  Select this option to enable support for ACPI operation
 	  region of the PMIC chip. The operation region can be used
@@ -403,13 +403,13 @@ menuconfig PMIC_OPREGION
 
 if PMIC_OPREGION
 config CRC_PMIC_OPREGION
-	bool "ACPI operation region support for CrystalCove PMIC"
+	tristate "ACPI operation region support for CrystalCove PMIC"
 	depends on INTEL_SOC_PMIC
 	help
 	  This config adds ACPI operation region support for CrystalCove PMIC.
 
 config XPOWER_PMIC_OPREGION
-	bool "ACPI operation region support for XPower AXP288 PMIC"
+	tristate "ACPI operation region support for XPower AXP288 PMIC"
 	depends on AXP288_ADC
 	help
 	  This config adds ACPI operation region support for XPower AXP288 PMIC.
diff --git a/drivers/acpi/pmic/intel_pmic_crc.c b/drivers/acpi/pmic/intel_pmic_crc.c
index 8955e5b41195..8a193381b5ee 100644
--- a/drivers/acpi/pmic/intel_pmic_crc.c
+++ b/drivers/acpi/pmic/intel_pmic_crc.c
@@ -194,13 +194,23 @@ static int intel_crc_pmic_opregion_probe(struct platform_device *pdev)
 			&intel_crc_pmic_opregion_data);
 }
 
+#define DRV_NAME "crystal_cove_region"
+
+static struct platform_device_id crc_opregion_id_table[] = {
+	{ .name = DRV_NAME },
+	{},
+};
+
 static struct platform_driver intel_crc_pmic_opregion_driver = {
 	.probe = intel_crc_pmic_opregion_probe,
+	.id_table = crc_opregion_id_table,
 	.driver = {
-		.name = "crystal_cove_region",
+		.name = DRV_NAME,
 	},
 };
 
+MODULE_DEVICE_TABLE(platform, crc_opregion_id_table);
+
 static int __init intel_crc_pmic_opregion_driver_init(void)
 {
 	return platform_driver_register(&intel_crc_pmic_opregion_driver);
diff --git a/drivers/acpi/pmic/intel_pmic_xpower.c b/drivers/acpi/pmic/intel_pmic_xpower.c
index 9ec57ebd81c9..4debcbbd6285 100644
--- a/drivers/acpi/pmic/intel_pmic_xpower.c
+++ b/drivers/acpi/pmic/intel_pmic_xpower.c
@@ -251,13 +251,23 @@ static int intel_xpower_pmic_opregion_probe(struct platform_device *pdev)
 	return result;
 }
 
+#define DRV_NAME "axp288_opregion"
+
+static struct platform_device_id xpower_opregion_id_table[] = {
+	{ .name = DRV_NAME },
+	{},
+};
+
 static struct platform_driver intel_xpower_pmic_opregion_driver = {
 	.probe = intel_xpower_pmic_opregion_probe,
+	.id_table = xpower_opregion_id_table,
 	.driver = {
-		.name = "axp288_opregion",
+		.name = DRV_NAME,
 	},
 };
 
+MODULE_DEVICE_TABLE(platform, xpower_opregion_id_table);
+
 static int __init intel_xpower_pmic_opregion_driver_init(void)
 {
 	return platform_driver_register(&intel_xpower_pmic_opregion_driver);
-- 
1.9.3


^ permalink raw reply related	[flat|nested] 34+ messages in thread

* Re: [PATCH v3 updated 3/3] ACPI / PMIC: AXP288: support virtual GPIO in ACPI table
  2014-11-24 15:19             ` Rafael J. Wysocki
  (?)
  (?)
@ 2014-11-25 12:01             ` Aaron Lu
       [not found]               ` <54746FA2.9030408-ral2JQCrhuEAvxtiuMwx3w@public.gmane.org>
  -1 siblings, 1 reply; 34+ messages in thread
From: Aaron Lu @ 2014-11-25 12:01 UTC (permalink / raw)
  To: Rafael J. Wysocki
  Cc: Lee Jones, Jacob Pan, Yegnesh Iyer, linux-acpi, linux-iio,
	linux-kernel, Lv Zheng

On 11/24/2014 11:19 PM, Rafael J. Wysocki wrote:
> On Monday, November 24, 2014 05:32:33 PM Aaron Lu wrote:
>> On 11/24/2014 09:06 AM, Rafael J. Wysocki wrote:
>>> On Friday, November 21, 2014 03:11:51 PM Aaron Lu wrote:
>>>> +	if (!result) {
>>>> +		status = acpi_install_address_space_handler(
>>>> +				ACPI_HANDLE(parent), ACPI_ADR_SPACE_GPIO,
>>>> +				intel_xpower_pmic_gpio_handler, NULL, NULL);
>>>
>>> So here we have a problem, because we can't unregister the opregion handler
>>> registered above if this fails.  Not nice.
>>
>> I'm not correct in the cover letter, the actual problem with operation
>> region remove is with module unload, it happens like this:
>>
>> The acpi_remove_address_space_handler doesn't wait for the current
>> running handler to return, so if we call
>> acpi_remove_address_space_handler in a module's exit function, the
>> handler's memory will be freed and the running handler will access to
>> a no longer valid memory place.
>>
>> So as long as we can make sure the handler will not go away from the
>> memory, we are safe.
> 
> This only means that address space handlers cannot be removed from kernel
> modules.

If the module can not be unloaded(no module exit call), then we should
be safe.

> 
> Lv was trying to add a wrapper for that some time ago, maybe it's a good
> idea to revive that?

Is it this one? If it is, I'll test it and then add the unload
functionality to the PMIC drivers.

From: Lv Zheng <lv.zheng@intel.com>
Date: Tue, 25 Nov 2014 15:42:44 +0800
Subject: [PATCH] ACPICA: Events: Add invocation protection for operation region callbacks.

This patch adds invocation protection around operation region callbacks to
offer a module safe environment for OSPM provided address space handlers.

It is likely that OSPM where ModuleDevice is supported will implement
specific address space handlers in the modules.  Thus the unloading of
the handlers' owner modules may lead to program crash around the invocation
if the handler callbacks are invoked without protection.  Since the
handler callbacks are invoked inside of ACPICA, it is better to implement
invocation protection inside of ACPICA.
As address space handlers are expected to be executed in parallel, it is
not a good choice to implement protection using locks.  This patch uses
reference counting based protection mechanism.  When OSPM calls
acpi_remove_address_space_handler(), the function waits until all invocations
exit to ensure no invocation can happen after the unloading of the modules.

Note that this might be a workaround and not tested, better solution could
be implemented to tune the design of the namespace objects. Lv Zheng.

Signed-off-by: Lv Zheng <lv.zheng@intel.com>
---
 drivers/acpi/acpica/acevents.h  |   9 ++++
 drivers/acpi/acpica/acobject.h  |   1 +
 drivers/acpi/acpica/evhandler.c | 117 ++++++++++++++++++++++++++++++++++++++++
 drivers/acpi/acpica/evregion.c  |  11 +++-
 drivers/acpi/acpica/evxfregn.c  |   9 ++++
 include/acpi/actypes.h          |   2 +
 6 files changed, 147 insertions(+), 2 deletions(-)

diff --git a/drivers/acpi/acpica/acevents.h b/drivers/acpi/acpica/acevents.h
index 7a7811a9fc26..3020ac4ab7a8 100644
--- a/drivers/acpi/acpica/acevents.h
+++ b/drivers/acpi/acpica/acevents.h
@@ -175,6 +175,15 @@ acpi_ev_install_space_handler(struct acpi_namespace_node *node,
 			      acpi_adr_space_handler handler,
 			      acpi_adr_space_setup setup, void *context);
 
+acpi_status
+acpi_ev_get_space_handler(union acpi_operand_object *handler_desc);
+
+void acpi_ev_put_space_handler(union acpi_operand_object *handler_desc);
+
+void acpi_ev_flush_space_handler(union acpi_operand_object *handler_desc);
+
+s16 acpi_ev_space_handler_count(union acpi_operand_object *handler_desc);
+
 /*
  * evregion - Operation region support
  */
diff --git a/drivers/acpi/acpica/acobject.h b/drivers/acpi/acpica/acobject.h
index 8abb393dafab..a719d9733e6b 100644
--- a/drivers/acpi/acpica/acobject.h
+++ b/drivers/acpi/acpica/acobject.h
@@ -304,6 +304,7 @@ struct acpi_object_notify_handler {
 
 struct acpi_object_addr_handler {
 	ACPI_OBJECT_COMMON_HEADER u8 space_id;
+	s16 invocation_count;
 	u8 handler_flags;
 	acpi_adr_space_handler handler;
 	struct acpi_namespace_node *node;	/* Parent device */
diff --git a/drivers/acpi/acpica/evhandler.c b/drivers/acpi/acpica/evhandler.c
index 78ac29351c9e..b27cc8e0507f 100644
--- a/drivers/acpi/acpica/evhandler.c
+++ b/drivers/acpi/acpica/evhandler.c
@@ -499,6 +499,7 @@ acpi_ev_install_space_handler(struct acpi_namespace_node * node,
 	handler_obj->address_space.space_id = (u8)space_id;
 	handler_obj->address_space.handler_flags = flags;
 	handler_obj->address_space.region_list = NULL;
+	handler_obj->address_space.invocation_count = 0;
 	handler_obj->address_space.node = node;
 	handler_obj->address_space.handler = handler;
 	handler_obj->address_space.context = context;
@@ -534,3 +535,119 @@ acpi_ev_install_space_handler(struct acpi_namespace_node * node,
 unlock_and_exit:
 	return_ACPI_STATUS(status);
 }
+
+/*******************************************************************************
+ *
+ * FUNCTION:    acpi_ev_flush_space_handler
+ *
+ * PARAMETERS:  handler_desc     - Address space object
+ *                                 (ACPI_TYPE_LOCAL_ADDRESS_HANDLER)
+ *
+ * RETURN:      None.
+ *
+ * DESCRIPTION: Flush the reference of the given address space handler object.
+ *
+ ******************************************************************************/
+
+void acpi_ev_flush_space_handler(union acpi_operand_object *handler_desc)
+{
+	acpi_cpu_flags lock_flags;
+
+	ACPI_FUNCTION_TRACE_PTR(acpi_ev_flush_space_handler, handler_desc);
+
+	if (handler_desc && handler_desc->address_space.invocation_count >= 0) {
+		lock_flags =
+		    acpi_os_acquire_lock(acpi_gbl_reference_count_lock);
+		handler_desc->address_space.invocation_count |= ACPI_INT16_MIN;
+		acpi_os_release_lock(acpi_gbl_reference_count_lock, lock_flags);
+	}
+
+	return_VOID;
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION:    acpi_ev_get_space_handler
+ *
+ * PARAMETERS:  handler_desc     - Address space object
+ *                                 (ACPI_TYPE_LOCAL_ADDRESS_HANDLER)
+ *
+ * RETURN:      Status.
+ *
+ * DESCRIPTION: Acquire an reference of the given address space handler object.
+ *
+ ******************************************************************************/
+
+acpi_status acpi_ev_get_space_handler(union acpi_operand_object *handler_desc)
+{
+	acpi_cpu_flags lock_flags;
+
+	ACPI_FUNCTION_TRACE_PTR(acpi_ev_get_space_handler, handler_desc);
+
+	if (handler_desc && handler_desc->address_space.invocation_count >= 0) {
+		lock_flags =
+		    acpi_os_acquire_lock(acpi_gbl_reference_count_lock);
+		handler_desc->address_space.invocation_count++;
+		acpi_os_release_lock(acpi_gbl_reference_count_lock, lock_flags);
+		return_ACPI_STATUS(AE_OK);
+	}
+
+	return_ACPI_STATUS(AE_ERROR);
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION:    acpi_ev_put_space_handler
+ *
+ * PARAMETERS:  handler_desc     - Address space object
+ *                                 (ACPI_TYPE_LOCAL_ADDRESS_HANDLER)
+ *
+ * RETURN:      None.
+ *
+ * DESCRIPTION: Release an reference of the given address space handler object.
+ *
+ ******************************************************************************/
+
+void acpi_ev_put_space_handler(union acpi_operand_object *handler_desc)
+{
+	acpi_cpu_flags lock_flags;
+
+	ACPI_FUNCTION_TRACE_PTR(acpi_ev_put_space_handler, handler_desc);
+
+	if (handler_desc) {
+		lock_flags =
+		    acpi_os_acquire_lock(acpi_gbl_reference_count_lock);
+		handler_desc->address_space.invocation_count--;
+		acpi_os_release_lock(acpi_gbl_reference_count_lock, lock_flags);
+	}
+
+	return_VOID;
+}
+
+/*******************************************************************************
+ *
+ * FUNCTION:    acpi_ev_space_handler_count
+ *
+ * PARAMETERS:  handler_desc     - Address space object
+ *                                 (ACPI_TYPE_LOCAL_ADDRESS_HANDLER)
+ *
+ * RETURN:      Invocation count of the handler.
+ *
+ * DESCRIPTION: Get the reference of the given address space handler object.
+ *
+ ******************************************************************************/
+
+s16 acpi_ev_space_handler_count(union acpi_operand_object *handler_desc)
+{
+	s16 count = 0;
+	acpi_cpu_flags lock_flags;
+
+	if (handler_desc) {
+		lock_flags =
+		    acpi_os_acquire_lock(acpi_gbl_reference_count_lock);
+		count = handler_desc->address_space.invocation_count;
+		acpi_os_release_lock(acpi_gbl_reference_count_lock, lock_flags);
+	}
+
+	return (count);
+}
diff --git a/drivers/acpi/acpica/evregion.c b/drivers/acpi/acpica/evregion.c
index 8eb8575e8c16..dcdd779257d0 100644
--- a/drivers/acpi/acpica/evregion.c
+++ b/drivers/acpi/acpica/evregion.c
@@ -165,6 +165,10 @@ acpi_ev_address_space_dispatch(union acpi_operand_object *region_obj,
 		return_ACPI_STATUS(AE_NOT_EXIST);
 	}
 
+	status = acpi_ev_get_space_handler(handler_desc);
+	if (ACPI_FAILURE(status)) {
+		return_ACPI_STATUS(AE_NOT_EXIST);
+	}
 	context = handler_desc->address_space.context;
 
 	/*
@@ -185,7 +189,8 @@ acpi_ev_address_space_dispatch(union acpi_operand_object *region_obj,
 				    region_obj,
 				    acpi_ut_get_region_name(region_obj->region.
 							    space_id)));
-			return_ACPI_STATUS(AE_NOT_EXIST);
+			status = AE_NOT_EXIST;
+			goto error_exit;
 		}
 
 		/*
@@ -210,7 +215,7 @@ acpi_ev_address_space_dispatch(union acpi_operand_object *region_obj,
 					acpi_ut_get_region_name(region_obj->
 								region.
 								space_id)));
-			return_ACPI_STATUS(status);
+			goto error_exit;
 		}
 
 		/* Region initialization may have been completed by region_setup */
@@ -306,6 +311,8 @@ acpi_ev_address_space_dispatch(union acpi_operand_object *region_obj,
 		acpi_ex_enter_interpreter();
 	}
 
+error_exit:
+	acpi_ev_put_space_handler(handler_desc);
 	return_ACPI_STATUS(status);
 }
 
diff --git a/drivers/acpi/acpica/evxfregn.c b/drivers/acpi/acpica/evxfregn.c
index 2d6f187939c7..c8c8538aae40 100644
--- a/drivers/acpi/acpica/evxfregn.c
+++ b/drivers/acpi/acpica/evxfregn.c
@@ -266,6 +266,15 @@ acpi_remove_address_space_handler(acpi_handle device,
 
 			*last_obj_ptr = handler_obj->address_space.next;
 
+			/* Wait for handlers to exit */
+
+			acpi_ev_flush_space_handler(handler_obj);
+			while (acpi_ev_space_handler_count(handler_obj) != ACPI_INT16_MIN) {
+				(void)acpi_ut_release_mutex(ACPI_MTX_NAMESPACE);
+				acpi_os_sleep((u64)10);
+				(void)acpi_ut_acquire_mutex(ACPI_MTX_NAMESPACE);
+			}
+
 			/* Now we can delete the handler object */
 
 			acpi_ut_remove_reference(handler_obj);
diff --git a/include/acpi/actypes.h b/include/acpi/actypes.h
index 7000e66f768e..a341a9a8157f 100644
--- a/include/acpi/actypes.h
+++ b/include/acpi/actypes.h
@@ -65,6 +65,8 @@
 #define ACPI_UINT16_MAX                 (u16)(~((u16) 0))	/* 0xFFFF             */
 #define ACPI_UINT32_MAX                 (u32)(~((u32) 0))	/* 0xFFFFFFFF         */
 #define ACPI_UINT64_MAX                 (u64)(~((u64) 0))	/* 0xFFFFFFFFFFFFFFFF */
+#define ACPI_INT16_MAX                  ((s16)(ACPI_UINT16_MAX>>1))
+#define ACPI_INT16_MIN                  ((s16)(-ACPI_INT16_MAX - 1))
 #define ACPI_ASCII_MAX                  0x7F
 
 /*
-- 
1.9.3

^ permalink raw reply related	[flat|nested] 34+ messages in thread

* Re: [PATCH v3 updated 3/3] ACPI / PMIC: AXP288: support virtual GPIO in ACPI table
  2014-11-25 12:01             ` Aaron Lu
@ 2014-11-25 20:34                   ` Rafael J. Wysocki
  0 siblings, 0 replies; 34+ messages in thread
From: Rafael J. Wysocki @ 2014-11-25 20:34 UTC (permalink / raw)
  To: Aaron Lu
  Cc: Lee Jones, Jacob Pan, Yegnesh Iyer,
	linux-acpi-u79uwXL29TY76Z2rM5mHXA,
	linux-iio-u79uwXL29TY76Z2rM5mHXA,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA, Lv Zheng

On Tuesday, November 25, 2014 08:01:38 PM Aaron Lu wrote:
> On 11/24/2014 11:19 PM, Rafael J. Wysocki wrote:
> > On Monday, November 24, 2014 05:32:33 PM Aaron Lu wrote:
> >> On 11/24/2014 09:06 AM, Rafael J. Wysocki wrote:
> >>> On Friday, November 21, 2014 03:11:51 PM Aaron Lu wrote:
> >>>> +	if (!result) {
> >>>> +		status = acpi_install_address_space_handler(
> >>>> +				ACPI_HANDLE(parent), ACPI_ADR_SPACE_GPIO,
> >>>> +				intel_xpower_pmic_gpio_handler, NULL, NULL);
> >>>
> >>> So here we have a problem, because we can't unregister the opregion handler
> >>> registered above if this fails.  Not nice.
> >>
> >> I'm not correct in the cover letter, the actual problem with operation
> >> region remove is with module unload, it happens like this:
> >>
> >> The acpi_remove_address_space_handler doesn't wait for the current
> >> running handler to return, so if we call
> >> acpi_remove_address_space_handler in a module's exit function, the
> >> handler's memory will be freed and the running handler will access to
> >> a no longer valid memory place.
> >>
> >> So as long as we can make sure the handler will not go away from the
> >> memory, we are safe.
> > 
> > This only means that address space handlers cannot be removed from kernel
> > modules.
> 
> If the module can not be unloaded(no module exit call), then we should
> be safe.
> 
> > 
> > Lv was trying to add a wrapper for that some time ago, maybe it's a good
> > idea to revive that?
> 
> Is it this one? If it is, I'll test it and then add the unload
> functionality to the PMIC drivers.

Well, you need to wait for the patch below to be applied to upstream ACPICA
and transfered to Linux from there.

> From: Lv Zheng <lv.zheng-ral2JQCrhuEAvxtiuMwx3w@public.gmane.org>
> Date: Tue, 25 Nov 2014 15:42:44 +0800
> Subject: [PATCH] ACPICA: Events: Add invocation protection for operation region callbacks.
> 
> This patch adds invocation protection around operation region callbacks to
> offer a module safe environment for OSPM provided address space handlers.
> 
> It is likely that OSPM where ModuleDevice is supported will implement
> specific address space handlers in the modules.  Thus the unloading of
> the handlers' owner modules may lead to program crash around the invocation
> if the handler callbacks are invoked without protection.  Since the
> handler callbacks are invoked inside of ACPICA, it is better to implement
> invocation protection inside of ACPICA.
> As address space handlers are expected to be executed in parallel, it is
> not a good choice to implement protection using locks.  This patch uses
> reference counting based protection mechanism.  When OSPM calls
> acpi_remove_address_space_handler(), the function waits until all invocations
> exit to ensure no invocation can happen after the unloading of the modules.
> 
> Note that this might be a workaround and not tested, better solution could
> be implemented to tune the design of the namespace objects. Lv Zheng.
> 
> Signed-off-by: Lv Zheng <lv.zheng-ral2JQCrhuEAvxtiuMwx3w@public.gmane.org>
> ---
>  drivers/acpi/acpica/acevents.h  |   9 ++++
>  drivers/acpi/acpica/acobject.h  |   1 +
>  drivers/acpi/acpica/evhandler.c | 117 ++++++++++++++++++++++++++++++++++++++++
>  drivers/acpi/acpica/evregion.c  |  11 +++-
>  drivers/acpi/acpica/evxfregn.c  |   9 ++++
>  include/acpi/actypes.h          |   2 +
>  6 files changed, 147 insertions(+), 2 deletions(-)
> 
> diff --git a/drivers/acpi/acpica/acevents.h b/drivers/acpi/acpica/acevents.h
> index 7a7811a9fc26..3020ac4ab7a8 100644
> --- a/drivers/acpi/acpica/acevents.h
> +++ b/drivers/acpi/acpica/acevents.h
> @@ -175,6 +175,15 @@ acpi_ev_install_space_handler(struct acpi_namespace_node *node,
>  			      acpi_adr_space_handler handler,
>  			      acpi_adr_space_setup setup, void *context);
>  
> +acpi_status
> +acpi_ev_get_space_handler(union acpi_operand_object *handler_desc);
> +
> +void acpi_ev_put_space_handler(union acpi_operand_object *handler_desc);
> +
> +void acpi_ev_flush_space_handler(union acpi_operand_object *handler_desc);
> +
> +s16 acpi_ev_space_handler_count(union acpi_operand_object *handler_desc);
> +
>  /*
>   * evregion - Operation region support
>   */
> diff --git a/drivers/acpi/acpica/acobject.h b/drivers/acpi/acpica/acobject.h
> index 8abb393dafab..a719d9733e6b 100644
> --- a/drivers/acpi/acpica/acobject.h
> +++ b/drivers/acpi/acpica/acobject.h
> @@ -304,6 +304,7 @@ struct acpi_object_notify_handler {
>  
>  struct acpi_object_addr_handler {
>  	ACPI_OBJECT_COMMON_HEADER u8 space_id;
> +	s16 invocation_count;
>  	u8 handler_flags;
>  	acpi_adr_space_handler handler;
>  	struct acpi_namespace_node *node;	/* Parent device */
> diff --git a/drivers/acpi/acpica/evhandler.c b/drivers/acpi/acpica/evhandler.c
> index 78ac29351c9e..b27cc8e0507f 100644
> --- a/drivers/acpi/acpica/evhandler.c
> +++ b/drivers/acpi/acpica/evhandler.c
> @@ -499,6 +499,7 @@ acpi_ev_install_space_handler(struct acpi_namespace_node * node,
>  	handler_obj->address_space.space_id = (u8)space_id;
>  	handler_obj->address_space.handler_flags = flags;
>  	handler_obj->address_space.region_list = NULL;
> +	handler_obj->address_space.invocation_count = 0;
>  	handler_obj->address_space.node = node;
>  	handler_obj->address_space.handler = handler;
>  	handler_obj->address_space.context = context;
> @@ -534,3 +535,119 @@ acpi_ev_install_space_handler(struct acpi_namespace_node * node,
>  unlock_and_exit:
>  	return_ACPI_STATUS(status);
>  }
> +
> +/*******************************************************************************
> + *
> + * FUNCTION:    acpi_ev_flush_space_handler
> + *
> + * PARAMETERS:  handler_desc     - Address space object
> + *                                 (ACPI_TYPE_LOCAL_ADDRESS_HANDLER)
> + *
> + * RETURN:      None.
> + *
> + * DESCRIPTION: Flush the reference of the given address space handler object.
> + *
> + ******************************************************************************/
> +
> +void acpi_ev_flush_space_handler(union acpi_operand_object *handler_desc)
> +{
> +	acpi_cpu_flags lock_flags;
> +
> +	ACPI_FUNCTION_TRACE_PTR(acpi_ev_flush_space_handler, handler_desc);
> +
> +	if (handler_desc && handler_desc->address_space.invocation_count >= 0) {
> +		lock_flags =
> +		    acpi_os_acquire_lock(acpi_gbl_reference_count_lock);
> +		handler_desc->address_space.invocation_count |= ACPI_INT16_MIN;
> +		acpi_os_release_lock(acpi_gbl_reference_count_lock, lock_flags);
> +	}
> +
> +	return_VOID;
> +}
> +
> +/*******************************************************************************
> + *
> + * FUNCTION:    acpi_ev_get_space_handler
> + *
> + * PARAMETERS:  handler_desc     - Address space object
> + *                                 (ACPI_TYPE_LOCAL_ADDRESS_HANDLER)
> + *
> + * RETURN:      Status.
> + *
> + * DESCRIPTION: Acquire an reference of the given address space handler object.
> + *
> + ******************************************************************************/
> +
> +acpi_status acpi_ev_get_space_handler(union acpi_operand_object *handler_desc)
> +{
> +	acpi_cpu_flags lock_flags;
> +
> +	ACPI_FUNCTION_TRACE_PTR(acpi_ev_get_space_handler, handler_desc);
> +
> +	if (handler_desc && handler_desc->address_space.invocation_count >= 0) {
> +		lock_flags =
> +		    acpi_os_acquire_lock(acpi_gbl_reference_count_lock);
> +		handler_desc->address_space.invocation_count++;
> +		acpi_os_release_lock(acpi_gbl_reference_count_lock, lock_flags);
> +		return_ACPI_STATUS(AE_OK);
> +	}
> +
> +	return_ACPI_STATUS(AE_ERROR);
> +}
> +
> +/*******************************************************************************
> + *
> + * FUNCTION:    acpi_ev_put_space_handler
> + *
> + * PARAMETERS:  handler_desc     - Address space object
> + *                                 (ACPI_TYPE_LOCAL_ADDRESS_HANDLER)
> + *
> + * RETURN:      None.
> + *
> + * DESCRIPTION: Release an reference of the given address space handler object.
> + *
> + ******************************************************************************/
> +
> +void acpi_ev_put_space_handler(union acpi_operand_object *handler_desc)
> +{
> +	acpi_cpu_flags lock_flags;
> +
> +	ACPI_FUNCTION_TRACE_PTR(acpi_ev_put_space_handler, handler_desc);
> +
> +	if (handler_desc) {
> +		lock_flags =
> +		    acpi_os_acquire_lock(acpi_gbl_reference_count_lock);
> +		handler_desc->address_space.invocation_count--;
> +		acpi_os_release_lock(acpi_gbl_reference_count_lock, lock_flags);
> +	}
> +
> +	return_VOID;
> +}
> +
> +/*******************************************************************************
> + *
> + * FUNCTION:    acpi_ev_space_handler_count
> + *
> + * PARAMETERS:  handler_desc     - Address space object
> + *                                 (ACPI_TYPE_LOCAL_ADDRESS_HANDLER)
> + *
> + * RETURN:      Invocation count of the handler.
> + *
> + * DESCRIPTION: Get the reference of the given address space handler object.
> + *
> + ******************************************************************************/
> +
> +s16 acpi_ev_space_handler_count(union acpi_operand_object *handler_desc)
> +{
> +	s16 count = 0;
> +	acpi_cpu_flags lock_flags;
> +
> +	if (handler_desc) {
> +		lock_flags =
> +		    acpi_os_acquire_lock(acpi_gbl_reference_count_lock);
> +		count = handler_desc->address_space.invocation_count;
> +		acpi_os_release_lock(acpi_gbl_reference_count_lock, lock_flags);
> +	}
> +
> +	return (count);
> +}
> diff --git a/drivers/acpi/acpica/evregion.c b/drivers/acpi/acpica/evregion.c
> index 8eb8575e8c16..dcdd779257d0 100644
> --- a/drivers/acpi/acpica/evregion.c
> +++ b/drivers/acpi/acpica/evregion.c
> @@ -165,6 +165,10 @@ acpi_ev_address_space_dispatch(union acpi_operand_object *region_obj,
>  		return_ACPI_STATUS(AE_NOT_EXIST);
>  	}
>  
> +	status = acpi_ev_get_space_handler(handler_desc);
> +	if (ACPI_FAILURE(status)) {
> +		return_ACPI_STATUS(AE_NOT_EXIST);
> +	}
>  	context = handler_desc->address_space.context;
>  
>  	/*
> @@ -185,7 +189,8 @@ acpi_ev_address_space_dispatch(union acpi_operand_object *region_obj,
>  				    region_obj,
>  				    acpi_ut_get_region_name(region_obj->region.
>  							    space_id)));
> -			return_ACPI_STATUS(AE_NOT_EXIST);
> +			status = AE_NOT_EXIST;
> +			goto error_exit;
>  		}
>  
>  		/*
> @@ -210,7 +215,7 @@ acpi_ev_address_space_dispatch(union acpi_operand_object *region_obj,
>  					acpi_ut_get_region_name(region_obj->
>  								region.
>  								space_id)));
> -			return_ACPI_STATUS(status);
> +			goto error_exit;
>  		}
>  
>  		/* Region initialization may have been completed by region_setup */
> @@ -306,6 +311,8 @@ acpi_ev_address_space_dispatch(union acpi_operand_object *region_obj,
>  		acpi_ex_enter_interpreter();
>  	}
>  
> +error_exit:
> +	acpi_ev_put_space_handler(handler_desc);
>  	return_ACPI_STATUS(status);
>  }
>  
> diff --git a/drivers/acpi/acpica/evxfregn.c b/drivers/acpi/acpica/evxfregn.c
> index 2d6f187939c7..c8c8538aae40 100644
> --- a/drivers/acpi/acpica/evxfregn.c
> +++ b/drivers/acpi/acpica/evxfregn.c
> @@ -266,6 +266,15 @@ acpi_remove_address_space_handler(acpi_handle device,
>  
>  			*last_obj_ptr = handler_obj->address_space.next;
>  
> +			/* Wait for handlers to exit */
> +
> +			acpi_ev_flush_space_handler(handler_obj);
> +			while (acpi_ev_space_handler_count(handler_obj) != ACPI_INT16_MIN) {
> +				(void)acpi_ut_release_mutex(ACPI_MTX_NAMESPACE);
> +				acpi_os_sleep((u64)10);
> +				(void)acpi_ut_acquire_mutex(ACPI_MTX_NAMESPACE);
> +			}
> +
>  			/* Now we can delete the handler object */
>  
>  			acpi_ut_remove_reference(handler_obj);
> diff --git a/include/acpi/actypes.h b/include/acpi/actypes.h
> index 7000e66f768e..a341a9a8157f 100644
> --- a/include/acpi/actypes.h
> +++ b/include/acpi/actypes.h
> @@ -65,6 +65,8 @@
>  #define ACPI_UINT16_MAX                 (u16)(~((u16) 0))	/* 0xFFFF             */
>  #define ACPI_UINT32_MAX                 (u32)(~((u32) 0))	/* 0xFFFFFFFF         */
>  #define ACPI_UINT64_MAX                 (u64)(~((u64) 0))	/* 0xFFFFFFFFFFFFFFFF */
> +#define ACPI_INT16_MAX                  ((s16)(ACPI_UINT16_MAX>>1))
> +#define ACPI_INT16_MIN                  ((s16)(-ACPI_INT16_MAX - 1))
>  #define ACPI_ASCII_MAX                  0x7F
>  
>  /*
> 

-- 
I speak only for myself.
Rafael J. Wysocki, Intel Open Source Technology Center.

^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [PATCH v3 updated 3/3] ACPI / PMIC: AXP288: support virtual GPIO in ACPI table
@ 2014-11-25 20:34                   ` Rafael J. Wysocki
  0 siblings, 0 replies; 34+ messages in thread
From: Rafael J. Wysocki @ 2014-11-25 20:34 UTC (permalink / raw)
  To: Aaron Lu
  Cc: Lee Jones, Jacob Pan, Yegnesh Iyer, linux-acpi, linux-iio,
	linux-kernel, Lv Zheng

On Tuesday, November 25, 2014 08:01:38 PM Aaron Lu wrote:
> On 11/24/2014 11:19 PM, Rafael J. Wysocki wrote:
> > On Monday, November 24, 2014 05:32:33 PM Aaron Lu wrote:
> >> On 11/24/2014 09:06 AM, Rafael J. Wysocki wrote:
> >>> On Friday, November 21, 2014 03:11:51 PM Aaron Lu wrote:
> >>>> +	if (!result) {
> >>>> +		status = acpi_install_address_space_handler(
> >>>> +				ACPI_HANDLE(parent), ACPI_ADR_SPACE_GPIO,
> >>>> +				intel_xpower_pmic_gpio_handler, NULL, NULL);
> >>>
> >>> So here we have a problem, because we can't unregister the opregion handler
> >>> registered above if this fails.  Not nice.
> >>
> >> I'm not correct in the cover letter, the actual problem with operation
> >> region remove is with module unload, it happens like this:
> >>
> >> The acpi_remove_address_space_handler doesn't wait for the current
> >> running handler to return, so if we call
> >> acpi_remove_address_space_handler in a module's exit function, the
> >> handler's memory will be freed and the running handler will access to
> >> a no longer valid memory place.
> >>
> >> So as long as we can make sure the handler will not go away from the
> >> memory, we are safe.
> > 
> > This only means that address space handlers cannot be removed from kernel
> > modules.
> 
> If the module can not be unloaded(no module exit call), then we should
> be safe.
> 
> > 
> > Lv was trying to add a wrapper for that some time ago, maybe it's a good
> > idea to revive that?
> 
> Is it this one? If it is, I'll test it and then add the unload
> functionality to the PMIC drivers.

Well, you need to wait for the patch below to be applied to upstream ACPICA
and transfered to Linux from there.

> From: Lv Zheng <lv.zheng@intel.com>
> Date: Tue, 25 Nov 2014 15:42:44 +0800
> Subject: [PATCH] ACPICA: Events: Add invocation protection for operation region callbacks.
> 
> This patch adds invocation protection around operation region callbacks to
> offer a module safe environment for OSPM provided address space handlers.
> 
> It is likely that OSPM where ModuleDevice is supported will implement
> specific address space handlers in the modules.  Thus the unloading of
> the handlers' owner modules may lead to program crash around the invocation
> if the handler callbacks are invoked without protection.  Since the
> handler callbacks are invoked inside of ACPICA, it is better to implement
> invocation protection inside of ACPICA.
> As address space handlers are expected to be executed in parallel, it is
> not a good choice to implement protection using locks.  This patch uses
> reference counting based protection mechanism.  When OSPM calls
> acpi_remove_address_space_handler(), the function waits until all invocations
> exit to ensure no invocation can happen after the unloading of the modules.
> 
> Note that this might be a workaround and not tested, better solution could
> be implemented to tune the design of the namespace objects. Lv Zheng.
> 
> Signed-off-by: Lv Zheng <lv.zheng@intel.com>
> ---
>  drivers/acpi/acpica/acevents.h  |   9 ++++
>  drivers/acpi/acpica/acobject.h  |   1 +
>  drivers/acpi/acpica/evhandler.c | 117 ++++++++++++++++++++++++++++++++++++++++
>  drivers/acpi/acpica/evregion.c  |  11 +++-
>  drivers/acpi/acpica/evxfregn.c  |   9 ++++
>  include/acpi/actypes.h          |   2 +
>  6 files changed, 147 insertions(+), 2 deletions(-)
> 
> diff --git a/drivers/acpi/acpica/acevents.h b/drivers/acpi/acpica/acevents.h
> index 7a7811a9fc26..3020ac4ab7a8 100644
> --- a/drivers/acpi/acpica/acevents.h
> +++ b/drivers/acpi/acpica/acevents.h
> @@ -175,6 +175,15 @@ acpi_ev_install_space_handler(struct acpi_namespace_node *node,
>  			      acpi_adr_space_handler handler,
>  			      acpi_adr_space_setup setup, void *context);
>  
> +acpi_status
> +acpi_ev_get_space_handler(union acpi_operand_object *handler_desc);
> +
> +void acpi_ev_put_space_handler(union acpi_operand_object *handler_desc);
> +
> +void acpi_ev_flush_space_handler(union acpi_operand_object *handler_desc);
> +
> +s16 acpi_ev_space_handler_count(union acpi_operand_object *handler_desc);
> +
>  /*
>   * evregion - Operation region support
>   */
> diff --git a/drivers/acpi/acpica/acobject.h b/drivers/acpi/acpica/acobject.h
> index 8abb393dafab..a719d9733e6b 100644
> --- a/drivers/acpi/acpica/acobject.h
> +++ b/drivers/acpi/acpica/acobject.h
> @@ -304,6 +304,7 @@ struct acpi_object_notify_handler {
>  
>  struct acpi_object_addr_handler {
>  	ACPI_OBJECT_COMMON_HEADER u8 space_id;
> +	s16 invocation_count;
>  	u8 handler_flags;
>  	acpi_adr_space_handler handler;
>  	struct acpi_namespace_node *node;	/* Parent device */
> diff --git a/drivers/acpi/acpica/evhandler.c b/drivers/acpi/acpica/evhandler.c
> index 78ac29351c9e..b27cc8e0507f 100644
> --- a/drivers/acpi/acpica/evhandler.c
> +++ b/drivers/acpi/acpica/evhandler.c
> @@ -499,6 +499,7 @@ acpi_ev_install_space_handler(struct acpi_namespace_node * node,
>  	handler_obj->address_space.space_id = (u8)space_id;
>  	handler_obj->address_space.handler_flags = flags;
>  	handler_obj->address_space.region_list = NULL;
> +	handler_obj->address_space.invocation_count = 0;
>  	handler_obj->address_space.node = node;
>  	handler_obj->address_space.handler = handler;
>  	handler_obj->address_space.context = context;
> @@ -534,3 +535,119 @@ acpi_ev_install_space_handler(struct acpi_namespace_node * node,
>  unlock_and_exit:
>  	return_ACPI_STATUS(status);
>  }
> +
> +/*******************************************************************************
> + *
> + * FUNCTION:    acpi_ev_flush_space_handler
> + *
> + * PARAMETERS:  handler_desc     - Address space object
> + *                                 (ACPI_TYPE_LOCAL_ADDRESS_HANDLER)
> + *
> + * RETURN:      None.
> + *
> + * DESCRIPTION: Flush the reference of the given address space handler object.
> + *
> + ******************************************************************************/
> +
> +void acpi_ev_flush_space_handler(union acpi_operand_object *handler_desc)
> +{
> +	acpi_cpu_flags lock_flags;
> +
> +	ACPI_FUNCTION_TRACE_PTR(acpi_ev_flush_space_handler, handler_desc);
> +
> +	if (handler_desc && handler_desc->address_space.invocation_count >= 0) {
> +		lock_flags =
> +		    acpi_os_acquire_lock(acpi_gbl_reference_count_lock);
> +		handler_desc->address_space.invocation_count |= ACPI_INT16_MIN;
> +		acpi_os_release_lock(acpi_gbl_reference_count_lock, lock_flags);
> +	}
> +
> +	return_VOID;
> +}
> +
> +/*******************************************************************************
> + *
> + * FUNCTION:    acpi_ev_get_space_handler
> + *
> + * PARAMETERS:  handler_desc     - Address space object
> + *                                 (ACPI_TYPE_LOCAL_ADDRESS_HANDLER)
> + *
> + * RETURN:      Status.
> + *
> + * DESCRIPTION: Acquire an reference of the given address space handler object.
> + *
> + ******************************************************************************/
> +
> +acpi_status acpi_ev_get_space_handler(union acpi_operand_object *handler_desc)
> +{
> +	acpi_cpu_flags lock_flags;
> +
> +	ACPI_FUNCTION_TRACE_PTR(acpi_ev_get_space_handler, handler_desc);
> +
> +	if (handler_desc && handler_desc->address_space.invocation_count >= 0) {
> +		lock_flags =
> +		    acpi_os_acquire_lock(acpi_gbl_reference_count_lock);
> +		handler_desc->address_space.invocation_count++;
> +		acpi_os_release_lock(acpi_gbl_reference_count_lock, lock_flags);
> +		return_ACPI_STATUS(AE_OK);
> +	}
> +
> +	return_ACPI_STATUS(AE_ERROR);
> +}
> +
> +/*******************************************************************************
> + *
> + * FUNCTION:    acpi_ev_put_space_handler
> + *
> + * PARAMETERS:  handler_desc     - Address space object
> + *                                 (ACPI_TYPE_LOCAL_ADDRESS_HANDLER)
> + *
> + * RETURN:      None.
> + *
> + * DESCRIPTION: Release an reference of the given address space handler object.
> + *
> + ******************************************************************************/
> +
> +void acpi_ev_put_space_handler(union acpi_operand_object *handler_desc)
> +{
> +	acpi_cpu_flags lock_flags;
> +
> +	ACPI_FUNCTION_TRACE_PTR(acpi_ev_put_space_handler, handler_desc);
> +
> +	if (handler_desc) {
> +		lock_flags =
> +		    acpi_os_acquire_lock(acpi_gbl_reference_count_lock);
> +		handler_desc->address_space.invocation_count--;
> +		acpi_os_release_lock(acpi_gbl_reference_count_lock, lock_flags);
> +	}
> +
> +	return_VOID;
> +}
> +
> +/*******************************************************************************
> + *
> + * FUNCTION:    acpi_ev_space_handler_count
> + *
> + * PARAMETERS:  handler_desc     - Address space object
> + *                                 (ACPI_TYPE_LOCAL_ADDRESS_HANDLER)
> + *
> + * RETURN:      Invocation count of the handler.
> + *
> + * DESCRIPTION: Get the reference of the given address space handler object.
> + *
> + ******************************************************************************/
> +
> +s16 acpi_ev_space_handler_count(union acpi_operand_object *handler_desc)
> +{
> +	s16 count = 0;
> +	acpi_cpu_flags lock_flags;
> +
> +	if (handler_desc) {
> +		lock_flags =
> +		    acpi_os_acquire_lock(acpi_gbl_reference_count_lock);
> +		count = handler_desc->address_space.invocation_count;
> +		acpi_os_release_lock(acpi_gbl_reference_count_lock, lock_flags);
> +	}
> +
> +	return (count);
> +}
> diff --git a/drivers/acpi/acpica/evregion.c b/drivers/acpi/acpica/evregion.c
> index 8eb8575e8c16..dcdd779257d0 100644
> --- a/drivers/acpi/acpica/evregion.c
> +++ b/drivers/acpi/acpica/evregion.c
> @@ -165,6 +165,10 @@ acpi_ev_address_space_dispatch(union acpi_operand_object *region_obj,
>  		return_ACPI_STATUS(AE_NOT_EXIST);
>  	}
>  
> +	status = acpi_ev_get_space_handler(handler_desc);
> +	if (ACPI_FAILURE(status)) {
> +		return_ACPI_STATUS(AE_NOT_EXIST);
> +	}
>  	context = handler_desc->address_space.context;
>  
>  	/*
> @@ -185,7 +189,8 @@ acpi_ev_address_space_dispatch(union acpi_operand_object *region_obj,
>  				    region_obj,
>  				    acpi_ut_get_region_name(region_obj->region.
>  							    space_id)));
> -			return_ACPI_STATUS(AE_NOT_EXIST);
> +			status = AE_NOT_EXIST;
> +			goto error_exit;
>  		}
>  
>  		/*
> @@ -210,7 +215,7 @@ acpi_ev_address_space_dispatch(union acpi_operand_object *region_obj,
>  					acpi_ut_get_region_name(region_obj->
>  								region.
>  								space_id)));
> -			return_ACPI_STATUS(status);
> +			goto error_exit;
>  		}
>  
>  		/* Region initialization may have been completed by region_setup */
> @@ -306,6 +311,8 @@ acpi_ev_address_space_dispatch(union acpi_operand_object *region_obj,
>  		acpi_ex_enter_interpreter();
>  	}
>  
> +error_exit:
> +	acpi_ev_put_space_handler(handler_desc);
>  	return_ACPI_STATUS(status);
>  }
>  
> diff --git a/drivers/acpi/acpica/evxfregn.c b/drivers/acpi/acpica/evxfregn.c
> index 2d6f187939c7..c8c8538aae40 100644
> --- a/drivers/acpi/acpica/evxfregn.c
> +++ b/drivers/acpi/acpica/evxfregn.c
> @@ -266,6 +266,15 @@ acpi_remove_address_space_handler(acpi_handle device,
>  
>  			*last_obj_ptr = handler_obj->address_space.next;
>  
> +			/* Wait for handlers to exit */
> +
> +			acpi_ev_flush_space_handler(handler_obj);
> +			while (acpi_ev_space_handler_count(handler_obj) != ACPI_INT16_MIN) {
> +				(void)acpi_ut_release_mutex(ACPI_MTX_NAMESPACE);
> +				acpi_os_sleep((u64)10);
> +				(void)acpi_ut_acquire_mutex(ACPI_MTX_NAMESPACE);
> +			}
> +
>  			/* Now we can delete the handler object */
>  
>  			acpi_ut_remove_reference(handler_obj);
> diff --git a/include/acpi/actypes.h b/include/acpi/actypes.h
> index 7000e66f768e..a341a9a8157f 100644
> --- a/include/acpi/actypes.h
> +++ b/include/acpi/actypes.h
> @@ -65,6 +65,8 @@
>  #define ACPI_UINT16_MAX                 (u16)(~((u16) 0))	/* 0xFFFF             */
>  #define ACPI_UINT32_MAX                 (u32)(~((u32) 0))	/* 0xFFFFFFFF         */
>  #define ACPI_UINT64_MAX                 (u64)(~((u64) 0))	/* 0xFFFFFFFFFFFFFFFF */
> +#define ACPI_INT16_MAX                  ((s16)(ACPI_UINT16_MAX>>1))
> +#define ACPI_INT16_MIN                  ((s16)(-ACPI_INT16_MAX - 1))
>  #define ACPI_ASCII_MAX                  0x7F
>  
>  /*
> 

-- 
I speak only for myself.
Rafael J. Wysocki, Intel Open Source Technology Center.

^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [PATCH v3 0/3] Support PMIC operation region for CrystalCove and XPower
  2014-11-25 11:52         ` Aaron Lu
  (?)
@ 2014-11-25 20:41         ` Rafael J. Wysocki
       [not found]           ` <3232241.9QsNPyI1Av-sKB8Sp2ER+y1GS7QM15AGw@public.gmane.org>
  -1 siblings, 1 reply; 34+ messages in thread
From: Rafael J. Wysocki @ 2014-11-25 20:41 UTC (permalink / raw)
  To: Aaron Lu
  Cc: Lee Jones, Jacob Pan, Yegnesh Iyer, linux-acpi, linux-iio, linux-kernel

On Tuesday, November 25, 2014 07:52:31 PM Aaron Lu wrote:
> On 11/25/2014 09:47 AM, Rafael J. Wysocki wrote:
> > On Friday, November 21, 2014 03:11:48 PM Aaron Lu wrote:
> >> v3:
> >> Only some function/variable name changes, no functiona changes:
> >> - Replace the dptf/DPTF word originate from the BIOS ACPI table with more
> >>   meaningful word thermal/THERMAL in all places;
> >> - Eliminate the soc part in various structure and function names to make
> >>   them shorter:
> >>   intel_soc_pmic_opregion -> intel_pmic_opregion
> >>   intel_soc_pmic_pmop_handler -> intel_pmic_pmop_handler
> >>   intel_soc_pmic_install_opregion_handler -> intel_pmic_install_opregion_handler
> >>   etc.
> >>
> >>
> >> v2:
> >> Place PMIC operation files under drivers/acpi/pmic instead of
> >> drivers/acpi/pmic_opregion as suggested by Rafael;
> >> Rename PMIC operation files to make them shorter as suggested by Rafael.
> >>
> >> v1:
> >> On Intel Baytrail-T and Baytrail-T-CR platforms, there are two customized
> >> ACPI operation regions defined for the Power Management Integrated Circuit
> >> device, one is for power resource handling and one is for thermal: sensor
> >> temperature reporting, trip point setting, etc. There are different PMIC
> >> chips used on those platforms and though each has the same two operation
> >> regions and functionality, their implementation is different so every PMIC
> >> will need a driver. But since their functionality is similar, some common
> >> code is abstracted into the intel_soc_pmic_opregion.c.
> >>
> >> The last version is posted here:
> >> https://lkml.org/lkml/2014/9/8/801
> >>
> >> Changes since then:
> >> 1 Move to drivers/acpi as discussed on the above thread;
> >> 2 Added support for XPower AXP288 PMIC operation region support;
> >> 3 Since operation region handler can not be removed(at the moment at least),
> >>   use bool for the two operation region driver configs instead of tristate;
> >>   Another reason to do this is that, with Mika's MFD ACPI support patch, all
> >>   those MFD cell devices created will have the same modalias as their parent's
> >>   so it doesn't make much sense to compile these drivers into modules.
> >>
> >> Patch 1 applies on top of Rafael's pm-next branch, and then patch 2 and
> >> patch 3 needs merge of Lee's mfd/ib-mfd-iio-3.19 branch where the PMIC
> >> driver XPower AXP288 and iio driver axp288_adc is located.
> >>
> >>
> >> Aaron Lu (3):
> >>   ACPI / PMIC: support PMIC operation region for CrystalCove
> >>   ACPI / PMIC: support PMIC operation region for XPower AXP288
> >>   ACPI / PMIC: AXP288: support virtual GPIO in ACPI table
> > 
> > OK
> > 
> > I've pulled the Lee's 'mfd/ib-mfd-iio-3.19' branch and applied your updated
> > three on top of that.  Please check the bleeding-edge branch of linux-pm.git
> > for the result.
> 
> Thanks, and a fix patch is here:
>
> From: Aaron Lu <aaron.lu@intel.com>
> Date: Tue, 25 Nov 2014 14:35:38 +0800
> Subject: [PATCH] ACPI / PMIC: Make it possible to build PMIC driver as a module
> 
> This can solve a problem that when axp288_adc driver is built as a
> module and the PMIC driver is builtin, following error would ocur:

I would prefer that to be sloved by requiring axp288_adc to be built in
if the PMIC stuff is selected.  Otherwise we may need to deal with some
nasty module load ordering dependencies.

>  drivers/built-in.o: In function `intel_xpower_pmic_get_raw_temp':
>  intel_pmic_xpower.c:(.text+0xdfaa7): undefined reference to `iio_channel_get'
>  intel_pmic_xpower.c:(.text+0xdfb24): undefined reference to `iio_read_channel_raw'
>  intel_pmic_xpower.c:(.text+0xdfb4e): undefined reference to `iio_channel_release'
> 
> Also, with the fix commit: 52870786ff5d ("ACPI: Use ACPI companion to
> match only the first physical device"), the MFD cell device will have
> its own platform modalias instead of its parent's ACPI modalias, this
> made it possible for the module to be autoloaded.
> 
> Reported-by: kbuild test robot <fengguang.wu@intel.com>
> Signed-off-by: Aaron Lu <aaron.lu@intel.com>
> ---
>  drivers/acpi/Kconfig                  |  6 +++---
>  drivers/acpi/pmic/intel_pmic_crc.c    | 12 +++++++++++-
>  drivers/acpi/pmic/intel_pmic_xpower.c | 12 +++++++++++-
>  3 files changed, 25 insertions(+), 5 deletions(-)
> 
> diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig
> index 227f0692cbff..f9459ba4ce59 100644
> --- a/drivers/acpi/Kconfig
> +++ b/drivers/acpi/Kconfig
> @@ -394,7 +394,7 @@ config ACPI_EXTLOG
>  	  tracepoint which carries that information to userspace.
>  
>  menuconfig PMIC_OPREGION
> -	bool "PMIC (Power Management Integrated Circuit) operation region support"
> +	tristate "PMIC (Power Management Integrated Circuit) operation region support"
>  	help
>  	  Select this option to enable support for ACPI operation
>  	  region of the PMIC chip. The operation region can be used
> @@ -403,13 +403,13 @@ menuconfig PMIC_OPREGION
>  
>  if PMIC_OPREGION
>  config CRC_PMIC_OPREGION
> -	bool "ACPI operation region support for CrystalCove PMIC"
> +	tristate "ACPI operation region support for CrystalCove PMIC"
>  	depends on INTEL_SOC_PMIC
>  	help
>  	  This config adds ACPI operation region support for CrystalCove PMIC.
>  
>  config XPOWER_PMIC_OPREGION
> -	bool "ACPI operation region support for XPower AXP288 PMIC"
> +	tristate "ACPI operation region support for XPower AXP288 PMIC"
>  	depends on AXP288_ADC
>  	help
>  	  This config adds ACPI operation region support for XPower AXP288 PMIC.
> diff --git a/drivers/acpi/pmic/intel_pmic_crc.c b/drivers/acpi/pmic/intel_pmic_crc.c
> index 8955e5b41195..8a193381b5ee 100644
> --- a/drivers/acpi/pmic/intel_pmic_crc.c
> +++ b/drivers/acpi/pmic/intel_pmic_crc.c
> @@ -194,13 +194,23 @@ static int intel_crc_pmic_opregion_probe(struct platform_device *pdev)
>  			&intel_crc_pmic_opregion_data);
>  }
>  
> +#define DRV_NAME "crystal_cove_region"

This name is just horrible, BTW.

> +
> +static struct platform_device_id crc_opregion_id_table[] = {
> +	{ .name = DRV_NAME },
> +	{},
> +};
> +
>  static struct platform_driver intel_crc_pmic_opregion_driver = {
>  	.probe = intel_crc_pmic_opregion_probe,
> +	.id_table = crc_opregion_id_table,
>  	.driver = {
> -		.name = "crystal_cove_region",
> +		.name = DRV_NAME,
>  	},
>  };
>  
> +MODULE_DEVICE_TABLE(platform, crc_opregion_id_table);
> +
>  static int __init intel_crc_pmic_opregion_driver_init(void)
>  {
>  	return platform_driver_register(&intel_crc_pmic_opregion_driver);
> diff --git a/drivers/acpi/pmic/intel_pmic_xpower.c b/drivers/acpi/pmic/intel_pmic_xpower.c
> index 9ec57ebd81c9..4debcbbd6285 100644
> --- a/drivers/acpi/pmic/intel_pmic_xpower.c
> +++ b/drivers/acpi/pmic/intel_pmic_xpower.c
> @@ -251,13 +251,23 @@ static int intel_xpower_pmic_opregion_probe(struct platform_device *pdev)
>  	return result;
>  }
>  
> +#define DRV_NAME "axp288_opregion"

Same here.

The vast majority of people who will see those names have no idea what an
"opregion" is and "region" alone is just meaningless.

> +
> +static struct platform_device_id xpower_opregion_id_table[] = {
> +	{ .name = DRV_NAME },
> +	{},
> +};
> +
>  static struct platform_driver intel_xpower_pmic_opregion_driver = {
>  	.probe = intel_xpower_pmic_opregion_probe,
> +	.id_table = xpower_opregion_id_table,
>  	.driver = {
> -		.name = "axp288_opregion",
> +		.name = DRV_NAME,
>  	},
>  };
>  
> +MODULE_DEVICE_TABLE(platform, xpower_opregion_id_table);
> +
>  static int __init intel_xpower_pmic_opregion_driver_init(void)
>  {
>  	return platform_driver_register(&intel_xpower_pmic_opregion_driver);
> 

-- 
I speak only for myself.
Rafael J. Wysocki, Intel Open Source Technology Center.

^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [PATCH v3 0/3] Support PMIC operation region for CrystalCove and XPower
  2014-11-25 20:41         ` Rafael J. Wysocki
@ 2014-11-26  2:35               ` Aaron Lu
  0 siblings, 0 replies; 34+ messages in thread
From: Aaron Lu @ 2014-11-26  2:35 UTC (permalink / raw)
  To: Rafael J. Wysocki
  Cc: Lee Jones, Jacob Pan, Yegnesh Iyer,
	linux-acpi-u79uwXL29TY76Z2rM5mHXA,
	linux-iio-u79uwXL29TY76Z2rM5mHXA,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA

On 11/26/2014 04:41 AM, Rafael J. Wysocki wrote:
> On Tuesday, November 25, 2014 07:52:31 PM Aaron Lu wrote:
>> On 11/25/2014 09:47 AM, Rafael J. Wysocki wrote:
>>> On Friday, November 21, 2014 03:11:48 PM Aaron Lu wrote:
>>>> v3:
>>>> Only some function/variable name changes, no functiona changes:
>>>> - Replace the dptf/DPTF word originate from the BIOS ACPI table with more
>>>>   meaningful word thermal/THERMAL in all places;
>>>> - Eliminate the soc part in various structure and function names to make
>>>>   them shorter:
>>>>   intel_soc_pmic_opregion -> intel_pmic_opregion
>>>>   intel_soc_pmic_pmop_handler -> intel_pmic_pmop_handler
>>>>   intel_soc_pmic_install_opregion_handler -> intel_pmic_install_opregion_handler
>>>>   etc.
>>>>
>>>>
>>>> v2:
>>>> Place PMIC operation files under drivers/acpi/pmic instead of
>>>> drivers/acpi/pmic_opregion as suggested by Rafael;
>>>> Rename PMIC operation files to make them shorter as suggested by Rafael.
>>>>
>>>> v1:
>>>> On Intel Baytrail-T and Baytrail-T-CR platforms, there are two customized
>>>> ACPI operation regions defined for the Power Management Integrated Circuit
>>>> device, one is for power resource handling and one is for thermal: sensor
>>>> temperature reporting, trip point setting, etc. There are different PMIC
>>>> chips used on those platforms and though each has the same two operation
>>>> regions and functionality, their implementation is different so every PMIC
>>>> will need a driver. But since their functionality is similar, some common
>>>> code is abstracted into the intel_soc_pmic_opregion.c.
>>>>
>>>> The last version is posted here:
>>>> https://lkml.org/lkml/2014/9/8/801
>>>>
>>>> Changes since then:
>>>> 1 Move to drivers/acpi as discussed on the above thread;
>>>> 2 Added support for XPower AXP288 PMIC operation region support;
>>>> 3 Since operation region handler can not be removed(at the moment at least),
>>>>   use bool for the two operation region driver configs instead of tristate;
>>>>   Another reason to do this is that, with Mika's MFD ACPI support patch, all
>>>>   those MFD cell devices created will have the same modalias as their parent's
>>>>   so it doesn't make much sense to compile these drivers into modules.
>>>>
>>>> Patch 1 applies on top of Rafael's pm-next branch, and then patch 2 and
>>>> patch 3 needs merge of Lee's mfd/ib-mfd-iio-3.19 branch where the PMIC
>>>> driver XPower AXP288 and iio driver axp288_adc is located.
>>>>
>>>>
>>>> Aaron Lu (3):
>>>>   ACPI / PMIC: support PMIC operation region for CrystalCove
>>>>   ACPI / PMIC: support PMIC operation region for XPower AXP288
>>>>   ACPI / PMIC: AXP288: support virtual GPIO in ACPI table
>>>
>>> OK
>>>
>>> I've pulled the Lee's 'mfd/ib-mfd-iio-3.19' branch and applied your updated
>>> three on top of that.  Please check the bleeding-edge branch of linux-pm.git
>>> for the result.
>>
>> Thanks, and a fix patch is here:
>>
>> From: Aaron Lu <aaron.lu-ral2JQCrhuEAvxtiuMwx3w@public.gmane.org>
>> Date: Tue, 25 Nov 2014 14:35:38 +0800
>> Subject: [PATCH] ACPI / PMIC: Make it possible to build PMIC driver as a module
>>
>> This can solve a problem that when axp288_adc driver is built as a
>> module and the PMIC driver is builtin, following error would ocur:
> 
> I would prefer that to be sloved by requiring axp288_adc to be built in
> if the PMIC stuff is selected.  Otherwise we may need to deal with some
> nasty module load ordering dependencies.

Good point, and I saw you have solved this problem in the bleeding-edge
branch, thanks!

> 
>>  drivers/built-in.o: In function `intel_xpower_pmic_get_raw_temp':
>>  intel_pmic_xpower.c:(.text+0xdfaa7): undefined reference to `iio_channel_get'
>>  intel_pmic_xpower.c:(.text+0xdfb24): undefined reference to `iio_read_channel_raw'
>>  intel_pmic_xpower.c:(.text+0xdfb4e): undefined reference to `iio_channel_release'
>>
>> Also, with the fix commit: 52870786ff5d ("ACPI: Use ACPI companion to
>> match only the first physical device"), the MFD cell device will have
>> its own platform modalias instead of its parent's ACPI modalias, this
>> made it possible for the module to be autoloaded.
>>
>> Reported-by: kbuild test robot <fengguang.wu-ral2JQCrhuEAvxtiuMwx3w@public.gmane.org>
>> Signed-off-by: Aaron Lu <aaron.lu-ral2JQCrhuEAvxtiuMwx3w@public.gmane.org>
>> ---
>>  drivers/acpi/Kconfig                  |  6 +++---
>>  drivers/acpi/pmic/intel_pmic_crc.c    | 12 +++++++++++-
>>  drivers/acpi/pmic/intel_pmic_xpower.c | 12 +++++++++++-
>>  3 files changed, 25 insertions(+), 5 deletions(-)
>>
>> diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig
>> index 227f0692cbff..f9459ba4ce59 100644
>> --- a/drivers/acpi/Kconfig
>> +++ b/drivers/acpi/Kconfig
>> @@ -394,7 +394,7 @@ config ACPI_EXTLOG
>>  	  tracepoint which carries that information to userspace.
>>  
>>  menuconfig PMIC_OPREGION
>> -	bool "PMIC (Power Management Integrated Circuit) operation region support"
>> +	tristate "PMIC (Power Management Integrated Circuit) operation region support"
>>  	help
>>  	  Select this option to enable support for ACPI operation
>>  	  region of the PMIC chip. The operation region can be used
>> @@ -403,13 +403,13 @@ menuconfig PMIC_OPREGION
>>  
>>  if PMIC_OPREGION
>>  config CRC_PMIC_OPREGION
>> -	bool "ACPI operation region support for CrystalCove PMIC"
>> +	tristate "ACPI operation region support for CrystalCove PMIC"
>>  	depends on INTEL_SOC_PMIC
>>  	help
>>  	  This config adds ACPI operation region support for CrystalCove PMIC.
>>  
>>  config XPOWER_PMIC_OPREGION
>> -	bool "ACPI operation region support for XPower AXP288 PMIC"
>> +	tristate "ACPI operation region support for XPower AXP288 PMIC"
>>  	depends on AXP288_ADC
>>  	help
>>  	  This config adds ACPI operation region support for XPower AXP288 PMIC.
>> diff --git a/drivers/acpi/pmic/intel_pmic_crc.c b/drivers/acpi/pmic/intel_pmic_crc.c
>> index 8955e5b41195..8a193381b5ee 100644
>> --- a/drivers/acpi/pmic/intel_pmic_crc.c
>> +++ b/drivers/acpi/pmic/intel_pmic_crc.c
>> @@ -194,13 +194,23 @@ static int intel_crc_pmic_opregion_probe(struct platform_device *pdev)
>>  			&intel_crc_pmic_opregion_data);
>>  }
>>  
>> +#define DRV_NAME "crystal_cove_region"
> 
> This name is just horrible, BTW.

Heh. I tried to follow the pattern in CrystalCove's MFD driver, where
all its cell devices are named crystal_cove_XXX, and since the platform
bus' match is done by comparing the device's name and the name field of
the driver's id table entry, they have to be the same. The problem is,
the name field of the platform_device_id structure has a size limit of
20, I can't even put the 'op' before the 'region' there. Perhaps time to
enlarge that size limit?

Thanks,
Aaron

> 
>> +
>> +static struct platform_device_id crc_opregion_id_table[] = {
>> +	{ .name = DRV_NAME },
>> +	{},
>> +};
>> +
>>  static struct platform_driver intel_crc_pmic_opregion_driver = {
>>  	.probe = intel_crc_pmic_opregion_probe,
>> +	.id_table = crc_opregion_id_table,
>>  	.driver = {
>> -		.name = "crystal_cove_region",
>> +		.name = DRV_NAME,
>>  	},
>>  };
>>  
>> +MODULE_DEVICE_TABLE(platform, crc_opregion_id_table);
>> +
>>  static int __init intel_crc_pmic_opregion_driver_init(void)
>>  {
>>  	return platform_driver_register(&intel_crc_pmic_opregion_driver);
>> diff --git a/drivers/acpi/pmic/intel_pmic_xpower.c b/drivers/acpi/pmic/intel_pmic_xpower.c
>> index 9ec57ebd81c9..4debcbbd6285 100644
>> --- a/drivers/acpi/pmic/intel_pmic_xpower.c
>> +++ b/drivers/acpi/pmic/intel_pmic_xpower.c
>> @@ -251,13 +251,23 @@ static int intel_xpower_pmic_opregion_probe(struct platform_device *pdev)
>>  	return result;
>>  }
>>  
>> +#define DRV_NAME "axp288_opregion"
> 
> Same here.
> 
> The vast majority of people who will see those names have no idea what an
> "opregion" is and "region" alone is just meaningless.
> 
>> +
>> +static struct platform_device_id xpower_opregion_id_table[] = {
>> +	{ .name = DRV_NAME },
>> +	{},
>> +};
>> +
>>  static struct platform_driver intel_xpower_pmic_opregion_driver = {
>>  	.probe = intel_xpower_pmic_opregion_probe,
>> +	.id_table = xpower_opregion_id_table,
>>  	.driver = {
>> -		.name = "axp288_opregion",
>> +		.name = DRV_NAME,
>>  	},
>>  };
>>  
>> +MODULE_DEVICE_TABLE(platform, xpower_opregion_id_table);
>> +
>>  static int __init intel_xpower_pmic_opregion_driver_init(void)
>>  {
>>  	return platform_driver_register(&intel_xpower_pmic_opregion_driver);
>>
> 

^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [PATCH v3 0/3] Support PMIC operation region for CrystalCove and XPower
@ 2014-11-26  2:35               ` Aaron Lu
  0 siblings, 0 replies; 34+ messages in thread
From: Aaron Lu @ 2014-11-26  2:35 UTC (permalink / raw)
  To: Rafael J. Wysocki
  Cc: Lee Jones, Jacob Pan, Yegnesh Iyer, linux-acpi, linux-iio, linux-kernel

On 11/26/2014 04:41 AM, Rafael J. Wysocki wrote:
> On Tuesday, November 25, 2014 07:52:31 PM Aaron Lu wrote:
>> On 11/25/2014 09:47 AM, Rafael J. Wysocki wrote:
>>> On Friday, November 21, 2014 03:11:48 PM Aaron Lu wrote:
>>>> v3:
>>>> Only some function/variable name changes, no functiona changes:
>>>> - Replace the dptf/DPTF word originate from the BIOS ACPI table with more
>>>>   meaningful word thermal/THERMAL in all places;
>>>> - Eliminate the soc part in various structure and function names to make
>>>>   them shorter:
>>>>   intel_soc_pmic_opregion -> intel_pmic_opregion
>>>>   intel_soc_pmic_pmop_handler -> intel_pmic_pmop_handler
>>>>   intel_soc_pmic_install_opregion_handler -> intel_pmic_install_opregion_handler
>>>>   etc.
>>>>
>>>>
>>>> v2:
>>>> Place PMIC operation files under drivers/acpi/pmic instead of
>>>> drivers/acpi/pmic_opregion as suggested by Rafael;
>>>> Rename PMIC operation files to make them shorter as suggested by Rafael.
>>>>
>>>> v1:
>>>> On Intel Baytrail-T and Baytrail-T-CR platforms, there are two customized
>>>> ACPI operation regions defined for the Power Management Integrated Circuit
>>>> device, one is for power resource handling and one is for thermal: sensor
>>>> temperature reporting, trip point setting, etc. There are different PMIC
>>>> chips used on those platforms and though each has the same two operation
>>>> regions and functionality, their implementation is different so every PMIC
>>>> will need a driver. But since their functionality is similar, some common
>>>> code is abstracted into the intel_soc_pmic_opregion.c.
>>>>
>>>> The last version is posted here:
>>>> https://lkml.org/lkml/2014/9/8/801
>>>>
>>>> Changes since then:
>>>> 1 Move to drivers/acpi as discussed on the above thread;
>>>> 2 Added support for XPower AXP288 PMIC operation region support;
>>>> 3 Since operation region handler can not be removed(at the moment at least),
>>>>   use bool for the two operation region driver configs instead of tristate;
>>>>   Another reason to do this is that, with Mika's MFD ACPI support patch, all
>>>>   those MFD cell devices created will have the same modalias as their parent's
>>>>   so it doesn't make much sense to compile these drivers into modules.
>>>>
>>>> Patch 1 applies on top of Rafael's pm-next branch, and then patch 2 and
>>>> patch 3 needs merge of Lee's mfd/ib-mfd-iio-3.19 branch where the PMIC
>>>> driver XPower AXP288 and iio driver axp288_adc is located.
>>>>
>>>>
>>>> Aaron Lu (3):
>>>>   ACPI / PMIC: support PMIC operation region for CrystalCove
>>>>   ACPI / PMIC: support PMIC operation region for XPower AXP288
>>>>   ACPI / PMIC: AXP288: support virtual GPIO in ACPI table
>>>
>>> OK
>>>
>>> I've pulled the Lee's 'mfd/ib-mfd-iio-3.19' branch and applied your updated
>>> three on top of that.  Please check the bleeding-edge branch of linux-pm.git
>>> for the result.
>>
>> Thanks, and a fix patch is here:
>>
>> From: Aaron Lu <aaron.lu@intel.com>
>> Date: Tue, 25 Nov 2014 14:35:38 +0800
>> Subject: [PATCH] ACPI / PMIC: Make it possible to build PMIC driver as a module
>>
>> This can solve a problem that when axp288_adc driver is built as a
>> module and the PMIC driver is builtin, following error would ocur:
> 
> I would prefer that to be sloved by requiring axp288_adc to be built in
> if the PMIC stuff is selected.  Otherwise we may need to deal with some
> nasty module load ordering dependencies.

Good point, and I saw you have solved this problem in the bleeding-edge
branch, thanks!

> 
>>  drivers/built-in.o: In function `intel_xpower_pmic_get_raw_temp':
>>  intel_pmic_xpower.c:(.text+0xdfaa7): undefined reference to `iio_channel_get'
>>  intel_pmic_xpower.c:(.text+0xdfb24): undefined reference to `iio_read_channel_raw'
>>  intel_pmic_xpower.c:(.text+0xdfb4e): undefined reference to `iio_channel_release'
>>
>> Also, with the fix commit: 52870786ff5d ("ACPI: Use ACPI companion to
>> match only the first physical device"), the MFD cell device will have
>> its own platform modalias instead of its parent's ACPI modalias, this
>> made it possible for the module to be autoloaded.
>>
>> Reported-by: kbuild test robot <fengguang.wu@intel.com>
>> Signed-off-by: Aaron Lu <aaron.lu@intel.com>
>> ---
>>  drivers/acpi/Kconfig                  |  6 +++---
>>  drivers/acpi/pmic/intel_pmic_crc.c    | 12 +++++++++++-
>>  drivers/acpi/pmic/intel_pmic_xpower.c | 12 +++++++++++-
>>  3 files changed, 25 insertions(+), 5 deletions(-)
>>
>> diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig
>> index 227f0692cbff..f9459ba4ce59 100644
>> --- a/drivers/acpi/Kconfig
>> +++ b/drivers/acpi/Kconfig
>> @@ -394,7 +394,7 @@ config ACPI_EXTLOG
>>  	  tracepoint which carries that information to userspace.
>>  
>>  menuconfig PMIC_OPREGION
>> -	bool "PMIC (Power Management Integrated Circuit) operation region support"
>> +	tristate "PMIC (Power Management Integrated Circuit) operation region support"
>>  	help
>>  	  Select this option to enable support for ACPI operation
>>  	  region of the PMIC chip. The operation region can be used
>> @@ -403,13 +403,13 @@ menuconfig PMIC_OPREGION
>>  
>>  if PMIC_OPREGION
>>  config CRC_PMIC_OPREGION
>> -	bool "ACPI operation region support for CrystalCove PMIC"
>> +	tristate "ACPI operation region support for CrystalCove PMIC"
>>  	depends on INTEL_SOC_PMIC
>>  	help
>>  	  This config adds ACPI operation region support for CrystalCove PMIC.
>>  
>>  config XPOWER_PMIC_OPREGION
>> -	bool "ACPI operation region support for XPower AXP288 PMIC"
>> +	tristate "ACPI operation region support for XPower AXP288 PMIC"
>>  	depends on AXP288_ADC
>>  	help
>>  	  This config adds ACPI operation region support for XPower AXP288 PMIC.
>> diff --git a/drivers/acpi/pmic/intel_pmic_crc.c b/drivers/acpi/pmic/intel_pmic_crc.c
>> index 8955e5b41195..8a193381b5ee 100644
>> --- a/drivers/acpi/pmic/intel_pmic_crc.c
>> +++ b/drivers/acpi/pmic/intel_pmic_crc.c
>> @@ -194,13 +194,23 @@ static int intel_crc_pmic_opregion_probe(struct platform_device *pdev)
>>  			&intel_crc_pmic_opregion_data);
>>  }
>>  
>> +#define DRV_NAME "crystal_cove_region"
> 
> This name is just horrible, BTW.

Heh. I tried to follow the pattern in CrystalCove's MFD driver, where
all its cell devices are named crystal_cove_XXX, and since the platform
bus' match is done by comparing the device's name and the name field of
the driver's id table entry, they have to be the same. The problem is,
the name field of the platform_device_id structure has a size limit of
20, I can't even put the 'op' before the 'region' there. Perhaps time to
enlarge that size limit?

Thanks,
Aaron

> 
>> +
>> +static struct platform_device_id crc_opregion_id_table[] = {
>> +	{ .name = DRV_NAME },
>> +	{},
>> +};
>> +
>>  static struct platform_driver intel_crc_pmic_opregion_driver = {
>>  	.probe = intel_crc_pmic_opregion_probe,
>> +	.id_table = crc_opregion_id_table,
>>  	.driver = {
>> -		.name = "crystal_cove_region",
>> +		.name = DRV_NAME,
>>  	},
>>  };
>>  
>> +MODULE_DEVICE_TABLE(platform, crc_opregion_id_table);
>> +
>>  static int __init intel_crc_pmic_opregion_driver_init(void)
>>  {
>>  	return platform_driver_register(&intel_crc_pmic_opregion_driver);
>> diff --git a/drivers/acpi/pmic/intel_pmic_xpower.c b/drivers/acpi/pmic/intel_pmic_xpower.c
>> index 9ec57ebd81c9..4debcbbd6285 100644
>> --- a/drivers/acpi/pmic/intel_pmic_xpower.c
>> +++ b/drivers/acpi/pmic/intel_pmic_xpower.c
>> @@ -251,13 +251,23 @@ static int intel_xpower_pmic_opregion_probe(struct platform_device *pdev)
>>  	return result;
>>  }
>>  
>> +#define DRV_NAME "axp288_opregion"
> 
> Same here.
> 
> The vast majority of people who will see those names have no idea what an
> "opregion" is and "region" alone is just meaningless.
> 
>> +
>> +static struct platform_device_id xpower_opregion_id_table[] = {
>> +	{ .name = DRV_NAME },
>> +	{},
>> +};
>> +
>>  static struct platform_driver intel_xpower_pmic_opregion_driver = {
>>  	.probe = intel_xpower_pmic_opregion_probe,
>> +	.id_table = xpower_opregion_id_table,
>>  	.driver = {
>> -		.name = "axp288_opregion",
>> +		.name = DRV_NAME,
>>  	},
>>  };
>>  
>> +MODULE_DEVICE_TABLE(platform, xpower_opregion_id_table);
>> +
>>  static int __init intel_xpower_pmic_opregion_driver_init(void)
>>  {
>>  	return platform_driver_register(&intel_xpower_pmic_opregion_driver);
>>
> 


^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [PATCH v3 0/3] Support PMIC operation region for CrystalCove and XPower
  2014-11-26  2:35               ` Aaron Lu
@ 2014-11-26  2:44                   ` Aaron Lu
  -1 siblings, 0 replies; 34+ messages in thread
From: Aaron Lu @ 2014-11-26  2:44 UTC (permalink / raw)
  To: Rafael J. Wysocki
  Cc: Lee Jones, Jacob Pan, Yegnesh Iyer,
	linux-acpi-u79uwXL29TY76Z2rM5mHXA,
	linux-iio-u79uwXL29TY76Z2rM5mHXA,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA

On 11/26/2014 10:35 AM, Aaron Lu wrote:
> On 11/26/2014 04:41 AM, Rafael J. Wysocki wrote:
>> On Tuesday, November 25, 2014 07:52:31 PM Aaron Lu wrote:
>>> On 11/25/2014 09:47 AM, Rafael J. Wysocki wrote:
>>>> On Friday, November 21, 2014 03:11:48 PM Aaron Lu wrote:
>>>>> v3:
>>>>> Only some function/variable name changes, no functiona changes:
>>>>> - Replace the dptf/DPTF word originate from the BIOS ACPI table with more
>>>>>   meaningful word thermal/THERMAL in all places;
>>>>> - Eliminate the soc part in various structure and function names to make
>>>>>   them shorter:
>>>>>   intel_soc_pmic_opregion -> intel_pmic_opregion
>>>>>   intel_soc_pmic_pmop_handler -> intel_pmic_pmop_handler
>>>>>   intel_soc_pmic_install_opregion_handler -> intel_pmic_install_opregion_handler
>>>>>   etc.
>>>>>
>>>>>
>>>>> v2:
>>>>> Place PMIC operation files under drivers/acpi/pmic instead of
>>>>> drivers/acpi/pmic_opregion as suggested by Rafael;
>>>>> Rename PMIC operation files to make them shorter as suggested by Rafael.
>>>>>
>>>>> v1:
>>>>> On Intel Baytrail-T and Baytrail-T-CR platforms, there are two customized
>>>>> ACPI operation regions defined for the Power Management Integrated Circuit
>>>>> device, one is for power resource handling and one is for thermal: sensor
>>>>> temperature reporting, trip point setting, etc. There are different PMIC
>>>>> chips used on those platforms and though each has the same two operation
>>>>> regions and functionality, their implementation is different so every PMIC
>>>>> will need a driver. But since their functionality is similar, some common
>>>>> code is abstracted into the intel_soc_pmic_opregion.c.
>>>>>
>>>>> The last version is posted here:
>>>>> https://lkml.org/lkml/2014/9/8/801
>>>>>
>>>>> Changes since then:
>>>>> 1 Move to drivers/acpi as discussed on the above thread;
>>>>> 2 Added support for XPower AXP288 PMIC operation region support;
>>>>> 3 Since operation region handler can not be removed(at the moment at least),
>>>>>   use bool for the two operation region driver configs instead of tristate;
>>>>>   Another reason to do this is that, with Mika's MFD ACPI support patch, all
>>>>>   those MFD cell devices created will have the same modalias as their parent's
>>>>>   so it doesn't make much sense to compile these drivers into modules.
>>>>>
>>>>> Patch 1 applies on top of Rafael's pm-next branch, and then patch 2 and
>>>>> patch 3 needs merge of Lee's mfd/ib-mfd-iio-3.19 branch where the PMIC
>>>>> driver XPower AXP288 and iio driver axp288_adc is located.
>>>>>
>>>>>
>>>>> Aaron Lu (3):
>>>>>   ACPI / PMIC: support PMIC operation region for CrystalCove
>>>>>   ACPI / PMIC: support PMIC operation region for XPower AXP288
>>>>>   ACPI / PMIC: AXP288: support virtual GPIO in ACPI table
>>>>
>>>> OK
>>>>
>>>> I've pulled the Lee's 'mfd/ib-mfd-iio-3.19' branch and applied your updated
>>>> three on top of that.  Please check the bleeding-edge branch of linux-pm.git
>>>> for the result.
>>>
>>> Thanks, and a fix patch is here:
>>>
>>> From: Aaron Lu <aaron.lu-ral2JQCrhuEAvxtiuMwx3w@public.gmane.org>
>>> Date: Tue, 25 Nov 2014 14:35:38 +0800
>>> Subject: [PATCH] ACPI / PMIC: Make it possible to build PMIC driver as a module
>>>
>>> This can solve a problem that when axp288_adc driver is built as a
>>> module and the PMIC driver is builtin, following error would ocur:
>>
>> I would prefer that to be sloved by requiring axp288_adc to be built in
>> if the PMIC stuff is selected.  Otherwise we may need to deal with some
>> nasty module load ordering dependencies.
> 
> Good point, and I saw you have solved this problem in the bleeding-edge
> branch, thanks!
> 
>>
>>>  drivers/built-in.o: In function `intel_xpower_pmic_get_raw_temp':
>>>  intel_pmic_xpower.c:(.text+0xdfaa7): undefined reference to `iio_channel_get'
>>>  intel_pmic_xpower.c:(.text+0xdfb24): undefined reference to `iio_read_channel_raw'
>>>  intel_pmic_xpower.c:(.text+0xdfb4e): undefined reference to `iio_channel_release'
>>>
>>> Also, with the fix commit: 52870786ff5d ("ACPI: Use ACPI companion to
>>> match only the first physical device"), the MFD cell device will have
>>> its own platform modalias instead of its parent's ACPI modalias, this
>>> made it possible for the module to be autoloaded.
>>>
>>> Reported-by: kbuild test robot <fengguang.wu-ral2JQCrhuEAvxtiuMwx3w@public.gmane.org>
>>> Signed-off-by: Aaron Lu <aaron.lu-ral2JQCrhuEAvxtiuMwx3w@public.gmane.org>
>>> ---
>>>  drivers/acpi/Kconfig                  |  6 +++---
>>>  drivers/acpi/pmic/intel_pmic_crc.c    | 12 +++++++++++-
>>>  drivers/acpi/pmic/intel_pmic_xpower.c | 12 +++++++++++-
>>>  3 files changed, 25 insertions(+), 5 deletions(-)
>>>
>>> diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig
>>> index 227f0692cbff..f9459ba4ce59 100644
>>> --- a/drivers/acpi/Kconfig
>>> +++ b/drivers/acpi/Kconfig
>>> @@ -394,7 +394,7 @@ config ACPI_EXTLOG
>>>  	  tracepoint which carries that information to userspace.
>>>  
>>>  menuconfig PMIC_OPREGION
>>> -	bool "PMIC (Power Management Integrated Circuit) operation region support"
>>> +	tristate "PMIC (Power Management Integrated Circuit) operation region support"
>>>  	help
>>>  	  Select this option to enable support for ACPI operation
>>>  	  region of the PMIC chip. The operation region can be used
>>> @@ -403,13 +403,13 @@ menuconfig PMIC_OPREGION
>>>  
>>>  if PMIC_OPREGION
>>>  config CRC_PMIC_OPREGION
>>> -	bool "ACPI operation region support for CrystalCove PMIC"
>>> +	tristate "ACPI operation region support for CrystalCove PMIC"
>>>  	depends on INTEL_SOC_PMIC
>>>  	help
>>>  	  This config adds ACPI operation region support for CrystalCove PMIC.
>>>  
>>>  config XPOWER_PMIC_OPREGION
>>> -	bool "ACPI operation region support for XPower AXP288 PMIC"
>>> +	tristate "ACPI operation region support for XPower AXP288 PMIC"
>>>  	depends on AXP288_ADC
>>>  	help
>>>  	  This config adds ACPI operation region support for XPower AXP288 PMIC.
>>> diff --git a/drivers/acpi/pmic/intel_pmic_crc.c b/drivers/acpi/pmic/intel_pmic_crc.c
>>> index 8955e5b41195..8a193381b5ee 100644
>>> --- a/drivers/acpi/pmic/intel_pmic_crc.c
>>> +++ b/drivers/acpi/pmic/intel_pmic_crc.c
>>> @@ -194,13 +194,23 @@ static int intel_crc_pmic_opregion_probe(struct platform_device *pdev)
>>>  			&intel_crc_pmic_opregion_data);
>>>  }
>>>  
>>> +#define DRV_NAME "crystal_cove_region"
>>
>> This name is just horrible, BTW.
> 
> Heh. I tried to follow the pattern in CrystalCove's MFD driver, where
> all its cell devices are named crystal_cove_XXX, and since the platform
> bus' match is done by comparing the device's name and the name field of
> the driver's id table entry, they have to be the same. The problem is,
> the name field of the platform_device_id structure has a size limit of
> 20, I can't even put the 'op' before the 'region' there. Perhaps time to
> enlarge that size limit?

Or we can simply use 'acpi' instead of 'region' or 'opregion' here, to
mean that this cell device is for ACPI purpose. How does this sound?


Thanks,
Aaron

>>
>>> +
>>> +static struct platform_device_id crc_opregion_id_table[] = {
>>> +	{ .name = DRV_NAME },
>>> +	{},
>>> +};
>>> +
>>>  static struct platform_driver intel_crc_pmic_opregion_driver = {
>>>  	.probe = intel_crc_pmic_opregion_probe,
>>> +	.id_table = crc_opregion_id_table,
>>>  	.driver = {
>>> -		.name = "crystal_cove_region",
>>> +		.name = DRV_NAME,
>>>  	},
>>>  };
>>>  
>>> +MODULE_DEVICE_TABLE(platform, crc_opregion_id_table);
>>> +
>>>  static int __init intel_crc_pmic_opregion_driver_init(void)
>>>  {
>>>  	return platform_driver_register(&intel_crc_pmic_opregion_driver);
>>> diff --git a/drivers/acpi/pmic/intel_pmic_xpower.c b/drivers/acpi/pmic/intel_pmic_xpower.c
>>> index 9ec57ebd81c9..4debcbbd6285 100644
>>> --- a/drivers/acpi/pmic/intel_pmic_xpower.c
>>> +++ b/drivers/acpi/pmic/intel_pmic_xpower.c
>>> @@ -251,13 +251,23 @@ static int intel_xpower_pmic_opregion_probe(struct platform_device *pdev)
>>>  	return result;
>>>  }
>>>  
>>> +#define DRV_NAME "axp288_opregion"
>>
>> Same here.
>>
>> The vast majority of people who will see those names have no idea what an
>> "opregion" is and "region" alone is just meaningless.
>>
>>> +
>>> +static struct platform_device_id xpower_opregion_id_table[] = {
>>> +	{ .name = DRV_NAME },
>>> +	{},
>>> +};
>>> +
>>>  static struct platform_driver intel_xpower_pmic_opregion_driver = {
>>>  	.probe = intel_xpower_pmic_opregion_probe,
>>> +	.id_table = xpower_opregion_id_table,
>>>  	.driver = {
>>> -		.name = "axp288_opregion",
>>> +		.name = DRV_NAME,
>>>  	},
>>>  };
>>>  
>>> +MODULE_DEVICE_TABLE(platform, xpower_opregion_id_table);
>>> +
>>>  static int __init intel_xpower_pmic_opregion_driver_init(void)
>>>  {
>>>  	return platform_driver_register(&intel_xpower_pmic_opregion_driver);
>>>
>>
> 

^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [PATCH v3 0/3] Support PMIC operation region for CrystalCove and XPower
@ 2014-11-26  2:44                   ` Aaron Lu
  0 siblings, 0 replies; 34+ messages in thread
From: Aaron Lu @ 2014-11-26  2:44 UTC (permalink / raw)
  To: Rafael J. Wysocki
  Cc: Lee Jones, Jacob Pan, Yegnesh Iyer, linux-acpi, linux-iio, linux-kernel

On 11/26/2014 10:35 AM, Aaron Lu wrote:
> On 11/26/2014 04:41 AM, Rafael J. Wysocki wrote:
>> On Tuesday, November 25, 2014 07:52:31 PM Aaron Lu wrote:
>>> On 11/25/2014 09:47 AM, Rafael J. Wysocki wrote:
>>>> On Friday, November 21, 2014 03:11:48 PM Aaron Lu wrote:
>>>>> v3:
>>>>> Only some function/variable name changes, no functiona changes:
>>>>> - Replace the dptf/DPTF word originate from the BIOS ACPI table with more
>>>>>   meaningful word thermal/THERMAL in all places;
>>>>> - Eliminate the soc part in various structure and function names to make
>>>>>   them shorter:
>>>>>   intel_soc_pmic_opregion -> intel_pmic_opregion
>>>>>   intel_soc_pmic_pmop_handler -> intel_pmic_pmop_handler
>>>>>   intel_soc_pmic_install_opregion_handler -> intel_pmic_install_opregion_handler
>>>>>   etc.
>>>>>
>>>>>
>>>>> v2:
>>>>> Place PMIC operation files under drivers/acpi/pmic instead of
>>>>> drivers/acpi/pmic_opregion as suggested by Rafael;
>>>>> Rename PMIC operation files to make them shorter as suggested by Rafael.
>>>>>
>>>>> v1:
>>>>> On Intel Baytrail-T and Baytrail-T-CR platforms, there are two customized
>>>>> ACPI operation regions defined for the Power Management Integrated Circuit
>>>>> device, one is for power resource handling and one is for thermal: sensor
>>>>> temperature reporting, trip point setting, etc. There are different PMIC
>>>>> chips used on those platforms and though each has the same two operation
>>>>> regions and functionality, their implementation is different so every PMIC
>>>>> will need a driver. But since their functionality is similar, some common
>>>>> code is abstracted into the intel_soc_pmic_opregion.c.
>>>>>
>>>>> The last version is posted here:
>>>>> https://lkml.org/lkml/2014/9/8/801
>>>>>
>>>>> Changes since then:
>>>>> 1 Move to drivers/acpi as discussed on the above thread;
>>>>> 2 Added support for XPower AXP288 PMIC operation region support;
>>>>> 3 Since operation region handler can not be removed(at the moment at least),
>>>>>   use bool for the two operation region driver configs instead of tristate;
>>>>>   Another reason to do this is that, with Mika's MFD ACPI support patch, all
>>>>>   those MFD cell devices created will have the same modalias as their parent's
>>>>>   so it doesn't make much sense to compile these drivers into modules.
>>>>>
>>>>> Patch 1 applies on top of Rafael's pm-next branch, and then patch 2 and
>>>>> patch 3 needs merge of Lee's mfd/ib-mfd-iio-3.19 branch where the PMIC
>>>>> driver XPower AXP288 and iio driver axp288_adc is located.
>>>>>
>>>>>
>>>>> Aaron Lu (3):
>>>>>   ACPI / PMIC: support PMIC operation region for CrystalCove
>>>>>   ACPI / PMIC: support PMIC operation region for XPower AXP288
>>>>>   ACPI / PMIC: AXP288: support virtual GPIO in ACPI table
>>>>
>>>> OK
>>>>
>>>> I've pulled the Lee's 'mfd/ib-mfd-iio-3.19' branch and applied your updated
>>>> three on top of that.  Please check the bleeding-edge branch of linux-pm.git
>>>> for the result.
>>>
>>> Thanks, and a fix patch is here:
>>>
>>> From: Aaron Lu <aaron.lu@intel.com>
>>> Date: Tue, 25 Nov 2014 14:35:38 +0800
>>> Subject: [PATCH] ACPI / PMIC: Make it possible to build PMIC driver as a module
>>>
>>> This can solve a problem that when axp288_adc driver is built as a
>>> module and the PMIC driver is builtin, following error would ocur:
>>
>> I would prefer that to be sloved by requiring axp288_adc to be built in
>> if the PMIC stuff is selected.  Otherwise we may need to deal with some
>> nasty module load ordering dependencies.
> 
> Good point, and I saw you have solved this problem in the bleeding-edge
> branch, thanks!
> 
>>
>>>  drivers/built-in.o: In function `intel_xpower_pmic_get_raw_temp':
>>>  intel_pmic_xpower.c:(.text+0xdfaa7): undefined reference to `iio_channel_get'
>>>  intel_pmic_xpower.c:(.text+0xdfb24): undefined reference to `iio_read_channel_raw'
>>>  intel_pmic_xpower.c:(.text+0xdfb4e): undefined reference to `iio_channel_release'
>>>
>>> Also, with the fix commit: 52870786ff5d ("ACPI: Use ACPI companion to
>>> match only the first physical device"), the MFD cell device will have
>>> its own platform modalias instead of its parent's ACPI modalias, this
>>> made it possible for the module to be autoloaded.
>>>
>>> Reported-by: kbuild test robot <fengguang.wu@intel.com>
>>> Signed-off-by: Aaron Lu <aaron.lu@intel.com>
>>> ---
>>>  drivers/acpi/Kconfig                  |  6 +++---
>>>  drivers/acpi/pmic/intel_pmic_crc.c    | 12 +++++++++++-
>>>  drivers/acpi/pmic/intel_pmic_xpower.c | 12 +++++++++++-
>>>  3 files changed, 25 insertions(+), 5 deletions(-)
>>>
>>> diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig
>>> index 227f0692cbff..f9459ba4ce59 100644
>>> --- a/drivers/acpi/Kconfig
>>> +++ b/drivers/acpi/Kconfig
>>> @@ -394,7 +394,7 @@ config ACPI_EXTLOG
>>>  	  tracepoint which carries that information to userspace.
>>>  
>>>  menuconfig PMIC_OPREGION
>>> -	bool "PMIC (Power Management Integrated Circuit) operation region support"
>>> +	tristate "PMIC (Power Management Integrated Circuit) operation region support"
>>>  	help
>>>  	  Select this option to enable support for ACPI operation
>>>  	  region of the PMIC chip. The operation region can be used
>>> @@ -403,13 +403,13 @@ menuconfig PMIC_OPREGION
>>>  
>>>  if PMIC_OPREGION
>>>  config CRC_PMIC_OPREGION
>>> -	bool "ACPI operation region support for CrystalCove PMIC"
>>> +	tristate "ACPI operation region support for CrystalCove PMIC"
>>>  	depends on INTEL_SOC_PMIC
>>>  	help
>>>  	  This config adds ACPI operation region support for CrystalCove PMIC.
>>>  
>>>  config XPOWER_PMIC_OPREGION
>>> -	bool "ACPI operation region support for XPower AXP288 PMIC"
>>> +	tristate "ACPI operation region support for XPower AXP288 PMIC"
>>>  	depends on AXP288_ADC
>>>  	help
>>>  	  This config adds ACPI operation region support for XPower AXP288 PMIC.
>>> diff --git a/drivers/acpi/pmic/intel_pmic_crc.c b/drivers/acpi/pmic/intel_pmic_crc.c
>>> index 8955e5b41195..8a193381b5ee 100644
>>> --- a/drivers/acpi/pmic/intel_pmic_crc.c
>>> +++ b/drivers/acpi/pmic/intel_pmic_crc.c
>>> @@ -194,13 +194,23 @@ static int intel_crc_pmic_opregion_probe(struct platform_device *pdev)
>>>  			&intel_crc_pmic_opregion_data);
>>>  }
>>>  
>>> +#define DRV_NAME "crystal_cove_region"
>>
>> This name is just horrible, BTW.
> 
> Heh. I tried to follow the pattern in CrystalCove's MFD driver, where
> all its cell devices are named crystal_cove_XXX, and since the platform
> bus' match is done by comparing the device's name and the name field of
> the driver's id table entry, they have to be the same. The problem is,
> the name field of the platform_device_id structure has a size limit of
> 20, I can't even put the 'op' before the 'region' there. Perhaps time to
> enlarge that size limit?

Or we can simply use 'acpi' instead of 'region' or 'opregion' here, to
mean that this cell device is for ACPI purpose. How does this sound?


Thanks,
Aaron

>>
>>> +
>>> +static struct platform_device_id crc_opregion_id_table[] = {
>>> +	{ .name = DRV_NAME },
>>> +	{},
>>> +};
>>> +
>>>  static struct platform_driver intel_crc_pmic_opregion_driver = {
>>>  	.probe = intel_crc_pmic_opregion_probe,
>>> +	.id_table = crc_opregion_id_table,
>>>  	.driver = {
>>> -		.name = "crystal_cove_region",
>>> +		.name = DRV_NAME,
>>>  	},
>>>  };
>>>  
>>> +MODULE_DEVICE_TABLE(platform, crc_opregion_id_table);
>>> +
>>>  static int __init intel_crc_pmic_opregion_driver_init(void)
>>>  {
>>>  	return platform_driver_register(&intel_crc_pmic_opregion_driver);
>>> diff --git a/drivers/acpi/pmic/intel_pmic_xpower.c b/drivers/acpi/pmic/intel_pmic_xpower.c
>>> index 9ec57ebd81c9..4debcbbd6285 100644
>>> --- a/drivers/acpi/pmic/intel_pmic_xpower.c
>>> +++ b/drivers/acpi/pmic/intel_pmic_xpower.c
>>> @@ -251,13 +251,23 @@ static int intel_xpower_pmic_opregion_probe(struct platform_device *pdev)
>>>  	return result;
>>>  }
>>>  
>>> +#define DRV_NAME "axp288_opregion"
>>
>> Same here.
>>
>> The vast majority of people who will see those names have no idea what an
>> "opregion" is and "region" alone is just meaningless.
>>
>>> +
>>> +static struct platform_device_id xpower_opregion_id_table[] = {
>>> +	{ .name = DRV_NAME },
>>> +	{},
>>> +};
>>> +
>>>  static struct platform_driver intel_xpower_pmic_opregion_driver = {
>>>  	.probe = intel_xpower_pmic_opregion_probe,
>>> +	.id_table = xpower_opregion_id_table,
>>>  	.driver = {
>>> -		.name = "axp288_opregion",
>>> +		.name = DRV_NAME,
>>>  	},
>>>  };
>>>  
>>> +MODULE_DEVICE_TABLE(platform, xpower_opregion_id_table);
>>> +
>>>  static int __init intel_xpower_pmic_opregion_driver_init(void)
>>>  {
>>>  	return platform_driver_register(&intel_xpower_pmic_opregion_driver);
>>>
>>
> 


^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [PATCH v3 0/3] Support PMIC operation region for CrystalCove and XPower
  2014-11-26  2:44                   ` Aaron Lu
@ 2014-11-26 23:02                       ` Rafael J. Wysocki
  -1 siblings, 0 replies; 34+ messages in thread
From: Rafael J. Wysocki @ 2014-11-26 23:02 UTC (permalink / raw)
  To: Aaron Lu
  Cc: Lee Jones, Jacob Pan, Yegnesh Iyer,
	linux-acpi-u79uwXL29TY76Z2rM5mHXA,
	linux-iio-u79uwXL29TY76Z2rM5mHXA,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA

On Wednesday, November 26, 2014 10:44:34 AM Aaron Lu wrote:
> On 11/26/2014 10:35 AM, Aaron Lu wrote:
> > On 11/26/2014 04:41 AM, Rafael J. Wysocki wrote:
> >> On Tuesday, November 25, 2014 07:52:31 PM Aaron Lu wrote:
> >>> On 11/25/2014 09:47 AM, Rafael J. Wysocki wrote:
> >>>> On Friday, November 21, 2014 03:11:48 PM Aaron Lu wrote:
> >>>>> v3:
> >>>>> Only some function/variable name changes, no functiona changes:
> >>>>> - Replace the dptf/DPTF word originate from the BIOS ACPI table with more
> >>>>>   meaningful word thermal/THERMAL in all places;
> >>>>> - Eliminate the soc part in various structure and function names to make
> >>>>>   them shorter:
> >>>>>   intel_soc_pmic_opregion -> intel_pmic_opregion
> >>>>>   intel_soc_pmic_pmop_handler -> intel_pmic_pmop_handler
> >>>>>   intel_soc_pmic_install_opregion_handler -> intel_pmic_install_opregion_handler
> >>>>>   etc.
> >>>>>
> >>>>>
> >>>>> v2:
> >>>>> Place PMIC operation files under drivers/acpi/pmic instead of
> >>>>> drivers/acpi/pmic_opregion as suggested by Rafael;
> >>>>> Rename PMIC operation files to make them shorter as suggested by Rafael.
> >>>>>
> >>>>> v1:
> >>>>> On Intel Baytrail-T and Baytrail-T-CR platforms, there are two customized
> >>>>> ACPI operation regions defined for the Power Management Integrated Circuit
> >>>>> device, one is for power resource handling and one is for thermal: sensor
> >>>>> temperature reporting, trip point setting, etc. There are different PMIC
> >>>>> chips used on those platforms and though each has the same two operation
> >>>>> regions and functionality, their implementation is different so every PMIC
> >>>>> will need a driver. But since their functionality is similar, some common
> >>>>> code is abstracted into the intel_soc_pmic_opregion.c.
> >>>>>
> >>>>> The last version is posted here:
> >>>>> https://lkml.org/lkml/2014/9/8/801
> >>>>>
> >>>>> Changes since then:
> >>>>> 1 Move to drivers/acpi as discussed on the above thread;
> >>>>> 2 Added support for XPower AXP288 PMIC operation region support;
> >>>>> 3 Since operation region handler can not be removed(at the moment at least),
> >>>>>   use bool for the two operation region driver configs instead of tristate;
> >>>>>   Another reason to do this is that, with Mika's MFD ACPI support patch, all
> >>>>>   those MFD cell devices created will have the same modalias as their parent's
> >>>>>   so it doesn't make much sense to compile these drivers into modules.
> >>>>>
> >>>>> Patch 1 applies on top of Rafael's pm-next branch, and then patch 2 and
> >>>>> patch 3 needs merge of Lee's mfd/ib-mfd-iio-3.19 branch where the PMIC
> >>>>> driver XPower AXP288 and iio driver axp288_adc is located.
> >>>>>
> >>>>>
> >>>>> Aaron Lu (3):
> >>>>>   ACPI / PMIC: support PMIC operation region for CrystalCove
> >>>>>   ACPI / PMIC: support PMIC operation region for XPower AXP288
> >>>>>   ACPI / PMIC: AXP288: support virtual GPIO in ACPI table
> >>>>
> >>>> OK
> >>>>
> >>>> I've pulled the Lee's 'mfd/ib-mfd-iio-3.19' branch and applied your updated
> >>>> three on top of that.  Please check the bleeding-edge branch of linux-pm.git
> >>>> for the result.
> >>>
> >>> Thanks, and a fix patch is here:
> >>>
> >>> From: Aaron Lu <aaron.lu-ral2JQCrhuEAvxtiuMwx3w@public.gmane.org>
> >>> Date: Tue, 25 Nov 2014 14:35:38 +0800
> >>> Subject: [PATCH] ACPI / PMIC: Make it possible to build PMIC driver as a module
> >>>
> >>> This can solve a problem that when axp288_adc driver is built as a
> >>> module and the PMIC driver is builtin, following error would ocur:
> >>
> >> I would prefer that to be sloved by requiring axp288_adc to be built in
> >> if the PMIC stuff is selected.  Otherwise we may need to deal with some
> >> nasty module load ordering dependencies.
> > 
> > Good point, and I saw you have solved this problem in the bleeding-edge
> > branch, thanks!

No problem, I can fix up things like that occasionally.

> >>
> >>>  drivers/built-in.o: In function `intel_xpower_pmic_get_raw_temp':
> >>>  intel_pmic_xpower.c:(.text+0xdfaa7): undefined reference to `iio_channel_get'
> >>>  intel_pmic_xpower.c:(.text+0xdfb24): undefined reference to `iio_read_channel_raw'
> >>>  intel_pmic_xpower.c:(.text+0xdfb4e): undefined reference to `iio_channel_release'
> >>>
> >>> Also, with the fix commit: 52870786ff5d ("ACPI: Use ACPI companion to
> >>> match only the first physical device"), the MFD cell device will have
> >>> its own platform modalias instead of its parent's ACPI modalias, this
> >>> made it possible for the module to be autoloaded.
> >>>
> >>> Reported-by: kbuild test robot <fengguang.wu-ral2JQCrhuEAvxtiuMwx3w@public.gmane.org>
> >>> Signed-off-by: Aaron Lu <aaron.lu-ral2JQCrhuEAvxtiuMwx3w@public.gmane.org>
> >>> ---
> >>>  drivers/acpi/Kconfig                  |  6 +++---
> >>>  drivers/acpi/pmic/intel_pmic_crc.c    | 12 +++++++++++-
> >>>  drivers/acpi/pmic/intel_pmic_xpower.c | 12 +++++++++++-
> >>>  3 files changed, 25 insertions(+), 5 deletions(-)
> >>>
> >>> diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig
> >>> index 227f0692cbff..f9459ba4ce59 100644
> >>> --- a/drivers/acpi/Kconfig
> >>> +++ b/drivers/acpi/Kconfig
> >>> @@ -394,7 +394,7 @@ config ACPI_EXTLOG
> >>>  	  tracepoint which carries that information to userspace.
> >>>  
> >>>  menuconfig PMIC_OPREGION
> >>> -	bool "PMIC (Power Management Integrated Circuit) operation region support"
> >>> +	tristate "PMIC (Power Management Integrated Circuit) operation region support"
> >>>  	help
> >>>  	  Select this option to enable support for ACPI operation
> >>>  	  region of the PMIC chip. The operation region can be used
> >>> @@ -403,13 +403,13 @@ menuconfig PMIC_OPREGION
> >>>  
> >>>  if PMIC_OPREGION
> >>>  config CRC_PMIC_OPREGION
> >>> -	bool "ACPI operation region support for CrystalCove PMIC"
> >>> +	tristate "ACPI operation region support for CrystalCove PMIC"
> >>>  	depends on INTEL_SOC_PMIC
> >>>  	help
> >>>  	  This config adds ACPI operation region support for CrystalCove PMIC.
> >>>  
> >>>  config XPOWER_PMIC_OPREGION
> >>> -	bool "ACPI operation region support for XPower AXP288 PMIC"
> >>> +	tristate "ACPI operation region support for XPower AXP288 PMIC"
> >>>  	depends on AXP288_ADC
> >>>  	help
> >>>  	  This config adds ACPI operation region support for XPower AXP288 PMIC.
> >>> diff --git a/drivers/acpi/pmic/intel_pmic_crc.c b/drivers/acpi/pmic/intel_pmic_crc.c
> >>> index 8955e5b41195..8a193381b5ee 100644
> >>> --- a/drivers/acpi/pmic/intel_pmic_crc.c
> >>> +++ b/drivers/acpi/pmic/intel_pmic_crc.c
> >>> @@ -194,13 +194,23 @@ static int intel_crc_pmic_opregion_probe(struct platform_device *pdev)
> >>>  			&intel_crc_pmic_opregion_data);
> >>>  }
> >>>  
> >>> +#define DRV_NAME "crystal_cove_region"
> >>
> >> This name is just horrible, BTW.
> > 
> > Heh. I tried to follow the pattern in CrystalCove's MFD driver, where
> > all its cell devices are named crystal_cove_XXX, and since the platform
> > bus' match is done by comparing the device's name and the name field of
> > the driver's id table entry, they have to be the same.

So I called this one "crystal_cove_pmic" and the other one "axp288_pmic_acpi".

> The problem is,
> > the name field of the platform_device_id structure has a size limit of
> > 20, I can't even put the 'op' before the 'region' there. Perhaps time to
> > enlarge that size limit?
> 
> Or we can simply use 'acpi' instead of 'region' or 'opregion' here, to
> mean that this cell device is for ACPI purpose. How does this sound?

Better, but see above.

-- 
I speak only for myself.
Rafael J. Wysocki, Intel Open Source Technology Center.

^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [PATCH v3 0/3] Support PMIC operation region for CrystalCove and XPower
@ 2014-11-26 23:02                       ` Rafael J. Wysocki
  0 siblings, 0 replies; 34+ messages in thread
From: Rafael J. Wysocki @ 2014-11-26 23:02 UTC (permalink / raw)
  To: Aaron Lu
  Cc: Lee Jones, Jacob Pan, Yegnesh Iyer, linux-acpi, linux-iio, linux-kernel

On Wednesday, November 26, 2014 10:44:34 AM Aaron Lu wrote:
> On 11/26/2014 10:35 AM, Aaron Lu wrote:
> > On 11/26/2014 04:41 AM, Rafael J. Wysocki wrote:
> >> On Tuesday, November 25, 2014 07:52:31 PM Aaron Lu wrote:
> >>> On 11/25/2014 09:47 AM, Rafael J. Wysocki wrote:
> >>>> On Friday, November 21, 2014 03:11:48 PM Aaron Lu wrote:
> >>>>> v3:
> >>>>> Only some function/variable name changes, no functiona changes:
> >>>>> - Replace the dptf/DPTF word originate from the BIOS ACPI table with more
> >>>>>   meaningful word thermal/THERMAL in all places;
> >>>>> - Eliminate the soc part in various structure and function names to make
> >>>>>   them shorter:
> >>>>>   intel_soc_pmic_opregion -> intel_pmic_opregion
> >>>>>   intel_soc_pmic_pmop_handler -> intel_pmic_pmop_handler
> >>>>>   intel_soc_pmic_install_opregion_handler -> intel_pmic_install_opregion_handler
> >>>>>   etc.
> >>>>>
> >>>>>
> >>>>> v2:
> >>>>> Place PMIC operation files under drivers/acpi/pmic instead of
> >>>>> drivers/acpi/pmic_opregion as suggested by Rafael;
> >>>>> Rename PMIC operation files to make them shorter as suggested by Rafael.
> >>>>>
> >>>>> v1:
> >>>>> On Intel Baytrail-T and Baytrail-T-CR platforms, there are two customized
> >>>>> ACPI operation regions defined for the Power Management Integrated Circuit
> >>>>> device, one is for power resource handling and one is for thermal: sensor
> >>>>> temperature reporting, trip point setting, etc. There are different PMIC
> >>>>> chips used on those platforms and though each has the same two operation
> >>>>> regions and functionality, their implementation is different so every PMIC
> >>>>> will need a driver. But since their functionality is similar, some common
> >>>>> code is abstracted into the intel_soc_pmic_opregion.c.
> >>>>>
> >>>>> The last version is posted here:
> >>>>> https://lkml.org/lkml/2014/9/8/801
> >>>>>
> >>>>> Changes since then:
> >>>>> 1 Move to drivers/acpi as discussed on the above thread;
> >>>>> 2 Added support for XPower AXP288 PMIC operation region support;
> >>>>> 3 Since operation region handler can not be removed(at the moment at least),
> >>>>>   use bool for the two operation region driver configs instead of tristate;
> >>>>>   Another reason to do this is that, with Mika's MFD ACPI support patch, all
> >>>>>   those MFD cell devices created will have the same modalias as their parent's
> >>>>>   so it doesn't make much sense to compile these drivers into modules.
> >>>>>
> >>>>> Patch 1 applies on top of Rafael's pm-next branch, and then patch 2 and
> >>>>> patch 3 needs merge of Lee's mfd/ib-mfd-iio-3.19 branch where the PMIC
> >>>>> driver XPower AXP288 and iio driver axp288_adc is located.
> >>>>>
> >>>>>
> >>>>> Aaron Lu (3):
> >>>>>   ACPI / PMIC: support PMIC operation region for CrystalCove
> >>>>>   ACPI / PMIC: support PMIC operation region for XPower AXP288
> >>>>>   ACPI / PMIC: AXP288: support virtual GPIO in ACPI table
> >>>>
> >>>> OK
> >>>>
> >>>> I've pulled the Lee's 'mfd/ib-mfd-iio-3.19' branch and applied your updated
> >>>> three on top of that.  Please check the bleeding-edge branch of linux-pm.git
> >>>> for the result.
> >>>
> >>> Thanks, and a fix patch is here:
> >>>
> >>> From: Aaron Lu <aaron.lu@intel.com>
> >>> Date: Tue, 25 Nov 2014 14:35:38 +0800
> >>> Subject: [PATCH] ACPI / PMIC: Make it possible to build PMIC driver as a module
> >>>
> >>> This can solve a problem that when axp288_adc driver is built as a
> >>> module and the PMIC driver is builtin, following error would ocur:
> >>
> >> I would prefer that to be sloved by requiring axp288_adc to be built in
> >> if the PMIC stuff is selected.  Otherwise we may need to deal with some
> >> nasty module load ordering dependencies.
> > 
> > Good point, and I saw you have solved this problem in the bleeding-edge
> > branch, thanks!

No problem, I can fix up things like that occasionally.

> >>
> >>>  drivers/built-in.o: In function `intel_xpower_pmic_get_raw_temp':
> >>>  intel_pmic_xpower.c:(.text+0xdfaa7): undefined reference to `iio_channel_get'
> >>>  intel_pmic_xpower.c:(.text+0xdfb24): undefined reference to `iio_read_channel_raw'
> >>>  intel_pmic_xpower.c:(.text+0xdfb4e): undefined reference to `iio_channel_release'
> >>>
> >>> Also, with the fix commit: 52870786ff5d ("ACPI: Use ACPI companion to
> >>> match only the first physical device"), the MFD cell device will have
> >>> its own platform modalias instead of its parent's ACPI modalias, this
> >>> made it possible for the module to be autoloaded.
> >>>
> >>> Reported-by: kbuild test robot <fengguang.wu@intel.com>
> >>> Signed-off-by: Aaron Lu <aaron.lu@intel.com>
> >>> ---
> >>>  drivers/acpi/Kconfig                  |  6 +++---
> >>>  drivers/acpi/pmic/intel_pmic_crc.c    | 12 +++++++++++-
> >>>  drivers/acpi/pmic/intel_pmic_xpower.c | 12 +++++++++++-
> >>>  3 files changed, 25 insertions(+), 5 deletions(-)
> >>>
> >>> diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig
> >>> index 227f0692cbff..f9459ba4ce59 100644
> >>> --- a/drivers/acpi/Kconfig
> >>> +++ b/drivers/acpi/Kconfig
> >>> @@ -394,7 +394,7 @@ config ACPI_EXTLOG
> >>>  	  tracepoint which carries that information to userspace.
> >>>  
> >>>  menuconfig PMIC_OPREGION
> >>> -	bool "PMIC (Power Management Integrated Circuit) operation region support"
> >>> +	tristate "PMIC (Power Management Integrated Circuit) operation region support"
> >>>  	help
> >>>  	  Select this option to enable support for ACPI operation
> >>>  	  region of the PMIC chip. The operation region can be used
> >>> @@ -403,13 +403,13 @@ menuconfig PMIC_OPREGION
> >>>  
> >>>  if PMIC_OPREGION
> >>>  config CRC_PMIC_OPREGION
> >>> -	bool "ACPI operation region support for CrystalCove PMIC"
> >>> +	tristate "ACPI operation region support for CrystalCove PMIC"
> >>>  	depends on INTEL_SOC_PMIC
> >>>  	help
> >>>  	  This config adds ACPI operation region support for CrystalCove PMIC.
> >>>  
> >>>  config XPOWER_PMIC_OPREGION
> >>> -	bool "ACPI operation region support for XPower AXP288 PMIC"
> >>> +	tristate "ACPI operation region support for XPower AXP288 PMIC"
> >>>  	depends on AXP288_ADC
> >>>  	help
> >>>  	  This config adds ACPI operation region support for XPower AXP288 PMIC.
> >>> diff --git a/drivers/acpi/pmic/intel_pmic_crc.c b/drivers/acpi/pmic/intel_pmic_crc.c
> >>> index 8955e5b41195..8a193381b5ee 100644
> >>> --- a/drivers/acpi/pmic/intel_pmic_crc.c
> >>> +++ b/drivers/acpi/pmic/intel_pmic_crc.c
> >>> @@ -194,13 +194,23 @@ static int intel_crc_pmic_opregion_probe(struct platform_device *pdev)
> >>>  			&intel_crc_pmic_opregion_data);
> >>>  }
> >>>  
> >>> +#define DRV_NAME "crystal_cove_region"
> >>
> >> This name is just horrible, BTW.
> > 
> > Heh. I tried to follow the pattern in CrystalCove's MFD driver, where
> > all its cell devices are named crystal_cove_XXX, and since the platform
> > bus' match is done by comparing the device's name and the name field of
> > the driver's id table entry, they have to be the same.

So I called this one "crystal_cove_pmic" and the other one "axp288_pmic_acpi".

> The problem is,
> > the name field of the platform_device_id structure has a size limit of
> > 20, I can't even put the 'op' before the 'region' there. Perhaps time to
> > enlarge that size limit?
> 
> Or we can simply use 'acpi' instead of 'region' or 'opregion' here, to
> mean that this cell device is for ACPI purpose. How does this sound?

Better, but see above.

-- 
I speak only for myself.
Rafael J. Wysocki, Intel Open Source Technology Center.

^ permalink raw reply	[flat|nested] 34+ messages in thread

end of thread, other threads:[~2014-11-26 23:02 UTC | newest]

Thread overview: 34+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2014-11-21  7:11 [PATCH v3 0/3] Support PMIC operation region for CrystalCove and XPower Aaron Lu
2014-11-21  7:11 ` Aaron Lu
2014-11-21  7:11 ` [PATCH v3 2/3] ACPI / PMIC: support PMIC operation region for XPower AXP288 Aaron Lu
2014-11-24  1:04   ` Rafael J. Wysocki
2014-11-24  9:24     ` [PATCH v3 updated " Aaron Lu
2014-11-21  7:11 ` [PATCH v3 3/3] ACPI / PMIC: AXP288: support virtual GPIO in ACPI table Aaron Lu
2014-11-24  1:06   ` Rafael J. Wysocki
2014-11-24  6:01     ` Aaron Lu
     [not found]     ` <1648486.coeVgHKNdn-sKB8Sp2ER+y1GS7QM15AGw@public.gmane.org>
2014-11-24  9:32       ` [PATCH v3 updated " Aaron Lu
2014-11-24  9:32         ` Aaron Lu
     [not found]         ` <5472FB31.8000408-ral2JQCrhuEAvxtiuMwx3w@public.gmane.org>
2014-11-24 15:19           ` Rafael J. Wysocki
2014-11-24 15:19             ` Rafael J. Wysocki
     [not found]             ` <2204417.orosXAozvY-sKB8Sp2ER+y1GS7QM15AGw@public.gmane.org>
2014-11-25  1:44               ` Zheng, Lv
2014-11-25  1:44                 ` Zheng, Lv
2014-11-25  1:44                 ` Zheng, Lv
2014-11-25 12:01             ` Aaron Lu
     [not found]               ` <54746FA2.9030408-ral2JQCrhuEAvxtiuMwx3w@public.gmane.org>
2014-11-25 20:34                 ` Rafael J. Wysocki
2014-11-25 20:34                   ` Rafael J. Wysocki
     [not found] ` <1416553911-22990-1-git-send-email-aaron.lu-ral2JQCrhuEAvxtiuMwx3w@public.gmane.org>
2014-11-21  7:11   ` [PATCH v3 1/3] ACPI / PMIC: support PMIC operation region for CrystalCove Aaron Lu
2014-11-21  7:11     ` Aaron Lu
2014-11-24  0:59     ` Rafael J. Wysocki
2014-11-24  5:55       ` Aaron Lu
2014-11-24  9:21       ` [PATCH v3 1/3 updated] " Aaron Lu
2014-11-25  1:47   ` [PATCH v3 0/3] Support PMIC operation region for CrystalCove and XPower Rafael J. Wysocki
2014-11-25  1:47     ` Rafael J. Wysocki
     [not found]     ` <1987897.BgdIEqPdlH-sKB8Sp2ER+y1GS7QM15AGw@public.gmane.org>
2014-11-25 11:52       ` Aaron Lu
2014-11-25 11:52         ` Aaron Lu
2014-11-25 20:41         ` Rafael J. Wysocki
     [not found]           ` <3232241.9QsNPyI1Av-sKB8Sp2ER+y1GS7QM15AGw@public.gmane.org>
2014-11-26  2:35             ` Aaron Lu
2014-11-26  2:35               ` Aaron Lu
     [not found]               ` <54753C7E.8030503-ral2JQCrhuEAvxtiuMwx3w@public.gmane.org>
2014-11-26  2:44                 ` Aaron Lu
2014-11-26  2:44                   ` Aaron Lu
     [not found]                   ` <54753E92.2010609-ral2JQCrhuEAvxtiuMwx3w@public.gmane.org>
2014-11-26 23:02                     ` Rafael J. Wysocki
2014-11-26 23:02                       ` Rafael J. Wysocki

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.