p54: more accurate rssi to dBm conversion
[linux-2.6] / drivers / misc / intel_menlow.c
1 /*
2  *  intel_menlow.c - Intel menlow Driver for thermal management extension
3  *
4  *  Copyright (C) 2008 Intel Corp
5  *  Copyright (C) 2008 Sujith Thomas <sujith.thomas@intel.com>
6  *  Copyright (C) 2008 Zhang Rui <rui.zhang@intel.com>
7  *  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
8  *
9  *  This program is free software; you can redistribute it and/or modify
10  *  it under the terms of the GNU General Public License as published by
11  *  the Free Software Foundation; version 2 of the License.
12  *
13  *  This program is distributed in the hope that it will be useful, but
14  *  WITHOUT ANY WARRANTY; without even the implied warranty of
15  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  *  General Public License for more details.
17  *
18  *  You should have received a copy of the GNU General Public License along
19  *  with this program; if not, write to the Free Software Foundation, Inc.,
20  *  59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
21  *
22  * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
23  *
24  *  This driver creates the sys I/F for programming the sensors.
25  *  It also implements the driver for intel menlow memory controller (hardware
26  *  id is INT0002) which makes use of the platform specific ACPI methods
27  *  to get/set bandwidth.
28  */
29
30 #include <linux/kernel.h>
31 #include <linux/module.h>
32 #include <linux/init.h>
33 #include <linux/types.h>
34 #include <linux/pci.h>
35 #include <linux/pm.h>
36
37 #include <linux/thermal.h>
38 #include <acpi/acpi_bus.h>
39 #include <acpi/acpi_drivers.h>
40
41 MODULE_AUTHOR("Thomas Sujith");
42 MODULE_AUTHOR("Zhang Rui");
43 MODULE_DESCRIPTION("Intel Menlow platform specific driver");
44 MODULE_LICENSE("GPL");
45
46 /*
47  * Memory controller device control
48  */
49
50 #define MEMORY_GET_BANDWIDTH "GTHS"
51 #define MEMORY_SET_BANDWIDTH "STHS"
52 #define MEMORY_ARG_CUR_BANDWIDTH 1
53 #define MEMORY_ARG_MAX_BANDWIDTH 0
54
55 /*
56  * GTHS returning 'n' would mean that [0,n-1] states are supported
57  * In that case max_cstate would be n-1
58  * GTHS returning '0' would mean that no bandwidth control states are supported
59  */
60 static int memory_get_int_max_bandwidth(struct thermal_cooling_device *cdev,
61                                         unsigned long *max_state)
62 {
63         struct acpi_device *device = cdev->devdata;
64         acpi_handle handle = device->handle;
65         unsigned long long value;
66         struct acpi_object_list arg_list;
67         union acpi_object arg;
68         acpi_status status = AE_OK;
69
70         arg_list.count = 1;
71         arg_list.pointer = &arg;
72         arg.type = ACPI_TYPE_INTEGER;
73         arg.integer.value = MEMORY_ARG_MAX_BANDWIDTH;
74         status = acpi_evaluate_integer(handle, MEMORY_GET_BANDWIDTH,
75                                        &arg_list, &value);
76         if (ACPI_FAILURE(status))
77                 return -EFAULT;
78
79         if (!value)
80                 return -EINVAL;
81
82         *max_state = value - 1;
83         return 0;
84 }
85
86 static int memory_get_max_bandwidth(struct thermal_cooling_device *cdev,
87                                     char *buf)
88 {
89         unsigned long value;
90         if (memory_get_int_max_bandwidth(cdev, &value))
91                 return -EINVAL;
92
93         return sprintf(buf, "%ld\n", value);
94 }
95
96 static int memory_get_cur_bandwidth(struct thermal_cooling_device *cdev,
97                                     char *buf)
98 {
99         struct acpi_device *device = cdev->devdata;
100         acpi_handle handle = device->handle;
101         unsigned long long value;
102         struct acpi_object_list arg_list;
103         union acpi_object arg;
104         acpi_status status = AE_OK;
105
106         arg_list.count = 1;
107         arg_list.pointer = &arg;
108         arg.type = ACPI_TYPE_INTEGER;
109         arg.integer.value = MEMORY_ARG_CUR_BANDWIDTH;
110         status = acpi_evaluate_integer(handle, MEMORY_GET_BANDWIDTH,
111                                        &arg_list, &value);
112         if (ACPI_FAILURE(status))
113                 return -EFAULT;
114
115         return sprintf(buf, "%llu\n", value);
116 }
117
118 static int memory_set_cur_bandwidth(struct thermal_cooling_device *cdev,
119                                     unsigned int state)
120 {
121         struct acpi_device *device = cdev->devdata;
122         acpi_handle handle = device->handle;
123         struct acpi_object_list arg_list;
124         union acpi_object arg;
125         acpi_status status;
126         unsigned long long temp;
127         unsigned long max_state;
128
129         if (memory_get_int_max_bandwidth(cdev, &max_state))
130                 return -EFAULT;
131
132         if (state > max_state)
133                 return -EINVAL;
134
135         arg_list.count = 1;
136         arg_list.pointer = &arg;
137         arg.type = ACPI_TYPE_INTEGER;
138         arg.integer.value = state;
139
140         status =
141             acpi_evaluate_integer(handle, MEMORY_SET_BANDWIDTH, &arg_list,
142                                   &temp);
143
144         printk(KERN_INFO
145                "Bandwidth value was %d: status is %d\n", state, status);
146         if (ACPI_FAILURE(status))
147                 return -EFAULT;
148
149         return 0;
150 }
151
152 static struct thermal_cooling_device_ops memory_cooling_ops = {
153         .get_max_state = memory_get_max_bandwidth,
154         .get_cur_state = memory_get_cur_bandwidth,
155         .set_cur_state = memory_set_cur_bandwidth,
156 };
157
158 /*
159  * Memory Device Management
160  */
161 static int intel_menlow_memory_add(struct acpi_device *device)
162 {
163         int result = -ENODEV;
164         acpi_status status = AE_OK;
165         acpi_handle dummy;
166         struct thermal_cooling_device *cdev;
167
168         if (!device)
169                 return -EINVAL;
170
171         status = acpi_get_handle(device->handle, MEMORY_GET_BANDWIDTH, &dummy);
172         if (ACPI_FAILURE(status))
173                 goto end;
174
175         status = acpi_get_handle(device->handle, MEMORY_SET_BANDWIDTH, &dummy);
176         if (ACPI_FAILURE(status))
177                 goto end;
178
179         cdev = thermal_cooling_device_register("Memory controller", device,
180                                                &memory_cooling_ops);
181         if (IS_ERR(cdev)) {
182                 result = PTR_ERR(cdev);
183                 goto end;
184         }
185
186         device->driver_data = cdev;
187         result = sysfs_create_link(&device->dev.kobj,
188                                 &cdev->device.kobj, "thermal_cooling");
189         if (result)
190                 goto unregister;
191
192         result = sysfs_create_link(&cdev->device.kobj,
193                                 &device->dev.kobj, "device");
194         if (result) {
195                 sysfs_remove_link(&device->dev.kobj, "thermal_cooling");
196                 goto unregister;
197         }
198
199  end:
200         return result;
201
202  unregister:
203         thermal_cooling_device_unregister(cdev);
204         return result;
205
206 }
207
208 static int intel_menlow_memory_remove(struct acpi_device *device, int type)
209 {
210         struct thermal_cooling_device *cdev = acpi_driver_data(device);
211
212         if (!device || !cdev)
213                 return -EINVAL;
214
215         sysfs_remove_link(&device->dev.kobj, "thermal_cooling");
216         sysfs_remove_link(&cdev->device.kobj, "device");
217         thermal_cooling_device_unregister(cdev);
218
219         return 0;
220 }
221
222 static const struct acpi_device_id intel_menlow_memory_ids[] = {
223         {"INT0002", 0},
224         {"", 0},
225 };
226
227 static struct acpi_driver intel_menlow_memory_driver = {
228         .name = "intel_menlow_thermal_control",
229         .ids = intel_menlow_memory_ids,
230         .ops = {
231                 .add = intel_menlow_memory_add,
232                 .remove = intel_menlow_memory_remove,
233                 },
234 };
235
236 /*
237  * Sensor control on menlow platform
238  */
239
240 #define THERMAL_AUX0 0
241 #define THERMAL_AUX1 1
242 #define GET_AUX0 "GAX0"
243 #define GET_AUX1 "GAX1"
244 #define SET_AUX0 "SAX0"
245 #define SET_AUX1 "SAX1"
246
247 struct intel_menlow_attribute {
248         struct device_attribute attr;
249         struct device *device;
250         acpi_handle handle;
251         struct list_head node;
252 };
253
254 static LIST_HEAD(intel_menlow_attr_list);
255 static DEFINE_MUTEX(intel_menlow_attr_lock);
256
257 /*
258  * sensor_get_auxtrip - get the current auxtrip value from sensor
259  * @name: Thermalzone name
260  * @auxtype : AUX0/AUX1
261  * @buf: syfs buffer
262  */
263 static int sensor_get_auxtrip(acpi_handle handle, int index,
264                                                         unsigned long long *value)
265 {
266         acpi_status status;
267
268         if ((index != 0 && index != 1) || !value)
269                 return -EINVAL;
270
271         status = acpi_evaluate_integer(handle, index ? GET_AUX1 : GET_AUX0,
272                                        NULL, value);
273         if (ACPI_FAILURE(status))
274                 return -EIO;
275
276         return 0;
277 }
278
279 /*
280  * sensor_set_auxtrip - set the new auxtrip value to sensor
281  * @name: Thermalzone name
282  * @auxtype : AUX0/AUX1
283  * @buf: syfs buffer
284  */
285 static int sensor_set_auxtrip(acpi_handle handle, int index, int value)
286 {
287         acpi_status status;
288         union acpi_object arg = {
289                 ACPI_TYPE_INTEGER
290         };
291         struct acpi_object_list args = {
292                 1, &arg
293         };
294         unsigned long long temp;
295
296         if (index != 0 && index != 1)
297                 return -EINVAL;
298
299         status = acpi_evaluate_integer(handle, index ? GET_AUX0 : GET_AUX1,
300                                        NULL, &temp);
301         if (ACPI_FAILURE(status))
302                 return -EIO;
303         if ((index && value < temp) || (!index && value > temp))
304                 return -EINVAL;
305
306         arg.integer.value = value;
307         status = acpi_evaluate_integer(handle, index ? SET_AUX1 : SET_AUX0,
308                                        &args, &temp);
309         if (ACPI_FAILURE(status))
310                 return -EIO;
311
312         /* do we need to check the return value of SAX0/SAX1 ? */
313
314         return 0;
315 }
316
317 #define to_intel_menlow_attr(_attr)     \
318         container_of(_attr, struct intel_menlow_attribute, attr)
319
320 static ssize_t aux0_show(struct device *dev,
321                          struct device_attribute *dev_attr, char *buf)
322 {
323         struct intel_menlow_attribute *attr = to_intel_menlow_attr(dev_attr);
324         unsigned long long value;
325         int result;
326
327         result = sensor_get_auxtrip(attr->handle, 0, &value);
328
329         return result ? result : sprintf(buf, "%lu", KELVIN_TO_CELSIUS(value));
330 }
331
332 static ssize_t aux1_show(struct device *dev,
333                          struct device_attribute *dev_attr, char *buf)
334 {
335         struct intel_menlow_attribute *attr = to_intel_menlow_attr(dev_attr);
336         unsigned long long value;
337         int result;
338
339         result = sensor_get_auxtrip(attr->handle, 1, &value);
340
341         return result ? result : sprintf(buf, "%lu", KELVIN_TO_CELSIUS(value));
342 }
343
344 static ssize_t aux0_store(struct device *dev,
345                           struct device_attribute *dev_attr,
346                           const char *buf, size_t count)
347 {
348         struct intel_menlow_attribute *attr = to_intel_menlow_attr(dev_attr);
349         int value;
350         int result;
351
352         /*Sanity check; should be a positive integer */
353         if (!sscanf(buf, "%d", &value))
354                 return -EINVAL;
355
356         if (value < 0)
357                 return -EINVAL;
358
359         result = sensor_set_auxtrip(attr->handle, 0, CELSIUS_TO_KELVIN(value));
360         return result ? result : count;
361 }
362
363 static ssize_t aux1_store(struct device *dev,
364                           struct device_attribute *dev_attr,
365                           const char *buf, size_t count)
366 {
367         struct intel_menlow_attribute *attr = to_intel_menlow_attr(dev_attr);
368         int value;
369         int result;
370
371         /*Sanity check; should be a positive integer */
372         if (!sscanf(buf, "%d", &value))
373                 return -EINVAL;
374
375         if (value < 0)
376                 return -EINVAL;
377
378         result = sensor_set_auxtrip(attr->handle, 1, CELSIUS_TO_KELVIN(value));
379         return result ? result : count;
380 }
381
382 /* BIOS can enable/disable the thermal user application in dabney platform */
383 #define BIOS_ENABLED "\\_TZ.GSTS"
384 static ssize_t bios_enabled_show(struct device *dev,
385                                  struct device_attribute *attr, char *buf)
386 {
387         acpi_status status;
388         unsigned long long bios_enabled;
389
390         status = acpi_evaluate_integer(NULL, BIOS_ENABLED, NULL, &bios_enabled);
391         if (ACPI_FAILURE(status))
392                 return -ENODEV;
393
394         return sprintf(buf, "%s\n", bios_enabled ? "enabled" : "disabled");
395 }
396
397 static int intel_menlow_add_one_attribute(char *name, int mode, void *show,
398                                           void *store, struct device *dev,
399                                           acpi_handle handle)
400 {
401         struct intel_menlow_attribute *attr;
402         int result;
403
404         attr = kzalloc(sizeof(struct intel_menlow_attribute), GFP_KERNEL);
405         if (!attr)
406                 return -ENOMEM;
407
408         attr->attr.attr.name = name;
409         attr->attr.attr.mode = mode;
410         attr->attr.show = show;
411         attr->attr.store = store;
412         attr->device = dev;
413         attr->handle = handle;
414
415         result = device_create_file(dev, &attr->attr);
416         if (result)
417                 return result;
418
419         mutex_lock(&intel_menlow_attr_lock);
420         list_add_tail(&attr->node, &intel_menlow_attr_list);
421         mutex_unlock(&intel_menlow_attr_lock);
422
423         return 0;
424 }
425
426 static acpi_status intel_menlow_register_sensor(acpi_handle handle, u32 lvl,
427                                                 void *context, void **rv)
428 {
429         acpi_status status;
430         acpi_handle dummy;
431         struct thermal_zone_device *thermal;
432         int result;
433
434         result = acpi_bus_get_private_data(handle, (void **)&thermal);
435         if (result)
436                 return 0;
437
438         /* _TZ must have the AUX0/1 methods */
439         status = acpi_get_handle(handle, GET_AUX0, &dummy);
440         if (ACPI_FAILURE(status))
441                 goto not_found;
442
443         status = acpi_get_handle(handle, SET_AUX0, &dummy);
444         if (ACPI_FAILURE(status))
445                 goto not_found;
446
447         result = intel_menlow_add_one_attribute("aux0", 0644,
448                                                 aux0_show, aux0_store,
449                                                 &thermal->device, handle);
450         if (result)
451                 return AE_ERROR;
452
453         status = acpi_get_handle(handle, GET_AUX1, &dummy);
454         if (ACPI_FAILURE(status))
455                 goto not_found;
456
457         status = acpi_get_handle(handle, SET_AUX1, &dummy);
458         if (ACPI_FAILURE(status))
459                 goto not_found;
460
461         result = intel_menlow_add_one_attribute("aux1", 0644,
462                                                 aux1_show, aux1_store,
463                                                 &thermal->device, handle);
464         if (result)
465                 return AE_ERROR;
466
467         /*
468          * create the "dabney_enabled" attribute which means the user app
469          * should be loaded or not
470          */
471
472         result = intel_menlow_add_one_attribute("bios_enabled", 0444,
473                                                 bios_enabled_show, NULL,
474                                                 &thermal->device, handle);
475         if (result)
476                 return AE_ERROR;
477
478  not_found:
479         if (status == AE_NOT_FOUND)
480                 return AE_OK;
481         else
482                 return status;
483 }
484
485 static void intel_menlow_unregister_sensor(void)
486 {
487         struct intel_menlow_attribute *pos, *next;
488
489         mutex_lock(&intel_menlow_attr_lock);
490         list_for_each_entry_safe(pos, next, &intel_menlow_attr_list, node) {
491                 list_del(&pos->node);
492                 device_remove_file(pos->device, &pos->attr);
493                 kfree(pos);
494         }
495         mutex_unlock(&intel_menlow_attr_lock);
496
497         return;
498 }
499
500 static int __init intel_menlow_module_init(void)
501 {
502         int result = -ENODEV;
503         acpi_status status;
504         unsigned long long enable;
505
506         if (acpi_disabled)
507                 return result;
508
509         /* Looking for the \_TZ.GSTS method */
510         status = acpi_evaluate_integer(NULL, BIOS_ENABLED, NULL, &enable);
511         if (ACPI_FAILURE(status) || !enable)
512                 return -ENODEV;
513
514         /* Looking for ACPI device MEM0 with hardware id INT0002 */
515         result = acpi_bus_register_driver(&intel_menlow_memory_driver);
516         if (result)
517                 return result;
518
519         /* Looking for sensors in each ACPI thermal zone */
520         status = acpi_walk_namespace(ACPI_TYPE_THERMAL, ACPI_ROOT_OBJECT,
521                                      ACPI_UINT32_MAX,
522                                      intel_menlow_register_sensor, NULL, NULL);
523         if (ACPI_FAILURE(status))
524                 return -ENODEV;
525
526         return 0;
527 }
528
529 static void __exit intel_menlow_module_exit(void)
530 {
531         acpi_bus_unregister_driver(&intel_menlow_memory_driver);
532         intel_menlow_unregister_sensor();
533 }
534
535 module_init(intel_menlow_module_init);
536 module_exit(intel_menlow_module_exit);