V4L/DVB (5716): Tda10086,tda826x: fix tuning, STR/SNR values
[linux-2.6] / drivers / hwmon / hdaps.c
1 /*
2  * drivers/hwmon/hdaps.c - driver for IBM's Hard Drive Active Protection System
3  *
4  * Copyright (C) 2005 Robert Love <rml@novell.com>
5  * Copyright (C) 2005 Jesper Juhl <jesper.juhl@gmail.com>
6  *
7  * The HardDisk Active Protection System (hdaps) is present in IBM ThinkPads
8  * starting with the R40, T41, and X40.  It provides a basic two-axis
9  * accelerometer and other data, such as the device's temperature.
10  *
11  * This driver is based on the document by Mark A. Smith available at
12  * http://www.almaden.ibm.com/cs/people/marksmith/tpaps.html and a lot of trial
13  * and error.
14  *
15  * This program is free software; you can redistribute it and/or modify it
16  * under the terms of the GNU General Public License v2 as published by the
17  * Free Software Foundation.
18  *
19  * This program is distributed in the hope that it will be useful, but WITHOUT
20  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
21  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
22  * more details.
23  *
24  * You should have received a copy of the GNU General Public License along with
25  * this program; if not, write to the Free Software Foundation, Inc.,
26  * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
27  */
28
29 #include <linux/delay.h>
30 #include <linux/platform_device.h>
31 #include <linux/input.h>
32 #include <linux/kernel.h>
33 #include <linux/mutex.h>
34 #include <linux/module.h>
35 #include <linux/timer.h>
36 #include <linux/dmi.h>
37 #include <linux/jiffies.h>
38
39 #include <asm/io.h>
40
41 #define HDAPS_LOW_PORT          0x1600  /* first port used by hdaps */
42 #define HDAPS_NR_PORTS          0x30    /* number of ports: 0x1600 - 0x162f */
43
44 #define HDAPS_PORT_STATE        0x1611  /* device state */
45 #define HDAPS_PORT_YPOS         0x1612  /* y-axis position */
46 #define HDAPS_PORT_XPOS         0x1614  /* x-axis position */
47 #define HDAPS_PORT_TEMP1        0x1616  /* device temperature, in Celsius */
48 #define HDAPS_PORT_YVAR         0x1617  /* y-axis variance (what is this?) */
49 #define HDAPS_PORT_XVAR         0x1619  /* x-axis variance (what is this?) */
50 #define HDAPS_PORT_TEMP2        0x161b  /* device temperature (again?) */
51 #define HDAPS_PORT_UNKNOWN      0x161c  /* what is this? */
52 #define HDAPS_PORT_KMACT        0x161d  /* keyboard or mouse activity */
53
54 #define STATE_FRESH             0x50    /* accelerometer data is fresh */
55
56 #define KEYBD_MASK              0x20    /* set if keyboard activity */
57 #define MOUSE_MASK              0x40    /* set if mouse activity */
58 #define KEYBD_ISSET(n)          (!! (n & KEYBD_MASK))   /* keyboard used? */
59 #define MOUSE_ISSET(n)          (!! (n & MOUSE_MASK))   /* mouse used? */
60
61 #define INIT_TIMEOUT_MSECS      4000    /* wait up to 4s for device init ... */
62 #define INIT_WAIT_MSECS         200     /* ... in 200ms increments */
63
64 #define HDAPS_POLL_PERIOD       (HZ/20) /* poll for input every 1/20s */
65 #define HDAPS_INPUT_FUZZ        4       /* input event threshold */
66 #define HDAPS_INPUT_FLAT        4
67
68 static struct timer_list hdaps_timer;
69 static struct platform_device *pdev;
70 static struct input_dev *hdaps_idev;
71 static unsigned int hdaps_invert;
72 static u8 km_activity;
73 static int rest_x;
74 static int rest_y;
75
76 static DEFINE_MUTEX(hdaps_mtx);
77
78 /*
79  * __get_latch - Get the value from a given port.  Callers must hold hdaps_mtx.
80  */
81 static inline u8 __get_latch(u16 port)
82 {
83         return inb(port) & 0xff;
84 }
85
86 /*
87  * __check_latch - Check a port latch for a given value.  Returns zero if the
88  * port contains the given value.  Callers must hold hdaps_mtx.
89  */
90 static inline int __check_latch(u16 port, u8 val)
91 {
92         if (__get_latch(port) == val)
93                 return 0;
94         return -EINVAL;
95 }
96
97 /*
98  * __wait_latch - Wait up to 100us for a port latch to get a certain value,
99  * returning zero if the value is obtained.  Callers must hold hdaps_mtx.
100  */
101 static int __wait_latch(u16 port, u8 val)
102 {
103         unsigned int i;
104
105         for (i = 0; i < 20; i++) {
106                 if (!__check_latch(port, val))
107                         return 0;
108                 udelay(5);
109         }
110
111         return -EIO;
112 }
113
114 /*
115  * __device_refresh - request a refresh from the accelerometer.  Does not wait
116  * for refresh to complete.  Callers must hold hdaps_mtx.
117  */
118 static void __device_refresh(void)
119 {
120         udelay(200);
121         if (inb(0x1604) != STATE_FRESH) {
122                 outb(0x11, 0x1610);
123                 outb(0x01, 0x161f);
124         }
125 }
126
127 /*
128  * __device_refresh_sync - request a synchronous refresh from the
129  * accelerometer.  We wait for the refresh to complete.  Returns zero if
130  * successful and nonzero on error.  Callers must hold hdaps_mtx.
131  */
132 static int __device_refresh_sync(void)
133 {
134         __device_refresh();
135         return __wait_latch(0x1604, STATE_FRESH);
136 }
137
138 /*
139  * __device_complete - indicate to the accelerometer that we are done reading
140  * data, and then initiate an async refresh.  Callers must hold hdaps_mtx.
141  */
142 static inline void __device_complete(void)
143 {
144         inb(0x161f);
145         inb(0x1604);
146         __device_refresh();
147 }
148
149 /*
150  * hdaps_readb_one - reads a byte from a single I/O port, placing the value in
151  * the given pointer.  Returns zero on success or a negative error on failure.
152  * Can sleep.
153  */
154 static int hdaps_readb_one(unsigned int port, u8 *val)
155 {
156         int ret;
157
158         mutex_lock(&hdaps_mtx);
159
160         /* do a sync refresh -- we need to be sure that we read fresh data */
161         ret = __device_refresh_sync();
162         if (ret)
163                 goto out;
164
165         *val = inb(port);
166         __device_complete();
167
168 out:
169         mutex_unlock(&hdaps_mtx);
170         return ret;
171 }
172
173 /* __hdaps_read_pair - internal lockless helper for hdaps_read_pair(). */
174 static int __hdaps_read_pair(unsigned int port1, unsigned int port2,
175                              int *x, int *y)
176 {
177         /* do a sync refresh -- we need to be sure that we read fresh data */
178         if (__device_refresh_sync())
179                 return -EIO;
180
181         *y = inw(port2);
182         *x = inw(port1);
183         km_activity = inb(HDAPS_PORT_KMACT);
184         __device_complete();
185
186         /* if hdaps_invert is set, negate the two values */
187         if (hdaps_invert) {
188                 *x = -*x;
189                 *y = -*y;
190         }
191
192         return 0;
193 }
194
195 /*
196  * hdaps_read_pair - reads the values from a pair of ports, placing the values
197  * in the given pointers.  Returns zero on success.  Can sleep.
198  */
199 static int hdaps_read_pair(unsigned int port1, unsigned int port2,
200                            int *val1, int *val2)
201 {
202         int ret;
203
204         mutex_lock(&hdaps_mtx);
205         ret = __hdaps_read_pair(port1, port2, val1, val2);
206         mutex_unlock(&hdaps_mtx);
207
208         return ret;
209 }
210
211 /*
212  * hdaps_device_init - initialize the accelerometer.  Returns zero on success
213  * and negative error code on failure.  Can sleep.
214  */
215 static int hdaps_device_init(void)
216 {
217         int total, ret = -ENXIO;
218
219         mutex_lock(&hdaps_mtx);
220
221         outb(0x13, 0x1610);
222         outb(0x01, 0x161f);
223         if (__wait_latch(0x161f, 0x00))
224                 goto out;
225
226         /*
227          * Most ThinkPads return 0x01.
228          *
229          * Others--namely the R50p, T41p, and T42p--return 0x03.  These laptops
230          * have "inverted" axises.
231          *
232          * The 0x02 value occurs when the chip has been previously initialized.
233          */
234         if (__check_latch(0x1611, 0x03) &&
235                      __check_latch(0x1611, 0x02) &&
236                      __check_latch(0x1611, 0x01))
237                 goto out;
238
239         printk(KERN_DEBUG "hdaps: initial latch check good (0x%02x).\n",
240                __get_latch(0x1611));
241
242         outb(0x17, 0x1610);
243         outb(0x81, 0x1611);
244         outb(0x01, 0x161f);
245         if (__wait_latch(0x161f, 0x00))
246                 goto out;
247         if (__wait_latch(0x1611, 0x00))
248                 goto out;
249         if (__wait_latch(0x1612, 0x60))
250                 goto out;
251         if (__wait_latch(0x1613, 0x00))
252                 goto out;
253         outb(0x14, 0x1610);
254         outb(0x01, 0x1611);
255         outb(0x01, 0x161f);
256         if (__wait_latch(0x161f, 0x00))
257                 goto out;
258         outb(0x10, 0x1610);
259         outb(0xc8, 0x1611);
260         outb(0x00, 0x1612);
261         outb(0x02, 0x1613);
262         outb(0x01, 0x161f);
263         if (__wait_latch(0x161f, 0x00))
264                 goto out;
265         if (__device_refresh_sync())
266                 goto out;
267         if (__wait_latch(0x1611, 0x00))
268                 goto out;
269
270         /* we have done our dance, now let's wait for the applause */
271         for (total = INIT_TIMEOUT_MSECS; total > 0; total -= INIT_WAIT_MSECS) {
272                 int x, y;
273
274                 /* a read of the device helps push it into action */
275                 __hdaps_read_pair(HDAPS_PORT_XPOS, HDAPS_PORT_YPOS, &x, &y);
276                 if (!__wait_latch(0x1611, 0x02)) {
277                         ret = 0;
278                         break;
279                 }
280
281                 msleep(INIT_WAIT_MSECS);
282         }
283
284 out:
285         mutex_unlock(&hdaps_mtx);
286         return ret;
287 }
288
289
290 /* Device model stuff */
291
292 static int hdaps_probe(struct platform_device *dev)
293 {
294         int ret;
295
296         ret = hdaps_device_init();
297         if (ret)
298                 return ret;
299
300         printk(KERN_INFO "hdaps: device successfully initialized.\n");
301         return 0;
302 }
303
304 static int hdaps_resume(struct platform_device *dev)
305 {
306         return hdaps_device_init();
307 }
308
309 static struct platform_driver hdaps_driver = {
310         .probe = hdaps_probe,
311         .resume = hdaps_resume,
312         .driver = {
313                 .name = "hdaps",
314                 .owner = THIS_MODULE,
315         },
316 };
317
318 /*
319  * hdaps_calibrate - Set our "resting" values.  Callers must hold hdaps_mtx.
320  */
321 static void hdaps_calibrate(void)
322 {
323         __hdaps_read_pair(HDAPS_PORT_XPOS, HDAPS_PORT_YPOS, &rest_x, &rest_y);
324 }
325
326 static void hdaps_mousedev_poll(unsigned long unused)
327 {
328         int x, y;
329
330         /* Cannot sleep.  Try nonblockingly.  If we fail, try again later. */
331         if (mutex_trylock(&hdaps_mtx)) {
332                 mod_timer(&hdaps_timer,jiffies + HDAPS_POLL_PERIOD);
333                 return;
334         }
335
336         if (__hdaps_read_pair(HDAPS_PORT_XPOS, HDAPS_PORT_YPOS, &x, &y))
337                 goto out;
338
339         input_report_abs(hdaps_idev, ABS_X, x - rest_x);
340         input_report_abs(hdaps_idev, ABS_Y, y - rest_y);
341         input_sync(hdaps_idev);
342
343         mod_timer(&hdaps_timer, jiffies + HDAPS_POLL_PERIOD);
344
345 out:
346         mutex_unlock(&hdaps_mtx);
347 }
348
349
350 /* Sysfs Files */
351
352 static ssize_t hdaps_position_show(struct device *dev,
353                                    struct device_attribute *attr, char *buf)
354 {
355         int ret, x, y;
356
357         ret = hdaps_read_pair(HDAPS_PORT_XPOS, HDAPS_PORT_YPOS, &x, &y);
358         if (ret)
359                 return ret;
360
361         return sprintf(buf, "(%d,%d)\n", x, y);
362 }
363
364 static ssize_t hdaps_variance_show(struct device *dev,
365                                    struct device_attribute *attr, char *buf)
366 {
367         int ret, x, y;
368
369         ret = hdaps_read_pair(HDAPS_PORT_XVAR, HDAPS_PORT_YVAR, &x, &y);
370         if (ret)
371                 return ret;
372
373         return sprintf(buf, "(%d,%d)\n", x, y);
374 }
375
376 static ssize_t hdaps_temp1_show(struct device *dev,
377                                 struct device_attribute *attr, char *buf)
378 {
379         u8 temp;
380         int ret;
381
382         ret = hdaps_readb_one(HDAPS_PORT_TEMP1, &temp);
383         if (ret < 0)
384                 return ret;
385
386         return sprintf(buf, "%u\n", temp);
387 }
388
389 static ssize_t hdaps_temp2_show(struct device *dev,
390                                 struct device_attribute *attr, char *buf)
391 {
392         u8 temp;
393         int ret;
394
395         ret = hdaps_readb_one(HDAPS_PORT_TEMP2, &temp);
396         if (ret < 0)
397                 return ret;
398
399         return sprintf(buf, "%u\n", temp);
400 }
401
402 static ssize_t hdaps_keyboard_activity_show(struct device *dev,
403                                             struct device_attribute *attr,
404                                             char *buf)
405 {
406         return sprintf(buf, "%u\n", KEYBD_ISSET(km_activity));
407 }
408
409 static ssize_t hdaps_mouse_activity_show(struct device *dev,
410                                          struct device_attribute *attr,
411                                          char *buf)
412 {
413         return sprintf(buf, "%u\n", MOUSE_ISSET(km_activity));
414 }
415
416 static ssize_t hdaps_calibrate_show(struct device *dev,
417                                     struct device_attribute *attr, char *buf)
418 {
419         return sprintf(buf, "(%d,%d)\n", rest_x, rest_y);
420 }
421
422 static ssize_t hdaps_calibrate_store(struct device *dev,
423                                      struct device_attribute *attr,
424                                      const char *buf, size_t count)
425 {
426         mutex_lock(&hdaps_mtx);
427         hdaps_calibrate();
428         mutex_unlock(&hdaps_mtx);
429
430         return count;
431 }
432
433 static ssize_t hdaps_invert_show(struct device *dev,
434                                  struct device_attribute *attr, char *buf)
435 {
436         return sprintf(buf, "%u\n", hdaps_invert);
437 }
438
439 static ssize_t hdaps_invert_store(struct device *dev,
440                                   struct device_attribute *attr,
441                                   const char *buf, size_t count)
442 {
443         int invert;
444
445         if (sscanf(buf, "%d", &invert) != 1 || (invert != 1 && invert != 0))
446                 return -EINVAL;
447
448         hdaps_invert = invert;
449         hdaps_calibrate();
450
451         return count;
452 }
453
454 static DEVICE_ATTR(position, 0444, hdaps_position_show, NULL);
455 static DEVICE_ATTR(variance, 0444, hdaps_variance_show, NULL);
456 static DEVICE_ATTR(temp1, 0444, hdaps_temp1_show, NULL);
457 static DEVICE_ATTR(temp2, 0444, hdaps_temp2_show, NULL);
458 static DEVICE_ATTR(keyboard_activity, 0444, hdaps_keyboard_activity_show, NULL);
459 static DEVICE_ATTR(mouse_activity, 0444, hdaps_mouse_activity_show, NULL);
460 static DEVICE_ATTR(calibrate, 0644, hdaps_calibrate_show,hdaps_calibrate_store);
461 static DEVICE_ATTR(invert, 0644, hdaps_invert_show, hdaps_invert_store);
462
463 static struct attribute *hdaps_attributes[] = {
464         &dev_attr_position.attr,
465         &dev_attr_variance.attr,
466         &dev_attr_temp1.attr,
467         &dev_attr_temp2.attr,
468         &dev_attr_keyboard_activity.attr,
469         &dev_attr_mouse_activity.attr,
470         &dev_attr_calibrate.attr,
471         &dev_attr_invert.attr,
472         NULL,
473 };
474
475 static struct attribute_group hdaps_attribute_group = {
476         .attrs = hdaps_attributes,
477 };
478
479
480 /* Module stuff */
481
482 /* hdaps_dmi_match - found a match.  return one, short-circuiting the hunt. */
483 static int __init hdaps_dmi_match(struct dmi_system_id *id)
484 {
485         printk(KERN_INFO "hdaps: %s detected.\n", id->ident);
486         return 1;
487 }
488
489 /* hdaps_dmi_match_invert - found an inverted match. */
490 static int __init hdaps_dmi_match_invert(struct dmi_system_id *id)
491 {
492         hdaps_invert = 1;
493         printk(KERN_INFO "hdaps: inverting axis readings.\n");
494         return hdaps_dmi_match(id);
495 }
496
497 #define HDAPS_DMI_MATCH_NORMAL(vendor, model) {         \
498         .ident = vendor " " model,                      \
499         .callback = hdaps_dmi_match,                    \
500         .matches = {                                    \
501                 DMI_MATCH(DMI_BOARD_VENDOR, vendor),    \
502                 DMI_MATCH(DMI_PRODUCT_VERSION, model)   \
503         }                                               \
504 }
505
506 #define HDAPS_DMI_MATCH_INVERT(vendor, model) {         \
507         .ident = vendor " " model,                      \
508         .callback = hdaps_dmi_match_invert,             \
509         .matches = {                                    \
510                 DMI_MATCH(DMI_BOARD_VENDOR, vendor),    \
511                 DMI_MATCH(DMI_PRODUCT_VERSION, model)   \
512         }                                               \
513 }
514
515 /* Note that HDAPS_DMI_MATCH_NORMAL("ThinkPad T42") would match
516    "ThinkPad T42p", so the order of the entries matters.
517    If your ThinkPad is not recognized, please update to latest
518    BIOS. This is especially the case for some R52 ThinkPads. */
519 static struct dmi_system_id __initdata hdaps_whitelist[] = {
520         HDAPS_DMI_MATCH_INVERT("IBM", "ThinkPad R50p"),
521         HDAPS_DMI_MATCH_NORMAL("IBM", "ThinkPad R50"),
522         HDAPS_DMI_MATCH_NORMAL("IBM", "ThinkPad R51"),
523         HDAPS_DMI_MATCH_NORMAL("IBM", "ThinkPad R52"),
524         HDAPS_DMI_MATCH_INVERT("IBM", "ThinkPad T41p"),
525         HDAPS_DMI_MATCH_NORMAL("IBM", "ThinkPad T41"),
526         HDAPS_DMI_MATCH_INVERT("IBM", "ThinkPad T42p"),
527         HDAPS_DMI_MATCH_NORMAL("IBM", "ThinkPad T42"),
528         HDAPS_DMI_MATCH_NORMAL("IBM", "ThinkPad T43"),
529         HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad T60"),
530         HDAPS_DMI_MATCH_NORMAL("IBM", "ThinkPad X40"),
531         HDAPS_DMI_MATCH_NORMAL("IBM", "ThinkPad X41"),
532         HDAPS_DMI_MATCH_INVERT("LENOVO", "ThinkPad X60"),
533         HDAPS_DMI_MATCH_NORMAL("IBM", "ThinkPad Z60m"),
534         { .ident = NULL }
535 };
536
537 static int __init hdaps_init(void)
538 {
539         int ret;
540
541         if (!dmi_check_system(hdaps_whitelist)) {
542                 printk(KERN_WARNING "hdaps: supported laptop not found!\n");
543                 ret = -ENODEV;
544                 goto out;
545         }
546
547         if (!request_region(HDAPS_LOW_PORT, HDAPS_NR_PORTS, "hdaps")) {
548                 ret = -ENXIO;
549                 goto out;
550         }
551
552         ret = platform_driver_register(&hdaps_driver);
553         if (ret)
554                 goto out_region;
555
556         pdev = platform_device_register_simple("hdaps", -1, NULL, 0);
557         if (IS_ERR(pdev)) {
558                 ret = PTR_ERR(pdev);
559                 goto out_driver;
560         }
561
562         ret = sysfs_create_group(&pdev->dev.kobj, &hdaps_attribute_group);
563         if (ret)
564                 goto out_device;
565
566         hdaps_idev = input_allocate_device();
567         if (!hdaps_idev) {
568                 ret = -ENOMEM;
569                 goto out_group;
570         }
571
572         /* initial calibrate for the input device */
573         hdaps_calibrate();
574
575         /* initialize the input class */
576         hdaps_idev->name = "hdaps";
577         hdaps_idev->dev.parent = &pdev->dev;
578         hdaps_idev->evbit[0] = BIT(EV_ABS);
579         input_set_abs_params(hdaps_idev, ABS_X,
580                         -256, 256, HDAPS_INPUT_FUZZ, HDAPS_INPUT_FLAT);
581         input_set_abs_params(hdaps_idev, ABS_Y,
582                         -256, 256, HDAPS_INPUT_FUZZ, HDAPS_INPUT_FLAT);
583
584         ret = input_register_device(hdaps_idev);
585         if (ret)
586                 goto out_idev;
587
588         /* start up our timer for the input device */
589         init_timer(&hdaps_timer);
590         hdaps_timer.function = hdaps_mousedev_poll;
591         hdaps_timer.expires = jiffies + HDAPS_POLL_PERIOD;
592         add_timer(&hdaps_timer);
593
594         printk(KERN_INFO "hdaps: driver successfully loaded.\n");
595         return 0;
596
597 out_idev:
598         input_free_device(hdaps_idev);
599 out_group:
600         sysfs_remove_group(&pdev->dev.kobj, &hdaps_attribute_group);
601 out_device:
602         platform_device_unregister(pdev);
603 out_driver:
604         platform_driver_unregister(&hdaps_driver);
605 out_region:
606         release_region(HDAPS_LOW_PORT, HDAPS_NR_PORTS);
607 out:
608         printk(KERN_WARNING "hdaps: driver init failed (ret=%d)!\n", ret);
609         return ret;
610 }
611
612 static void __exit hdaps_exit(void)
613 {
614         del_timer_sync(&hdaps_timer);
615         input_unregister_device(hdaps_idev);
616         sysfs_remove_group(&pdev->dev.kobj, &hdaps_attribute_group);
617         platform_device_unregister(pdev);
618         platform_driver_unregister(&hdaps_driver);
619         release_region(HDAPS_LOW_PORT, HDAPS_NR_PORTS);
620
621         printk(KERN_INFO "hdaps: driver unloaded.\n");
622 }
623
624 module_init(hdaps_init);
625 module_exit(hdaps_exit);
626
627 module_param_named(invert, hdaps_invert, bool, 0);
628 MODULE_PARM_DESC(invert, "invert data along each axis");
629
630 MODULE_AUTHOR("Robert Love");
631 MODULE_DESCRIPTION("IBM Hard Drive Active Protection System (HDAPS) driver");
632 MODULE_LICENSE("GPL v2");