Merge git://git.kernel.org/pub/scm/linux/kernel/git/sage/ceph-client
[linux-2.6-block.git] / drivers / staging / nvec / nvec_ps2.c
1 /*
2  * nvec_ps2: mouse driver for a NVIDIA compliant embedded controller
3  *
4  * Copyright (C) 2011 The AC100 Kernel Team <ac100@lists.launchpad.net>
5  *
6  * Authors:  Pierre-Hugues Husson <phhusson@free.fr>
7  *           Ilya Petrov <ilya.muromec@gmail.com>
8  *           Marc Dietrich <marvin24@gmx.de>
9  *
10  * This file is subject to the terms and conditions of the GNU General Public
11  * License.  See the file "COPYING" in the main directory of this archive
12  * for more details.
13  *
14  */
15
16 #include <linux/module.h>
17 #include <linux/slab.h>
18 #include <linux/serio.h>
19 #include <linux/delay.h>
20 #include <linux/platform_device.h>
21
22 #include "nvec.h"
23
24 #define START_STREAMING {'\x06', '\x03', '\x06'}
25 #define STOP_STREAMING  {'\x06', '\x04'}
26 #define SEND_COMMAND    {'\x06', '\x01', '\xf4', '\x01'}
27
28 #ifdef NVEC_PS2_DEBUG
29 #define NVEC_PHD(str, buf, len) \
30         print_hex_dump(KERN_DEBUG, str, DUMP_PREFIX_NONE, \
31                         16, 1, buf, len, false)
32 #else
33 #define NVEC_PHD(str, buf, len)
34 #endif
35
36 static const unsigned char MOUSE_RESET[] = {'\x06', '\x01', '\xff', '\x03'};
37
38 struct nvec_ps2 {
39         struct serio *ser_dev;
40         struct notifier_block notifier;
41         struct nvec_chip *nvec;
42 };
43
44 static struct nvec_ps2 ps2_dev;
45
46 static int ps2_startstreaming(struct serio *ser_dev)
47 {
48         unsigned char buf[] = START_STREAMING;
49         return nvec_write_async(ps2_dev.nvec, buf, sizeof(buf));
50 }
51
52 static void ps2_stopstreaming(struct serio *ser_dev)
53 {
54         unsigned char buf[] = STOP_STREAMING;
55         nvec_write_async(ps2_dev.nvec, buf, sizeof(buf));
56 }
57
58 static int ps2_sendcommand(struct serio *ser_dev, unsigned char cmd)
59 {
60         unsigned char buf[] = SEND_COMMAND;
61
62         buf[2] = cmd & 0xff;
63
64         dev_dbg(&ser_dev->dev, "Sending ps2 cmd %02x\n", cmd);
65         return nvec_write_async(ps2_dev.nvec, buf, sizeof(buf));
66 }
67
68 static int nvec_ps2_notifier(struct notifier_block *nb,
69                              unsigned long event_type, void *data)
70 {
71         int i;
72         unsigned char *msg = (unsigned char *)data;
73
74         switch (event_type) {
75         case NVEC_PS2_EVT:
76                 for (i = 0; i < msg[1]; i++)
77                         serio_interrupt(ps2_dev.ser_dev, msg[2 + i], 0);
78                 NVEC_PHD("ps/2 mouse event: ", &msg[2], msg[1]);
79                 return NOTIFY_STOP;
80
81         case NVEC_PS2:
82                 if (msg[2] == 1) {
83                         for (i = 0; i < (msg[1] - 2); i++)
84                                 serio_interrupt(ps2_dev.ser_dev, msg[i + 4], 0);
85                         NVEC_PHD("ps/2 mouse reply: ", &msg[4], msg[1] - 2);
86                 }
87
88                 else if (msg[1] != 2) /* !ack */
89                         NVEC_PHD("unhandled mouse event: ", msg, msg[1] + 2);
90                 return NOTIFY_STOP;
91         }
92
93         return NOTIFY_DONE;
94 }
95
96 static int __devinit nvec_mouse_probe(struct platform_device *pdev)
97 {
98         struct nvec_chip *nvec = dev_get_drvdata(pdev->dev.parent);
99         struct serio *ser_dev = kzalloc(sizeof(struct serio), GFP_KERNEL);
100
101         ser_dev->id.type = SERIO_PS_PSTHRU;
102         ser_dev->write = ps2_sendcommand;
103         ser_dev->start = ps2_startstreaming;
104         ser_dev->stop = ps2_stopstreaming;
105
106         strlcpy(ser_dev->name, "nvec mouse", sizeof(ser_dev->name));
107         strlcpy(ser_dev->phys, "nvec", sizeof(ser_dev->phys));
108
109         ps2_dev.ser_dev = ser_dev;
110         ps2_dev.notifier.notifier_call = nvec_ps2_notifier;
111         ps2_dev.nvec = nvec;
112         nvec_register_notifier(nvec, &ps2_dev.notifier, 0);
113
114         serio_register_port(ser_dev);
115
116         /* mouse reset */
117         nvec_write_async(nvec, MOUSE_RESET, 4);
118
119         return 0;
120 }
121
122 static int nvec_mouse_suspend(struct platform_device *pdev, pm_message_t state)
123 {
124         struct nvec_chip *nvec = dev_get_drvdata(pdev->dev.parent);
125
126         /* disable mouse */
127         nvec_write_async(nvec, "\x06\xf4", 2);
128
129         /* send cancel autoreceive */
130         nvec_write_async(nvec, "\x06\x04", 2);
131
132         return 0;
133 }
134
135 static int nvec_mouse_resume(struct platform_device *pdev)
136 {
137         struct nvec_chip *nvec = dev_get_drvdata(pdev->dev.parent);
138
139         ps2_startstreaming(ps2_dev.ser_dev);
140
141         /* enable mouse */
142         nvec_write_async(nvec, "\x06\xf5", 2);
143
144         return 0;
145 }
146
147 static struct platform_driver nvec_mouse_driver = {
148         .probe  = nvec_mouse_probe,
149         .suspend = nvec_mouse_suspend,
150         .resume = nvec_mouse_resume,
151         .driver = {
152                 .name = "nvec-mouse",
153                 .owner = THIS_MODULE,
154         },
155 };
156
157 static int __init nvec_mouse_init(void)
158 {
159         return platform_driver_register(&nvec_mouse_driver);
160 }
161
162 module_init(nvec_mouse_init);
163
164 MODULE_DESCRIPTION("NVEC mouse driver");
165 MODULE_AUTHOR("Marc Dietrich <marvin24@gmx.de>");
166 MODULE_LICENSE("GPL");