linux-pm.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH 00/19] Thermal: ACPI INT340X thermal drivers
@ 2014-09-18  1:53 Zhang Rui
  2014-09-18  1:53 ` [PATCH 01/19] ACPI: introduce ACPI int340x thermal scan handler Zhang Rui
                   ` (18 more replies)
  0 siblings, 19 replies; 23+ messages in thread
From: Zhang Rui @ 2014-09-18  1:53 UTC (permalink / raw)
  To: linux-pm, linux-acpi; +Cc: rjw, lenb, eduardo.valentin, Zhang Rui

Hi,

Newer laptops and tablets that use ACPI may have thermal sensors and other
devices with thermal control capabilities outside the core CPU/SOC, for
thermal safety reasons.
They are exposed for the OS to use via the ACPI INT340x device objects.

This patch set introduces kernel drivers for those INT340X devices and
expose their telemetry and cooling capability to user space via the normal
thermal framework. So that a wide range of applications and GUI widgets
can show this information to the user or use this information for making
decisions. For example, the Intel Thermal Daemon can use this information
to allow the user to select his laptop to run w/ or w/o turning on the fans.

thanks,
rui

----------------------------------------------------------------
Aaron Lu (9):
      ACPI / fan: remove unused macro
      ACPI / fan: remove no need check for device pointer
      ACPI / fan: use acpi_device_xxx_power instead of acpi_bus equivelant
      ACPI / fan: convert to platform driver
      ACPI / Fan: add ACPI 4.0 style fan support
      ACPI / Fan: support INT3404 thermal device
      Thermal: move the KELVIN_TO_MILLICELSIUS macro to thermal.h
      Thermal: introduce INT3402 thermal driver
      Thermal: introduce INT3406 thermal driver

Jacob Pan (2):
      Thermal: int340x_thermal: expose acpi thermal relationship tables
      Thermal: int3400_thermal: use acpi_thermal_rel parsing APIs

Lan Tianyu (1):
      Thermal: introduce int3403 thermal driver

Zhang Rui (7):
      ACPI: introduce ACPI int340x thermal scan handler
      ACPI: make acpi_create_platform_device() an external API
      ACPI: add ACPI_TYPE_LOCAL_REFERENCE support to acpi_extract_package()
      Thermal: introduce int3400 thermal driver
      Thermal: int3400 thermal: add capability to detect supporting UUIDs
      Thermal: int3400 thermal: register to thermal framework
      Thermal: int340x thermal: select ACPI fan driver

 drivers/acpi/Kconfig                               |   2 +-
 drivers/acpi/Makefile                              |   1 +
 drivers/acpi/acpi_platform.c                       |   1 +
 drivers/acpi/device_pm.c                           |   1 +
 drivers/acpi/fan.c                                 | 341 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-----------------------
 drivers/acpi/int340x_thermal.c                     |  51 ++++++++++++++++
 drivers/acpi/internal.h                            |   3 +-
 drivers/acpi/scan.c                                |   1 +
 drivers/acpi/thermal.c                             |  18 +++---
 drivers/acpi/utils.c                               |  28 ++++++++-
 drivers/acpi/video.c                               |  78 +++++++++++++-----------
 drivers/thermal/Kconfig                            |  39 +++++++-----
 drivers/thermal/Makefile                           |   2 +-
 drivers/thermal/int3403_thermal.c                  | 296 ----------------------------------------------------------------------------------------
 drivers/thermal/int340x_thermal/Makefile           |   5 ++
 drivers/thermal/int340x_thermal/acpi_thermal_rel.c | 400 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 drivers/thermal/int340x_thermal/acpi_thermal_rel.h |  84 +++++++++++++++++++++++++
 drivers/thermal/int340x_thermal/int3400_thermal.c  | 271 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 drivers/thermal/int340x_thermal/int3402_thermal.c  | 242 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 drivers/thermal/int340x_thermal/int3403_thermal.c  | 489 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 drivers/thermal/int340x_thermal/int3406_thermal.c  | 230 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 drivers/video/backlight/backlight.c                |  44 +++++++++-----
 include/acpi/acpi_bus.h                            |   1 +
 include/acpi/video.h                               |  20 ++++++
 include/linux/acpi.h                               |   7 +++
 include/linux/backlight.h                          |   2 +
 include/linux/thermal.h                            |   4 ++
 27 files changed, 2208 insertions(+), 453 deletions(-)
 create mode 100644 drivers/acpi/int340x_thermal.c
 delete mode 100644 drivers/thermal/int3403_thermal.c
 create mode 100644 drivers/thermal/int340x_thermal/Makefile
 create mode 100644 drivers/thermal/int340x_thermal/acpi_thermal_rel.c
 create mode 100644 drivers/thermal/int340x_thermal/acpi_thermal_rel.h
 create mode 100644 drivers/thermal/int340x_thermal/int3400_thermal.c
 create mode 100644 drivers/thermal/int340x_thermal/int3402_thermal.c
 create mode 100644 drivers/thermal/int340x_thermal/int3403_thermal.c
 create mode 100644 drivers/thermal/int340x_thermal/int3406_thermal.c

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

* [PATCH 01/19] ACPI: introduce ACPI int340x thermal scan handler
  2014-09-18  1:53 [PATCH 00/19] Thermal: ACPI INT340X thermal drivers Zhang Rui
@ 2014-09-18  1:53 ` Zhang Rui
  2014-09-24 21:58   ` Rafael J. Wysocki
  2014-09-18  1:53 ` [PATCH 02/19] ACPI: make acpi_create_platform_device() an external API Zhang Rui
                   ` (17 subsequent siblings)
  18 siblings, 1 reply; 23+ messages in thread
From: Zhang Rui @ 2014-09-18  1:53 UTC (permalink / raw)
  To: linux-pm, linux-acpi; +Cc: rjw, lenb, eduardo.valentin, Zhang Rui

Newer laptops and tablets that use ACPI may have thermal sensors and
other devices with thermal control capabilities outside the core CPU/SOC,
for thermal safety reasons.
They are exposed for the OS to use via
1) INT3400 ACPI device object as the master.
2) INT3401 ~ INT340B ACPI device objects as the slaves.

This patch introduces a scan handler to enumerate the INT3400
ACPI device object to platform bus, and prevent its slaves
from being enumerated before the controller driver being probed.

Signed-off-by: Zhang Rui <rui.zhang@intel.com>
---
 drivers/acpi/Makefile          |  1 +
 drivers/acpi/int340x_thermal.c | 51 ++++++++++++++++++++++++++++++++++++++++++
 drivers/acpi/internal.h        |  1 +
 drivers/acpi/scan.c            |  1 +
 drivers/thermal/Kconfig        | 17 ++++++++++++++
 5 files changed, 71 insertions(+)
 create mode 100644 drivers/acpi/int340x_thermal.c

diff --git a/drivers/acpi/Makefile b/drivers/acpi/Makefile
index 505d4d7..c3b2fcb 100644
--- a/drivers/acpi/Makefile
+++ b/drivers/acpi/Makefile
@@ -43,6 +43,7 @@ acpi-y				+= pci_root.o pci_link.o pci_irq.o
 acpi-y				+= acpi_lpss.o
 acpi-y				+= acpi_platform.o
 acpi-y				+= acpi_pnp.o
+acpi-y				+= int340x_thermal.o
 acpi-y				+= power.o
 acpi-y				+= event.o
 acpi-y				+= sysfs.o
diff --git a/drivers/acpi/int340x_thermal.c b/drivers/acpi/int340x_thermal.c
new file mode 100644
index 0000000..2103bb6
--- /dev/null
+++ b/drivers/acpi/int340x_thermal.c
@@ -0,0 +1,51 @@
+/*
+ * ACPI support for int340x thermal drivers
+ *
+ * Copyright (C) 2014, Intel Corporation
+ * Authors: Zhang Rui <rui.zhang@intel.com>
+ *
+ * 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.
+ */
+
+#include <linux/acpi.h>
+#include <linux/module.h>
+
+#include "internal.h"
+
+#define DO_ENUMERATION 0x01
+static const struct acpi_device_id int340x_thermal_device_ids[] = {
+	{"INT3400", DO_ENUMERATION },
+	{"INT3401"},
+	{"INT3402"},
+	{"INT3403"},
+	{"INT3404"},
+	{"INT3406"},
+	{"INT3407"},
+	{"INT3408"},
+	{"INT3409"},
+	{"INT340A"},
+	{"INT340B"},
+	{""},
+};
+
+static int int340x_thermal_handler_attach(struct acpi_device *adev,
+					const struct acpi_device_id *id)
+{
+#ifdef CONFIG_INT340X_THERMAL
+	if (id->driver_data == DO_ENUMERATION)
+		acpi_create_platform_device(adev);
+#endif
+	return 1;
+}
+
+static struct acpi_scan_handler int340x_thermal_handler = {
+	.ids = int340x_thermal_device_ids,
+	.attach = int340x_thermal_handler_attach,
+};
+
+void __init acpi_int340x_thermal_init(void)
+{
+	acpi_scan_add_handler(&int340x_thermal_handler);
+}
diff --git a/drivers/acpi/internal.h b/drivers/acpi/internal.h
index 4c5cf77..de47f9f 100644
--- a/drivers/acpi/internal.h
+++ b/drivers/acpi/internal.h
@@ -31,6 +31,7 @@ void acpi_pci_link_init(void);
 void acpi_processor_init(void);
 void acpi_platform_init(void);
 void acpi_pnp_init(void);
+void acpi_int340x_thermal_init(void);
 int acpi_sysfs_init(void);
 void acpi_container_init(void);
 void acpi_memory_hotplug_init(void);
diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c
index 0a817ad..eed9740 100644
--- a/drivers/acpi/scan.c
+++ b/drivers/acpi/scan.c
@@ -2306,6 +2306,7 @@ int __init acpi_scan_init(void)
 	acpi_container_init();
 	acpi_memory_hotplug_init();
 	acpi_pnp_init();
+	acpi_int340x_thermal_init();
 
 	mutex_lock(&acpi_scan_lock);
 	/*
diff --git a/drivers/thermal/Kconfig b/drivers/thermal/Kconfig
index 693208e..2ff7416 100644
--- a/drivers/thermal/Kconfig
+++ b/drivers/thermal/Kconfig
@@ -234,6 +234,23 @@ config INTEL_SOC_DTS_THERMAL
 	  notification methods.The other trip is a critical trip point, which
 	  was set by the driver based on the TJ MAX temperature.
 
+config INT340X_THERMAL
+	bool
+	depends on X86 && ACPI
+	help
+	  Newer laptops and tablets that use ACPI may have thermal sensors and
+	  other devices with thermal control capabilities outside the core
+	  CPU/SOC, for thermal safety reasons.
+	  They are exposed for the OS to use via the INT3400 ACPI device object
+	  as the master, and INT3401~INT340B ACPI device objects as the slaves.
+	  Enable this to expose the temperature information and cooling ability
+	  from these objects to userspace via the normal thermal framework.
+	  This means that a wide range of applications and GUI widgets can show
+	  the information to the user or use this information for making
+	  decisions. For example, the Intel Thermal Daemon can use this
+	  information to allow the user to select his laptop to run without
+	  turning on the fans.
+
 menu "Texas Instruments thermal drivers"
 source "drivers/thermal/ti-soc-thermal/Kconfig"
 endmenu
-- 
1.8.3.2


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

* [PATCH 02/19] ACPI: make acpi_create_platform_device() an external API
  2014-09-18  1:53 [PATCH 00/19] Thermal: ACPI INT340X thermal drivers Zhang Rui
  2014-09-18  1:53 ` [PATCH 01/19] ACPI: introduce ACPI int340x thermal scan handler Zhang Rui
@ 2014-09-18  1:53 ` Zhang Rui
  2014-09-24 21:55   ` Rafael J. Wysocki
  2014-09-18  1:53 ` [PATCH 03/19] ACPI: add ACPI_TYPE_LOCAL_REFERENCE support to acpi_extract_package() Zhang Rui
                   ` (16 subsequent siblings)
  18 siblings, 1 reply; 23+ messages in thread
From: Zhang Rui @ 2014-09-18  1:53 UTC (permalink / raw)
  To: linux-pm, linux-acpi; +Cc: rjw, lenb, eduardo.valentin, Zhang Rui

Signed-off-by: Zhang Rui <rui.zhang@intel.com>
---
 drivers/acpi/acpi_platform.c | 1 +
 include/linux/acpi.h         | 7 +++++++
 2 files changed, 8 insertions(+)

diff --git a/drivers/acpi/acpi_platform.c b/drivers/acpi/acpi_platform.c
index 2bf9082..a3c89a1 100644
--- a/drivers/acpi/acpi_platform.c
+++ b/drivers/acpi/acpi_platform.c
@@ -113,3 +113,4 @@ struct platform_device *acpi_create_platform_device(struct acpi_device *adev)
 	kfree(resources);
 	return pdev;
 }
+EXPORT_SYMBOL_GPL(acpi_create_platform_device);
diff --git a/include/linux/acpi.h b/include/linux/acpi.h
index 807cbc4..3dd743d 100644
--- a/include/linux/acpi.h
+++ b/include/linux/acpi.h
@@ -432,6 +432,7 @@ static inline bool acpi_driver_match_device(struct device *dev,
 int acpi_device_uevent_modalias(struct device *, struct kobj_uevent_env *);
 int acpi_device_modalias(struct device *, char *, int);
 
+struct platform_device *acpi_create_platform_device(struct acpi_device *);
 #define ACPI_PTR(_ptr)	(_ptr)
 
 #else	/* !CONFIG_ACPI */
@@ -523,6 +524,12 @@ static inline int acpi_device_modalias(struct device *dev,
 	return -ENODEV;
 }
 
+static inline struct platform_device *acpi_create_platform_device(
+				struct acpi_device *)
+{
+	return NULL;
+}
+
 #define ACPI_PTR(_ptr)	(NULL)
 
 #endif	/* !CONFIG_ACPI */
-- 
1.8.3.2


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

* [PATCH 03/19] ACPI: add ACPI_TYPE_LOCAL_REFERENCE support to acpi_extract_package()
  2014-09-18  1:53 [PATCH 00/19] Thermal: ACPI INT340X thermal drivers Zhang Rui
  2014-09-18  1:53 ` [PATCH 01/19] ACPI: introduce ACPI int340x thermal scan handler Zhang Rui
  2014-09-18  1:53 ` [PATCH 02/19] ACPI: make acpi_create_platform_device() an external API Zhang Rui
@ 2014-09-18  1:53 ` Zhang Rui
  2014-09-24 21:58   ` Rafael J. Wysocki
  2014-09-18  1:53 ` [PATCH 04/19] Thermal: introduce int3400 thermal driver Zhang Rui
                   ` (15 subsequent siblings)
  18 siblings, 1 reply; 23+ messages in thread
From: Zhang Rui @ 2014-09-18  1:53 UTC (permalink / raw)
  To: linux-pm, linux-acpi; +Cc: rjw, lenb, eduardo.valentin, Zhang Rui

Add ACPI_TYPE_LOCAL_REFERENCE support to acpi_extract_package(),
so that we can use this helper for more cases like _ART/_TRT.

Signed-off-by: Zhang Rui <rui.zhang@intel.com>
---
 drivers/acpi/utils.c | 28 +++++++++++++++++++++++++++-
 1 file changed, 27 insertions(+), 1 deletion(-)

diff --git a/drivers/acpi/utils.c b/drivers/acpi/utils.c
index 07c8c5a..1ed7aba 100644
--- a/drivers/acpi/utils.c
+++ b/drivers/acpi/utils.c
@@ -149,6 +149,21 @@ acpi_extract_package(union acpi_object *package,
 				break;
 			}
 			break;
+		case ACPI_TYPE_LOCAL_REFERENCE:
+			switch (format_string[i]) {
+			case 'R':
+				size_required += sizeof(void *);
+				tail_offset += sizeof(void *);
+				break;
+			default:
+				printk(KERN_WARNING PREFIX "Invalid package element"
+					      " [%d] got reference,"
+					      " expecting [%c]\n",
+					      i, format_string[i]);
+				return AE_BAD_DATA;
+				break;
+			}
+			break;
 
 		case ACPI_TYPE_PACKAGE:
 		default:
@@ -247,7 +262,18 @@ acpi_extract_package(union acpi_object *package,
 				break;
 			}
 			break;
-
+		case ACPI_TYPE_LOCAL_REFERENCE:
+			switch (format_string[i]) {
+			case 'R':
+				*(void **)head =
+				    (void *)element->reference.handle;
+				head += sizeof(void *);
+				break;
+			default:
+				/* Should never get here */
+				break;
+			}
+			break;
 		case ACPI_TYPE_PACKAGE:
 			/* TBD: handle nested packages... */
 		default:
-- 
1.8.3.2


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

* [PATCH 04/19] Thermal: introduce int3400 thermal driver
  2014-09-18  1:53 [PATCH 00/19] Thermal: ACPI INT340X thermal drivers Zhang Rui
                   ` (2 preceding siblings ...)
  2014-09-18  1:53 ` [PATCH 03/19] ACPI: add ACPI_TYPE_LOCAL_REFERENCE support to acpi_extract_package() Zhang Rui
@ 2014-09-18  1:53 ` Zhang Rui
  2014-09-18  1:53 ` [PATCH 05/19] Thermal: int3400 thermal: add capability to detect supporting UUIDs Zhang Rui
                   ` (14 subsequent siblings)
  18 siblings, 0 replies; 23+ messages in thread
From: Zhang Rui @ 2014-09-18  1:53 UTC (permalink / raw)
  To: linux-pm, linux-acpi; +Cc: rjw, lenb, eduardo.valentin, Zhang Rui

Introduce int3400 thermal driver. And make INT3400 driver
enumerate the other int340x thermal components shown in _ART/_TRT.

Signed-off-by: Zhang Rui <rui.zhang@intel.com>
---
 drivers/acpi/int340x_thermal.c                    |   2 +-
 drivers/thermal/Kconfig                           |   2 +-
 drivers/thermal/Makefile                          |   1 +
 drivers/thermal/int340x_thermal/Makefile          |   1 +
 drivers/thermal/int340x_thermal/int3400_thermal.c | 245 ++++++++++++++++++++++
 5 files changed, 249 insertions(+), 2 deletions(-)
 create mode 100644 drivers/thermal/int340x_thermal/Makefile
 create mode 100644 drivers/thermal/int340x_thermal/int3400_thermal.c

diff --git a/drivers/acpi/int340x_thermal.c b/drivers/acpi/int340x_thermal.c
index 2103bb6..a27d31d 100644
--- a/drivers/acpi/int340x_thermal.c
+++ b/drivers/acpi/int340x_thermal.c
@@ -33,7 +33,7 @@ static const struct acpi_device_id int340x_thermal_device_ids[] = {
 static int int340x_thermal_handler_attach(struct acpi_device *adev,
 					const struct acpi_device_id *id)
 {
-#ifdef CONFIG_INT340X_THERMAL
+#if defined(CONFIG_INT340X_THERMAL) || defined(CONFIG_INT340X_THERMAL_MODULE)
 	if (id->driver_data == DO_ENUMERATION)
 		acpi_create_platform_device(adev);
 #endif
diff --git a/drivers/thermal/Kconfig b/drivers/thermal/Kconfig
index 2ff7416..6f5a87a 100644
--- a/drivers/thermal/Kconfig
+++ b/drivers/thermal/Kconfig
@@ -235,7 +235,7 @@ config INTEL_SOC_DTS_THERMAL
 	  was set by the driver based on the TJ MAX temperature.
 
 config INT340X_THERMAL
-	bool
+	tristate "ACPI INT340X thermal drivers"
 	depends on X86 && ACPI
 	help
 	  Newer laptops and tablets that use ACPI may have thermal sensors and
diff --git a/drivers/thermal/Makefile b/drivers/thermal/Makefile
index 31e232f..216503e 100644
--- a/drivers/thermal/Makefile
+++ b/drivers/thermal/Makefile
@@ -32,4 +32,5 @@ obj-$(CONFIG_X86_PKG_TEMP_THERMAL)	+= x86_pkg_temp_thermal.o
 obj-$(CONFIG_INTEL_SOC_DTS_THERMAL)	+= intel_soc_dts_thermal.o
 obj-$(CONFIG_TI_SOC_THERMAL)	+= ti-soc-thermal/
 obj-$(CONFIG_ACPI_INT3403_THERMAL)	+= int3403_thermal.o
+obj-$(CONFIG_INT340X_THERMAL)  += int340x_thermal/
 obj-$(CONFIG_ST_THERMAL)	+= st/
diff --git a/drivers/thermal/int340x_thermal/Makefile b/drivers/thermal/int340x_thermal/Makefile
new file mode 100644
index 0000000..e10a53b
--- /dev/null
+++ b/drivers/thermal/int340x_thermal/Makefile
@@ -0,0 +1 @@
+obj-$(CONFIG_INT340X_THERMAL)	+= int3400_thermal.o
diff --git a/drivers/thermal/int340x_thermal/int3400_thermal.c b/drivers/thermal/int340x_thermal/int3400_thermal.c
new file mode 100644
index 0000000..308c185
--- /dev/null
+++ b/drivers/thermal/int340x_thermal/int3400_thermal.c
@@ -0,0 +1,245 @@
+/*
+ * INT3400 thermal driver
+ *
+ * Copyright (C) 2014, Intel Corporation
+ * Authors: Zhang Rui <rui.zhang@intel.com>
+ *
+ * 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.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/acpi.h>
+
+struct art {
+	acpi_handle source;
+	acpi_handle target;
+	u64 weight;
+	u64 ac0_max;
+	u64 ac1_max;
+	u64 ac2_max;
+	u64 ac3_max;
+	u64 ac4_max;
+	u64 ac5_max;
+	u64 ac6_max;
+	u64 ac7_max;
+	u64 ac8_max;
+	u64 ac9_max;
+};
+
+struct trt {
+	acpi_handle source;
+	acpi_handle target;
+	u64 influence;
+	u64 sampling_period;
+	u64 reverved1;
+	u64 reverved2;
+	u64 reverved3;
+	u64 reverved4;
+};
+
+struct int3400_thermal_priv {
+	struct acpi_device *adev;
+	int art_count;
+	struct art *arts;
+	int trt_count;
+	struct trt *trts;
+};
+
+static int parse_art(struct int3400_thermal_priv *priv)
+{
+	acpi_handle handle = priv->adev->handle;
+	acpi_status status;
+	int result = 0;
+	int i;
+	struct acpi_device *adev;
+	union acpi_object *p;
+	struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
+	struct acpi_buffer element = { 0, NULL };
+	struct acpi_buffer art_format = {
+				sizeof("RRNNNNNNNNNNN"), "RRNNNNNNNNNNN" };
+
+	if (!acpi_has_method(handle, "_ART"))
+		return 0;
+
+	status = acpi_evaluate_object(handle, "_ART", NULL, &buffer);
+	if (ACPI_FAILURE(status))
+		return -ENODEV;
+
+	p = buffer.pointer;
+	if (!p || (p->type != ACPI_TYPE_PACKAGE)) {
+		pr_err("Invalid _ART data\n");
+		result = -EFAULT;
+		goto end;
+	}
+
+	/* ignore p->package.elements[0], as this is _ART Revision field */
+	priv->art_count = p->package.count - 1;
+	priv->arts = kzalloc(sizeof(struct art) * priv->art_count, GFP_KERNEL);
+	if (!priv->arts) {
+		result = -ENOMEM;
+		goto end;
+	}
+
+	for (i = 0; i < priv->art_count; i++) {
+		struct art *art = &(priv->arts[i]);
+
+		element.length = sizeof(struct art);
+		element.pointer = art;
+
+		status = acpi_extract_package(&(p->package.elements[i + 1]),
+					      &art_format, &element);
+		if (ACPI_FAILURE(status)) {
+			pr_err("Invalid _ART data");
+			result = -EFAULT;
+			kfree(priv->arts);
+			goto end;
+		}
+		result = acpi_bus_get_device(art->source, &adev);
+		if (!result)
+			acpi_create_platform_device(adev, NULL);
+		else
+			pr_warn("Failed to get source ACPI device\n");
+		result = acpi_bus_get_device(art->target, &adev);
+		if (!result)
+			acpi_create_platform_device(adev, NULL);
+		else
+			pr_warn("Failed to get source ACPI device\n");
+	}
+end:
+	kfree(buffer.pointer);
+	return result;
+}
+
+static int parse_trt(struct int3400_thermal_priv *priv)
+{
+	acpi_handle handle = priv->adev->handle;
+	acpi_status status;
+	int result = 0;
+	int i;
+	struct acpi_device *adev;
+	union acpi_object *p;
+	struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
+	struct acpi_buffer element = { 0, NULL };
+	struct acpi_buffer trt_format = { sizeof("RRNNNNNN"), "RRNNNNNN" };
+
+	if (!acpi_has_method(handle, "_TRT"))
+		return 0;
+
+	status = acpi_evaluate_object(handle, "_TRT", NULL, &buffer);
+	if (ACPI_FAILURE(status))
+		return -ENODEV;
+
+	p = buffer.pointer;
+	if (!p || (p->type != ACPI_TYPE_PACKAGE)) {
+		pr_err("Invalid _TRT data\n");
+		result = -EFAULT;
+		goto end;
+	}
+
+	priv->trt_count = p->package.count;
+	priv->trts = kzalloc(sizeof(struct trt) * priv->trt_count, GFP_KERNEL);
+	if (!priv->trts) {
+		result = -ENOMEM;
+		goto end;
+	}
+
+	for (i = 0; i < priv->trt_count; i++) {
+		struct trt *trt = &(priv->trts[i]);
+
+		element.length = sizeof(struct trt);
+		element.pointer = trt;
+
+		status = acpi_extract_package(&(p->package.elements[i]),
+					      &trt_format, &element);
+		if (ACPI_FAILURE(status)) {
+			pr_err("Invalid _ART data");
+			result = -EFAULT;
+			kfree(priv->trts);
+			goto end;
+		}
+
+		result = acpi_bus_get_device(trt->source, &adev);
+		if (!result)
+			acpi_create_platform_device(adev, NULL);
+		else
+			pr_warn("Failed to get source ACPI device\n");
+		result = acpi_bus_get_device(trt->target, &adev);
+		if (!result)
+			acpi_create_platform_device(adev, NULL);
+		else
+			pr_warn("Failed to get target ACPI device\n");
+	}
+end:
+	kfree(buffer.pointer);
+	return result;
+}
+
+static int int3400_thermal_probe(struct platform_device *pdev)
+{
+	struct acpi_device *adev = ACPI_COMPANION(&pdev->dev);
+	struct int3400_thermal_priv *priv;
+	int result;
+
+	if (!adev)
+		return -ENODEV;
+
+	priv = kzalloc(sizeof(struct int3400_thermal_priv), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+
+	priv->adev = adev;
+
+	result = parse_art(priv);
+	if (result)
+		goto free_priv;
+
+	result = parse_trt(priv);
+	if (result)
+		goto free_art;
+
+	platform_set_drvdata(pdev, priv);
+
+	return 0;
+free_art:
+	kfree(priv->arts);
+free_priv:
+	kfree(priv);
+	return result;
+}
+
+static int int3400_thermal_remove(struct platform_device *pdev)
+{
+	struct int3400_thermal_priv *priv = platform_get_drvdata(pdev);
+
+	kfree(priv->trts);
+	kfree(priv->arts);
+	kfree(priv);
+	return 0;
+}
+
+static const struct acpi_device_id int3400_thermal_match[] = {
+	{"INT3400", 0},
+	{}
+};
+
+MODULE_DEVICE_TABLE(acpi, int3400_thermal_match);
+
+static struct platform_driver int3400_thermal_driver = {
+	.probe = int3400_thermal_probe,
+	.remove = int3400_thermal_remove,
+	.driver = {
+		   .name = "int3400 thermal",
+		   .owner = THIS_MODULE,
+		   .acpi_match_table = ACPI_PTR(int3400_thermal_match),
+		   },
+};
+
+module_platform_driver(int3400_thermal_driver);
+
+MODULE_DESCRIPTION("INT3400 Thermal driver");
+MODULE_AUTHOR("Zhang Rui <rui.zhang@intel.com>");
+MODULE_LICENSE("GPL");
-- 
1.8.3.2


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

* [PATCH 05/19] Thermal: int3400 thermal: add capability to detect supporting UUIDs
  2014-09-18  1:53 [PATCH 00/19] Thermal: ACPI INT340X thermal drivers Zhang Rui
                   ` (3 preceding siblings ...)
  2014-09-18  1:53 ` [PATCH 04/19] Thermal: introduce int3400 thermal driver Zhang Rui
@ 2014-09-18  1:53 ` Zhang Rui
  2014-09-18  1:53 ` [PATCH 06/19] Thermal: int3400 thermal: register to thermal framework Zhang Rui
                   ` (13 subsequent siblings)
  18 siblings, 0 replies; 23+ messages in thread
