diff options
author | Zhang Rui <rui.zhang@intel.com> | 2014-03-14 00:45:05 -0400 |
---|---|---|
committer | Zhang Rui <rui.zhang@intel.com> | 2014-10-10 01:57:09 -0400 |
commit | 816cab931f288c92a3404b1b984576f4822b0445 (patch) | |
tree | f09e5a1e9124056e086e53fbbea39bbf2d9acf32 | |
parent | e3ec483a7e24c6ebb5eb763ee56c65c239701066 (diff) |
Thermal: introduce int3400 thermal driver
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>
-rw-r--r-- | drivers/acpi/int340x_thermal.c | 2 | ||||
-rw-r--r-- | drivers/thermal/Kconfig | 2 | ||||
-rw-r--r-- | drivers/thermal/Makefile | 1 | ||||
-rw-r--r-- | drivers/thermal/int340x_thermal/Makefile | 1 | ||||
-rw-r--r-- | drivers/thermal/int340x_thermal/int3400_thermal.c | 245 |
5 files changed, 249 insertions, 2 deletions
diff --git a/drivers/acpi/int340x_thermal.c b/drivers/acpi/int340x_thermal.c index 2103bb6d9016..a27d31d1ba24 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[] = { | |||
33 | static int int340x_thermal_handler_attach(struct acpi_device *adev, | 33 | static int int340x_thermal_handler_attach(struct acpi_device *adev, |
34 | const struct acpi_device_id *id) | 34 | const struct acpi_device_id *id) |
35 | { | 35 | { |
36 | #ifdef CONFIG_INT340X_THERMAL | 36 | #if defined(CONFIG_INT340X_THERMAL) || defined(CONFIG_INT340X_THERMAL_MODULE) |
37 | if (id->driver_data == DO_ENUMERATION) | 37 | if (id->driver_data == DO_ENUMERATION) |
38 | acpi_create_platform_device(adev); | 38 | acpi_create_platform_device(adev); |
39 | #endif | 39 | #endif |
diff --git a/drivers/thermal/Kconfig b/drivers/thermal/Kconfig index 2ff7416ca930..6f5a87a8f19f 100644 --- a/drivers/thermal/Kconfig +++ b/drivers/thermal/Kconfig | |||
@@ -235,7 +235,7 @@ config INTEL_SOC_DTS_THERMAL | |||
235 | was set by the driver based on the TJ MAX temperature. | 235 | was set by the driver based on the TJ MAX temperature. |
236 | 236 | ||
237 | config INT340X_THERMAL | 237 | config INT340X_THERMAL |
238 | bool | 238 | tristate "ACPI INT340X thermal drivers" |
239 | depends on X86 && ACPI | 239 | depends on X86 && ACPI |
240 | help | 240 | help |
241 | Newer laptops and tablets that use ACPI may have thermal sensors and | 241 | Newer laptops and tablets that use ACPI may have thermal sensors and |
diff --git a/drivers/thermal/Makefile b/drivers/thermal/Makefile index 31e232f84b6b..216503eaa232 100644 --- a/drivers/thermal/Makefile +++ b/drivers/thermal/Makefile | |||
@@ -32,4 +32,5 @@ obj-$(CONFIG_X86_PKG_TEMP_THERMAL) += x86_pkg_temp_thermal.o | |||
32 | obj-$(CONFIG_INTEL_SOC_DTS_THERMAL) += intel_soc_dts_thermal.o | 32 | obj-$(CONFIG_INTEL_SOC_DTS_THERMAL) += intel_soc_dts_thermal.o |
33 | obj-$(CONFIG_TI_SOC_THERMAL) += ti-soc-thermal/ | 33 | obj-$(CONFIG_TI_SOC_THERMAL) += ti-soc-thermal/ |
34 | obj-$(CONFIG_ACPI_INT3403_THERMAL) += int3403_thermal.o | 34 | obj-$(CONFIG_ACPI_INT3403_THERMAL) += int3403_thermal.o |
35 | obj-$(CONFIG_INT340X_THERMAL) += int340x_thermal/ | ||
35 | obj-$(CONFIG_ST_THERMAL) += st/ | 36 | obj-$(CONFIG_ST_THERMAL) += st/ |
diff --git a/drivers/thermal/int340x_thermal/Makefile b/drivers/thermal/int340x_thermal/Makefile new file mode 100644 index 000000000000..e10a53bcefe7 --- /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 000000000000..308c1850edee --- /dev/null +++ b/drivers/thermal/int340x_thermal/int3400_thermal.c | |||
@@ -0,0 +1,245 @@ | |||
1 | /* | ||
2 | * INT3400 thermal driver | ||
3 | * | ||
4 | * Copyright (C) 2014, Intel Corporation | ||
5 | * Authors: Zhang Rui <rui.zhang@intel.com> | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify | ||
8 | * it under the terms of the GNU General Public License version 2 as | ||
9 | * published by the Free Software Foundation. | ||
10 | * | ||
11 | */ | ||
12 | |||
13 | #include <linux/module.h> | ||
14 | #include <linux/platform_device.h> | ||
15 | #include <linux/acpi.h> | ||
16 | |||
17 | struct art { | ||
18 | acpi_handle source; | ||
19 | acpi_handle target; | ||
20 | u64 weight; | ||
21 | u64 ac0_max; | ||
22 | u64 ac1_max; | ||
23 | u64 ac2_max; | ||
24 | u64 ac3_max; | ||
25 | u64 ac4_max; | ||
26 | u64 ac5_max; | ||
27 | u64 ac6_max; | ||
28 | u64 ac7_max; | ||
29 | u64 ac8_max; | ||
30 | u64 ac9_max; | ||
31 | }; | ||
32 | |||
33 | struct trt { | ||
34 | acpi_handle source; | ||
35 | acpi_handle target; | ||
36 | u64 influence; | ||
37 | u64 sampling_period; | ||
38 | u64 reverved1; | ||
39 | u64 reverved2; | ||
40 | u64 reverved3; | ||
41 | u64 reverved4; | ||
42 | }; | ||
43 | |||
44 | struct int3400_thermal_priv { | ||
45 | struct acpi_device *adev; | ||
46 | int art_count; | ||
47 | struct art *arts; | ||
48 | int trt_count; | ||
49 | struct trt *trts; | ||
50 | }; | ||
51 | |||
52 | static int parse_art(struct int3400_thermal_priv *priv) | ||
53 | { | ||
54 | acpi_handle handle = priv->adev->handle; | ||
55 | acpi_status status; | ||
56 | int result = 0; | ||
57 | int i; | ||
58 | struct acpi_device *adev; | ||
59 | union acpi_object *p; | ||
60 | struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; | ||
61 | struct acpi_buffer element = { 0, NULL }; | ||
62 | struct acpi_buffer art_format = { | ||
63 | sizeof("RRNNNNNNNNNNN"), "RRNNNNNNNNNNN" }; | ||
64 | |||
65 | if (!acpi_has_method(handle, "_ART")) | ||
66 | return 0; | ||
67 | |||
68 | status = acpi_evaluate_object(handle, "_ART", NULL, &buffer); | ||
69 | if (ACPI_FAILURE(status)) | ||
70 | return -ENODEV; | ||
71 | |||
72 | p = buffer.pointer; | ||
73 | if (!p || (p->type != ACPI_TYPE_PACKAGE)) { | ||
74 | pr_err("Invalid _ART data\n"); | ||
75 | result = -EFAULT; | ||
76 | goto end; | ||
77 | } | ||
78 | |||
79 | /* ignore p->package.elements[0], as this is _ART Revision field */ | ||
80 | priv->art_count = p->package.count - 1; | ||
81 | priv->arts = kzalloc(sizeof(struct art) * priv->art_count, GFP_KERNEL); | ||
82 | if (!priv->arts) { | ||
83 | result = -ENOMEM; | ||
84 | goto end; | ||
85 | } | ||
86 | |||
87 | for (i = 0; i < priv->art_count; i++) { | ||
88 | struct art *art = &(priv->arts[i]); | ||
89 | |||
90 | element.length = sizeof(struct art); | ||
91 | element.pointer = art; | ||
92 | |||
93 | status = acpi_extract_package(&(p->package.elements[i + 1]), | ||
94 | &art_format, &element); | ||
95 | if (ACPI_FAILURE(status)) { | ||
96 | pr_err("Invalid _ART data"); | ||
97 | result = -EFAULT; | ||
98 | kfree(priv->arts); | ||
99 | goto end; | ||
100 | } | ||
101 | result = acpi_bus_get_device(art->source, &adev); | ||
102 | if (!result) | ||
103 | acpi_create_platform_device(adev, NULL); | ||
104 | else | ||
105 | pr_warn("Failed to get source ACPI device\n"); | ||
106 | result = acpi_bus_get_device(art->target, &adev); | ||
107 | if (!result) | ||
108 | acpi_create_platform_device(adev, NULL); | ||
109 | else | ||
110 | pr_warn("Failed to get source ACPI device\n"); | ||
111 | } | ||
112 | end: | ||
113 | kfree(buffer.pointer); | ||
114 | return result; | ||
115 | } | ||
116 | |||
117 | static int parse_trt(struct int3400_thermal_priv *priv) | ||
118 | { | ||
119 | acpi_handle handle = priv->adev->handle; | ||
120 | acpi_status status; | ||
121 | int result = 0; | ||
122 | int i; | ||
123 | struct acpi_device *adev; | ||
124 | union acpi_object *p; | ||
125 | struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; | ||
126 | struct acpi_buffer element = { 0, NULL }; | ||
127 | struct acpi_buffer trt_format = { sizeof("RRNNNNNN"), "RRNNNNNN" }; | ||
128 | |||
129 | if (!acpi_has_method(handle, "_TRT")) | ||
130 | return 0; | ||
131 | |||
132 | status = acpi_evaluate_object(handle, "_TRT", NULL, &buffer); | ||
133 | if (ACPI_FAILURE(status)) | ||
134 | return -ENODEV; | ||
135 | |||
136 | p = buffer.pointer; | ||
137 | if (!p || (p->type != ACPI_TYPE_PACKAGE)) { | ||
138 | pr_err("Invalid _TRT data\n"); | ||
139 | result = -EFAULT; | ||
140 | goto end; | ||
141 | } | ||
142 | |||
143 | priv->trt_count = p->package.count; | ||
144 | priv->trts = kzalloc(sizeof(struct trt) * priv->trt_count, GFP_KERNEL); | ||
145 | if (!priv->trts) { | ||
146 | result = -ENOMEM; | ||
147 | goto end; | ||
148 | } | ||
149 | |||
150 | for (i = 0; i < priv->trt_count; i++) { | ||
151 | struct trt *trt = &(priv->trts[i]); | ||
152 | |||
153 | element.length = sizeof(struct trt); | ||
154 | element.pointer = trt; | ||
155 | |||
156 | status = acpi_extract_package(&(p->package.elements[i]), | ||
157 | &trt_format, &element); | ||
158 | if (ACPI_FAILURE(status)) { | ||
159 | pr_err("Invalid _ART data"); | ||
160 | result = -EFAULT; | ||
161 | kfree(priv->trts); | ||
162 | goto end; | ||
163 | } | ||
164 | |||
165 | result = acpi_bus_get_device(trt->source, &adev); | ||
166 | if (!result) | ||
167 | acpi_create_platform_device(adev, NULL); | ||
168 | else | ||
169 | pr_warn("Failed to get source ACPI device\n"); | ||
170 | result = acpi_bus_get_device(trt->target, &adev); | ||
171 | if (!result) | ||
172 | acpi_create_platform_device(adev, NULL); | ||
173 | else | ||
174 | pr_warn("Failed to get target ACPI device\n"); | ||
175 | } | ||
176 | end: | ||
177 | kfree(buffer.pointer); | ||
178 | return result; | ||
179 | } | ||
180 | |||
181 | static int int3400_thermal_probe(struct platform_device *pdev) | ||
182 | { | ||
183 | struct acpi_device *adev = ACPI_COMPANION(&pdev->dev); | ||
184 | struct int3400_thermal_priv *priv; | ||
185 | int result; | ||
186 | |||
187 | if (!adev) | ||
188 | return -ENODEV; | ||
189 | |||
190 | priv = kzalloc(sizeof(struct int3400_thermal_priv), GFP_KERNEL); | ||
191 | if (!priv) | ||
192 | return -ENOMEM; | ||
193 | |||
194 | priv->adev = adev; | ||
195 | |||
196 | result = parse_art(priv); | ||
197 | if (result) | ||
198 | goto free_priv; | ||
199 | |||
200 | result = parse_trt(priv); | ||
201 | if (result) | ||
202 | goto free_art; | ||
203 | |||
204 | platform_set_drvdata(pdev, priv); | ||
205 | |||
206 | return 0; | ||
207 | free_art: | ||
208 | kfree(priv->arts); | ||
209 | free_priv: | ||
210 | kfree(priv); | ||
211 | return result; | ||
212 | } | ||
213 | |||
214 | static int int3400_thermal_remove(struct platform_device *pdev) | ||
215 | { | ||
216 | struct int3400_thermal_priv *priv = platform_get_drvdata(pdev); | ||
217 | |||
218 | kfree(priv->trts); | ||
219 | kfree(priv->arts); | ||
220 | kfree(priv); | ||
221 | return 0; | ||
222 | } | ||
223 | |||
224 | static const struct acpi_device_id int3400_thermal_match[] = { | ||
225 | {"INT3400", 0}, | ||
226 | {} | ||
227 | }; | ||
228 | |||
229 | MODULE_DEVICE_TABLE(acpi, int3400_thermal_match); | ||
230 | |||
231 | static struct platform_driver int3400_thermal_driver = { | ||
232 | .probe = int3400_thermal_probe, | ||
233 | .remove = int3400_thermal_remove, | ||
234 | .driver = { | ||
235 | .name = "int3400 thermal", | ||
236 | .owner = THIS_MODULE, | ||
237 | .acpi_match_table = ACPI_PTR(int3400_thermal_match), | ||
238 | }, | ||
239 | }; | ||
240 | |||
241 | module_platform_driver(int3400_thermal_driver); | ||
242 | |||
243 | MODULE_DESCRIPTION("INT3400 Thermal driver"); | ||
244 | MODULE_AUTHOR("Zhang Rui <rui.zhang@intel.com>"); | ||
245 | MODULE_LICENSE("GPL"); | ||