1971bcfcaSGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
2162c7d8cSMarc Dietrich /*
3162c7d8cSMarc Dietrich * nvec_ps2: mouse driver for a NVIDIA compliant embedded controller
4162c7d8cSMarc Dietrich *
5162c7d8cSMarc Dietrich * Copyright (C) 2011 The AC100 Kernel Team <ac100@lists.launchpad.net>
6162c7d8cSMarc Dietrich *
7162c7d8cSMarc Dietrich * Authors: Pierre-Hugues Husson <phhusson@free.fr>
8162c7d8cSMarc Dietrich * Ilya Petrov <ilya.muromec@gmail.com>
9162c7d8cSMarc Dietrich * Marc Dietrich <marvin24@gmx.de>
10162c7d8cSMarc Dietrich */
11162c7d8cSMarc Dietrich
127974035cSJulian Andres Klode #include <linux/module.h>
1332890b98SMarc Dietrich #include <linux/slab.h>
1432890b98SMarc Dietrich #include <linux/serio.h>
1532890b98SMarc Dietrich #include <linux/delay.h>
16f686e9afSMarc Dietrich #include <linux/platform_device.h>
17162c7d8cSMarc Dietrich
1832890b98SMarc Dietrich #include "nvec.h"
1932890b98SMarc Dietrich
2093eff83fSMarc Dietrich #define PACKET_SIZE 6
2132890b98SMarc Dietrich
2285a90528SMarc Dietrich #define ENABLE_MOUSE 0xf4
2385a90528SMarc Dietrich #define DISABLE_MOUSE 0xf5
2493eff83fSMarc Dietrich #define PSMOUSE_RST 0xff
2585a90528SMarc Dietrich
260eedab70SMarc Dietrich #ifdef NVEC_PS2_DEBUG
270eedab70SMarc Dietrich #define NVEC_PHD(str, buf, len) \
280eedab70SMarc Dietrich print_hex_dump(KERN_DEBUG, str, DUMP_PREFIX_NONE, \
290eedab70SMarc Dietrich 16, 1, buf, len, false)
300eedab70SMarc Dietrich #else
31946e2f00SArnd Bergmann #define NVEC_PHD(str, buf, len) do { } while (0)
320eedab70SMarc Dietrich #endif
330eedab70SMarc Dietrich
3493eff83fSMarc Dietrich enum ps2_subcmds {
3593eff83fSMarc Dietrich SEND_COMMAND = 1,
3693eff83fSMarc Dietrich RECEIVE_N,
3793eff83fSMarc Dietrich AUTO_RECEIVE_N,
3893eff83fSMarc Dietrich CANCEL_AUTO_RECEIVE,
3993eff83fSMarc Dietrich };
40162c7d8cSMarc Dietrich
41162c7d8cSMarc Dietrich struct nvec_ps2 {
4232890b98SMarc Dietrich struct serio *ser_dev;
4332890b98SMarc Dietrich struct notifier_block notifier;
4432890b98SMarc Dietrich struct nvec_chip *nvec;
4532890b98SMarc Dietrich };
4632890b98SMarc Dietrich
4732890b98SMarc Dietrich static struct nvec_ps2 ps2_dev;
4832890b98SMarc Dietrich
ps2_startstreaming(struct serio * ser_dev)4932890b98SMarc Dietrich static int ps2_startstreaming(struct serio *ser_dev)
5032890b98SMarc Dietrich {
5193eff83fSMarc Dietrich unsigned char buf[] = { NVEC_PS2, AUTO_RECEIVE_N, PACKET_SIZE };
52fa799617SPawel Lebioda
53ff006d12SJulian Andres Klode return nvec_write_async(ps2_dev.nvec, buf, sizeof(buf));
5432890b98SMarc Dietrich }
5532890b98SMarc Dietrich
ps2_stopstreaming(struct serio * ser_dev)5632890b98SMarc Dietrich static void ps2_stopstreaming(struct serio *ser_dev)
5732890b98SMarc Dietrich {
5893eff83fSMarc Dietrich unsigned char buf[] = { NVEC_PS2, CANCEL_AUTO_RECEIVE };
59fa799617SPawel Lebioda
6032890b98SMarc Dietrich nvec_write_async(ps2_dev.nvec, buf, sizeof(buf));
6132890b98SMarc Dietrich }
6232890b98SMarc Dietrich
ps2_sendcommand(struct serio * ser_dev,unsigned char cmd)6332890b98SMarc Dietrich static int ps2_sendcommand(struct serio *ser_dev, unsigned char cmd)
6432890b98SMarc Dietrich {
6593eff83fSMarc Dietrich unsigned char buf[] = { NVEC_PS2, SEND_COMMAND, ENABLE_MOUSE, 1 };
6632890b98SMarc Dietrich
6732890b98SMarc Dietrich buf[2] = cmd & 0xff;
6832890b98SMarc Dietrich
6932890b98SMarc Dietrich dev_dbg(&ser_dev->dev, "Sending ps2 cmd %02x\n", cmd);
70ff006d12SJulian Andres Klode return nvec_write_async(ps2_dev.nvec, buf, sizeof(buf));
7132890b98SMarc Dietrich }
7232890b98SMarc Dietrich
nvec_ps2_notifier(struct notifier_block * nb,unsigned long event_type,void * data)7332890b98SMarc Dietrich static int nvec_ps2_notifier(struct notifier_block *nb,
7432890b98SMarc Dietrich unsigned long event_type, void *data)
7532890b98SMarc Dietrich {
7632890b98SMarc Dietrich int i;
770df8f51eSBen Marsh unsigned char *msg = data;
7832890b98SMarc Dietrich
7932890b98SMarc Dietrich switch (event_type) {
8032890b98SMarc Dietrich case NVEC_PS2_EVT:
811e46e627SJulian Andres Klode for (i = 0; i < msg[1]; i++)
821e46e627SJulian Andres Klode serio_interrupt(ps2_dev.ser_dev, msg[2 + i], 0);
830eedab70SMarc Dietrich NVEC_PHD("ps/2 mouse event: ", &msg[2], msg[1]);
8432890b98SMarc Dietrich return NOTIFY_STOP;
8532890b98SMarc Dietrich
8632890b98SMarc Dietrich case NVEC_PS2:
870eedab70SMarc Dietrich if (msg[2] == 1) {
8832890b98SMarc Dietrich for (i = 0; i < (msg[1] - 2); i++)
8932890b98SMarc Dietrich serio_interrupt(ps2_dev.ser_dev, msg[i + 4], 0);
900eedab70SMarc Dietrich NVEC_PHD("ps/2 mouse reply: ", &msg[4], msg[1] - 2);
9132890b98SMarc Dietrich }
9232890b98SMarc Dietrich
930eedab70SMarc Dietrich else if (msg[1] != 2) /* !ack */
940eedab70SMarc Dietrich NVEC_PHD("unhandled mouse event: ", msg, msg[1] + 2);
9532890b98SMarc Dietrich return NOTIFY_STOP;
9632890b98SMarc Dietrich }
9732890b98SMarc Dietrich
9832890b98SMarc Dietrich return NOTIFY_DONE;
9932890b98SMarc Dietrich }
10032890b98SMarc Dietrich
nvec_mouse_probe(struct platform_device * pdev)10146620803SBill Pemberton static int nvec_mouse_probe(struct platform_device *pdev)
10232890b98SMarc Dietrich {
103f686e9afSMarc Dietrich struct nvec_chip *nvec = dev_get_drvdata(pdev->dev.parent);
104f5e3352eSMarc Dietrich struct serio *ser_dev;
105f5e3352eSMarc Dietrich
106cf13a747SArushi Singhal ser_dev = kzalloc(sizeof(*ser_dev), GFP_KERNEL);
1074c42d979SSomya Anand if (!ser_dev)
108f5e3352eSMarc Dietrich return -ENOMEM;
10932890b98SMarc Dietrich
11017c1c9baSPaul Fertser ser_dev->id.type = SERIO_8042;
11132890b98SMarc Dietrich ser_dev->write = ps2_sendcommand;
11234ba143bSMarc Dietrich ser_dev->start = ps2_startstreaming;
11334ba143bSMarc Dietrich ser_dev->stop = ps2_stopstreaming;
11432890b98SMarc Dietrich
115aca1bf72SKumar Kartikeya Dwivedi strscpy(ser_dev->name, "nvec mouse", sizeof(ser_dev->name));
116aca1bf72SKumar Kartikeya Dwivedi strscpy(ser_dev->phys, "nvec", sizeof(ser_dev->phys));
11732890b98SMarc Dietrich
11832890b98SMarc Dietrich ps2_dev.ser_dev = ser_dev;
11932890b98SMarc Dietrich ps2_dev.notifier.notifier_call = nvec_ps2_notifier;
12032890b98SMarc Dietrich ps2_dev.nvec = nvec;
12132890b98SMarc Dietrich nvec_register_notifier(nvec, &ps2_dev.notifier, 0);
12232890b98SMarc Dietrich
12332890b98SMarc Dietrich serio_register_port(ser_dev);
12432890b98SMarc Dietrich
12532890b98SMarc Dietrich return 0;
12632890b98SMarc Dietrich }
127f686e9afSMarc Dietrich
nvec_mouse_remove(struct platform_device * pdev)128*cedff4e3SUwe Kleine-König static void nvec_mouse_remove(struct platform_device *pdev)
1293cdde3a3SMarc Dietrich {
130c2b62f60SMarc Dietrich struct nvec_chip *nvec = dev_get_drvdata(pdev->dev.parent);
131c2b62f60SMarc Dietrich
132c2b62f60SMarc Dietrich ps2_sendcommand(ps2_dev.ser_dev, DISABLE_MOUSE);
133c2b62f60SMarc Dietrich ps2_stopstreaming(ps2_dev.ser_dev);
134c2b62f60SMarc Dietrich nvec_unregister_notifier(nvec, &ps2_dev.notifier);
1353cdde3a3SMarc Dietrich serio_unregister_port(ps2_dev.ser_dev);
1363cdde3a3SMarc Dietrich }
1373cdde3a3SMarc Dietrich
138ebefae28SMarc Dietrich #ifdef CONFIG_PM_SLEEP
nvec_mouse_suspend(struct device * dev)139ebefae28SMarc Dietrich static int nvec_mouse_suspend(struct device *dev)
140d1b5342cSMarc Dietrich {
141a573298bSMarc Dietrich /* disable mouse */
14285a90528SMarc Dietrich ps2_sendcommand(ps2_dev.ser_dev, DISABLE_MOUSE);
143a573298bSMarc Dietrich
144d1b5342cSMarc Dietrich /* send cancel autoreceive */
14585a90528SMarc Dietrich ps2_stopstreaming(ps2_dev.ser_dev);
146d1b5342cSMarc Dietrich
147d1b5342cSMarc Dietrich return 0;
148d1b5342cSMarc Dietrich }
149d1b5342cSMarc Dietrich
nvec_mouse_resume(struct device * dev)150ebefae28SMarc Dietrich static int nvec_mouse_resume(struct device *dev)
151d1b5342cSMarc Dietrich {
15285a90528SMarc Dietrich /* start streaming */
153d1b5342cSMarc Dietrich ps2_startstreaming(ps2_dev.ser_dev);
154d1b5342cSMarc Dietrich
155a573298bSMarc Dietrich /* enable mouse */
15685a90528SMarc Dietrich ps2_sendcommand(ps2_dev.ser_dev, ENABLE_MOUSE);
157a573298bSMarc Dietrich
158d1b5342cSMarc Dietrich return 0;
159d1b5342cSMarc Dietrich }
160ebefae28SMarc Dietrich #endif
161ebefae28SMarc Dietrich
162716baa7bSPeng Fan static SIMPLE_DEV_PM_OPS(nvec_mouse_pm_ops, nvec_mouse_suspend,
163ebefae28SMarc Dietrich nvec_mouse_resume);
164d1b5342cSMarc Dietrich
165f686e9afSMarc Dietrich static struct platform_driver nvec_mouse_driver = {
166f686e9afSMarc Dietrich .probe = nvec_mouse_probe,
167*cedff4e3SUwe Kleine-König .remove_new = nvec_mouse_remove,
168f686e9afSMarc Dietrich .driver = {
169f686e9afSMarc Dietrich .name = "nvec-mouse",
170ebefae28SMarc Dietrich .pm = &nvec_mouse_pm_ops,
171f686e9afSMarc Dietrich },
172f686e9afSMarc Dietrich };
173f686e9afSMarc Dietrich
1749891b1ceSMarc Dietrich module_platform_driver(nvec_mouse_driver);
175162c7d8cSMarc Dietrich
176162c7d8cSMarc Dietrich MODULE_DESCRIPTION("NVEC mouse driver");
177162c7d8cSMarc Dietrich MODULE_AUTHOR("Marc Dietrich <marvin24@gmx.de>");
17899758decSMarc Dietrich MODULE_ALIAS("platform:nvec-mouse");
179162c7d8cSMarc Dietrich MODULE_LICENSE("GPL");
180