From: Zhang Rui @ 2014-09-18  1:53 UTC (permalink / raw)
  To: linux-pm, linux-acpi; +Cc: rjw, lenb, eduardo.valentin, Zhang Rui

Signed-off-by: Zhang Rui <rui.zhang@intel.com>
---
 drivers/thermal/int340x_thermal/int3400_thermal.c | 69 +++++++++++++++++++++++
 1 file changed, 69 insertions(+)

diff --git a/drivers/thermal/int340x_thermal/int3400_thermal.c b/drivers/thermal/int340x_thermal/int3400_thermal.c
index 308c185..65c63ba 100644
--- a/drivers/thermal/int340x_thermal/int3400_thermal.c
+++ b/drivers/thermal/int340x_thermal/int3400_thermal.c
@@ -41,14 +41,79 @@ struct trt {
 	u64 reverved4;
 };
 
+enum int3400_thermal_uuid {
+	INT3400_THERMAL_PASSIVE_1,
+	INT3400_THERMAL_PASSIVE_2,
+	INT3400_THERMAL_ACTIVE,
+	INT3400_THERMAL_CRITICAL,
+	INT3400_THERMAL_COOLING_MODE,
+	INT3400_THERMAL_MAXIMUM_UUID,
+};
+
+static u8 *int3400_thermal_uuids[INT3400_THERMAL_MAXIMUM_UUID] = {
+	"42A441D6-AE6A-462b-A84B-4A8CE79027D3",
+	"9E04115A-AE87-4D1C-9500-0F3E340BFE75",
+	"3A95C389-E4B8-4629-A526-C52C88626BAE",
+	"97C68AE7-15FA-499c-B8C9-5DA81D606E0A",
+	"16CAF1B7-DD38-40ed-B1C1-1B8A1913D531",
+};
+
 struct int3400_thermal_priv {
 	struct acpi_device *adev;
 	int art_count;
 	struct art *arts;
 	int trt_count;
 	struct trt *trts;
+	u8 uuid_bitmap;
 };
 
+static int int3400_thermal_get_uuids(struct int3400_thermal_priv *priv)
+{
+	struct acpi_buffer buf = { ACPI_ALLOCATE_BUFFER, NULL};
+	union acpi_object *obja, *objb;
+	int i, j;
+	int result = 0;
+	acpi_status status;
+
+	status = acpi_evaluate_object(priv->adev->handle, "IDSP", NULL, &buf);
+	if (ACPI_FAILURE(status))
+		return -ENODEV;
+
+	obja = (union acpi_object *)buf.pointer;
+	if (obja->type != ACPI_TYPE_PACKAGE) {
+		result = -EINVAL;
+		goto end;
+	}
+
+	for (i = 0; i < obja->package.count; i++) {
+		objb = &obja->package.elements[i];
+		if (objb->type != ACPI_TYPE_BUFFER) {
+			result = -EINVAL;
+			goto end;
+		}
+
+		/* UUID must be 16 bytes */
+		if (objb->buffer.length != 16) {
+			result = -EINVAL;
+			goto end;
+		}
+
+		for (j = 0; j < INT3400_THERMAL_MAXIMUM_UUID; j++) {
+			u8 uuid[16];
+
+			acpi_str_to_uuid(int3400_thermal_uuids[j], uuid);
+			if (!strncmp(uuid, objb->buffer.pointer, 16)) {
+				priv->uuid_bitmap |= (1 << j);
+				break;
+			}
+		}
+	}
+
+end:
+	kfree(buf.pointer);
+	return result;
+}
+
 static int parse_art(struct int3400_thermal_priv *priv)
 {
 	acpi_handle handle = priv->adev->handle;
@@ -193,6 +258,10 @@ static int int3400_thermal_probe(struct platform_device *pdev)
 
 	priv->adev = adev;
 
+	result = int3400_thermal_get_uuids(priv);
+	if (result)
+		goto free_priv;
+
 	result = parse_art(priv);
 	if (result)
 		goto free_priv;
-- 
1.8.3.2


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

* [PATCH 06/19] Thermal: int3400 thermal: register to thermal framework
  2014-09-18  1:53 [PATCH 00/19] Thermal: ACPI INT340X thermal drivers Zhang Rui
                   ` (4 preceding siblings ...)
  2014-09-18  1:53 ` [PATCH 05/19] Thermal: int3400 thermal: add capability to detect supporting UUIDs Zhang Rui
@ 2014-09-18  1:53 ` Zhang Rui
  2014-09-18  1:53 ` [PATCH 07/19] ACPI / fan: remove unused macro Zhang Rui
                   ` (12 subsequent siblings)
  18 siblings, 0 replies; 23+ messages in thread
From: Zhang Rui @ 2014-09-18  1:53 UTC (permalink / raw)
  To: linux-pm, linux-acpi; +Cc: rjw, lenb, eduardo.valentin, Zhang Rui

Signed-off-by: Zhang Rui <rui.zhang@intel.com>
---
 drivers/thermal/Kconfig                           |   1 +
 drivers/thermal/int340x_thermal/int3400_thermal.c | 103 ++++++++++++++++++++++
 2 files changed, 104 insertions(+)

diff --git a/drivers/thermal/Kconfig b/drivers/thermal/Kconfig
index 6f5a87a..b34c5f5 100644
--- a/drivers/thermal/Kconfig
+++ b/drivers/thermal/Kconfig
@@ -237,6 +237,7 @@ config INTEL_SOC_DTS_THERMAL
 config INT340X_THERMAL
 	tristate "ACPI INT340X thermal drivers"
 	depends on X86 && ACPI
+	select THERMAL_GOV_USER_SPACE
 	help
 	  Newer laptops and tablets that use ACPI may have thermal sensors and
 	  other devices with thermal control capabilities outside the core
diff --git a/drivers/thermal/int340x_thermal/int3400_thermal.c b/drivers/thermal/int340x_thermal/int3400_thermal.c
index 65c63ba..9104b4f 100644
--- a/drivers/thermal/int340x_thermal/int3400_thermal.c
+++ b/drivers/thermal/int340x_thermal/int3400_thermal.c
@@ -13,6 +13,7 @@
 #include <linux/module.h>
 #include <linux/platform_device.h>
 #include <linux/acpi.h>
+#include <linux/thermal.h>
 
 struct art {
 	acpi_handle source;
@@ -60,6 +61,8 @@ static u8 *int3400_thermal_uuids[INT3400_THERMAL_MAXIMUM_UUID] = {
 
 struct int3400_thermal_priv {
 	struct acpi_device *adev;
+	struct thermal_zone_device *thermal;
+	int mode;
 	int art_count;
 	struct art *arts;
 	int trt_count;
@@ -114,6 +117,36 @@ static int int3400_thermal_get_uuids(struct int3400_thermal_priv *priv)
 	return result;
 }
 
+static int int3400_thermal_run_osc(acpi_handle handle,
+				enum int3400_thermal_uuid uuid, bool enable)
+{
+	u32 ret, buf[2];
+	acpi_status status;
+	int result = 0;
+	struct acpi_osc_context context = {
+		.uuid_str = int3400_thermal_uuids[uuid],
+		.rev = 1,
+		.cap.length = 8,
+	};
+
+	buf[OSC_QUERY_DWORD] = 0;
+	buf[OSC_SUPPORT_DWORD] = enable;
+
+	context.cap.pointer = buf;
+
+	status = acpi_run_osc(handle, &context);
+	if (ACPI_SUCCESS(status)) {
+		ret = *((u32 *)(context.ret.pointer + 4));
+		if (ret != enable)
+			result = -EPERM;
+	} else
+		result = -EPERM;
+
+	kfree(context.ret.pointer);
+	return result;
+}
+
+
 static int parse_art(struct int3400_thermal_priv *priv)
 {
 	acpi_handle handle = priv->adev->handle;
@@ -243,6 +276,61 @@ static int parse_trt(struct int3400_thermal_priv *priv)
 	return result;
 }
 
+static int int3400_thermal_get_temp(struct thermal_zone_device *thermal,
+			unsigned long *temp)
+{
+	*temp = 20 * 1000; /* faked temp sensor with 20C */
+	return 0;
+}
+
+static int int3400_thermal_get_mode(struct thermal_zone_device *thermal,
+				enum thermal_device_mode *mode)
+{
+	struct int3400_thermal_priv *priv = thermal->devdata;
+
+	if (!priv)
+		return -EINVAL;
+
+	*mode = priv->mode;
+
+	return 0;
+}
+
+static int int3400_thermal_set_mode(struct thermal_zone_device *thermal,
+				enum thermal_device_mode mode)
+{
+	struct int3400_thermal_priv *priv = thermal->devdata;
+	bool enable;
+	int result = 0;
+
+	if (!priv)
+		return -EINVAL;
+
+	if (mode == THERMAL_DEVICE_ENABLED)
+		enable = true;
+	else if (mode == THERMAL_DEVICE_DISABLED)
+		enable = false;
+	else
+		return -EINVAL;
+
+	if (enable != priv->mode) {
+		priv->mode = enable;
+		/* currently, only PASSIVE COOLING is supported */
+		result = int3400_thermal_run_osc(priv->adev->handle,
+					INT3400_THERMAL_PASSIVE_1, enable);
+	}
+	return result;
+}
+
+static struct thermal_zone_device_ops int3400_thermal_ops = {
+	.get_temp = int3400_thermal_get_temp,
+};
+
+static struct thermal_zone_params int3400_thermal_params = {
+	.governor_name = "user_space",
+	.no_hwmon = true,
+};
+
 static int int3400_thermal_probe(struct platform_device *pdev)
 {
 	struct acpi_device *adev = ACPI_COMPANION(&pdev->dev);
@@ -272,7 +360,21 @@ static int int3400_thermal_probe(struct platform_device *pdev)
 
 	platform_set_drvdata(pdev, priv);
 
+	if (priv->uuid_bitmap & 1 << INT3400_THERMAL_PASSIVE_1) {
+		int3400_thermal_ops.get_mode = int3400_thermal_get_mode;
+		int3400_thermal_ops.set_mode = int3400_thermal_set_mode;
+	}
+	priv->thermal = thermal_zone_device_register("INT3400 Thermal", 0, 0,
+						priv, &int3400_thermal_ops,
+						&int3400_thermal_params, 0, 0);
+	if (IS_ERR(priv->thermal)) {
+		result = PTR_ERR(priv->thermal);
+		goto free_trt;
+	}
+
 	return 0;
+free_trt:
+	kfree(priv->trts);
 free_art:
 	kfree(priv->arts);
 free_priv:
@@ -284,6 +386,7 @@ static int int3400_thermal_remove(struct platform_device *pdev)
 {
 	struct int3400_thermal_priv *priv = platform_get_drvdata(pdev);
 
+	thermal_zone_device_unregister(priv->thermal);
 	kfree(priv->trts);
 	kfree(priv->arts);
 	kfree(priv);
-- 
1.8.3.2


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

* [PATCH 07/19] ACPI / fan: remove unused macro
  2014-09-18  1:53 [PATCH 00/19] Thermal: ACPI INT340X thermal drivers Zhang Rui
                   ` (5 preceding siblings ...)
  2014-09-18  1:53 ` [PATCH 06/19] Thermal: int3400 thermal: register to thermal framework Zhang Rui
@ 2014-09-18  1:53 ` Zhang Rui
  2014-09-18  1:53 ` [PATCH 08/19] ACPI / fan: remove no need check for device pointer Zhang Rui
                   ` (11 subsequent siblings)
  18 siblings, 0 replies; 23+ messages in thread
From: Zhang Rui @ 2014-09-18  1:53 UTC (permalink / raw)
  To: linux-pm, linux-acpi; +Cc: rjw, lenb, eduardo.valentin, Aaron Lu, Zhang Rui

From: Aaron Lu <aaron.lu@intel.com>

The _COMPONENT, ACPI_MODULE_NAME(name) and ACPI_FAN_FILE_STATE are not
used anywhere so remove them.

Signed-off-by: Aaron Lu <aaron.lu@intel.com>
Signed-off-by: Zhang Rui <rui.zhang@intel.com>
---
 drivers/acpi/fan.c | 4 ----
 1 file changed, 4 deletions(-)

diff --git a/drivers/acpi/fan.c b/drivers/acpi/fan.c
index 8acf53e..562d5f3 100644
--- a/drivers/acpi/fan.c
+++ b/drivers/acpi/fan.c
@@ -34,10 +34,6 @@
 #define PREFIX "ACPI: "
 
 #define ACPI_FAN_CLASS			"fan"
-#define ACPI_FAN_FILE_STATE		"state"
-
-#define _COMPONENT		ACPI_FAN_COMPONENT
-ACPI_MODULE_NAME("fan");
 
 MODULE_AUTHOR("Paul Diefenbaugh");
 MODULE_DESCRIPTION("ACPI Fan Driver");
-- 
1.8.3.2


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

* [PATCH 08/19] ACPI / fan: remove no need check for device pointer
  2014-09-18  1:53 [PATCH 00/19] Thermal: ACPI INT340X thermal drivers Zhang Rui
                   ` (6 preceding siblings ...)
  2014-09-18  1:53 ` [PATCH 07/19] ACPI / fan: remove unused macro Zhang Rui
@ 2014-09-18  1:53 ` Zhang Rui
  2014-09-18  1:53 ` [PATCH 09/19] ACPI / fan: use acpi_device_xxx_power instead of acpi_bus equivelant Zhang Rui
                   ` (10 subsequent siblings)
  18 siblings, 0 replies; 23+ messages in thread
From: Zhang Rui @ 2014-09-18  1:53 UTC (permalink / raw)
  To: linux-pm, linux-acpi; +Cc: rjw, lenb, eduardo.valentin, Aaron Lu, Zhang Rui

From: Aaron Lu <aaron.lu@intel.com>

The device pointer will not be NULL in the PM callback and ACPI driver's
add/remove callback, so checking NULL for them isn't necessary.

Signed-off-by: Aaron Lu <aaron.lu@intel.com>
Signed-off-by: Zhang Rui <rui.zhang@intel.com>
---
 drivers/acpi/fan.c | 18 +-----------------
 1 file changed, 1 insertion(+), 17 deletions(-)

diff --git a/drivers/acpi/fan.c b/drivers/acpi/fan.c
index 562d5f3..df861bb 100644
--- a/drivers/acpi/fan.c
+++ b/drivers/acpi/fan.c
@@ -131,9 +131,6 @@ static int acpi_fan_add(struct acpi_device *device)
 	int result = 0;
 	struct thermal_cooling_device *cdev;
 
-	if (!device)
-		return -EINVAL;
-
 	strcpy(acpi_device_name(device), "Fan");
 	strcpy(acpi_device_class(device), ACPI_FAN_CLASS);
 
@@ -177,14 +174,7 @@ static int acpi_fan_add(struct acpi_device *device)
 
 static int acpi_fan_remove(struct acpi_device *device)
 {
-	struct thermal_cooling_device *cdev;
-
-	if (!device)
-		return -EINVAL;
-
-	cdev =  acpi_driver_data(device);
-	if (!cdev)
-		return -EINVAL;
+	struct thermal_cooling_device *cdev = acpi_driver_data(device);
 
 	sysfs_remove_link(&device->dev.kobj, "thermal_cooling");
 	sysfs_remove_link(&cdev->device.kobj, "device");
@@ -196,9 +186,6 @@ static int acpi_fan_remove(struct acpi_device *device)
 #ifdef CONFIG_PM_SLEEP
 static int acpi_fan_suspend(struct device *dev)
 {
-	if (!dev)
-		return -EINVAL;
-
 	acpi_bus_set_power(to_acpi_device(dev)->handle, ACPI_STATE_D0);
 
 	return AE_OK;
@@ -208,9 +195,6 @@ static int acpi_fan_resume(struct device *dev)
 {
 	int result;
 
-	if (!dev)
-		return -EINVAL;
-
 	result = acpi_bus_update_power(to_acpi_device(dev)->handle, NULL);
 	if (result)
 		printk(KERN_ERR PREFIX "Error updating fan power state\n");
-- 
1.8.3.2


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

* [PATCH 09/19] ACPI / fan: use acpi_device_xxx_power instead of acpi_bus equivelant
  2014-09-18  1:53 [PATCH 00/19] Thermal: ACPI INT340X thermal drivers Zhang Rui
                   ` (7 preceding siblings ...)
  2014-09-18  1:53 ` [PATCH 08/19] ACPI / fan: remove no need check for device pointer Zhang Rui
@ 2014-09-18  1:53 ` Zhang Rui
  2014-09-18  1:53 ` [PATCH 10/19] ACPI / fan: convert to platform driver Zhang Rui
                   ` (9 subsequent siblings)
  18 siblings, 0 replies; 23+ messages in thread
From: Zhang Rui @ 2014-09-18  1:53 UTC (permalink / raw)
  To: linux-pm, linux-acpi; +Cc: rjw, lenb, eduardo.valentin, Aaron Lu, Zhang Rui

From: Aaron Lu <aaron.lu@intel.com>

When we have the acpi_device pointer, there is no need to pass the
device's handle to the acpi_bus_xxx_power functions to get/set/update
the device's power state, instead, use the acpi_device_xxx_power
functions directly.

To make this happen for fan module, export acpi_device_update_power.

Signed-off-by: Aaron Lu <aaron.lu@intel.com>
Signed-off-by: Zhang Rui <rui.zhang@intel.com>
---
 drivers/acpi/device_pm.c |  1 +
 drivers/acpi/fan.c       | 10 +++++-----
 drivers/acpi/internal.h  |  2 --
 include/acpi/acpi_bus.h  |  1 +
 4 files changed, 7 insertions(+), 7 deletions(-)

diff --git a/drivers/acpi/device_pm.c b/drivers/acpi/device_pm.c
index 67075f8..9177547 100644
--- a/drivers/acpi/device_pm.c
+++ b/drivers/acpi/device_pm.c
@@ -343,6 +343,7 @@ int acpi_device_update_power(struct acpi_device *device, int *state_p)
 
 	return 0;
 }
