]> Pileus Git - ~andy/linux/blob - drivers/staging/nvec/nvec_ps2.c
Merge tag 'msm-dt-for-3.7' of git://git.kernel.org/pub/scm/linux/kernel/git/davidb...
[~andy/linux] / 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;
100
101         ser_dev = devm_kzalloc(&pdev->dev, sizeof(struct serio), GFP_KERNEL);
102         if (ser_dev == NULL)
103                 return -ENOMEM;
104
105         ser_dev->id.type = SERIO_PS_PSTHRU;
106         ser_dev->write = ps2_sendcommand;
107         ser_dev->start = ps2_startstreaming;
108         ser_dev->stop = ps2_stopstreaming;
109
110         strlcpy(ser_dev->name, "nvec mouse", sizeof(ser_dev->name));
111         strlcpy(ser_dev->phys, "nvec", sizeof(ser_dev->phys));
112
113         ps2_dev.ser_dev = ser_dev;
114         ps2_dev.notifier.notifier_call = nvec_ps2_notifier;
115         ps2_dev.nvec = nvec;
116         nvec_register_notifier(nvec, &ps2_dev.notifier, 0);
117
118         serio_register_port(ser_dev);
119
120         /* mouse reset */
121         nvec_write_async(nvec, MOUSE_RESET, 4);
122
123         return 0;
124 }
125
126 static int __devexit nvec_mouse_remove(struct platform_device *pdev)
127 {
128         serio_unregister_port(ps2_dev.ser_dev);
129
130         return 0;
131 }
132
133 #ifdef CONFIG_PM_SLEEP
134 static int nvec_mouse_suspend(struct device *dev)
135 {
136         struct platform_device *pdev = to_platform_device(dev);
137         struct nvec_chip *nvec = dev_get_drvdata(pdev->dev.parent);
138
139         /* disable mouse */
140         nvec_write_async(nvec, "\x06\xf4", 2);
141
142         /* send cancel autoreceive */
143         nvec_write_async(nvec, "\x06\x04", 2);
144
145         return 0;
146 }
147
148 static int nvec_mouse_resume(struct device *dev)
149 {
150         struct platform_device *pdev = to_platform_device(dev);
151         struct nvec_chip *nvec = dev_get_drvdata(pdev->dev.parent);
152
153         ps2_startstreaming(ps2_dev.ser_dev);
154
155         /* enable mouse */
156         nvec_write_async(nvec, "\x06\xf5", 2);
157
158         return 0;
159 }
160 #endif
161
162 static const SIMPLE_DEV_PM_OPS(nvec_mouse_pm_ops, nvec_mouse_suspend,
163                                 nvec_mouse_resume);
164
165 static struct platform_driver nvec_mouse_driver = {
166         .probe  = nvec_mouse_probe,
167         .remove = __devexit_p(nvec_mouse_remove),
168         .driver = {
169                 .name = "nvec-mouse",
170                 .owner = THIS_MODULE,
171                 .pm = &nvec_mouse_pm_ops,
172         },
173 };
174
175 module_platform_driver(nvec_mouse_driver);
176
177 MODULE_DESCRIPTION("NVEC mouse driver");
178 MODULE_AUTHOR("Marc Dietrich <marvin24@gmx.de>");
179 MODULE_LICENSE("GPL");