xref: /openbmc/linux/drivers/staging/nvec/nvec_ps2.c (revision cedff4e3)
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