1
2
3
4
5
6
7
8
9
10
11
12
13#include <linux/module.h>
14#include <linux/platform_device.h>
15#include <linux/acpi.h>
16
17struct 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
33struct 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
44struct 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
52static 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
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);
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);
109 else
110 pr_warn("Failed to get source ACPI device\n");
111 }
112end:
113 kfree(buffer.pointer);
114 return result;
115}
116
117static 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);
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);
173 else
174 pr_warn("Failed to get target ACPI device\n");
175 }
176end:
177 kfree(buffer.pointer);
178 return result;
179}
180
181static 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;
207free_art:
208 kfree(priv->arts);
209free_priv:
210 kfree(priv);
211 return result;
212}
213
214static 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
224static const struct acpi_device_id int3400_thermal_match[] = {
225 {"INT3400", 0},
226 {}
227};
228
229MODULE_DEVICE_TABLE(acpi, int3400_thermal_match);
230
231static 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
241module_platform_driver(int3400_thermal_driver);
242
243MODULE_DESCRIPTION("INT3400 Thermal driver");
244MODULE_AUTHOR("Zhang Rui <rui.zhang@intel.com>");
245MODULE_LICENSE("GPL");
246