+EXPORT_SYMBOL_GPL(acpi_device_update_power);
 
 int acpi_bus_update_power(acpi_handle handle, int *state_p)
 {
diff --git a/drivers/acpi/fan.c b/drivers/acpi/fan.c
index df861bb..fff9696 100644
--- a/drivers/acpi/fan.c
+++ b/drivers/acpi/fan.c
@@ -92,7 +92,7 @@ static int fan_get_cur_state(struct thermal_cooling_device *cdev, unsigned long
 	if (!device)
 		return -EINVAL;
 
-	result = acpi_bus_update_power(device->handle, &acpi_state);
+	result = acpi_device_update_power(device, &acpi_state);
 	if (result)
 		return result;
 
@@ -110,7 +110,7 @@ fan_set_cur_state(struct thermal_cooling_device *cdev, unsigned long state)
 	if (!device || (state != 0 && state != 1))
 		return -EINVAL;
 
-	result = acpi_bus_set_power(device->handle,
+	result = acpi_device_set_power(device,
 				state ? ACPI_STATE_D0 : ACPI_STATE_D3_COLD);
 
 	return result;
@@ -134,7 +134,7 @@ static int acpi_fan_add(struct acpi_device *device)
 	strcpy(acpi_device_name(device), "Fan");
 	strcpy(acpi_device_class(device), ACPI_FAN_CLASS);
 
-	result = acpi_bus_update_power(device->handle, NULL);
+	result = acpi_device_update_power(device, NULL);
 	if (result) {
 		printk(KERN_ERR PREFIX "Setting initial power state\n");
 		goto end;
@@ -186,7 +186,7 @@ static int acpi_fan_remove(struct acpi_device *device)
 #ifdef CONFIG_PM_SLEEP
 static int acpi_fan_suspend(struct device *dev)
 {
-	acpi_bus_set_power(to_acpi_device(dev)->handle, ACPI_STATE_D0);
+	acpi_device_set_power(to_acpi_device(dev), ACPI_STATE_D0);
 
 	return AE_OK;
 }
@@ -195,7 +195,7 @@ static int acpi_fan_resume(struct device *dev)
 {
 	int result;
 
-	result = acpi_bus_update_power(to_acpi_device(dev)->handle, NULL);
+	result = acpi_device_update_power(to_acpi_device(dev), NULL);
 	if (result)
 		printk(KERN_ERR PREFIX "Error updating fan power state\n");
 
diff --git a/drivers/acpi/internal.h b/drivers/acpi/internal.h
index de47f9f..2d6af3b 100644
--- a/drivers/acpi/internal.h
+++ b/drivers/acpi/internal.h
@@ -104,8 +104,6 @@ int acpi_power_get_inferred_state(struct acpi_device *device, int *state);
 int acpi_power_on_resources(struct acpi_device *device, int state);
 int acpi_power_transition(struct acpi_device *device, int state);
 
-int acpi_device_update_power(struct acpi_device *device, int *state_p);
-
 int acpi_wakeup_device_init(void);
 
 #ifdef CONFIG_ARCH_MIGHT_HAVE_ACPI_PDC
diff --git a/include/acpi/acpi_bus.h b/include/acpi/acpi_bus.h
index bcfd808..6ca3281 100644
--- a/include/acpi/acpi_bus.h
+++ b/include/acpi/acpi_bus.h
@@ -435,6 +435,7 @@ int acpi_device_set_power(struct acpi_device *device, int state);
 int acpi_bus_init_power(struct acpi_device *device);
 int acpi_device_fix_up_power(struct acpi_device *device);
 int acpi_bus_update_power(acpi_handle handle, int *state_p);
+int acpi_device_update_power(struct acpi_device *device, int *state_p);
 bool acpi_bus_power_manageable(acpi_handle handle);
 
 #ifdef CONFIG_PM
-- 
1.8.3.2


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

* [PATCH 10/19] ACPI / fan: convert to platform driver
  2014-09-18  1:53 [PATCH 00/19] Thermal: ACPI INT340X thermal drivers Zhang Rui
                   ` (8 preceding siblings ...)
  2014-09-18  1:53 ` [PATCH 09/19] ACPI / fan: use acpi_device_xxx_power instead of acpi_bus equivelant Zhang Rui
@ 2014-09-18  1:53 ` Zhang Rui
  2014-09-18  1:53 ` [PATCH 11/19] ACPI / Fan: add ACPI 4.0 style fan support Zhang Rui
                   ` (8 subsequent siblings)
  18 siblings, 0 replies; 23+ messages in thread
From: Zhang Rui @ 2014-09-18  1:53 UTC (permalink / raw)
  To: linux-pm, linux-acpi; +Cc: rjw, lenb, eduardo.valentin, Aaron Lu, Zhang Rui

From: Aaron Lu <aaron.lu@intel.com>

Convert ACPI fan driver to a platform driver for the purpose of phasing
out ACPI bus.

Signed-off-by: Aaron Lu <aaron.lu@intel.com>
Signed-off-by: Zhang Rui <rui.zhang@intel.com>
---
 drivers/acpi/fan.c | 62 ++++++++++++++++++++++++------------------------------
 1 file changed, 28 insertions(+), 34 deletions(-)

diff --git a/drivers/acpi/fan.c b/drivers/acpi/fan.c
index fff9696..8a5b450 100644
--- a/drivers/acpi/fan.c
+++ b/drivers/acpi/fan.c
@@ -30,17 +30,14 @@
 #include <asm/uaccess.h>
 #include <linux/thermal.h>
 #include <linux/acpi.h>
-
-#define PREFIX "ACPI: "
-
-#define ACPI_FAN_CLASS			"fan"
+#include <linux/platform_device.h>
 
 MODULE_AUTHOR("Paul Diefenbaugh");
 MODULE_DESCRIPTION("ACPI Fan Driver");
 MODULE_LICENSE("GPL");
 
-static int acpi_fan_add(struct acpi_device *device);
-static int acpi_fan_remove(struct acpi_device *device);
+static int acpi_fan_probe(struct platform_device *pdev);
+static int acpi_fan_remove(struct platform_device *pdev);
 
 static const struct acpi_device_id fan_device_ids[] = {
 	{"PNP0C0B", 0},
@@ -62,15 +59,14 @@ static struct dev_pm_ops acpi_fan_pm = {
 #define FAN_PM_OPS_PTR NULL
 #endif
 
-static struct acpi_driver acpi_fan_driver = {
-	.name = "fan",
-	.class = ACPI_FAN_CLASS,
-	.ids = fan_device_ids,
-	.ops = {
-		.add = acpi_fan_add,
-		.remove = acpi_fan_remove,
-		},
-	.drv.pm = FAN_PM_OPS_PTR,
+static struct platform_driver acpi_fan_driver = {
+	.probe = acpi_fan_probe,
+	.remove = acpi_fan_remove,
+	.driver = {
+		.name = "acpi-fan",
+		.acpi_match_table = fan_device_ids,
+		.pm = FAN_PM_OPS_PTR,
+	},
 };
 
 /* thermal cooling device callbacks */
@@ -126,17 +122,15 @@ static const struct thermal_cooling_device_ops fan_cooling_ops = {
                                  Driver Interface
    -------------------------------------------------------------------------- */
 
-static int acpi_fan_add(struct acpi_device *device)
+static int acpi_fan_probe(struct platform_device *pdev)
 {
 	int result = 0;
 	struct thermal_cooling_device *cdev;
-
-	strcpy(acpi_device_name(device), "Fan");
-	strcpy(acpi_device_class(device), ACPI_FAN_CLASS);
+	struct acpi_device *device = ACPI_COMPANION(&pdev->dev);
 
 	result = acpi_device_update_power(device, NULL);
 	if (result) {
-		printk(KERN_ERR PREFIX "Setting initial power state\n");
+		dev_err(&pdev->dev, "Setting initial power state\n");
 		goto end;
 	}
 
@@ -147,24 +141,24 @@ static int acpi_fan_add(struct acpi_device *device)
 		goto end;
 	}
 
-	dev_dbg(&device->dev, "registered as cooling_device%d\n", cdev->id);
+	dev_dbg(&pdev->dev, "registered as cooling_device%d\n", cdev->id);
 
-	device->driver_data = cdev;
-	result = sysfs_create_link(&device->dev.kobj,
+	platform_set_drvdata(pdev, cdev);
+	result = sysfs_create_link(&pdev->dev.kobj,
 				   &cdev->device.kobj,
 				   "thermal_cooling");
 	if (result)
-		dev_err(&device->dev, "Failed to create sysfs link "
+		dev_err(&pdev->dev, "Failed to create sysfs link "
 			"'thermal_cooling'\n");
 
 	result = sysfs_create_link(&cdev->device.kobj,
-				   &device->dev.kobj,
+				   &pdev->dev.kobj,
 				   "device");
 	if (result)
-		dev_err(&device->dev, "Failed to create sysfs link "
+		dev_err(&pdev->dev, "Failed to create sysfs link "
 			"'device'\n");
 
-	printk(KERN_INFO PREFIX "%s [%s] (%s)\n",
+	dev_info(&pdev->dev, "%s [%s] (%s)\n",
 	       acpi_device_name(device), acpi_device_bid(device),
 	       !device->power.state ? "on" : "off");
 
@@ -172,11 +166,11 @@ static int acpi_fan_add(struct acpi_device *device)
 	return result;
 }
 
-static int acpi_fan_remove(struct acpi_device *device)
+static int acpi_fan_remove(struct platform_device *pdev)
 {
-	struct thermal_cooling_device *cdev = acpi_driver_data(device);
+	struct thermal_cooling_device *cdev = platform_get_drvdata(pdev);
 
-	sysfs_remove_link(&device->dev.kobj, "thermal_cooling");
+	sysfs_remove_link(&pdev->dev.kobj, "thermal_cooling");
 	sysfs_remove_link(&cdev->device.kobj, "device");
 	thermal_cooling_device_unregister(cdev);
 
@@ -186,7 +180,7 @@ static int acpi_fan_remove(struct acpi_device *device)
 #ifdef CONFIG_PM_SLEEP
 static int acpi_fan_suspend(struct device *dev)
 {
-	acpi_device_set_power(to_acpi_device(dev), ACPI_STATE_D0);
+	acpi_device_set_power(ACPI_COMPANION(dev), ACPI_STATE_D0);
 
 	return AE_OK;
 }
@@ -195,12 +189,12 @@ static int acpi_fan_resume(struct device *dev)
 {
 	int result;
 
-	result = acpi_device_update_power(to_acpi_device(dev), NULL);
+	result = acpi_device_update_power(ACPI_COMPANION(dev), NULL);
 	if (result)
-		printk(KERN_ERR PREFIX "Error updating fan power state\n");
+		dev_err(dev, "Error updating fan power state\n");
 
 	return result;
 }
 #endif
 
-module_acpi_driver(acpi_fan_driver);
+module_platform_driver(acpi_fan_driver);
-- 
1.8.3.2


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

* [PATCH 11/19] ACPI / Fan: add ACPI 4.0 style fan support
  2014-09-18  1:53 [PATCH 00/19] Thermal: ACPI INT340X thermal drivers Zhang Rui
                   ` (9 preceding siblings ...)
  2014-09-18  1:53 ` [PATCH 10/19] ACPI / fan: convert to platform driver Zhang Rui
@ 2014-09-18  1:53 ` Zhang Rui
  2014-09-18  1:53 ` [PATCH 12/19] ACPI / Fan: support INT3404 thermal device Zhang Rui
                   ` (7 subsequent siblings)
  18 siblings, 0 replies; 23+ messages in thread
From: Zhang Rui @ 2014-09-18  1:53 UTC (permalink / raw)
  To: linux-pm, linux-acpi; +Cc: rjw, lenb, eduardo.valentin, Aaron Lu, Zhang Rui

From: Aaron Lu <aaron.lu@intel.com>

This patch adds support for ACPI 4.0 style fan, lacking part is: no
support for 'Low Speed Notification Support', 'Fine Grain Control' is
not used yet.

It's not clear what to do on suspend/resume callback for 4.0 style ACPI
fan, so it does nothing for now.

Signed-off-by: Aaron Lu <aaron.lu@intel.com>
Signed-off-by: Zhang Rui <rui.zhang@intel.com>
---
 drivers/acpi/fan.c | 268 +++++++++++++++++++++++++++++++++++++++++++++++------
 1 file changed, 241 insertions(+), 27 deletions(-)

diff --git a/drivers/acpi/fan.c b/drivers/acpi/fan.c
index 8a5b450..f7d1c80 100644
--- a/drivers/acpi/fan.c
+++ b/drivers/acpi/fan.c
@@ -31,6 +31,7 @@
 #include <linux/thermal.h>
 #include <linux/acpi.h>
 #include <linux/platform_device.h>
+#include <linux/sort.h>
 
 MODULE_AUTHOR("Paul Diefenbaugh");
 MODULE_DESCRIPTION("ACPI Fan Driver");
@@ -59,6 +60,29 @@ static struct dev_pm_ops acpi_fan_pm = {
 #define FAN_PM_OPS_PTR NULL
 #endif
 
+struct acpi_fan_fps {
+	u64 control;
+	u64 trip_point;
+	u64 speed;
+	u64 noise_level;
+	u64 power;
+};
+
+struct acpi_fan_fif {
+	u64 revision;
+	u64 fine_grain_ctrl;
+	u64 step_size;
+	u64 low_speed_notification;
+};
+
+struct acpi_fan {
+	bool acpi4;
+	struct acpi_fan_fif fif;
+	struct acpi_fan_fps *fps;
+	int fps_count;
+	struct thermal_cooling_device *cdev;
+};
+
 static struct platform_driver acpi_fan_driver = {
 	.probe = acpi_fan_probe,
 	.remove = acpi_fan_remove,
@@ -73,21 +97,62 @@ static struct platform_driver acpi_fan_driver = {
 static int fan_get_max_state(struct thermal_cooling_device *cdev, unsigned long
 			     *state)
 {
-	/* ACPI fan device only support two states: ON/OFF */
-	*state = 1;
+	struct acpi_device *device = cdev->devdata;
+	struct acpi_fan *fan = acpi_driver_data(device);
+
+	if (fan->acpi4)
+		*state = fan->fps_count - 1;
+	else
+		*state = 1;
 	return 0;
 }
 
-static int fan_get_cur_state(struct thermal_cooling_device *cdev, unsigned long
-			     *state)
+static int fan_get_state_acpi4(struct acpi_device *device, unsigned long *state)
+{
+	struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
+	struct acpi_fan *fan = acpi_driver_data(device);
+	union acpi_object *obj;
+	acpi_status status;
+	int control, i;
+
+	status = acpi_evaluate_object(device->handle, "_FST", NULL, &buffer);
+	if (ACPI_FAILURE(status)) {
+		dev_err(&device->dev, "Get fan state failed\n");
+		return status;
+	}
+
+	obj = buffer.pointer;
+	if (!obj || obj->type != ACPI_TYPE_PACKAGE ||
+	    obj->package.count != 3 ||
+	    obj->package.elements[1].type != ACPI_TYPE_INTEGER) {
+		dev_err(&device->dev, "Invalid _FST data\n");
+		status = -EINVAL;
+		goto err;
+	}
+
+	control = obj->package.elements[1].integer.value;
+	for (i = 0; i < fan->fps_count; i++) {
+		if (control == fan->fps[i].control)
+			break;
+	}
+	if (i == fan->fps_count) {
+		dev_dbg(&device->dev, "Invalid control value returned\n");
+		status = -EINVAL;
+		goto err;
+	}
+
+	*state = i;
+
+err:
+	kfree(obj);
+	return status;
+}
+
+static int fan_get_state(struct acpi_device *device, unsigned long *state)
 {
-	struct acpi_device *device = cdev->devdata;
 	int result;
 	int acpi_state = ACPI_STATE_D0;
 
-	if (!device)
-		return -EINVAL;
-
 	result = acpi_device_update_power(device, &acpi_state);
 	if (result)
 		return result;
@@ -97,21 +162,57 @@ static int fan_get_cur_state(struct thermal_cooling_device *cdev, unsigned long
 	return 0;
 }
 
-static int
-fan_set_cur_state(struct thermal_cooling_device *cdev, unsigned long state)
+static int fan_get_cur_state(struct thermal_cooling_device *cdev, unsigned long
+			     *state)
 {
 	struct acpi_device *device = cdev->devdata;
-	int result;
+	struct acpi_fan *fan = acpi_driver_data(device);
+
+	if (fan->acpi4)
+		return fan_get_state_acpi4(device, state);
+	else
+		return fan_get_state(device, state);
+}
 
-	if (!device || (state != 0 && state != 1))
+static int fan_set_state(struct acpi_device *device, unsigned long state)
+{
+	if (state != 0 && state != 1)
 		return -EINVAL;
 
-	result = acpi_device_set_power(device,
-				state ? ACPI_STATE_D0 : ACPI_STATE_D3_COLD);
+	return acpi_device_set_power(device,
+				     state ? ACPI_STATE_D0 : ACPI_STATE_D3_COLD);
+}
 
-	return result;
+static int fan_set_state_acpi4(struct acpi_device *device, unsigned long state)
+{
+	struct acpi_fan *fan = acpi_driver_data(device);
+	acpi_status status;
+
+	if (state >= fan->fps_count)
+		return -EINVAL;
+
+	status = acpi_execute_simple_method(device->handle, "_FSL",
+					    fan->fps[state].control);
+	if (ACPI_FAILURE(status)) {
+		dev_dbg(&device->dev, "Failed to set state by _FSL\n");
+		return status;
+	}
+
+	return 0;
 }
 
+static int
+fan_set_cur_state(struct thermal_cooling_device *cdev, unsigned long state)
+{
+	struct acpi_device *device = cdev->devdata;
+	struct acpi_fan *fan = acpi_driver_data(device);
+
+	if (fan->acpi4)
+		return fan_set_state_acpi4(device, state);
+	else
+		return fan_set_state(device, state);
+ }
+
 static const struct thermal_cooling_device_ops fan_cooling_ops = {
 	.get_max_state = fan_get_max_state,
 	.get_cur_state = fan_get_cur_state,
@@ -122,16 +223,125 @@ static const struct thermal_cooling_device_ops fan_cooling_ops = {
                                  Driver Interface
    -------------------------------------------------------------------------- */
 
+static bool acpi_fan_is_acpi4(struct acpi_device *device)
+{
+	return acpi_has_method(device->handle, "_FIF") &&
+	       acpi_has_method(device->handle, "_FPS") &&
+	       acpi_has_method(device->handle, "_FSL") &&
+	       acpi_has_method(device->handle, "_FST");
+}
+
+static int acpi_fan_get_fif(struct acpi_device *device)
+{
+	struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
+	struct acpi_fan *fan = acpi_driver_data(device);
+	struct acpi_buffer format = { sizeof("NNNN"), "NNNN" };
+	struct acpi_buffer fif = { sizeof(fan->fif), &fan->fif };
+	union acpi_object *obj;
+	acpi_status status;
+
+	status = acpi_evaluate_object(device->handle, "_FIF", NULL, &buffer);
+	if (ACPI_FAILURE(status))
+		return status;
+
+	obj = buffer.pointer;
+	if (!obj || obj->type != ACPI_TYPE_PACKAGE) {
+		dev_err(&device->dev, "Invalid _FIF data\n");
+		status = -EINVAL;
+		goto err;
+	}
+
+	status = acpi_extract_package(obj, &format, &fif);
+	if (ACPI_FAILURE(status)) {
+		dev_err(&device->dev, "Invalid _FIF element\n");
+		status = -EINVAL;
+	}
+
+err:
+	kfree(obj);
+	return status;
+}
+
+static int acpi_fan_speed_cmp(const void *a, const void *b)
+{
+	const struct acpi_fan_fps *fps1 = a;
+	const struct acpi_fan_fps *fps2 = b;
+	return fps1->speed - fps2->speed;
+}
+
+static int acpi_fan_get_fps(struct acpi_device *device)
+{
+	struct acpi_fan *fan = acpi_driver_data(device);
+	struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
+	union acpi_object *obj;
+	acpi_status status;
+	int i;
+
+	status = acpi_evaluate_object(device->handle, "_FPS", NULL, &buffer);
+	if (ACPI_FAILURE(status))
+		return status;
+
+	obj = buffer.pointer;
+	if (!obj || obj->type != ACPI_TYPE_PACKAGE || obj->package.count < 2) {
+		dev_err(&device->dev, "Invalid _FPS data\n");
+		status = -EINVAL;
+		goto err;
+	}
+
+	fan->fps_count = obj->package.count - 1; /* minus revision field */
+	fan->fps = devm_kzalloc(&device->dev,
+				fan->fps_count * sizeof(struct acpi_fan_fps),
+				GFP_KERNEL);
+	if (!fan->fps) {
+		dev_err(&device->dev, "Not enough memory\n");
+		status = -ENOMEM;
+		goto err;
+	}
+	for (i = 0; i < fan->fps_count; i++) {
+		struct acpi_buffer format = { sizeof("NNNNN"), "NNNNN" };
+		struct acpi_buffer fps = { sizeof(fan->fps[i]), &fan->fps[i] };
+		status = acpi_extract_package(&obj->package.elements[i + 1],
+					      &format, &fps);
+		if (ACPI_FAILURE(status)) {
+			dev_err(&device->dev, "Invalid _FPS element\n");
+			break;
+		}
+	}
+
+	/* sort the state array according to fan speed in increase order */
+	sort(fan->fps, fan->fps_count, sizeof(*fan->fps),
+	     acpi_fan_speed_cmp, NULL);
+
+err:
+	kfree(obj);
+	return status;
+}
+
 static int acpi_fan_probe(struct platform_device *pdev)
 {
 	int result = 0;
 	struct thermal_cooling_device *cdev;
+	struct acpi_fan *fan;
 	struct acpi_device *device = ACPI_COMPANION(&pdev->dev);
 
-	result = acpi_device_update_power(device, NULL);
-	if (result) {
-		dev_err(&pdev->dev, "Setting initial power state\n");
-		goto end;
+	fan = devm_kzalloc(&pdev->dev, sizeof(*fan), GFP_KERNEL);
+	if (!fan) {
+		dev_err(&device->dev, "No memory for fan\n");
+		return -ENOMEM;
+	}
+	device->driver_data = fan;
+	platform_set_drvdata(pdev, fan);
+
+	if (acpi_fan_is_acpi4(device)) {
+		if (acpi_fan_get_fif(device) || acpi_fan_get_fps(device))
+			goto end;
+		fan->acpi4 = true;
+	} else {
+		result = acpi_device_update_power(device, NULL);
+		if (result) {
+			dev_err(&device->dev, "Setting initial power state\n");
+			goto end;
+		}
 	}
 
 	cdev = thermal_cooling_device_register("Fan", device,
@@ -143,7 +353,7 @@ static int acpi_fan_probe(struct platform_device *pdev)
 
 	dev_dbg(&pdev->dev, "registered as cooling_device%d\n", cdev->id);
 
-	platform_set_drvdata(pdev, cdev);
+	fan->cdev = cdev;
 	result = sysfs_create_link(&pdev->dev.kobj,
 				   &cdev->device.kobj,
 				   "thermal_cooling");
@@ -158,21 +368,17 @@ static int acpi_fan_probe(struct platform_device *pdev)
 		dev_err(&pdev->dev, "Failed to create sysfs link "
 			"'device'\n");
 
-	dev_info(&pdev->dev, "%s [%s] (%s)\n",
-	       acpi_device_name(device), acpi_device_bid(device),
-	       !device->power.state ? "on" : "off");
-
 end:
 	return result;
 }
 
 static int acpi_fan_remove(struct platform_device *pdev)
 {
-	struct thermal_cooling_device *cdev = platform_get_drvdata(pdev);
+	struct acpi_fan *fan = platform_get_drvdata(pdev);
 
 	sysfs_remove_link(&pdev->dev.kobj, "thermal_cooling");
-	sysfs_remove_link(&cdev->device.kobj, "device");
-	thermal_cooling_device_unregister(cdev);
+	sysfs_remove_link(&fan->cdev->device.kobj, "device");
+	thermal_cooling_device_unregister(fan->cdev);
 
 	return 0;
 }
@@ -180,6 +386,10 @@ static int acpi_fan_remove(struct platform_device *pdev)
 #ifdef CONFIG_PM_SLEEP
 static int acpi_fan_suspend(struct device *dev)
 {
+	struct acpi_fan *fan = dev_get_drvdata(dev);
+	if (fan->acpi4)
+		return 0;
+
 	acpi_device_set_power(ACPI_COMPANION(dev), ACPI_STATE_D0);
 
 	return AE_OK;
@@ -188,6 +398,10 @@ static int acpi_fan_suspend(struct device *dev)
 static int acpi_fan_resume(struct device *dev)
 {
 	int result;
+	struct acpi_fan *fan = dev_get_drvdata(dev);
+
+	if (fan->acpi4)
+		return 0;
 
 	result = acpi_device_update_power(ACPI_COMPANION(dev), NULL);
 	if (result)
-- 
1.8.3.2


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

* [PATCH 12/19] ACPI / Fan: support INT3404 thermal device
  2014-09-18  1:53 [PATCH 00/19] Thermal: ACPI INT340X thermal drivers Zhang Rui
                   ` (10 preceding siblings ...)
  2014-09-18  1:53 ` [PATCH 11/19] ACPI / Fan: add ACPI 4.0 style fan support Zhang Rui
@ 2014-09-18  1:53 ` Zhang Rui
  2014-09-18  1:53 ` [PATCH 13/19] Thermal: move the KELVIN_TO_MILLICELSIUS macro to thermal.h Zhang Rui
                   ` (6 subsequent siblings)
  18 siblings, 0 replies; 23+ messages in thread
From: Zhang Rui @ 2014-09-18  1:53 UTC (permalink / raw)
  To: linux-pm, linux-acpi; +Cc: rjw, lenb, eduardo.valentin, Aaron Lu, Zhang Rui

From: Aaron Lu <aaron.lu@intel.com>

INT3404 ACPI object follows the ACPI 5.0 fan object definition as
described in section 11.3 of the ACPI 5.0 Specification.
Thus we can reuse the ACPI fan driver for INT3404 ACPI object.

Signed-off-by: Aaron Lu <aaron.lu@intel.com>
Signed-off-by: Zhang Rui <rui.zhang@intel.com>
---
 drivers/acpi/fan.c | 1 +
 1 file changed, 1 insertion(+)

diff --git a/drivers/acpi/fan.c b/drivers/acpi/fan.c
index f7d1c80..e007c49 100644
--- a/drivers/acpi/fan.c
+++ b/drivers/acpi/fan.c
@@ -42,6 +42,7 @@ static int acpi_fan_remove(struct platform_device *pdev);
 
 static const struct acpi_device_id fan_device_ids[] = {
 	{"PNP0C0B", 0},
+	{"INT3404", 0},
 	{"", 0},
 };
 MODULE_DEVICE_TABLE(acpi, fan_device_ids);
-- 
1.8.3.2


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

* [PATCH 13/19] Thermal: move the KELVIN_TO_MILLICELSIUS macro to thermal.h
  2014-09-18  1:53 [PATCH 00/19] Thermal: ACPI INT340X thermal drivers Zhang Rui
                   ` (11 preceding siblings ...)
  2014-09-18  1:53 ` [PATCH 12/19] ACPI / Fan: support INT3404 thermal device Zhang Rui
@ 2014-09-18  1:53 ` Zhang Rui
  2014-09-18  1:53 ` [PATCH 14/19] Thermal: introduce INT3402 thermal driver Zhang Rui
                   ` (5 subsequent siblings)
  18 siblings, 0 replies; 23+ messages in thread
From: Zhang Rui @ 2014-09-18  1:53 UTC (permalink / raw)
  To: linux-pm, linux-acpi; +Cc: rjw, lenb, eduardo.valentin, Aaron Lu, Zhang Rui

From: Aaron Lu <aaron.lu@intel.com>

This macro can be used by other component so move it to a common header,
but in a slightly different way: define two macros, one macro with an
offset and the other doesn't.

Signed-off-by: Aaron Lu <aaron.lu@intel.com>
Signed-off-by: Zhang Rui <rui.zhang@intel.com>
---
 drivers/acpi/thermal.c  | 18 +++++++++---------
 include/linux/thermal.h |  2 ++
 2 files changed, 11 insertions(+), 9 deletions(-)

diff --git a/drivers/acpi/thermal.c b/drivers/acpi/thermal.c
index 112817e..d24fa19 100644
--- a/drivers/acpi/thermal.c
+++ b/drivers/acpi/thermal.c
@@ -528,7 +528,6 @@ static void acpi_thermal_check(void *data)
 }
 
 /* sys I/F for generic thermal sysfs support */
-#define KELVIN_TO_MILLICELSIUS(t, off) (((t) - (off)) * 100)
 
 static int thermal_get_temp(struct thermal_zone_device *thermal,
 			    unsigned long *temp)
@@ -543,7 +542,8 @@ static int thermal_get_temp(struct thermal_zone_device *thermal,
 	if (result)
 		return result;
 
-	*temp = KELVIN_TO_MILLICELSIUS(tz->temperature, tz->kelvin_offset);
+	*temp = DECI_KELVIN_TO_MILLICELSIUS_WITH_OFFSET(tz->temperature,
+							tz->kelvin_offset);
 	return 0;
 }
 
@@ -647,7 +647,7 @@ static int thermal_get_trip_temp(struct thermal_zone_device *thermal,
 
 	if (tz->trips.critical.flags.valid) {
 		if (!trip) {
-			*temp = KELVIN_TO_MILLICELSIUS(
+			*temp = DECI_KELVIN_TO_MILLICELSIUS_WITH_OFFSET(
 				tz->trips.critical.temperature,
 				tz->kelvin_offset);
 			return 0;
@@ -657,7 +657,7 @@ static int thermal_get_trip_temp(struct thermal_zone_device *thermal,
 
 	if (tz->trips.hot.flags.valid) {
 		if (!trip) {
-			*temp = KELVIN_TO_MILLICELSIUS(
+			*temp = DECI_KELVIN_TO_MILLICELSIUS_WITH_OFFSET(
 				tz->trips.hot.temperature,
 				tz->kelvin_offset);
 			return 0;
@@ -667,7 +667,7 @@ static int thermal_get_trip_temp(struct thermal_zone_device *thermal,
 
 	if (tz->trips.passive.flags.valid) {
 		if (!trip) {
-			*temp = KELVIN_TO_MILLICELSIUS(
+			*temp = DECI_KELVIN_TO_MILLICELSIUS_WITH_OFFSET(
 				tz->trips.passive.temperature,
 				tz->kelvin_offset);
 			return 0;
@@ -678,7 +678,7 @@ static int thermal_get_trip_temp(struct thermal_zone_device *thermal,
 	for (i = 0; i < ACPI_THERMAL_MAX_ACTIVE &&
 		tz->trips.active[i].flags.valid; i++) {
 		if (!trip) {
-			*temp = KELVIN_TO_MILLICELSIUS(
+			*temp = DECI_KELVIN_TO_MILLICELSIUS_WITH_OFFSET(
 				tz->trips.active[i].temperature,
 				tz->kelvin_offset);
 			return 0;
@@ -694,7 +694,7 @@ static int thermal_get_crit_temp(struct thermal_zone_device *thermal,
 	struct acpi_thermal *tz = thermal->devdata;
 
 	if (tz->trips.critical.flags.valid) {
-		*temperature = KELVIN_TO_MILLICELSIUS(
+		*temperature = DECI_KELVIN_TO_MILLICELSIUS_WITH_OFFSET(
 				tz->trips.critical.temperature,
 				tz->kelvin_offset);
 		return 0;
@@ -714,8 +714,8 @@ static int thermal_get_trend(struct thermal_zone_device *thermal,
 
 	if (type == THERMAL_TRIP_ACTIVE) {
 		unsigned long trip_temp;
-		unsigned long temp = KELVIN_TO_MILLICELSIUS(tz->temperature,
-							tz->kelvin_offset);
+		unsigned long temp = DECI_KELVIN_TO_MILLICELSIUS_WITH_OFFSET(
+					tz->temperature, tz->kelvin_offset);
 		if (thermal_get_trip_temp(thermal, trip, &trip_temp))
 			return -EINVAL;
 
diff --git a/include/linux/thermal.h b/include/linux/thermal.h
index 0305cde..79ce6b9 100644
--- a/include/linux/thermal.h
+++ b/include/linux/thermal.h
@@ -44,6 +44,8 @@
 #define KELVIN_TO_CELSIUS(t)	(long)(((long)t-2732 >= 0) ?	\
 				((long)t-2732+5)/10 : ((long)t-2732-5)/10)
 #define CELSIUS_TO_KELVIN(t)	((t)*10+2732)
+#define DECI_KELVIN_TO_MILLICELSIUS_WITH_OFFSET(t, off) (((t) - (off)) * 100)
+#define DECI_KELVIN_TO_MILLICELSIUS(t) DECI_KELVIN_TO_MILLICELSIUS_WITH_OFFSET(t, 2732)
 
 /* Adding event notification support elements */
 #define THERMAL_GENL_FAMILY_NAME                "thermal_event"
-- 
1.8.3.2


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

* [PATCH 14/19] Thermal: introduce INT3402 thermal driver
  2014-09-18  1:53 [PATCH 00/19] Thermal: ACPI INT340X thermal drivers Zhang Rui
                   ` (12 preceding siblings ...)
  2014-09-18  1:53 ` [PATCH 13/19] Thermal: move the KELVIN_TO_MILLICELSIUS macro to thermal.h Zhang Rui
@ 2014-09-18  1:53 ` Zhang Rui
  2014-09-18  1:53 ` [PATCH 15/19] Thermal: introduce int3403 " Zhang Rui
                   ` (4 subsequent siblings)
  18 siblings, 0 replies; 23+ messages in thread
From: Zhang Rui @ 2014-09-18  1:53 UTC (permalink / raw)
  To: linux-pm, linux-acpi; +Cc: rjw, lenb, eduardo.valentin, Aaron Lu, Zhang Rui

From: Aaron Lu <aaron.lu@intel.com>

ACPI INT3402 device object could report temperature for the memory module.
To expose such information to user space, a thermal zone device is registered
for it so that the thermal sysfs interface can expose such information for
userspace to use.

Signed-off-by: Aaron Lu <aaron.lu@intel.com>
Signed-off-by: Zhang Rui <rui.zhang@intel.com>
---
 drivers/thermal/int340x_thermal/Makefile          |   1 +
 drivers/thermal/int340x_thermal/int3402_thermal.c | 242 ++++++++++++++++++++++
 include/linux/thermal.h                           |   2 +
 3 files changed, 245 insertions(+)
 create mode 100644 drivers/thermal/int340x_thermal/int3402_thermal.c

diff --git a/drivers/thermal/int340x_thermal/Makefile b/drivers/thermal/int340x_thermal/Makefile
index e10a53b..67c98fd 100644
--- a/drivers/thermal/int340x_thermal/Makefile
+++ b/drivers/thermal/int340x_thermal/Makefile
@@ -1 +1,2 @@
 obj-$(CONFIG_INT340X_THERMAL)	+= int3400_thermal.o
+obj-$(CONFIG_INT340X_THERMAL)	+= int3402_thermal.o
diff --git a/drivers/thermal/int340x_thermal/int3402_thermal.c b/drivers/thermal/int340x_thermal/int3402_thermal.c
new file mode 100644
index 0000000..2eb27cb
--- /dev/null
+++ b/drivers/thermal/int340x_thermal/int3402_thermal.c
@@ -0,0 +1,242 @@
+/*
+ * INT3402 thermal driver for memory temperature reporting
+ *
+ * Copyright (C) 2014, Intel Corporation
+ * Authors: Aaron Lu <aaron.lu@intel.com>
+ *
+ * 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.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/acpi.h>
+#include <linux/thermal.h>
+
+#define ACPI_ACTIVE_COOLING_MAX_NR 10
+
+struct active_trip {
+	unsigned long temp;
+	int id;
+	bool valid;
+};
+
+struct int3402_thermal_data {
+	unsigned long *aux_trips;
+	int aux_trip_nr;
+	unsigned long psv_temp;
+	int psv_trip_id;
+	unsigned long crt_temp;
+	int crt_trip_id;
+	unsigned long hot_temp;
+	int hot_trip_id;
+	struct active_trip act_trips[ACPI_ACTIVE_COOLING_MAX_NR];
+	acpi_handle *handle;
+};
+
+static int int3402_thermal_get_zone_temp(struct thermal_zone_device *zone,
+					 unsigned long *temp)
+{
+	struct int3402_thermal_data *d = zone->devdata;
+	unsigned long long tmp;
+	acpi_status status;
+
+	status = acpi_evaluate_integer(d->handle, "_TMP", NULL, &tmp);
+	if (ACPI_FAILURE(status))
+		return -ENODEV;
+
+	/* _TMP returns the temperature in tenths of degrees Kelvin */
+	*temp = DECI_KELVIN_TO_MILLICELSIUS(tmp);
+
+	return 0;
+}
+
+static int int3402_thermal_get_trip_temp(struct thermal_zone_device *zone,
+					 int trip, unsigned long *temp)
+{
+	struct int3402_thermal_data *d = zone->devdata;
+	int i;
+
+	if (trip < d->aux_trip_nr)
+		*temp = d->aux_trips[trip];
+	else if (trip == d->crt_trip_id)
+		*temp = d->crt_temp;
+	else if (trip == d->psv_trip_id)
+		*temp = d->psv_temp;
+	else if (trip == d->hot_trip_id)
+		*temp = d->hot_temp;
+	else {
+		for (i = 0; i < ACPI_ACTIVE_COOLING_MAX_NR; i++) {
+			if (d->act_trips[i].valid &&
+			    d->act_trips[i].id == trip) {
+				*temp = d->act_trips[i].temp;
+				break;
+			}
+		}
+		if (i == ACPI_ACTIVE_COOLING_MAX_NR)
+			return -EINVAL;
+	}
+	return 0;
+}
+
+static int int3402_thermal_get_trip_type(struct thermal_zone_device *zone,
+					 int trip, enum thermal_trip_type *type)
+{
+	struct int3402_thermal_data *d = zone->devdata;
+	int i;
+
+	if (trip < d->aux_trip_nr)
+		*type = THERMAL_TRIP_PASSIVE;
+	else if (trip == d->crt_trip_id)
+		*type = THERMAL_TRIP_CRITICAL;
+	else if (trip == d->hot_trip_id)
+		*type = THERMAL_TRIP_HOT;
+	else if (trip == d->psv_trip_id)
+		*type = THERMAL_TRIP_PASSIVE;
+	else {
+		for (i = 0; i < ACPI_ACTIVE_COOLING_MAX_NR; i++) {
+			if (d->act_trips[i].valid &&
+			    d->act_trips[i].id == trip) {
+				*type = THERMAL_TRIP_ACTIVE;
+				break;
+			}
+		}
+		if (i == ACPI_ACTIVE_COOLING_MAX_NR)
+			return -EINVAL;
+	}
+	return 0;
+}
+
+int int3402_thermal_set_trip_temp(struct thermal_zone_device *zone, int trip,
+				  unsigned long temp)
+{
+	struct int3402_thermal_data *d = zone->devdata;
+	acpi_status status;
+	char name[10];
+
+	snprintf(name, sizeof(name), "PAT%d", trip);
+	status = acpi_execute_simple_method(d->handle, name,
+			MILLICELSIUS_TO_DECI_KELVIN(temp));
+	if (ACPI_FAILURE(status))
+		return -EIO;
+
+	d->aux_trips[trip] = temp;
+	return 0;
+}
+
+static struct thermal_zone_device_ops int3402_thermal_zone_ops = {
+	.get_temp       = int3402_thermal_get_zone_temp,
+	.get_trip_temp	= int3402_thermal_get_trip_temp,
+	.get_trip_type	= int3402_thermal_get_trip_type,
+	.set_trip_temp	= int3402_thermal_set_trip_temp,
+};
+
+static struct thermal_zone_params int3402_thermal_params = {
+	.governor_name = "user_space",
+	.no_hwmon = true,
+};
+
+static int int3402_thermal_get_temp(acpi_handle handle, char *name,
+				    unsigned long *temp)
+{
+	unsigned long long r;
+	acpi_status status;
+
+	status = acpi_evaluate_integer(handle, name, NULL, &r);
+	if (ACPI_FAILURE(status))
+		return -EIO;
+
+	*temp = DECI_KELVIN_TO_MILLICELSIUS(r);
+	return 0;
+}
+
+static int int3402_thermal_probe(struct platform_device *pdev)
+{
+	struct acpi_device *adev = ACPI_COMPANION(&pdev->dev);
+	struct int3402_thermal_data *d;
+	struct thermal_zone_device *zone;
+	acpi_status status;
+	unsigned long long trip_cnt;
+	int trip_mask = 0, i;
+
+	if (!acpi_has_method(adev->handle, "_TMP"))
+		return -ENODEV;
+
+	d = devm_kzalloc(&pdev->dev, sizeof(*d), GFP_KERNEL);
+	if (!d)
+		return -ENOMEM;
+
+	status = acpi_evaluate_integer(adev->handle, "PATC", NULL, &trip_cnt);
+	if (ACPI_FAILURE(status))
+		trip_cnt = 0;
+	else {
+		d->aux_trips = devm_kzalloc(&pdev->dev,
+				sizeof(*d->aux_trips) * trip_cnt, GFP_KERNEL);
+		if (!d->aux_trips)
+			return -ENOMEM;
+		trip_mask = trip_cnt - 1;
+		d->handle = adev->handle;
+		d->aux_trip_nr = trip_cnt;
+	}
+
+	d->crt_trip_id = -1;
+	if (!int3402_thermal_get_temp(adev->handle, "_CRT", &d->crt_temp))
+		d->crt_trip_id = trip_cnt++;
+	d->hot_trip_id = -1;
+	if (!int3402_thermal_get_temp(adev->handle, "_HOT", &d->hot_temp))
+		d->hot_trip_id = trip_cnt++;
+	d->psv_trip_id = -1;
+	if (!int3402_thermal_get_temp(adev->handle, "_PSV", &d->psv_temp))
+		d->psv_trip_id = trip_cnt++;
+	for (i = 0; i < ACPI_ACTIVE_COOLING_MAX_NR; i++) {
+		char name[5] = { '_', 'A', 'C', '0' + i, '\0' };
+		if (int3402_thermal_get_temp(adev->handle, name,
+					     &d->act_trips[i].temp))
+			break;
+		d->act_trips[i].id = trip_cnt++;
+		d->act_trips[i].valid = true;
+	}
+
+	zone = thermal_zone_device_register(acpi_device_bid(adev), trip_cnt,
+					    trip_mask, d,
+					    &int3402_thermal_zone_ops,
+					    &int3402_thermal_params,
+					    0, 0);
+	if (IS_ERR(zone))
+		return PTR_ERR(zone);
+	platform_set_drvdata(pdev, zone);
+
+	return 0;
+}
+
+static int int3402_thermal_remove(struct platform_device *pdev)
+{
+	struct thermal_zone_device *zone = platform_get_drvdata(pdev);
+
+	thermal_zone_device_unregister(zone);
+	return 0;
+}
+
+static const struct acpi_device_id int3402_thermal_match[] = {
+	{"INT3402", 0},
+	{}
+};
+
+MODULE_DEVICE_TABLE(acpi, int3402_thermal_match);
+
+static struct platform_driver int3402_thermal_driver = {
+	.probe = int3402_thermal_probe,
+	.remove = int3402_thermal_remove,
+	.driver = {
+		   .name = "int3402 thermal",
+		   .owner = THIS_MODULE,
+		   .acpi_match_table = int3402_thermal_match,
+		   },
+};
+
+module_platform_driver(int3402_thermal_driver);
+
+MODULE_DESCRIPTION("INT3402 Thermal driver");
+MODULE_LICENSE("GPL");
diff --git a/include/linux/thermal.h b/include/linux/thermal.h
index 79ce6b9..ef90838 100644
--- a/include/linux/thermal.h
+++ b/include/linux/thermal.h
@@ -46,6 +46,8 @@
 #define CELSIUS_TO_KELVIN(t)	((t)*10+2732)
 #define DECI_KELVIN_TO_MILLICELSIUS_WITH_OFFSET(t, off) (((t) - (off)) * 100)
 #define DECI_KELVIN_TO_MILLICELSIUS(t) DECI_KELVIN_TO_MILLICELSIUS_WITH_OFFSET(t, 2732)
+#define MILLICELSIUS_TO_DECI_KELVIN_WITH_OFFSET(t, off) (((t) / 100) + (off))
+#define MILLICELSIUS_TO_DECI_KELVIN(t) MILLICELSIUS_TO_DECI_KELVIN_WITH_OFFSET(t, 2732)
 
 /* Adding event notification support elements */
 #define THERMAL_GENL_FAMILY_NAME                "thermal_event"
-- 
1.8.3.2


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

* [PATCH 15/19] Thermal: introduce int3403 thermal driver
  2014-09-18  1:53 [PATCH 00/19] Thermal: ACPI INT340X thermal drivers Zhang Rui
                   ` (13 preceding siblings ...)
  2014-09-18  1:53 ` [PATCH 14/19] Thermal: introduce INT3402 thermal driver Zhang Rui
@ 2014-09-18  1:53 ` Zhang Rui
  2014-09-18  1:53 ` [PATCH 16/19] Thermal: int340x_thermal: expose acpi thermal relationship tables Zhang Rui
                   ` (3 subsequent siblings)
  18 siblings, 0 replies; 23+ messages in thread
From: Zhang Rui @ 2014-09-18  1:53 UTC (permalink / raw)
  To: linux-pm, linux-acpi; +Cc: rjw, lenb, eduardo.valentin, Lan Tianyu, Zhang Rui

From: Lan Tianyu <tianyu.lan@intel.com>

ACPI INT3403 device object can be used to retrieve temperature date
from temperature sensors present in the system, and to expose
device' performance control.

The previous INT3403 thermal driver supports temperature reporting only,
thus remove it and introduce this new & enhanced one.

Signed-off-by: Lan Tianyu <tianyu.lan@intel.com>
Signed-off-by: Zhang Rui <rui.zhang@intel.com>
---
 drivers/thermal/Kconfig                           |  15 -
 drivers/thermal/Makefile                          |   1 -
 drivers/thermal/int3403_thermal.c                 | 296 -------------
 drivers/thermal/int340x_thermal/Makefile          |   1 +
 drivers/thermal/int340x_thermal/int3403_thermal.c | 489 ++++++++++++++++++++++
 5 files changed, 490 insertions(+), 312 deletions(-)
 delete mode 100644 drivers/thermal/int3403_thermal.c
 create mode 100644 drivers/thermal/int340x_thermal/int3403_thermal.c

diff --git a/drivers/thermal/Kconfig b/drivers/thermal/Kconfig
index b34c5f5..6f93e5c 100644
--- a/drivers/thermal/Kconfig
+++ b/drivers/thermal/Kconfig
@@ -207,21 +207,6 @@ config X86_PKG_TEMP_THERMAL
 	  two trip points which can be set by user to get notifications via thermal
 	  notification methods.
 
-config ACPI_INT3403_THERMAL
-	tristate "ACPI INT3403 thermal driver"
-	depends on X86 && ACPI
-	help
-	  Newer laptops and tablets that use ACPI may have thermal sensors
-	  outside the core CPU/SOC for thermal safety reasons. These
-	  temperature sensors are also exposed for the OS to use via the so
-	  called INT3403 ACPI object. This driver will, on devices that have
-	  such sensors, expose the temperature information from these sensors
-	  to userspace via the normal thermal framework. This means that a wide
-	  range of applications and GUI widgets can show this information to
-	  the user or use this information for making decisions. For example,
-	  the Intel Thermal Daemon can use this information to allow the user
-	  to select his laptop to run without turning on the fans.
-
 config INTEL_SOC_DTS_THERMAL
 	tristate "Intel SoCs DTS thermal driver"
 	depends on X86 && IOSF_MBI
diff --git a/drivers/thermal/Makefile b/drivers/thermal/Makefile
index 216503e..39b85b5 100644
--- a/drivers/thermal/Makefile
+++ b/drivers/thermal/Makefile
@@ -31,6 +31,5 @@ obj-$(CONFIG_INTEL_POWERCLAMP)	+= intel_powerclamp.o
 obj-$(CONFIG_X86_PKG_TEMP_THERMAL)	+= x86_pkg_temp_thermal.o
 obj-$(CONFIG_INTEL_SOC_DTS_THERMAL)	+= intel_soc_dts_thermal.o
 obj-$(CONFIG_TI_SOC_THERMAL)	+= ti-soc-thermal/
-obj-$(CONFIG_ACPI_INT3403_THERMAL)	+= int3403_thermal.o
 obj-$(CONFIG_INT340X_THERMAL)  += int340x_thermal/
 obj-$(CONFIG_ST_THERMAL)	+= st/
diff --git a/drivers/thermal/int3403_thermal.c b/drivers/thermal/int3403_thermal.c
deleted file mode 100644
index 17554ee..0000000
--- a/drivers/thermal/int3403_thermal.c
+++ /dev/null
@@ -1,296 +0,0 @@
-/*
- * ACPI INT3403 thermal driver
- * Copyright (c) 2013, Intel Corporation.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions of the GNU General Public License,
- * version 2, as published by the Free Software Foundation.
- *
- * This program is distributed in the hope 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/kernel.h>
-#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/types.h>
-#include <linux/acpi.h>
-#include <linux/thermal.h>
-
-#define INT3403_TYPE_SENSOR		0x03
-#define INT3403_PERF_CHANGED_EVENT	0x80
-#define INT3403_THERMAL_EVENT		0x90
-
-#define DECI_KELVIN_TO_MILLI_CELSIUS(t, off) (((t) - (off)) * 100)
-#define KELVIN_OFFSET	2732
-#define MILLI_CELSIUS_TO_DECI_KELVIN(t, off) (((t) / 100) + (off))
-
-#define ACPI_INT3403_CLASS		"int3403"
-#define ACPI_INT3403_FILE_STATE		"state"
-
-struct int3403_sensor {
-	struct thermal_zone_device *tzone;
-	unsigned long *thresholds;
-	unsigned long	crit_temp;
-	int		crit_trip_id;
-	unsigned long	psv_temp;
-	int		psv_trip_id;
-};
-
-static int sys_get_curr_temp(struct thermal_zone_device *tzone,
-				unsigned long *temp)
-{
-	struct acpi_device *device = tzone->devdata;
-	unsigned long long tmp;
-	acpi_status status;
-
-	status = acpi_evaluate_integer(device->handle, "_TMP", NULL, &tmp);
-	if (ACPI_FAILURE(status))
-		return -EIO;
-
-	*temp = DECI_KELVIN_TO_MILLI_CELSIUS(tmp, KELVIN_OFFSET);
-
-	return 0;
-}
-
-static int sys_get_trip_hyst(struct thermal_zone_device *tzone,
-		int trip, unsigned long *temp)
-{
-	struct acpi_device *device = tzone->devdata;
-	unsigned long long hyst;
-	acpi_status status;
-
-	status = acpi_evaluate_integer(device->handle, "GTSH", NULL, &hyst);
-	if (ACPI_FAILURE(status))
-		return -EIO;
-
-	/*
-	 * Thermal hysteresis represents a temperature difference.
-	 * Kelvin and Celsius have same degree size. So the
-	 * conversion here between tenths of degree Kelvin unit
-	 * and Milli-Celsius unit is just to multiply 100.
-	 */
-	*temp = hyst * 100;
-
-	return 0;
-}
-
-static int sys_get_trip_temp(struct thermal_zone_device *tzone,
-		int trip, unsigned long *temp)
-{
-	struct acpi_device *device = tzone->devdata;
-	struct int3403_sensor *obj = acpi_driver_data(device);
-
-	if (trip == obj->crit_trip_id)
-		*temp = obj->crit_temp;
-	else if (trip == obj->psv_trip_id)
-		*temp = obj->psv_temp;
-	else {
-		/*
-		 * get_trip_temp is a mandatory callback but
-		 * PATx method doesn't return any value, so return
-		 * cached value, which was last set from user space.
-		 */
-		*temp = obj->thresholds[trip];
-	}
-
-	return 0;
-}
-
-static int sys_get_trip_type(struct thermal_zone_device *thermal,
-		int trip, enum thermal_trip_type *type)
-{
-	struct acpi_device *device = thermal->devdata;
-	struct int3403_sensor *obj = acpi_driver_data(device);
-
-	/* Mandatory callback, may not mean much here */
-	if (trip == obj->crit_trip_id)
-		*type = THERMAL_TRIP_CRITICAL;
-	else
-		*type = THERMAL_TRIP_PASSIVE;
-
-	return 0;
-}
-
-int sys_set_trip_temp(struct thermal_zone_device *tzone, int trip,
-							unsigned long temp)
-{
-	struct acpi_device *device = tzone->devdata;
-	acpi_status status;
-	char name[10];
-	int ret = 0;
-	struct int3403_sensor *obj = acpi_driver_data(device);
-
-	snprintf(name, sizeof(name), "PAT%d", trip);
-	if (acpi_has_method(device->handle, name)) {
-		status = acpi_execute_simple_method(device->handle, name,
-				MILLI_CELSIUS_TO_DECI_KELVIN(temp,
-							KELVIN_OFFSET));
-		if (ACPI_FAILURE(status))
-			ret = -EIO;
-		else
-			obj->thresholds[trip] = temp;
-	} else {
-		ret = -EIO;
-		dev_err(&device->dev, "sys_set_trip_temp: method not found\n");
-	}
-
-	return ret;
-}
-
-static struct thermal_zone_device_ops tzone_ops = {
-	.get_temp = sys_get_curr_temp,
-	.get_trip_temp = sys_get_trip_temp,
-	.get_trip_type = sys_get_trip_type,
-	.set_trip_temp = sys_set_trip_temp,
-	.get_trip_hyst =  sys_get_trip_hyst,
-};
-
-static void acpi_thermal_notify(struct acpi_device *device, u32 event)
-{
-	struct int3403_sensor *obj;
-
-	if (!device)
-		return;
-
-	obj = acpi_driver_data(device);
-	if (!obj)
-		return;
-
-	switch (event) {
-	case INT3403_PERF_CHANGED_EVENT:
-		break;
-	case INT3403_THERMAL_EVENT:
-		thermal_zone_device_update(obj->tzone);
-		break;
-	default:
-		dev_err(&device->dev, "Unsupported event [0x%x]\n", event);
-		break;
-	}
-}
-
-static int sys_get_trip_crt(struct acpi_device *device, unsigned long *temp)
-{
-	unsigned long long crt;
-	acpi_status status;
-
-	status = acpi_evaluate_integer(device->handle, "_CRT", NULL, &crt);
-	if (ACPI_FAILURE(status))
-		return -EIO;
-
-	*temp = DECI_KELVIN_TO_MILLI_CELSIUS(crt, KELVIN_OFFSET);
-
-	return 0;
-}
-
-static int sys_get_trip_psv(struct acpi_device *device, unsigned long *temp)
-{
-	unsigned long long psv;
-	acpi_status status;
-
-	status = acpi_evaluate_integer(device->handle, "_PSV", NULL, &psv);
-	if (ACPI_FAILURE(status))
-		return -EIO;
-
-	*temp = DECI_KELVIN_TO_MILLI_CELSIUS(psv, KELVIN_OFFSET);
-
-	return 0;
-}
-
-static int acpi_int3403_add(struct acpi_device *device)
-{
-	int result = 0;
-	unsigned long long ptyp;
-	acpi_status status;
-	struct int3403_sensor *obj;
-	unsigned long long trip_cnt;
-	int trip_mask = 0;
-
-	if (!device)
-		return -EINVAL;
-
-	status = acpi_evaluate_integer(device->handle, "PTYP", NULL, &ptyp);
-	if (ACPI_FAILURE(status))
-		return -EINVAL;
-
-	if (ptyp != INT3403_TYPE_SENSOR)
-		return -EINVAL;
-
-	obj = devm_kzalloc(&device->dev, sizeof(*obj), GFP_KERNEL);
-	if (!obj)
-		return -ENOMEM;
-
-	device->driver_data = obj;
-
-	status = acpi_evaluate_integer(device->handle, "PATC", NULL,
-						&trip_cnt);
-	if (ACPI_FAILURE(status))
-		trip_cnt = 0;
-
-	if (trip_cnt) {
-		/* We have to cache, thresholds can't be readback */
-		obj->thresholds = devm_kzalloc(&device->dev,
-					sizeof(*obj->thresholds) * trip_cnt,
-					GFP_KERNEL);
-		if (!obj->thresholds)
-			return -ENOMEM;
-		trip_mask = BIT(trip_cnt) - 1;
-	}
-
-	obj->psv_trip_id = -1;
-	if (!sys_get_trip_psv(device, &obj->psv_temp))
-		obj->psv_trip_id = trip_cnt++;
-
-	obj->crit_trip_id = -1;
-	if (!sys_get_trip_crt(device, &obj->crit_temp))
-		obj->crit_trip_id = trip_cnt++;
-
-	obj->tzone = thermal_zone_device_register(acpi_device_bid(device),
-				trip_cnt, trip_mask, device, &tzone_ops,
-				NULL, 0, 0);
-	if (IS_ERR(obj->tzone)) {
-		result = PTR_ERR(obj->tzone);
-		return result;
-	}
-
-	strcpy(acpi_device_name(device), "INT3403");
-	strcpy(acpi_device_class(device), ACPI_INT3403_CLASS);
-
-	return 0;
-}
-
-static int acpi_int3403_remove(struct acpi_device *device)
-{
-	struct int3403_sensor *obj;
-
-	obj = acpi_driver_data(device);
-	thermal_zone_device_unregister(obj->tzone);
-
-	return 0;
-}
-
-ACPI_MODULE_NAME("int3403");
-static const struct acpi_device_id int3403_device_ids[] = {
-	{"INT3403", 0},
-	{"", 0},
-};
-MODULE_DEVICE_TABLE(acpi, int3403_device_ids);
-
-static struct acpi_driver acpi_int3403_driver = {
-	.name = "INT3403",
-	.class = ACPI_INT3403_CLASS,
-	.ids = int3403_device_ids,
-	.ops = {
-		.add = acpi_int3403_add,
-		.remove = acpi_int3403_remove,
-		.notify = acpi_thermal_notify,
-		},
-};
-
-module_acpi_driver(acpi_int3403_driver);
-
-MODULE_AUTHOR("Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>");
-MODULE_LICENSE("GPL v2");
-MODULE_DESCRIPTION("ACPI INT3403 thermal driver");
diff --git a/drivers/thermal/int340x_thermal/Makefile b/drivers/thermal/int340x_thermal/Makefile
index 67c98fd..c4a5c50 100644
--- a/drivers/thermal/int340x_thermal/Makefile
+++ b/drivers/thermal/int340x_thermal/Makefile
@@ -1,2 +1,3 @@
 obj-$(CONFIG_INT340X_THERMAL)	+= int3400_thermal.o
 obj-$(CONFIG_INT340X_THERMAL)	+= int3402_thermal.o
+obj-$(CONFIG_INT340X_THERMAL)	+= int3403_thermal.o
diff --git a/drivers/thermal/int340x_thermal/int3403_thermal.c b/drivers/thermal/int340x_thermal/int3403_thermal.c
new file mode 100644
index 0000000..fea2b63
--- /dev/null
+++ b/drivers/thermal/int340x_thermal/int3403_thermal.c
@@ -0,0 +1,489 @@
+/*
+ * ACPI INT3403 thermal driver
+ * Copyright (c) 2013, Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/acpi.h>
+#include <linux/thermal.h>
+#include <linux/platform_device.h>
+
+#define INT3403_TYPE_SENSOR		0x03
+#define INT3403_TYPE_CHARGER		0x0B
+#define INT3403_TYPE_BATTERY		0x0C
+#define INT3403_PERF_CHANGED_EVENT	0x80
+#define INT3403_THERMAL_EVENT		0x90
+
+#define DECI_KELVIN_TO_MILLI_CELSIUS(t, off) (((t) - (off)) * 100)
+#define KELVIN_OFFSET	2732
+#define MILLI_CELSIUS_TO_DECI_KELVIN(t, off) (((t) / 100) + (off))
+
+struct int3403_sensor {
+	struct thermal_zone_device *tzone;
+	unsigned long *thresholds;
+	unsigned long	crit_temp;
+	int		crit_trip_id;
+	unsigned long	psv_temp;
+	int		psv_trip_id;
+
+};
+
+struct int3403_performance_state {
+	u64 performance;
+	u64 power;
+	u64 latency;
+	u64 linear;
+	u64 control;
+	u64 raw_performace;
+	char *raw_unit;
+	int reserved;
+};
+
+struct int3403_cdev {
+	struct thermal_cooling_device *cdev;
+	unsigned long max_state;
+};
+
+struct int3403_priv {
+	struct platform_device *pdev;
+	struct acpi_device *adev;
+	unsigned long long type;
+	void *priv;
+};
+
+static int sys_get_curr_temp(struct thermal_zone_device *tzone,
+				unsigned long *temp)
+{
+	struct int3403_priv *priv = tzone->devdata;
+	struct acpi_device *device = priv->adev;
+	unsigned long long tmp;
+	acpi_status status;
+
+	status = acpi_evaluate_integer(device->handle, "_TMP", NULL, &tmp);
+	if (ACPI_FAILURE(status))
+		return -EIO;
+
+	*temp = DECI_KELVIN_TO_MILLI_CELSIUS(tmp, KELVIN_OFFSET);
+
+	return 0;
+}
+
+static int sys_get_trip_hyst(struct thermal_zone_device *tzone,
+		int trip, unsigned long *temp)
+{
+	struct int3403_priv *priv = tzone->devdata;
+	struct acpi_device *device = priv->adev;
+	unsigned long long hyst;
+	acpi_status status;
+
+	status = acpi_evaluate_integer(device->handle, "GTSH", NULL, &hyst);
+	if (ACPI_FAILURE(status))
+		return -EIO;
+
+	*temp = DECI_KELVIN_TO_MILLI_CELSIUS(hyst, KELVIN_OFFSET);
+
+	return 0;
+}
+
+static int sys_get_trip_temp(struct thermal_zone_device *tzone,
+		int trip, unsigned long *temp)
+{
+	struct int3403_priv *priv = tzone->devdata;
+	struct int3403_sensor *obj = priv->priv;
+
+	if (priv->type != INT3403_TYPE_SENSOR || !obj)
+		return -EINVAL;
+
+	if (trip == obj->crit_trip_id)
+		*temp = obj->crit_temp;
+	else if (trip == obj->psv_trip_id)
+		*temp = obj->psv_temp;
+	else {
+		/*
+		 * get_trip_temp is a mandatory callback but
+		 * PATx method doesn't return any value, so return
+		 * cached value, which was last set from user space
+		 */
+		*temp = obj->thresholds[trip];
+	}
+
+	return 0;
+}
+
+static int sys_get_trip_type(struct thermal_zone_device *thermal,
+		int trip, enum thermal_trip_type *type)
+{
+	struct int3403_priv *priv = thermal->devdata;
+	struct int3403_sensor *obj = priv->priv;
+
+	/* Mandatory callback, may not mean much here */
+	if (trip == obj->crit_trip_id)
+		*type = THERMAL_TRIP_CRITICAL;
+	else
+		*type = THERMAL_TRIP_PASSIVE;
+
+	return 0;
+}
+
+int sys_set_trip_temp(struct thermal_zone_device *tzone, int trip,
+							unsigned long temp)
+{
+	struct int3403_priv *priv = tzone->devdata;
+	struct acpi_device *device = priv->adev;
+	struct int3403_sensor *obj = priv->priv;
+	acpi_status status;
+	char name[10];
+	int ret = 0;
+
+	snprintf(name, sizeof(name), "PAT%d", trip);
+	if (acpi_has_method(device->handle, name)) {
+		status = acpi_execute_simple_method(device->handle, name,
+				MILLI_CELSIUS_TO_DECI_KELVIN(temp,
+							KELVIN_OFFSET));
+		if (ACPI_FAILURE(status))
+			ret = -EIO;
+		else
+			obj->thresholds[trip] = temp;
+	} else {
+		ret = -EIO;
+		dev_err(&device->dev, "sys_set_trip_temp: method not found\n");
+	}
+
+	return ret;
+}
+
+static struct thermal_zone_device_ops tzone_ops = {
+	.get_temp = sys_get_curr_temp,
+	.get_trip_temp = sys_get_trip_temp,
+	.get_trip_type = sys_get_trip_type,
+	.set_trip_temp = sys_set_trip_temp,
+	.get_trip_hyst =  sys_get_trip_hyst,
+};
+
+static struct thermal_zone_params int3403_thermal_params = {
+	.governor_name = "user_space",
+	.no_hwmon = true,
+};
+
+static void int3403_notify(acpi_handle handle,
+		u32 event, void *data)
+{
+	struct int3403_priv *priv = data;
+	struct int3403_sensor *obj;
+
+	if (!priv)
+		return;
+
+	obj = priv->priv;
+	if (priv->type != INT3403_TYPE_SENSOR || !obj)
+		return;
+
+	switch (event) {
+	case INT3403_PERF_CHANGED_EVENT:
+		break;
+	case INT3403_THERMAL_EVENT:
+		thermal_zone_device_update(obj->tzone);
+		break;
+	default:
+		dev_err(&priv->pdev->dev, "Unsupported event [0x%x]\n", event);
+		break;
+	}
+}
+
+static int sys_get_trip_crt(struct acpi_device *device, unsigned long *temp)
+{
+	unsigned long long crt;
+	acpi_status status;
+
+	status = acpi_evaluate_integer(device->handle, "_CRT", NULL, &crt);
+	if (ACPI_FAILURE(status))
+		return -EIO;
+
+	*temp = DECI_KELVIN_TO_MILLI_CELSIUS(crt, KELVIN_OFFSET);
+
+	return 0;
+}
+
+static int sys_get_trip_psv(struct acpi_device *device, unsigned long *temp)
+{
+	unsigned long long psv;
+	acpi_status status;
+
+	status = acpi_evaluate_integer(device->handle, "_PSV", NULL, &psv);
+	if (ACPI_FAILURE(status))
+		return -EIO;
+
+	*temp = DECI_KELVIN_TO_MILLI_CELSIUS(psv, KELVIN_OFFSET);
+
+	return 0;
+}
+
+static int int3403_sensor_add(struct int3403_priv *priv)
+{
+	int result = 0;
+	acpi_status status;
+	struct int3403_sensor *obj;
+	unsigned long long trip_cnt;
+	int trip_mask = 0;
+
+	obj = devm_kzalloc(&priv->pdev->dev, sizeof(*obj), GFP_KERNEL);
+	if (!obj)
+		return -ENOMEM;
+
+	priv->priv = obj;
+
+	status = acpi_evaluate_integer(priv->adev->handle, "PATC", NULL,
+						&trip_cnt);
+	if (ACPI_FAILURE(status))
+		trip_cnt = 0;
+
+	if (trip_cnt) {
+		/* We have to cache, thresholds can't be readback */
+		obj->thresholds = devm_kzalloc(&priv->pdev->dev,
+					sizeof(*obj->thresholds) * trip_cnt,
+					GFP_KERNEL);
+		if (!obj->thresholds) {
+			result = -ENOMEM;
+			goto err_free_obj;
+		}
+		trip_mask = BIT(trip_cnt) - 1;
+	}
+
+	obj->psv_trip_id = -1;
+	if (!sys_get_trip_psv(priv->adev, &obj->psv_temp))
+		obj->psv_trip_id = trip_cnt++;
+
+	obj->crit_trip_id = -1;
+	if (!sys_get_trip_crt(priv->adev, &obj->crit_temp))
+		obj->crit_trip_id = trip_cnt++;
+
+	obj->tzone = thermal_zone_device_register(acpi_device_bid(priv->adev),
+				trip_cnt, trip_mask, priv, &tzone_ops,
+				&int3403_thermal_params, 0, 0);
+	if (IS_ERR(obj->tzone)) {
+		result = PTR_ERR(obj->tzone);
+		obj->tzone = NULL;
+		goto err_free_obj;
+	}
+
+	result = acpi_install_notify_handler(priv->adev->handle,
+			ACPI_DEVICE_NOTIFY, int3403_notify,
+			(void *)priv);
+	if (result)
+		goto err_free_obj;
+
+	return 0;
+
+ err_free_obj:
+	if (obj->tzone)
+		thermal_zone_device_unregister(obj->tzone);
+	kfree(obj->thresholds);
+	kfree(obj);
+	return result;
+}
+
+static int int3403_sensor_remove(struct int3403_priv *priv)
+{
+	struct int3403_sensor *obj = priv->priv;
+
+	thermal_zone_device_unregister(obj->tzone);
+	kfree(obj->thresholds);
+	kfree(obj);
+	return 0;
+}
+
+/* INT3403 Cooling devices */
+static int int3403_get_max_state(struct thermal_cooling_device *cdev,
+				 unsigned long *state)
+{
+	struct int3403_priv *priv = cdev->devdata;
+	struct int3403_cdev *obj = priv->priv;
+
+	*state = obj->max_state;
+	return 0;
+}
+
+static int int3403_get_cur_state(struct thermal_cooling_device *cdev,
+				 unsigned long *state)
+{
+	struct int3403_priv *priv = cdev->devdata;
+	unsigned long long level;
+	acpi_status status;
+
+	status = acpi_evaluate_integer(priv->adev->handle, "PPPC", NULL, &level);
+	if (ACPI_SUCCESS(status)) {
+		*state = level;
+		return 0;
+	} else
+		return -EINVAL;
+}
+
+static int
+int3403_set_cur_state(struct thermal_cooling_device *cdev, unsigned long state)
+{
+	struct int3403_priv *priv = cdev->devdata;
+	acpi_status status;
+
+	status = acpi_execute_simple_method(priv->adev->handle, "SPPC", state);
+	if (ACPI_SUCCESS(status))
+		return 0;
+	else
+		return -EINVAL;
+}
+
+static const struct thermal_cooling_device_ops int3403_cooling_ops = {
+	.get_max_state = int3403_get_max_state,
+	.get_cur_state = int3403_get_cur_state,
+	.set_cur_state = int3403_set_cur_state,
+};
+
+static int int3403_cdev_add(struct int3403_priv *priv)
+{
+	int result = 0;
+	acpi_status status;
+	struct int3403_cdev *obj;
+	struct acpi_buffer buf = { ACPI_ALLOCATE_BUFFER, NULL };
+	union acpi_object *p;
+
+	obj = devm_kzalloc(&priv->pdev->dev, sizeof(*obj), GFP_KERNEL);
+	if (!obj)
+		return -ENOMEM;
+
+	status = acpi_evaluate_object(priv->adev->handle, "PPSS", NULL, &buf);
+	if (ACPI_FAILURE(status)) {
+		result = -ENODEV;
+		goto end;
+	}
+
+	p = buf.pointer;
+	if (!p || (p->type != ACPI_TYPE_PACKAGE)) {
+		printk(KERN_WARNING "Invalid PPSS data\n");
+		result = -EFAULT;
+		goto end;
+	}
+
+	obj->max_state = p->package.count - 1;
+	obj->cdev =
+		thermal_cooling_device_register(acpi_device_bid(priv->adev),
+				priv, &int3403_cooling_ops);
+	if (IS_ERR(obj->cdev))
+		result = PTR_ERR(obj->cdev);
+
+	priv->priv = obj;
+
+	/* TODO: add ACPI notification support */
+end:
+	if (result)
+		kfree(obj);
+	return result;
+}
+
+static int int3403_cdev_remove(struct int3403_priv *priv)
+{
+	struct int3403_cdev *obj = priv->priv;
+
+	thermal_cooling_device_unregister(obj->cdev);
+	kfree(priv->priv);
+	return 0;
+}
+
+static int int3403_add(struct platform_device *pdev)
+{
+	struct int3403_priv *priv;
+	int result = 0;
+	acpi_status status;
+
+	priv = devm_kzalloc(&pdev->dev, sizeof(struct int3403_priv),
+			    GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+
+	priv->pdev = pdev;
+	priv->adev = ACPI_COMPANION(&(pdev->dev));
+	if (!priv->adev) {
+		result = -EINVAL;
+		goto err;
+	}
+
+	status = acpi_evaluate_integer(priv->adev->handle, "PTYP",
+				       NULL, &priv->type);
+	if (ACPI_FAILURE(status)) {
+		result = -EINVAL;
+		goto err;
+	}
+
+	platform_set_drvdata(pdev, priv);
+	switch (priv->type) {
+	case INT3403_TYPE_SENSOR:
+		result = int3403_sensor_add(priv);
+		break;
+	case INT3403_TYPE_CHARGER:
+	case INT3403_TYPE_BATTERY:
+		result = int3403_cdev_add(priv);
+		break;
+	default:
+		result = -EINVAL;
+	}
+
+	if (result)
+		goto err;
+	return result;
+
+err:
+	kfree(priv);
+	return result;
+}
+
+static int int3403_remove(struct platform_device *pdev)
+{
+	struct int3403_priv *priv = platform_get_drvdata(pdev);
+
+	switch (priv->type) {
+	case INT3403_TYPE_SENSOR:
+		int3403_sensor_remove(priv);
+		break;
+	case INT3403_TYPE_CHARGER:
+	case INT3403_TYPE_BATTERY:
+		int3403_cdev_remove(priv);
+		break;
+	default:
+		break;
+	}
+
+	kfree(priv);
+	return 0;
+}
+
+static const struct acpi_device_id int3403_device_ids[] = {
+	{"INT3403", 0},
+	{"", 0},
+};
+MODULE_DEVICE_TABLE(acpi, int3403_device_ids);
+
+static struct platform_driver int3403_driver = {
+	.probe = int3403_add,
+	.remove = int3403_remove,
+	.driver = {
+		.name = "int3403 thermal",
+		.owner  = THIS_MODULE,
+		.acpi_match_table = int3403_device_ids,
+	},
+};
+
+module_platform_driver(int3403_driver);
+
+MODULE_AUTHOR("Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>");
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("ACPI INT3403 thermal driver");
-- 
1.8.3.2


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

* [PATCH 16/19] Thermal: int340x_thermal: expose acpi thermal relationship tables
  2014-09-18  1:53 [PATCH 00/19] Thermal: ACPI INT340X thermal drivers Zhang Rui
                   ` (14 preceding siblings ...)
  2014-09-18  1:53 ` [PATCH 15/19] Thermal: introduce int3403 " Zhang Rui
@ 2014-09-18  1:53 ` Zhang Rui
  2014-09-18  1:53 ` [PATCH 17/19] Thermal: int3400_thermal: use acpi_thermal_rel parsing APIs Zhang Rui
                   ` (2 subsequent siblings)
  18 siblings, 0 replies; 23+ messages in thread
From: Zhang Rui @ 2014-09-18  1:53 UTC (permalink / raw)
  To: linux-pm, linux-acpi; +Cc: rjw, lenb, eduardo.valentin, Jacob Pan, Zhang Rui

From: Jacob Pan <jacob.jun.pan@linux.intel.com>

ACPI 4.0 introduced two thermal relationship tables via _ART
(active cooling) and  _TRT (passive cooling) objects. These
tables contain many to many relationships among thermal sensors
and cooling devices.

This patch parses _ART and _TRT and makes the result available to
the userspace via an misc device interface. At the same time,
kernel drivers can also request parsing results from internal
kernel APIs.

The results include source and target devices, influence, and
sampling rate in case of _TRT. For _ART, the result shows source
device, target device, and weight percentage.

Signed-off-by: Jacob Pan <jacob.jun.pan@linux.intel.com>
Signed-off-by: Zhang Rui <rui.zhang@intel.com>
---
 drivers/thermal/Kconfig                            |   5 +
 drivers/thermal/int340x_thermal/Makefile           |   1 +
 drivers/thermal/int340x_thermal/acpi_thermal_rel.c | 400 +++++++++++++++++++++
 drivers/thermal/int340x_thermal/acpi_thermal_rel.h |  84 +++++
 4 files changed, 490 insertions(+)
 create mode 100644 drivers/thermal/int340x_thermal/acpi_thermal_rel.c
 create mode 100644 drivers/thermal/int340x_thermal/acpi_thermal_rel.h

diff --git a/drivers/thermal/Kconfig b/drivers/thermal/Kconfig
index 6f93e5c..3a89292 100644
--- a/drivers/thermal/Kconfig
+++ b/drivers/thermal/Kconfig
@@ -223,6 +223,7 @@ config INT340X_THERMAL
 	tristate "ACPI INT340X thermal drivers"
 	depends on X86 && ACPI
 	select THERMAL_GOV_USER_SPACE
+	select ACPI_THERMAL_REL
 	help
 	  Newer laptops and tablets that use ACPI may have thermal sensors and
 	  other devices with thermal control capabilities outside the core
@@ -237,6 +238,10 @@ config INT340X_THERMAL
 	  information to allow the user to select his laptop to run without
 	  turning on the fans.
 
+config ACPI_THERMAL_REL
+	tristate
+	depends on ACPI
+
 menu "Texas Instruments thermal drivers"
 source "drivers/thermal/ti-soc-thermal/Kconfig"
 endmenu
diff --git a/drivers/thermal/int340x_thermal/Makefile b/drivers/thermal/int340x_thermal/Makefile
index c4a5c50..ffe40bf 100644
--- a/drivers/thermal/int340x_thermal/Makefile
+++ b/drivers/thermal/int340x_thermal/Makefile
@@ -1,3 +1,4 @@
 obj-$(CONFIG_INT340X_THERMAL)	+= int3400_thermal.o
 obj-$(CONFIG_INT340X_THERMAL)	+= int3402_thermal.o
 obj-$(CONFIG_INT340X_THERMAL)	+= int3403_thermal.o
+obj-$(CONFIG_ACPI_THERMAL_REL)	+= acpi_thermal_rel.o
diff --git a/drivers/thermal/int340x_thermal/acpi_thermal_rel.c b/drivers/thermal/int340x_thermal/acpi_thermal_rel.c
new file mode 100644
index 0000000..0d8db80
--- /dev/null
+++ b/drivers/thermal/int340x_thermal/acpi_thermal_rel.c
@@ -0,0 +1,400 @@
+/* acpi_thermal_rel.c driver for exporting ACPI thermal relationship
+ *
+ * Copyright (c) 2014 Intel Corp
+ *
+ * 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.
+ *
+ */
+
+/*
+ * Two functionalities included:
+ * 1. Export _TRT, _ART, via misc device interface to the userspace.
+ * 2. Provide parsing result to kernel drivers
+ *
+ */
+#include <linux/init.h>
+#include <linux/export.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/io.h>
+#include <linux/acpi.h>
+#include <linux/uaccess.h>
+#include <linux/miscdevice.h>
+#include "acpi_thermal_rel.h"
+
+static acpi_handle acpi_thermal_rel_handle;
+static DEFINE_SPINLOCK(acpi_thermal_rel_chrdev_lock);
+static int acpi_thermal_rel_chrdev_count;	/* #times opened */
+static int acpi_thermal_rel_chrdev_exclu;	/* already open exclusive? */
+
+static int acpi_thermal_rel_open(struct inode *inode, struct file *file)
+{
+	spin_lock(&acpi_thermal_rel_chrdev_lock);
+	if (acpi_thermal_rel_chrdev_exclu ||
+	    (acpi_thermal_rel_chrdev_count && (file->f_flags & O_EXCL))) {
+		spin_unlock(&acpi_thermal_rel_chrdev_lock);
+		return -EBUSY;
+	}
+
+	if (file->f_flags & O_EXCL)
+		acpi_thermal_rel_chrdev_exclu = 1;
+	acpi_thermal_rel_chrdev_count++;
+
+	spin_unlock(&acpi_thermal_rel_chrdev_lock);
+
+	return nonseekable_open(inode, file);
+}
+
+static int acpi_thermal_rel_release(struct inode *inode, struct file *file)
+{
+	spin_lock(&acpi_thermal_rel_chrdev_lock);
+	acpi_thermal_rel_chrdev_count--;
+	acpi_thermal_rel_chrdev_exclu = 0;
+	spin_unlock(&acpi_thermal_rel_chrdev_lock);
+
+	return 0;
+}
+
+/**
+ * acpi_parse_trt - Thermal Relationship Table _TRT for passive cooling
+ *
+ * @handle: ACPI handle of the device contains _TRT
+ * @art_count: the number of valid entries resulted from parsing _TRT
+ * @artp: pointer to pointer of array of art entries in parsing result
+ * @create_dev: whether to create platform devices for target and source
+ *
+ */
+int acpi_parse_trt(acpi_handle handle, int *trt_count, struct trt **trtp,
+		bool create_dev)
+{
+	acpi_status status;
+	int result = 0;
+	int i;
+	int nr_bad_entries = 0;
+	struct trt *trts;
+	struct acpi_device *adev;
+	union acpi_object *p;
+	struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
+	struct acpi_buffer element = { 0, NULL };
+	struct acpi_buffer trt_format = { sizeof("RRNNNNNN"), "RRNNNNNN" };
+
+	if (!acpi_has_method(handle, "_TRT"))
+		return 0;
+
+	status = acpi_evaluate_object(handle, "_TRT", NULL, &buffer);
+	if (ACPI_FAILURE(status))
+		return -ENODEV;
+
+	p = buffer.pointer;
+	if (!p || (p->type != ACPI_TYPE_PACKAGE)) {
+		pr_err("Invalid _TRT data\n");
+		result = -EFAULT;
+		goto end;
+	}
+
+	*trt_count = p->package.count;
+	trts = kzalloc(*trt_count * sizeof(struct trt), GFP_KERNEL);
+	if (!trts) {
+		result = -ENOMEM;
+		goto end;
+	}
+
+	for (i = 0; i < *trt_count; i++) {
+		struct trt *trt = &trts[i - nr_bad_entries];
+
+		element.length = sizeof(struct trt);
+		element.pointer = trt;
+
+		status = acpi_extract_package(&(p->package.elements[i]),
+					      &trt_format, &element);
+		if (ACPI_FAILURE(status)) {
+			nr_bad_entries++;
+			pr_warn("_TRT package %d is invalid, ignored\n", i);
+			continue;
+		}
+		if (!create_dev)
+			continue;
+
+		result = acpi_bus_get_device(trt->source, &adev);
+		if (!result)
+			acpi_create_platform_device(adev);
+		else
+			pr_warn("Failed to get source ACPI device\n");
+
+		result = acpi_bus_get_device(trt->target, &adev);
+		if (!result)
+			acpi_create_platform_device(adev);
+		else
+			pr_warn("Failed to get target ACPI device\n");
+	}
+
+	*trtp = trts;
+	/* don't count bad entries */
+	*trt_count -= nr_bad_entries;
+end:
+	kfree(buffer.pointer);
+	return result;
+}
+EXPORT_SYMBOL(acpi_parse_trt);
+
+/**
+ * acpi_parse_art - Parse Active Relationship Table _ART
+ *
+ * @handle: ACPI handle of the device contains _ART
+ * @art_count: the number of valid entries resulted from parsing _ART
+ * @artp: pointer to pointer of array of art entries in parsing result
+ * @create_dev: whether to create platform devices for target and source
+ *
+ */
+int acpi_parse_art(acpi_handle handle, int *art_count, struct art **artp,
+		bool create_dev)
+{
+	acpi_status status;
+	int result = 0;
+	int i;
+	int nr_bad_entries = 0;
+	struct art *arts;
+	struct acpi_device *adev;
+	union acpi_object *p;
+	struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
+	struct acpi_buffer element = { 0, NULL };
+	struct acpi_buffer art_format =	{
+		sizeof("RRNNNNNNNNNNN"), "RRNNNNNNNNNNN" };
+
+	if (!acpi_has_method(handle, "_ART"))
+		return 0;
+
+	status = acpi_evaluate_object(handle, "_ART", NULL, &buffer);
+	if (ACPI_FAILURE(status))
+		return -ENODEV;
+
+	p = buffer.pointer;
+	if (!p || (p->type != ACPI_TYPE_PACKAGE)) {
+		pr_err("Invalid _ART data\n");
+		result = -EFAULT;
+		goto end;
+	}
+
+	/* ignore p->package.elements[0], as this is _ART Revision field */
+	*art_count = p->package.count - 1;
+	arts = kzalloc(*art_count * sizeof(struct art), GFP_KERNEL);
+	if (!arts) {
+		result = -ENOMEM;
+		goto end;
+	}
+
+	for (i = 0; i < *art_count; i++) {
+		struct art *art = &arts[i - nr_bad_entries];
+
+		element.length = sizeof(struct art);
+		element.pointer = art;
+
+		status = acpi_extract_package(&(p->package.elements[i + 1]),
+					      &art_format, &element);
+		if (ACPI_FAILURE(status)) {
+			pr_warn("_ART package %d is invalid, ignored", i);
+			nr_bad_entries++;
+			continue;
+		}
+		if (!create_dev)
+			continue;
+
+		if (art->source) {
+			result = acpi_bus_get_device(art->source, &adev);
+			if (!result)
+				acpi_create_platform_device(adev);
+			else
+				pr_warn("Failed to get source ACPI device\n");
+		}
+		if (art->target) {
+			result = acpi_bus_get_device(art->target, &adev);
+			if (!result)
+				acpi_create_platform_device(adev);
+			else
+				pr_warn("Failed to get source ACPI device\n");
+		}
+	}
+
+	*artp = arts;
+	/* don't count bad entries */
+	*art_count -= nr_bad_entries;
+end:
+	kfree(buffer.pointer);
+	return result;
+}
+EXPORT_SYMBOL(acpi_parse_art);
+
+
+/* get device name from acpi handle */
+static void get_single_name(acpi_handle handle, char *name)
+{
+	struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER};
+
+	if (ACPI_FAILURE(acpi_get_name(handle, ACPI_SINGLE_NAME, &buffer)))
+		pr_warn("Failed get name from handle\n");
+	else {
+		memcpy(name, buffer.pointer, ACPI_NAME_SIZE);
+		kfree(buffer.pointer);
+	}
+}
+
+static int fill_art(char __user *ubuf)
+{
+	int i;
+	int ret;
+	int count;
+	int art_len;
+	struct art *arts = NULL;
+	union art_object *art_user;
+
+	ret = acpi_parse_art(acpi_thermal_rel_handle, &count, &arts, false);
+	if (ret)
+		goto free_art;
+	art_len = count * sizeof(union art_object);
+	art_user = kzalloc(art_len, GFP_KERNEL);
+	if (!art_user) {
+		ret = -ENOMEM;
+		goto free_art;
+	}
+	/* now fill in user art data */
+	for (i = 0; i < count; i++) {
+		/* userspace art needs device name instead of acpi reference */
+		get_single_name(arts[i].source, art_user[i].source_device);
+		get_single_name(arts[i].target, art_user[i].target_device);
+		/* copy the rest int data in addition to source and target */
+		memcpy(&art_user[i].weight, &arts[i].weight,
+			sizeof(u64) * (ACPI_NR_ART_ELEMENTS - 2));
+	}
+
+	if (copy_to_user(ubuf, art_user, art_len))
+		ret = -EFAULT;
+	kfree(art_user);
+free_art:
+	kfree(arts);
+	return ret;
+}
+
+static int fill_trt(char __user *ubuf)
+{
+	int i;
+	int ret;
+	int count;
+	int trt_len;
+	struct trt *trts = NULL;
+	union trt_object *trt_user;
+
+	ret = acpi_parse_trt(acpi_thermal_rel_handle, &count, &trts, false);
+	if (ret)
+		goto free_trt;
+	trt_len = count * sizeof(union trt_object);
+	trt_user = kzalloc(trt_len, GFP_KERNEL);
+	if (!trt_user) {
+		ret = -ENOMEM;
+		goto free_trt;
+	}
+	/* now fill in user trt data */
+	for (i = 0; i < count; i++) {
+		/* userspace trt needs device name instead of acpi reference */
+		get_single_name(trts[i].source, trt_user[i].source_device);
+		get_single_name(trts[i].target, trt_user[i].target_device);
+		trt_user[i].sample_period = trts[i].sample_period;
+		trt_user[i].influence = trts[i].influence;
+	}
+
+	if (copy_to_user(ubuf, trt_user, trt_len))
+		ret = -EFAULT;
+	kfree(trt_user);
+free_trt:
+	kfree(trts);
+	return ret;
+}
+
+static long acpi_thermal_rel_ioctl(struct file *f, unsigned int cmd,
+				   unsigned long __arg)
+{
+	int ret = 0;
+	unsigned long length = 0;
+	unsigned long count = 0;
+	char __user *arg = (void __user *)__arg;
+	struct trt *trts;
+	struct art *arts;
+
+	switch (cmd) {
+	case ACPI_THERMAL_GET_TRT_COUNT:
+		ret = acpi_parse_trt(acpi_thermal_rel_handle, (int *)&count,
+				&trts, false);
+		kfree(trts);
+		if (!ret)
+			return put_user(count, (unsigned long __user *)__arg);
+		return ret;
+	case ACPI_THERMAL_GET_TRT_LEN:
+		ret = acpi_parse_trt(acpi_thermal_rel_handle, (int *)&count,
+				&trts, false);
+		kfree(trts);
+		length = count * sizeof(union trt_object);
+		if (!ret)
+			return put_user(length, (unsigned long __user *)__arg);
+		return ret;
+	case ACPI_THERMAL_GET_TRT:
+		return fill_trt(arg);
+	case ACPI_THERMAL_GET_ART_COUNT:
+		ret = acpi_parse_art(acpi_thermal_rel_handle, (int *)&count,
+				&arts, false);
+		kfree(arts);
+		if (!ret)
+			return put_user(count, (unsigned long __user *)__arg);
+		return ret;
+	case ACPI_THERMAL_GET_ART_LEN:
+		ret = acpi_parse_art(acpi_thermal_rel_handle, (int *)&count,
+				&arts, false);
+		kfree(arts);
+		length = count * sizeof(union art_object);
+		if (!ret)
+			return put_user(length, (unsigned long __user *)__arg);
+		return ret;
+
+	case ACPI_THERMAL_GET_ART:
+		return fill_art(arg);
+
+	default:
+		return -ENOTTY;
+	}
+}
+
+static const struct file_operations acpi_thermal_rel_fops = {
+	.owner		= THIS_MODULE,
+	.open		= acpi_thermal_rel_open,
+	.release	= acpi_thermal_rel_release,
+	.unlocked_ioctl	= acpi_thermal_rel_ioctl,
+	.llseek		= no_llseek,
+};
+
+static struct miscdevice acpi_thermal_rel_misc_device = {
+	.minor	= MISC_DYNAMIC_MINOR,
+	"acpi_thermal_rel",
+	&acpi_thermal_rel_fops
+};
+
+int acpi_thermal_rel_misc_device_add(acpi_handle handle)
+{
+	acpi_thermal_rel_handle = handle;
+
+	return misc_register(&acpi_thermal_rel_misc_device);
+}
+EXPORT_SYMBOL(acpi_thermal_rel_misc_device_add);
+
+int acpi_thermal_rel_misc_device_remove(acpi_handle handle)
+{
+	misc_deregister(&acpi_thermal_rel_misc_device);
+
+	return 0;
+}
+EXPORT_SYMBOL(acpi_thermal_rel_misc_device_remove);
+
+MODULE_AUTHOR("Zhang Rui <rui.zhang@intel.com>");
+MODULE_AUTHOR("Jacob Pan <jacob.jun.pan@intel.com");
+MODULE_DESCRIPTION("Intel acpi thermal rel misc dev driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/thermal/int340x_thermal/acpi_thermal_rel.h b/drivers/thermal/int340x_thermal/acpi_thermal_rel.h
new file mode 100644
index 0000000..f00700b
--- /dev/null
+++ b/drivers/thermal/int340x_thermal/acpi_thermal_rel.h
@@ -0,0 +1,84 @@
+#ifndef __ACPI_ACPI_THERMAL_H
+#define __ACPI_ACPI_THERMAL_H
+
+#include <asm/ioctl.h>
+
+#define ACPI_THERMAL_MAGIC 's'
+
+#define ACPI_THERMAL_GET_TRT_LEN _IOR(ACPI_THERMAL_MAGIC, 1, unsigned long)
+#define ACPI_THERMAL_GET_ART_LEN _IOR(ACPI_THERMAL_MAGIC, 2, unsigned long)
+#define ACPI_THERMAL_GET_TRT_COUNT _IOR(ACPI_THERMAL_MAGIC, 3, unsigned long)
+#define ACPI_THERMAL_GET_ART_COUNT _IOR(ACPI_THERMAL_MAGIC, 4, unsigned long)
+
+#define ACPI_THERMAL_GET_TRT	_IOR(ACPI_THERMAL_MAGIC, 5, unsigned long)
+#define ACPI_THERMAL_GET_ART	_IOR(ACPI_THERMAL_MAGIC, 6, unsigned long)
+
+struct art {
+	acpi_handle source;
+	acpi_handle target;
+	u64 weight;
+	u64 ac0_max;
+	u64 ac1_max;
+	u64 ac2_max;
+	u64 ac3_max;
+	u64 ac4_max;
+	u64 ac5_max;
+	u64 ac6_max;
+	u64 ac7_max;
+	u64 ac8_max;
+	u64 ac9_max;
+} __packed;
+
+struct trt {
+	acpi_handle source;
+	acpi_handle target;
+	u64 influence;
+	u64 sample_period;
+	u64 reverved1;
+	u64 reverved2;
+	u64 reverved3;
+	u64 reverved4;
+} __packed;
+
+#define ACPI_NR_ART_ELEMENTS 13
+/* for usrspace */
+union art_object {
+	struct {
+		char source_device[8]; /* ACPI single name */
+		char target_device[8]; /* ACPI single name */
+		u64 weight;
+		u64 ac0_max_level;
+		u64 ac1_max_level;
+		u64 ac2_max_level;
+		u64 ac3_max_level;
+		u64 ac4_max_level;
+		u64 ac5_max_level;
+		u64 ac6_max_level;
+		u64 ac7_max_level;
+		u64 ac8_max_level;
+		u64 ac9_max_level;
+	};
+	u64 __data[ACPI_NR_ART_ELEMENTS];
+};
+
+union trt_object {
+	struct {
+		char source_device[8]; /* ACPI single name */
+		char target_device[8]; /* ACPI single name */
+		u64 influence;
+		u64 sample_period;
+		u64 reserved[4];
+	};
+	u64 __data[8];
+};
+
+#ifdef __KERNEL__
+int acpi_thermal_rel_misc_device_add(acpi_handle handle);
+int acpi_thermal_rel_misc_device_remove(acpi_handle handle);
+int acpi_parse_art(acpi_handle handle, int *art_count, struct art **arts,
+		bool create_dev);
+int acpi_parse_trt(acpi_handle handle, int *trt_count, struct trt **trts,
+		bool create_dev);
+#endif
+
+#endif /* __ACPI_ACPI_THERMAL_H */
-- 
1.8.3.2


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

* [PATCH 17/19] Thermal: int3400_thermal: use acpi_thermal_rel parsing APIs
  2014-09-18  1:53 [PATCH 00/19] Thermal: ACPI INT340X thermal drivers Zhang Rui
                   ` (15 preceding siblings ...)
  2014-09-18  1:53 ` [PATCH 16/19] Thermal: int340x_thermal: expose acpi thermal relationship tables Zhang Rui
@ 2014-09-18  1:53 ` Zhang Rui
  2014-09-18  1:53 ` [PATCH 18/19] Thermal: introduce INT3406 thermal driver Zhang Rui
  2014-09-18  1:53 ` [PATCH 19/19] Thermal: int340x thermal: select ACPI fan driver Zhang Rui
  18 siblings, 0 replies; 23+ messages in thread
From: Zhang Rui @ 2014-09-18  1:53 UTC (permalink / raw)
  To: linux-pm, linux-acpi; +Cc: rjw, lenb, eduardo.valentin, Jacob Pan, Zhang Rui

From: Jacob Pan <jacob.jun.pan@linux.intel.com>

ACPI _TRT and _ART parsing code has been moved to acpi_thermal_rel such
that it can be used by other devices in the future. Use the parsing APIs
in acpi_thermal_rel.c instead.

Signed-off-by: Jacob Pan <jacob.jun.pan@linux.intel.com>
Signed-off-by: Zhang Rui <rui.zhang@intel.com>
---
 drivers/thermal/int340x_thermal/int3400_thermal.c | 172 ++--------------------
 1 file changed, 13 insertions(+), 159 deletions(-)

diff --git a/drivers/thermal/int340x_thermal/int3400_thermal.c b/drivers/thermal/int340x_thermal/int3400_thermal.c
index 9104b4f..edc1cce 100644
--- a/drivers/thermal/int340x_thermal/int3400_thermal.c
+++ b/drivers/thermal/int340x_thermal/int3400_thermal.c
@@ -14,33 +14,7 @@
 #include <linux/platform_device.h>
 #include <linux/acpi.h>
 #include <linux/thermal.h>
-
-struct art {
-	acpi_handle source;
-	acpi_handle target;
-	u64 weight;
-	u64 ac0_max;
-	u64 ac1_max;
-	u64 ac2_max;
-	u64 ac3_max;
-	u64 ac4_max;
-	u64 ac5_max;
-	u64 ac6_max;
-	u64 ac7_max;
-	u64 ac8_max;
-	u64 ac9_max;
-};
-
-struct trt {
-	acpi_handle source;
-	acpi_handle target;
-	u64 influence;
-	u64 sampling_period;
-	u64 reverved1;
-	u64 reverved2;
-	u64 reverved3;
-	u64 reverved4;
-};
+#include "acpi_thermal_rel.h"
 
 enum int3400_thermal_uuid {
 	INT3400_THERMAL_PASSIVE_1,
@@ -68,6 +42,7 @@ struct int3400_thermal_priv {
 	int trt_count;
 	struct trt *trts;
 	u8 uuid_bitmap;
+	int rel_misc_dev_res;
 };
 
 static int int3400_thermal_get_uuids(struct int3400_thermal_priv *priv)
@@ -146,136 +121,6 @@ static int int3400_thermal_run_osc(acpi_handle handle,
 	return result;
 }
 
-
-static int parse_art(struct int3400_thermal_priv *priv)
-{
-	acpi_handle handle = priv->adev->handle;
-	acpi_status status;
-	int result = 0;
-	int i;
-	struct acpi_device *adev;
-	union acpi_object *p;
-	struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
-	struct acpi_buffer element = { 0, NULL };
-	struct acpi_buffer art_format = {
-				sizeof("RRNNNNNNNNNNN"), "RRNNNNNNNNNNN" };
-
-	if (!acpi_has_method(handle, "_ART"))
-		return 0;
-
-	status = acpi_evaluate_object(handle, "_ART", NULL, &buffer);
-	if (ACPI_FAILURE(status))
-		return -ENODEV;
-
-	p = buffer.pointer;
-	if (!p || (p->type != ACPI_TYPE_PACKAGE)) {
-		pr_err("Invalid _ART data\n");
-		result = -EFAULT;
-		goto end;
-	}
-
-	/* ignore p->package.elements[0], as this is _ART Revision field */
-	priv->art_count = p->package.count - 1;
-	priv->arts = kzalloc(sizeof(struct art) * priv->art_count, GFP_KERNEL);
-	if (!priv->arts) {
-		result = -ENOMEM;
-		goto end;
-	}
-
-	for (i = 0; i < priv->art_count; i++) {
-		struct art *art = &(priv->arts[i]);
-
-		element.length = sizeof(struct art);
-		element.pointer = art;
-
-		status = acpi_extract_package(&(p->package.elements[i + 1]),
-					      &art_format, &element);
-		if (ACPI_FAILURE(status)) {
-			pr_err("Invalid _ART data");
-			result = -EFAULT;
-			kfree(priv->arts);
-			goto end;
-		}
-		result = acpi_bus_get_device(art->source, &adev);
-		if (!result)
-			acpi_create_platform_device(adev, NULL);
-		else
-			pr_warn("Failed to get source ACPI device\n");
-		result = acpi_bus_get_device(art->target, &adev);
-		if (!result)
-			acpi_create_platform_device(adev, NULL);
-		else
-			pr_warn("Failed to get source ACPI device\n");
-	}
-end:
-	kfree(buffer.pointer);
-	return result;
-}
-
-static int parse_trt(struct int3400_thermal_priv *priv)
-{
-	acpi_handle handle = priv->adev->handle;
-	acpi_status status;
-	int result = 0;
-	int i;
-	struct acpi_device *adev;
-	union acpi_object *p;
-	struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
-	struct acpi_buffer element = { 0, NULL };
-	struct acpi_buffer trt_format = { sizeof("RRNNNNNN"), "RRNNNNNN" };
-
-	if (!acpi_has_method(handle, "_TRT"))
-		return 0;
-
-	status = acpi_evaluate_object(handle, "_TRT", NULL, &buffer);
-	if (ACPI_FAILURE(status))
-		return -ENODEV;
-
-	p = buffer.pointer;
-	if (!p || (p->type != ACPI_TYPE_PACKAGE)) {
-		pr_err("Invalid _TRT data\n");
-		result = -EFAULT;
-		goto end;
-	}
-
-	priv->trt_count = p->package.count;
-	priv->trts = kzalloc(sizeof(struct trt) * priv->trt_count, GFP_KERNEL);
-	if (!priv->trts) {
-		result = -ENOMEM;
-		goto end;
-	}
-
-	for (i = 0; i < priv->trt_count; i++) {
-		struct trt *trt = &(priv->trts[i]);
-
-		element.length = sizeof(struct trt);
-		element.pointer = trt;
-
-		status = acpi_extract_package(&(p->package.elements[i]),
-					      &trt_format, &element);
-		if (ACPI_FAILURE(status)) {
-			pr_err("Invalid _ART data");
-			result = -EFAULT;
-			kfree(priv->trts);
-			goto end;
-		}
-
-		result = acpi_bus_get_device(trt->source, &adev);
-		if (!result)
-			acpi_create_platform_device(adev, NULL);
-		else
-			pr_warn("Failed to get source ACPI device\n");
-		result = acpi_bus_get_device(trt->target, &adev);
-		if (!result)
-			acpi_create_platform_device(adev, NULL);
-		else
-			pr_warn("Failed to get target ACPI device\n");
-	}
-end:
-	kfree(buffer.pointer);
-	return result;
-}
-
 static int int3400_thermal_get_temp(struct thermal_zone_device *thermal,
 			unsigned long *temp)
 {
@@ -350,11 +195,14 @@ static int int3400_thermal_probe(struct platform_device *pdev)
 	if (result)
 		goto free_priv;
 
-	result = parse_art(priv);
+	result = acpi_parse_art(priv->adev->handle, &priv->art_count,
+				&priv->arts, true);
 	if (result)
 		goto free_priv;
 
-	result = parse_trt(priv);
+
+	result = acpi_parse_trt(priv->adev->handle, &priv->trt_count,
+				&priv->trts, true);
 	if (result)
 		goto free_art;
 
@@ -372,6 +220,9 @@ static int int3400_thermal_probe(struct platform_device *pdev)
 		goto free_trt;
 	}
 
+	priv->rel_misc_dev_res = acpi_thermal_rel_misc_device_add(
+							priv->adev->handle);
+
 	return 0;
 free_trt:
 	kfree(priv->trts);
@@ -386,6 +237,9 @@ static int int3400_thermal_remove(struct platform_device *pdev)
 {
 	struct int3400_thermal_priv *priv = platform_get_drvdata(pdev);
 
+	if (!priv->rel_misc_dev_res)
+		acpi_thermal_rel_misc_device_remove(priv->adev->handle);
+
 	thermal_zone_device_unregister(priv->thermal);
 	kfree(priv->trts);
 	kfree(priv->arts);
-- 
1.8.3.2


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

* [PATCH 18/19] Thermal: introduce INT3406 thermal driver
  2014-09-18  1:53 [PATCH 00/19] Thermal: ACPI INT340X thermal drivers Zhang Rui
                   ` (16 preceding siblings ...)
  2014-09-18  1:53 ` [PATCH 17/19] Thermal: int3400_thermal: use acpi_thermal_rel parsing APIs Zhang Rui
@ 2014-09-18  1:53 ` Zhang Rui
  2014-09-18  1:53 ` [PATCH 19/19] Thermal: int340x thermal: select ACPI fan driver Zhang Rui
  18 siblings, 0 replies; 23+ messages in thread
From: Zhang Rui @ 2014-09-18  1:53 UTC (permalink / raw)
  To: linux-pm, linux-acpi; +Cc: rjw, lenb, eduardo.valentin, Aaron Lu, Zhang Rui

From: Aaron Lu <aaron.lu@intel.com>

INT3406 ACPI device object resembles an ACPI video output device, but its
_BCM is said to be deprecated and should not be used. So we will make
use of the raw interface to do the actual cooling. Due to this, the
backlight core has some modifications. Also, to re-use some of the ACPI
video module's code, one function has been exported.

Signed-off-by: Aaron Lu <aaron.lu@intel.com>
Signed-off-by: Zhang Rui <rui.zhang@intel.com>
---
 drivers/acpi/video.c                              |  78 ++++----
 drivers/thermal/int340x_thermal/Makefile          |   1 +
 drivers/thermal/int340x_thermal/int3406_thermal.c | 230 ++++++++++++++++++++++
 drivers/video/backlight/backlight.c               |  44 +++--
 include/acpi/video.h                              |  20 ++
 include/linux/backlight.h                         |   2 +
 6 files changed, 323 insertions(+), 52 deletions(-)
 create mode 100644 drivers/thermal/int340x_thermal/int3406_thermal.c

diff --git a/drivers/acpi/video.c b/drivers/acpi/video.c
index 8268843..b260432 100644
--- a/drivers/acpi/video.c
+++ b/drivers/acpi/video.c
@@ -186,19 +186,6 @@ struct acpi_video_device_cap {
 	u8 _DDC:1;		/* Return the EDID for this device */
 };
 
-struct acpi_video_brightness_flags {
-	u8 _BCL_no_ac_battery_levels:1;	/* no AC/Battery levels in _BCL */
-	u8 _BCL_reversed:1;		/* _BCL package is in a reversed order */
-	u8 _BQC_use_index:1;		/* _BQC returns an index value */
-};
-
-struct acpi_video_device_brightness {
-	int curr;
-	int count;
-	int *levels;
-	struct acpi_video_brightness_flags flags;
-};
-
 struct acpi_video_device {
 	unsigned long device_id;
 	struct acpi_video_device_flags flags;
@@ -344,7 +331,7 @@ static const struct thermal_cooling_device_ops video_cooling_ops = {
  */
 
 static int
-acpi_video_device_lcd_query_levels(struct acpi_video_device *device,
+acpi_video_device_lcd_query_levels(acpi_handle handle,
 				   union acpi_object **levels)
 {
 	int status;
@@ -354,7 +341,7 @@ acpi_video_device_lcd_query_levels(struct acpi_video_device *device,
 
 	*levels = NULL;
 
-	status = acpi_evaluate_object(device->dev->handle, "_BCL", NULL, &buffer);
+	status = acpi_evaluate_object(handle, "_BCL", NULL, &buffer);
 	if (!ACPI_SUCCESS(status))
 		return status;
 	obj = (union acpi_object *)buffer.pointer;
@@ -943,29 +930,18 @@ static int acpi_video_bqc_quirk(struct acpi_video_device *device,
 	return 0;
 }
 
-
-/*
- *  Arg:
- *	device	: video output device (LCD, CRT, ..)
- *
- *  Return Value:
- *	Maximum brightness level
- *
- *  Allocate and initialize device->brightness.
- */
-
-static int
-acpi_video_init_brightness(struct acpi_video_device *device)
+int acpi_video_get_levels(struct acpi_device *device,
+			  struct acpi_video_device_brightness **dev_br)
 {
 	union acpi_object *obj = NULL;
 	int i, max_level = 0, count = 0, level_ac_battery = 0;
-	unsigned long long level, level_old;
 	union acpi_object *o;
 	struct acpi_video_device_brightness *br = NULL;
-	int result = -EINVAL;
+	int result = 0;
 	u32 value;
 
-	if (!ACPI_SUCCESS(acpi_video_device_lcd_query_levels(device, &obj))) {
+	if (!ACPI_SUCCESS(acpi_video_device_lcd_query_levels(device->handle,
+								&obj))) {
 		ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Could not query available "
 						"LCD brightness level\n"));
 		goto out;
@@ -1038,6 +1014,38 @@ acpi_video_init_brightness(struct acpi_video_device *device)
 			    "Found unordered _BCL package"));
 
 	br->count = count;
+	*dev_br = br;
+
+out:
+	kfree(obj);
+	return result;
+out_free:
+	kfree(br);
+	goto out;
+}
+EXPORT_SYMBOL(acpi_video_get_levels);
+
+/*
+ *  Arg:
+ *	device	: video output device (LCD, CRT, ..)
+ *
+ *  Return Value:
+ *	Maximum brightness level
+ *
+ *  Allocate and initialize device->brightness.
+ */
+
+static int
+acpi_video_init_brightness(struct acpi_video_device *device)
+{
+	int i, max_level = 0;
+	unsigned long long level, level_old;
+	struct acpi_video_device_brightness *br = NULL;
+	int result = -EINVAL;
+
+	result = acpi_video_get_levels(device->dev, &br);
+	if (result)
+		return result;
 	device->brightness = br;
 
 	/* _BQC uses INDEX while _BCL uses VALUE in some laptops */
@@ -1080,17 +1088,13 @@ acpi_video_init_brightness(struct acpi_video_device *device)
 		goto out_free_levels;
 
 	ACPI_DEBUG_PRINT((ACPI_DB_INFO,
-			  "found %d brightness levels\n", count - 2));
-	kfree(obj);
-	return result;
+			  "found %d brightness levels\n", br->count - 2));
+	return 0;
 
 out_free_levels:
 	kfree(br->levels);
-out_free:
 	kfree(br);
-out:
 	device->brightness = NULL;
-	kfree(obj);
 	return result;
 }
 
diff --git a/drivers/thermal/int340x_thermal/Makefile b/drivers/thermal/int340x_thermal/Makefile
index ffe40bf..e3e2eb2 100644
--- a/drivers/thermal/int340x_thermal/Makefile
+++ b/drivers/thermal/int340x_thermal/Makefile
@@ -1,4 +1,5 @@
 obj-$(CONFIG_INT340X_THERMAL)	+= int3400_thermal.o
 obj-$(CONFIG_INT340X_THERMAL)	+= int3402_thermal.o
 obj-$(CONFIG_INT340X_THERMAL)	+= int3403_thermal.o
+obj-$(CONFIG_INT340X_THERMAL)	+= int3406_thermal.o
 obj-$(CONFIG_ACPI_THERMAL_REL)	+= acpi_thermal_rel.o
diff --git a/drivers/thermal/int340x_thermal/int3406_thermal.c b/drivers/thermal/int340x_thermal/int3406_thermal.c
new file mode 100644
index 0000000..162ddee
--- /dev/null
+++ b/drivers/thermal/int340x_thermal/int3406_thermal.c
@@ -0,0 +1,230 @@
+/*
+ * INT3406 thermal driver for display participant device
+ *
+ * Copyright (C) 2014, Intel Corporation
+ * Authors: Aaron Lu <aaron.lu@intel.com>
+ *
+ * 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.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/acpi.h>
+#include <linux/backlight.h>
+#include <linux/thermal.h>
+#include <acpi/video.h>
+
+#define INT3406_BRIGHTNESS_LIMITS_CHANGED	0x80
+
+struct int3406_thermal_data {
+	int upper_limit;
+	int upper_limit_index;
+	int lower_limit;
+	int lower_limit_index;
+	acpi_handle handle;
+	struct acpi_video_device_brightness *br;
+	struct backlight_device *raw_bd;
+	struct thermal_cooling_device *cooling_dev;
+};
+
+static int int3406_thermal_to_raw(int level, struct int3406_thermal_data *d)
+{
+	int max_level = d->br->levels[d->br->count - 1];
+	int raw_max = d->raw_bd->props.max_brightness;
+
+	return level * raw_max / max_level;
+}
+
+static int int3406_thermal_to_acpi(int level, struct int3406_thermal_data *d)
+{
+	int raw_max = d->raw_bd->props.max_brightness;
+	int max_level = d->br->levels[d->br->count - 1];
+
+	return level * max_level / raw_max;
+}
+
+static int
+int3406_thermal_get_max_state(struct thermal_cooling_device *cooling_dev,
+			      unsigned long *state)
+{
+	struct int3406_thermal_data *d = cooling_dev->devdata;
+	int index = d->lower_limit_index ? d->lower_limit_index : 2;
+
+	*state = d->br->count - 1 - index;
+	return 0;
+}
+
+static int
+int3406_thermal_set_cur_state(struct thermal_cooling_device *cooling_dev,
+			      unsigned long state)
+{
+	struct int3406_thermal_data *d = cooling_dev->devdata;
+	int level, raw_level;
+
+	if (state > d->br->count - 3)
+		return -EINVAL;
+
+	state = d->br->count - 1 - state;
+	level = d->br->levels[state];
+
+	if ((d->upper_limit && level > d->upper_limit) ||
+	    (d->lower_limit && level < d->lower_limit))
+		return -EINVAL;
+
+	raw_level = int3406_thermal_to_raw(level, d);
+	return backlight_device_set_brightness(d->raw_bd, raw_level);
+}
+
+static int
+int3406_thermal_get_cur_state(struct thermal_cooling_device *cooling_dev,
+			      unsigned long *state)
+{
+	struct int3406_thermal_data *d = cooling_dev->devdata;
+	int raw_level, level, i;
+
+	raw_level = d->raw_bd->props.brightness;
+	level = int3406_thermal_to_acpi(raw_level, d);
+
+	/*
+	 * There is no 1:1 mapping between the firmware interface level with the
+	 * raw interface level, we will have to find one that is close enough.
+	 */
+	for (i = 2; i < d->br->count - 1; i++) {
+		if (level >= d->br->levels[i] && level <= d->br->levels[i + 1])
+			break;
+	}
+
+	*state = i;
+	return 0;
+}
+
+static const struct thermal_cooling_device_ops video_cooling_ops = {
+	.get_max_state = int3406_thermal_get_max_state,
+	.get_cur_state = int3406_thermal_get_cur_state,
+	.set_cur_state = int3406_thermal_set_cur_state,
+};
+
+static int int3406_thermal_get_index(int *array, int nr, int value)
+{
+	int i;
+
+	for (i = 0; i < nr; i++) {
+		if (array[i] == value)
+			break;
+	}
+	return i == nr ? -ENOENT : i;
+}
+
+static void int3406_thermal_get_limit(struct int3406_thermal_data *d)
+{
+	acpi_status status;
+	unsigned long long lower_limit, upper_limit;
+	int index;
+
+	status = acpi_evaluate_integer(d->handle, "DDDL", NULL, &lower_limit);
+	if (ACPI_SUCCESS(status)) {
+		index = int3406_thermal_get_index(d->br->levels, d->br->count,
+						  lower_limit);
+		if (index > 0) {
+			d->lower_limit = (int)lower_limit;
+			d->lower_limit_index = index;
+		}
+	}
+
+	status = acpi_evaluate_integer(d->handle, "DDPC", NULL, &upper_limit);
+	if (ACPI_SUCCESS(status)) {
+		index = int3406_thermal_get_index(d->br->levels, d->br->count,
+						  upper_limit);
+		if (index > 0) {
+			d->upper_limit = (int)upper_limit;
+			d->upper_limit_index = index;
+		}
+	}
+}
+
+static void int3406_notify(acpi_handle handle, u32 event, void *data)
+{
+	if (event == INT3406_BRIGHTNESS_LIMITS_CHANGED)
+		int3406_thermal_get_limit(data);
+}
+
+static int int3406_thermal_probe(struct platform_device *pdev)
+{
+	struct acpi_device *adev = ACPI_COMPANION(&pdev->dev);
+	struct int3406_thermal_data *d;
+	struct backlight_device *bd;
+	int ret;
+
+	if (!ACPI_HANDLE(&pdev->dev))
+		return -ENODEV;
+
+	d = devm_kzalloc(&pdev->dev, sizeof(*d), GFP_KERNEL);
+	if (!d)
+		return -ENOMEM;
+	d->handle = ACPI_HANDLE(&pdev->dev);
+
+	bd = backlight_device_get_by_type(BACKLIGHT_RAW);
+	if (!bd)
+		return -ENODEV;
+	d->raw_bd = bd;
+
+	ret = acpi_video_get_levels(ACPI_COMPANION(&pdev->dev), &d->br);
+	if (ret)
+		return ret;
+
+	int3406_thermal_get_limit(d);
+
+	d->cooling_dev = thermal_cooling_device_register(acpi_device_bid(adev),
+							 d, &video_cooling_ops);
+	if (IS_ERR(d->cooling_dev))
+		goto err;
+
+	ret = acpi_install_notify_handler(adev->handle, ACPI_DEVICE_NOTIFY,
+					  int3406_notify, d);
+	if (ret)
+		goto err_cdev;
+
+	platform_set_drvdata(pdev, d);
+
+	return 0;
+
+err_cdev:
+	thermal_cooling_device_unregister(d->cooling_dev);
+err:
+	kfree(d->br);
+	return -ENODEV;
+}
+
+static int int3406_thermal_remove(struct platform_device *pdev)
+{
+	struct int3406_thermal_data *d = platform_get_drvdata(pdev);
+
+	thermal_cooling_device_unregister(platform_get_drvdata(pdev));
+	kfree(d->br);
+	return 0;
+}
+
+static const struct acpi_device_id int3406_thermal_match[] = {
+	{"INT3406", 0},
+	{}
+};
+
+MODULE_DEVICE_TABLE(acpi, int3406_thermal_match);
+
+static struct platform_driver int3406_thermal_driver = {
+	.probe = int3406_thermal_probe,
+	.remove = int3406_thermal_remove,
+	.driver = {
+		   .name = "int3406 thermal",
+		   .owner = THIS_MODULE,
+		   .acpi_match_table = int3406_thermal_match,
+		   },
+};
+
+module_platform_driver(int3406_thermal_driver);
+
+MODULE_DESCRIPTION("INT3406 Thermal driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/video/backlight/backlight.c b/drivers/video/backlight/backlight.c
index bddc8b1..bea7493 100644
--- a/drivers/video/backlight/backlight.c
+++ b/drivers/video/backlight/backlight.c
@@ -164,28 +164,19 @@ static ssize_t brightness_show(struct device *dev,
 	return sprintf(buf, "%d\n", bd->props.brightness);
 }
 
-static ssize_t brightness_store(struct device *dev,
-		struct device_attribute *attr, const char *buf, size_t count)
+int backlight_device_set_brightness(struct backlight_device *bd, int brightness)
 {
-	int rc;
-	struct backlight_device *bd = to_backlight_device(dev);
-	unsigned long brightness;
-
-	rc = kstrtoul(buf, 0, &brightness);
-	if (rc)
-		return rc;
-
-	rc = -ENXIO;
+	int rc = -ENXIO;
 
 	mutex_lock(&bd->ops_lock);
 	if (bd->ops) {
 		if (brightness > bd->props.max_brightness)
 			rc = -EINVAL;
 		else {
-			pr_debug("set brightness to %lu\n", brightness);
+			pr_debug("set brightness to %u\n", brightness);
 			bd->props.brightness = brightness;
 			backlight_update_status(bd);
-			rc = count;
+			rc = 0;
 		}
 	}
 	mutex_unlock(&bd->ops_lock);
@@ -194,6 +185,23 @@ static ssize_t brightness_store(struct device *dev,
 
 	return rc;
 }
+EXPORT_SYMBOL(backlight_device_set_brightness);
+
+static ssize_t brightness_store(struct device *dev,
+		struct device_attribute *attr, const char *buf, size_t count)
+{
+	int rc;
+	struct backlight_device *bd = to_backlight_device(dev);
+	unsigned long brightness;
+
+	rc = kstrtoul(buf, 0, &brightness);
+	if (rc)
+		return rc;
+
+	rc = backlight_device_set_brightness(bd, brightness);
+
+	return rc ? rc : count;
+}
 static DEVICE_ATTR_RW(brightness);
 
 static ssize_t type_show(struct device *dev, struct device_attribute *attr,
@@ -380,7 +388,7 @@ struct backlight_device *backlight_device_register(const char *name,
 }
 EXPORT_SYMBOL(backlight_device_register);
 
-bool backlight_device_registered(enum backlight_type type)
+struct backlight_device *backlight_device_get_by_type(enum backlight_type type)
 {
 	bool found = false;
 	struct backlight_device *bd;
@@ -394,7 +402,13 @@ bool backlight_device_registered(enum backlight_type type)
 	}
 	mutex_unlock(&backlight_dev_list_mutex);
 
-	return found;
+	return found ? bd : NULL;
+}
+EXPORT_SYMBOL(backlight_device_get_by_type);
+
+bool backlight_device_registered(enum backlight_type type)
+{
+	return backlight_device_get_by_type(type) ? true : false;
 }
 EXPORT_SYMBOL(backlight_device_registered);
 
diff --git a/include/acpi/video.h b/include/acpi/video.h
index 843ef1a..956300d 100644
--- a/include/acpi/video.h
+++ b/include/acpi/video.h
@@ -3,6 +3,19 @@
 
 #include <linux/errno.h> /* for ENODEV */
 
+struct acpi_video_brightness_flags {
+	u8 _BCL_no_ac_battery_levels:1;	/* no AC/Battery levels in _BCL */
+	u8 _BCL_reversed:1;		/* _BCL package is in a reversed order */
+	u8 _BQC_use_index:1;		/* _BQC returns an index value */
+};
+
+struct acpi_video_device_brightness {
+	int curr;
+	int count;
+	int *levels;
+	struct acpi_video_brightness_flags flags;
+};
+
 struct acpi_device;
 
 #define ACPI_VIDEO_CLASS	"video"
@@ -22,6 +35,8 @@ extern void acpi_video_unregister(void);
 extern void acpi_video_unregister_backlight(void);
 extern int acpi_video_get_edid(struct acpi_device *device, int type,
 			       int device_id, void **edid);
+extern int acpi_video_get_levels(struct acpi_device *device,
+				struct acpi_video_device_brightness **dev_br);
 extern bool acpi_video_verify_backlight_support(void);
 #else
 static inline int acpi_video_register(void) { return 0; }
@@ -32,6 +47,11 @@ static inline int acpi_video_get_edid(struct acpi_device *device, int type,
 {
 	return -ENODEV;
 }
+static int acpi_video_get_levels(struct acpi_device *device,
+			struct acpi_video_device_brightness **dev_br)
+{
+	return -ENODEV;
+}
 static inline bool acpi_video_verify_backlight_support(void) { return false; }
 #endif
 
diff --git a/include/linux/backlight.h b/include/linux/backlight.h
index adb14a8..c59a020 100644
--- a/include/linux/backlight.h
+++ b/include/linux/backlight.h
@@ -140,6 +140,8 @@ extern void backlight_force_update(struct backlight_device *bd,
 extern bool backlight_device_registered(enum backlight_type type);
 extern int backlight_register_notifier(struct notifier_block *nb);
 extern int backlight_unregister_notifier(struct notifier_block *nb);
+extern struct backlight_device *backlight_device_get_by_type(enum backlight_type type);
+extern int backlight_device_set_brightness(struct backlight_device *bd, int brightness);
 
 #define to_backlight_device(obj) container_of(obj, struct backlight_device, dev)
 
-- 
1.8.3.2


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

* [PATCH 19/19] Thermal: int340x thermal: select ACPI fan driver
  2014-09-18  1:53 [PATCH 00/19] Thermal: ACPI INT340X thermal drivers Zhang Rui
                   ` (17 preceding siblings ...)
  2014-09-18  1:53 ` [PATCH 18/19] Thermal: introduce INT3406 thermal driver Zhang Rui
@ 2014-09-18  1:53 ` Zhang Rui
  18 siblings, 0 replies; 23+ messages in thread
From: Zhang Rui @ 2014-09-18  1:53 UTC (permalink / raw)
  To: linux-pm, linux-acpi; +Cc: rjw, lenb, eduardo.valentin, Zhang Rui

we share the same driver for both ACPI predefined Fan device
and INT3404 Fan device, thus we should select the ACPI Fan
driver when int340x thermal drivers are enabeld.

Signed-off-by: Zhang Rui <rui.zhang@intel.com>
---
 drivers/acpi/Kconfig    | 2 +-
 drivers/thermal/Kconfig | 1 +
 2 files changed, 2 insertions(+), 1 deletion(-)

diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig
index d0f3265..b23fe37 100644
--- a/drivers/acpi/Kconfig
+++ b/drivers/acpi/Kconfig
@@ -144,7 +144,7 @@ config ACPI_VIDEO
 
 config ACPI_FAN
 	tristate "Fan"
-	select THERMAL
+	depends on THERMAL
 	default y
 	help
 	  This driver supports ACPI fan devices, allowing user-mode
diff --git a/drivers/thermal/Kconfig b/drivers/thermal/Kconfig
index 3a89292..e65ca2f 100644
--- a/drivers/thermal/Kconfig
+++ b/drivers/thermal/Kconfig
@@ -224,6 +224,7 @@ config INT340X_THERMAL
 	depends on X86 && ACPI
 	select THERMAL_GOV_USER_SPACE
 	select ACPI_THERMAL_REL
+	select ACPI_FAN
 	help
 	  Newer laptops and tablets that use ACPI may have thermal sensors and
 	  other devices with thermal control capabilities outside the core
-- 
1.8.3.2


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

* Re: [PATCH 02/19] ACPI: make acpi_create_platform_device() an external API
  2014-09-18  1:53 ` [PATCH 02/19] ACPI: make acpi_create_platform_device() an external API Zhang Rui
@ 2014-09-24 21:55   ` Rafael J. Wysocki
  0 siblings, 0 replies; 23+ messages in thread
From: Rafael J. Wysocki @ 2014-09-24 21:55 UTC (permalink / raw)
  To: Zhang Rui; +Cc: linux-pm, linux-acpi, lenb, eduardo.valentin

On Thursday, September 18, 2014 09:53:33 AM Zhang Rui wrote:
> Signed-off-by: Zhang Rui <rui.zhang@intel.com>
> ---
>  drivers/acpi/acpi_platform.c | 1 +
>  include/linux/acpi.h         | 7 +++++++
>  2 files changed, 8 insertions(+)
> 
> diff --git a/drivers/acpi/acpi_platform.c b/drivers/acpi/acpi_platform.c
> index 2bf9082..a3c89a1 100644
> --- a/drivers/acpi/acpi_platform.c
> +++ b/drivers/acpi/acpi_platform.c
> @@ -113,3 +113,4 @@ struct platform_device *acpi_create_platform_device(struct acpi_device *adev)
>  	kfree(resources);
>  	return pdev;
>  }
> +EXPORT_SYMBOL_GPL(acpi_create_platform_device);
> diff --git a/include/linux/acpi.h b/include/linux/acpi.h
> index 807cbc4..3dd743d 100644
> --- a/include/linux/acpi.h
> +++ b/include/linux/acpi.h
> @@ -432,6 +432,7 @@ static inline bool acpi_driver_match_device(struct device *dev,
>  int acpi_device_uevent_modalias(struct device *, struct kobj_uevent_env *);
>  int acpi_device_modalias(struct device *, char *, int);
>  
> +struct platform_device *acpi_create_platform_device(struct acpi_device *);

But then please drop the header from internal.h.

>  #define ACPI_PTR(_ptr)	(_ptr)
>  
>  #else	/* !CONFIG_ACPI */
> @@ -523,6 +524,12 @@ static inline int acpi_device_modalias(struct device *dev,
>  	return -ENODEV;
>  }
>  
> +static inline struct platform_device *acpi_create_platform_device(
> +				struct acpi_device *)
> +{
> +	return NULL;
> +}
> +
>  #define ACPI_PTR(_ptr)	(NULL)
>  
>  #endif	/* !CONFIG_ACPI */
> 

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

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

* Re: [PATCH 01/19] ACPI: introduce ACPI int340x thermal scan handler
  2014-09-18  1:53 ` [PATCH 01/19] ACPI: introduce ACPI int340x thermal scan handler Zhang Rui
@ 2014-09-24 21:58   ` Rafael J. Wysocki
  0 siblings, 0 replies; 23+ messages in thread
From: Rafael J. Wysocki @ 2014-09-24 21:58 UTC (permalink / raw)
  To: Zhang Rui; +Cc: linux-pm, linux-acpi, lenb, eduardo.valentin

On Thursday, September 18, 2014 09:53:32 AM Zhang Rui wrote:
> Newer laptops and tablets that use ACPI may have thermal sensors and
> other devices with thermal control capabilities outside the core CPU/SOC,
> for thermal safety reasons.
> They are exposed for the OS to use via
> 1) INT3400 ACPI device object as the master.
> 2) INT3401 ~ INT340B ACPI device objects as the slaves.
> 
> This patch introduces a scan handler to enumerate the INT3400
> ACPI device object to platform bus, and prevent its slaves
> from being enumerated before the controller driver being probed.
> 
> Signed-off-by: Zhang Rui <rui.zhang@intel.com>

ACK

> ---
>  drivers/acpi/Makefile          |  1 +
>  drivers/acpi/int340x_thermal.c | 51 ++++++++++++++++++++++++++++++++++++++++++
>  drivers/acpi/internal.h        |  1 +
>  drivers/acpi/scan.c            |  1 +
>  drivers/thermal/Kconfig        | 17 ++++++++++++++
>  5 files changed, 71 insertions(+)
>  create mode 100644 drivers/acpi/int340x_thermal.c
> 
> diff --git a/drivers/acpi/Makefile b/drivers/acpi/Makefile
> index 505d4d7..c3b2fcb 100644
> --- a/drivers/acpi/Makefile
> +++ b/drivers/acpi/Makefile
> @@ -43,6 +43,7 @@ acpi-y				+= pci_root.o pci_link.o pci_irq.o
>  acpi-y				+= acpi_lpss.o
>  acpi-y				+= acpi_platform.o
>  acpi-y				+= acpi_pnp.o
> +acpi-y				+= int340x_thermal.o
>  acpi-y				+= power.o
>  acpi-y				+= event.o
>  acpi-y				+= sysfs.o
> diff --git a/drivers/acpi/int340x_thermal.c b/drivers/acpi/int340x_thermal.c
> new file mode 100644
> index 0000000..2103bb6
> --- /dev/null
> +++ b/drivers/acpi/int340x_thermal.c
> @@ -0,0 +1,51 @@
> +/*
> + * ACPI support for int340x thermal drivers
> + *
> + * Copyright (C) 2014, Intel Corporation
> + * Authors: Zhang Rui <rui.zhang@intel.com>
> + *
> + * 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.
> + */
> +
> +#include <linux/acpi.h>
> +#include <linux/module.h>
> +
> +#include "internal.h"
> +
> +#define DO_ENUMERATION 0x01
> +static const struct acpi_device_id int340x_thermal_device_ids[] = {
> +	{"INT3400", DO_ENUMERATION },
> +	{"INT3401"},
> +	{"INT3402"},
> +	{"INT3403"},
> +	{"INT3404"},
> +	{"INT3406"},
> +	{"INT3407"},
> +	{"INT3408"},
> +	{"INT3409"},
> +	{"INT340A"},
> +	{"INT340B"},
> +	{""},
> +};
> +
> +static int int340x_thermal_handler_attach(struct acpi_device *adev,
> +					const struct acpi_device_id *id)
> +{
> +#ifdef CONFIG_INT340X_THERMAL
> +	if (id->driver_data == DO_ENUMERATION)
> +		acpi_create_platform_device(adev);
> +#endif
> +	return 1;
> +}
> +
> +static struct acpi_scan_handler int340x_thermal_handler = {
> +	.ids = int340x_thermal_device_ids,
> +	.attach = int340x_thermal_handler_attach,
> +};
> +
> +void __init acpi_int340x_thermal_init(void)
> +{
> +	acpi_scan_add_handler(&int340x_thermal_handler);
> +}
> diff --git a/drivers/acpi/internal.h b/drivers/acpi/internal.h
> index 4c5cf77..de47f9f 100644
> --- a/drivers/acpi/internal.h
> +++ b/drivers/acpi/internal.h
> @@ -31,6 +31,7 @@ void acpi_pci_link_init(void);
>  void acpi_processor_init(void);
>  void acpi_platform_init(void);
>  void acpi_pnp_init(void);
> +void acpi_int340x_thermal_init(void);
>  int acpi_sysfs_init(void);
>  void acpi_container_init(void);
>  void acpi_memory_hotplug_init(void);
> diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c
> index 0a817ad..eed9740 100644
> --- a/drivers/acpi/scan.c
> +++ b/drivers/acpi/scan.c
> @@ -2306,6 +2306,7 @@ int __init acpi_scan_init(void)
>  	acpi_container_init();
>  	acpi_memory_hotplug_init();
>  	acpi_pnp_init();
> +	acpi_int340x_thermal_init();
>  
>  	mutex_lock(&acpi_scan_lock);
>  	/*
> diff --git a/drivers/thermal/Kconfig b/drivers/thermal/Kconfig
> index 693208e..2ff7416 100644
> --- a/drivers/thermal/Kconfig
> +++ b/drivers/thermal/Kconfig
> @@ -234,6 +234,23 @@ config INTEL_SOC_DTS_THERMAL
>  	  notification methods.The other trip is a critical trip point, which
>  	  was set by the driver based on the TJ MAX temperature.
>  
> +config INT340X_THERMAL
> +	bool
> +	depends on X86 && ACPI
> +	help
> +	  Newer laptops and tablets that use ACPI may have thermal sensors and
> +	  other devices with thermal control capabilities outside the core
> +	  CPU/SOC, for thermal safety reasons.
> +	  They are exposed for the OS to use via the INT3400 ACPI device object
> +	  as the master, and INT3401~INT340B ACPI device objects as the slaves.
> +	  Enable this to expose the temperature information and cooling ability
> +	  from these objects to userspace via the normal thermal framework.
> +	  This means that a wide range of applications and GUI widgets can show
> +	  the information to the user or use this information for making
> +	  decisions. For example, the Intel Thermal Daemon can use this
> +	  information to allow the user to select his laptop to run without
> +	  turning on the fans.
> +
>  menu "Texas Instruments thermal drivers"
>  source "drivers/thermal/ti-soc-thermal/Kconfig"
>  endmenu
> 

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

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

* Re: [PATCH 03/19] ACPI: add ACPI_TYPE_LOCAL_REFERENCE support to acpi_extract_package()
  2014-09-18  1:53 ` [PATCH 03/19] ACPI: add ACPI_TYPE_LOCAL_REFERENCE support to acpi_extract_package() Zhang Rui
@ 2014-09-24 21:58   ` Rafael J. Wysocki
  0 siblings, 0 replies; 23+ messages in thread
From: Rafael J. Wysocki @ 2014-09-24 21:58 UTC (permalink / raw)
  To: Zhang Rui; +Cc: linux-pm, linux-acpi, lenb, eduardo.valentin

On Thursday, September 18, 2014 09:53:34 AM Zhang Rui wrote:
> Add ACPI_TYPE_LOCAL_REFERENCE support to acpi_extract_package(),
> so that we can use this helper for more cases like _ART/_TRT.
> 
> Signed-off-by: Zhang Rui <rui.zhang@intel.com>

ACK

> ---
>  drivers/acpi/utils.c | 28 +++++++++++++++++++++++++++-
>  1 file changed, 27 insertions(+), 1 deletion(-)
> 
> diff --git a/drivers/acpi/utils.c b/drivers/acpi/utils.c
> index 07c8c5a..1ed7aba 100644
> --- a/drivers/acpi/utils.c
> +++ b/drivers/acpi/utils.c
> @@ -149,6 +149,21 @@ acpi_extract_package(union acpi_object *package,
>  				break;
>  			}
>  			break;
> +		case ACPI_TYPE_LOCAL_REFERENCE:
> +			switch (format_string[i]) {
> +			case 'R':
> +				size_required += sizeof(void *);
> +				tail_offset += sizeof(void *);
> +				break;
> +			default:
> +				printk(KERN_WARNING PREFIX "Invalid package element"
> +					      " [%d] got reference,"
> +					      " expecting [%c]\n",
> +					      i, format_string[i]);
> +				return AE_BAD_DATA;
> +				break;
> +			}
> +			break;
>  
>  		case ACPI_TYPE_PACKAGE:
>  		default:
> @@ -247,7 +262,18 @@ acpi_extract_package(union acpi_object *package,
>  				break;
>  			}
>  			break;
> -
> +		case ACPI_TYPE_LOCAL_REFERENCE:
> +			switch (format_string[i]) {
> +			case 'R':
> +				*(void **)head =
> +				    (void *)element->reference.handle;
> +				head += sizeof(void *);
> +				break;
> +			default:
> +				/* Should never get here */
> +				break;
> +			}
> +			break;
>  		case ACPI_TYPE_PACKAGE:
>  			/* TBD: handle nested packages... */
>  		default:
> 

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

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

end of thread, other threads:[~2014-09-24 21:39 UTC | newest]

Thread overview: 23+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2014-09-18  1:53 [PATCH 00/19] Thermal: ACPI INT340X thermal drivers Zhang Rui
2014-09-18  1:53 ` [PATCH 01/19] ACPI: introduce ACPI int340x thermal scan handler Zhang Rui
2014-09-24 21:58   ` Rafael J. Wysocki
2014-09-18  1:53 ` [PATCH 02/19] ACPI: make acpi_create_platform_device() an external API Zhang Rui
2014-09-24 21:55   ` Rafael J. Wysocki
2014-09-18  1:53 ` [PATCH 03/19] ACPI: add ACPI_TYPE_LOCAL_REFERENCE support to acpi_extract_package() Zhang Rui
2014-09-24 21:58   ` Rafael J. Wysocki
2014-09-18  1:53 ` [PATCH 04/19] Thermal: introduce int3400 thermal driver Zhang Rui
2014-09-18  1:53 ` [PATCH 05/19] Thermal: int3400 thermal: add capability to detect supporting UUIDs Zhang Rui
2014-09-18  1:53 ` [PATCH 06/19] Thermal: int3400 thermal: register to thermal framework Zhang Rui
2014-09-18  1:53 ` [PATCH 07/19] ACPI / fan: remove unused macro Zhang Rui
2014-09-18  1:53 ` [PATCH 08/19] ACPI / fan: remove no need check for device pointer Zhang Rui
2014-09-18  1:53 ` [PATCH 09/19] ACPI / fan: use acpi_device_xxx_power instead of acpi_bus equivelant Zhang Rui
2014-09-18  1:53 ` [PATCH 10/19] ACPI / fan: convert to platform driver Zhang Rui
2014-09-18  1:53 ` [PATCH 11/19] ACPI / Fan: add ACPI 4.0 style fan support Zhang Rui
2014-09-18  1:53 ` [PATCH 12/19] ACPI / Fan: support INT3404 thermal device Zhang Rui
2014-09-18  1:53 ` [PATCH 13/19] Thermal: move the KELVIN_TO_MILLICELSIUS macro to thermal.h Zhang Rui
2014-09-18  1:53 ` [PATCH 14/19] Thermal: introduce INT3402 thermal driver Zhang Rui
2014-09-18  1:53 ` [PATCH 15/19] Thermal: introduce int3403 " Zhang Rui
2014-09-18  1:53 ` [PATCH 16/19] Thermal: int340x_thermal: expose acpi thermal relationship tables Zhang Rui
2014-09-18  1:53 ` [PATCH 17/19] Thermal: int3400_thermal: use acpi_thermal_rel parsing APIs Zhang Rui
2014-09-18  1:53 ` [PATCH 18/19] Thermal: introduce INT3406 thermal driver Zhang Rui
2014-09-18  1:53 ` [PATCH 19/19] Thermal: int340x thermal: select ACPI fan driver Zhang Rui

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).