diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-11 08:27:49 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-11 08:27:49 +0000 |
commit | ace9429bb58fd418f0c81d4c2835699bddf6bde6 (patch) | |
tree | b2d64bc10158fdd5497876388cd68142ca374ed3 /drivers/staging/nvec/nvec_ps2.c | |
parent | Initial commit. (diff) | |
download | linux-ace9429bb58fd418f0c81d4c2835699bddf6bde6.tar.xz linux-ace9429bb58fd418f0c81d4c2835699bddf6bde6.zip |
Adding upstream version 6.6.15.upstream/6.6.15
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'drivers/staging/nvec/nvec_ps2.c')
-rw-r--r-- | drivers/staging/nvec/nvec_ps2.c | 179 |
1 files changed, 179 insertions, 0 deletions
diff --git a/drivers/staging/nvec/nvec_ps2.c b/drivers/staging/nvec/nvec_ps2.c new file mode 100644 index 0000000000..cb6d71b8dc --- /dev/null +++ b/drivers/staging/nvec/nvec_ps2.c @@ -0,0 +1,179 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * nvec_ps2: mouse driver for a NVIDIA compliant embedded controller + * + * Copyright (C) 2011 The AC100 Kernel Team <ac100@lists.launchpad.net> + * + * Authors: Pierre-Hugues Husson <phhusson@free.fr> + * Ilya Petrov <ilya.muromec@gmail.com> + * Marc Dietrich <marvin24@gmx.de> + */ + +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/serio.h> +#include <linux/delay.h> +#include <linux/platform_device.h> + +#include "nvec.h" + +#define PACKET_SIZE 6 + +#define ENABLE_MOUSE 0xf4 +#define DISABLE_MOUSE 0xf5 +#define PSMOUSE_RST 0xff + +#ifdef NVEC_PS2_DEBUG +#define NVEC_PHD(str, buf, len) \ + print_hex_dump(KERN_DEBUG, str, DUMP_PREFIX_NONE, \ + 16, 1, buf, len, false) +#else +#define NVEC_PHD(str, buf, len) do { } while (0) +#endif + +enum ps2_subcmds { + SEND_COMMAND = 1, + RECEIVE_N, + AUTO_RECEIVE_N, + CANCEL_AUTO_RECEIVE, +}; + +struct nvec_ps2 { + struct serio *ser_dev; + struct notifier_block notifier; + struct nvec_chip *nvec; +}; + +static struct nvec_ps2 ps2_dev; + +static int ps2_startstreaming(struct serio *ser_dev) +{ + unsigned char buf[] = { NVEC_PS2, AUTO_RECEIVE_N, PACKET_SIZE }; + + return nvec_write_async(ps2_dev.nvec, buf, sizeof(buf)); +} + +static void ps2_stopstreaming(struct serio *ser_dev) +{ + unsigned char buf[] = { NVEC_PS2, CANCEL_AUTO_RECEIVE }; + + nvec_write_async(ps2_dev.nvec, buf, sizeof(buf)); +} + +static int ps2_sendcommand(struct serio *ser_dev, unsigned char cmd) +{ + unsigned char buf[] = { NVEC_PS2, SEND_COMMAND, ENABLE_MOUSE, 1 }; + + buf[2] = cmd & 0xff; + + dev_dbg(&ser_dev->dev, "Sending ps2 cmd %02x\n", cmd); + return nvec_write_async(ps2_dev.nvec, buf, sizeof(buf)); +} + +static int nvec_ps2_notifier(struct notifier_block *nb, + unsigned long event_type, void *data) +{ + int i; + unsigned char *msg = data; + + switch (event_type) { + case NVEC_PS2_EVT: + for (i = 0; i < msg[1]; i++) + serio_interrupt(ps2_dev.ser_dev, msg[2 + i], 0); + NVEC_PHD("ps/2 mouse event: ", &msg[2], msg[1]); + return NOTIFY_STOP; + + case NVEC_PS2: + if (msg[2] == 1) { + for (i = 0; i < (msg[1] - 2); i++) + serio_interrupt(ps2_dev.ser_dev, msg[i + 4], 0); + NVEC_PHD("ps/2 mouse reply: ", &msg[4], msg[1] - 2); + } + + else if (msg[1] != 2) /* !ack */ + NVEC_PHD("unhandled mouse event: ", msg, msg[1] + 2); + return NOTIFY_STOP; + } + + return NOTIFY_DONE; +} + +static int nvec_mouse_probe(struct platform_device *pdev) +{ + struct nvec_chip *nvec = dev_get_drvdata(pdev->dev.parent); + struct serio *ser_dev; + + ser_dev = kzalloc(sizeof(*ser_dev), GFP_KERNEL); + if (!ser_dev) + return -ENOMEM; + + ser_dev->id.type = SERIO_8042; + ser_dev->write = ps2_sendcommand; + ser_dev->start = ps2_startstreaming; + ser_dev->stop = ps2_stopstreaming; + + strscpy(ser_dev->name, "nvec mouse", sizeof(ser_dev->name)); + strscpy(ser_dev->phys, "nvec", sizeof(ser_dev->phys)); + + ps2_dev.ser_dev = ser_dev; + ps2_dev.notifier.notifier_call = nvec_ps2_notifier; + ps2_dev.nvec = nvec; + nvec_register_notifier(nvec, &ps2_dev.notifier, 0); + + serio_register_port(ser_dev); + + return 0; +} + +static void nvec_mouse_remove(struct platform_device *pdev) +{ + struct nvec_chip *nvec = dev_get_drvdata(pdev->dev.parent); + + ps2_sendcommand(ps2_dev.ser_dev, DISABLE_MOUSE); + ps2_stopstreaming(ps2_dev.ser_dev); + nvec_unregister_notifier(nvec, &ps2_dev.notifier); + serio_unregister_port(ps2_dev.ser_dev); +} + +#ifdef CONFIG_PM_SLEEP +static int nvec_mouse_suspend(struct device *dev) +{ + /* disable mouse */ + ps2_sendcommand(ps2_dev.ser_dev, DISABLE_MOUSE); + + /* send cancel autoreceive */ + ps2_stopstreaming(ps2_dev.ser_dev); + + return 0; +} + +static int nvec_mouse_resume(struct device *dev) +{ + /* start streaming */ + ps2_startstreaming(ps2_dev.ser_dev); + + /* enable mouse */ + ps2_sendcommand(ps2_dev.ser_dev, ENABLE_MOUSE); + + return 0; +} +#endif + +static SIMPLE_DEV_PM_OPS(nvec_mouse_pm_ops, nvec_mouse_suspend, + nvec_mouse_resume); + +static struct platform_driver nvec_mouse_driver = { + .probe = nvec_mouse_probe, + .remove_new = nvec_mouse_remove, + .driver = { + .name = "nvec-mouse", + .pm = &nvec_mouse_pm_ops, + }, +}; + +module_platform_driver(nvec_mouse_driver); + +MODULE_DESCRIPTION("NVEC mouse driver"); +MODULE_AUTHOR("Marc Dietrich <marvin24@gmx.de>"); +MODULE_ALIAS("platform:nvec-mouse"); +MODULE_LICENSE("GPL"); |