nvec_ps2.c (4242B)
1// SPDX-License-Identifier: GPL-2.0 2/* 3 * nvec_ps2: mouse driver for a NVIDIA compliant embedded controller 4 * 5 * Copyright (C) 2011 The AC100 Kernel Team <ac100@lists.launchpad.net> 6 * 7 * Authors: Pierre-Hugues Husson <phhusson@free.fr> 8 * Ilya Petrov <ilya.muromec@gmail.com> 9 * Marc Dietrich <marvin24@gmx.de> 10 */ 11 12#include <linux/module.h> 13#include <linux/slab.h> 14#include <linux/serio.h> 15#include <linux/delay.h> 16#include <linux/platform_device.h> 17 18#include "nvec.h" 19 20#define PACKET_SIZE 6 21 22#define ENABLE_MOUSE 0xf4 23#define DISABLE_MOUSE 0xf5 24#define PSMOUSE_RST 0xff 25 26#ifdef NVEC_PS2_DEBUG 27#define NVEC_PHD(str, buf, len) \ 28 print_hex_dump(KERN_DEBUG, str, DUMP_PREFIX_NONE, \ 29 16, 1, buf, len, false) 30#else 31#define NVEC_PHD(str, buf, len) do { } while (0) 32#endif 33 34enum ps2_subcmds { 35 SEND_COMMAND = 1, 36 RECEIVE_N, 37 AUTO_RECEIVE_N, 38 CANCEL_AUTO_RECEIVE, 39}; 40 41struct nvec_ps2 { 42 struct serio *ser_dev; 43 struct notifier_block notifier; 44 struct nvec_chip *nvec; 45}; 46 47static struct nvec_ps2 ps2_dev; 48 49static int ps2_startstreaming(struct serio *ser_dev) 50{ 51 unsigned char buf[] = { NVEC_PS2, AUTO_RECEIVE_N, PACKET_SIZE }; 52 53 return nvec_write_async(ps2_dev.nvec, buf, sizeof(buf)); 54} 55 56static void ps2_stopstreaming(struct serio *ser_dev) 57{ 58 unsigned char buf[] = { NVEC_PS2, CANCEL_AUTO_RECEIVE }; 59 60 nvec_write_async(ps2_dev.nvec, buf, sizeof(buf)); 61} 62 63static int ps2_sendcommand(struct serio *ser_dev, unsigned char cmd) 64{ 65 unsigned char buf[] = { NVEC_PS2, SEND_COMMAND, ENABLE_MOUSE, 1 }; 66 67 buf[2] = cmd & 0xff; 68 69 dev_dbg(&ser_dev->dev, "Sending ps2 cmd %02x\n", cmd); 70 return nvec_write_async(ps2_dev.nvec, buf, sizeof(buf)); 71} 72 73static int nvec_ps2_notifier(struct notifier_block *nb, 74 unsigned long event_type, void *data) 75{ 76 int i; 77 unsigned char *msg = data; 78 79 switch (event_type) { 80 case NVEC_PS2_EVT: 81 for (i = 0; i < msg[1]; i++) 82 serio_interrupt(ps2_dev.ser_dev, msg[2 + i], 0); 83 NVEC_PHD("ps/2 mouse event: ", &msg[2], msg[1]); 84 return NOTIFY_STOP; 85 86 case NVEC_PS2: 87 if (msg[2] == 1) { 88 for (i = 0; i < (msg[1] - 2); i++) 89 serio_interrupt(ps2_dev.ser_dev, msg[i + 4], 0); 90 NVEC_PHD("ps/2 mouse reply: ", &msg[4], msg[1] - 2); 91 } 92 93 else if (msg[1] != 2) /* !ack */ 94 NVEC_PHD("unhandled mouse event: ", msg, msg[1] + 2); 95 return NOTIFY_STOP; 96 } 97 98 return NOTIFY_DONE; 99} 100 101static int nvec_mouse_probe(struct platform_device *pdev) 102{ 103 struct nvec_chip *nvec = dev_get_drvdata(pdev->dev.parent); 104 struct serio *ser_dev; 105 106 ser_dev = kzalloc(sizeof(*ser_dev), GFP_KERNEL); 107 if (!ser_dev) 108 return -ENOMEM; 109 110 ser_dev->id.type = SERIO_8042; 111 ser_dev->write = ps2_sendcommand; 112 ser_dev->start = ps2_startstreaming; 113 ser_dev->stop = ps2_stopstreaming; 114 115 strscpy(ser_dev->name, "nvec mouse", sizeof(ser_dev->name)); 116 strscpy(ser_dev->phys, "nvec", sizeof(ser_dev->phys)); 117 118 ps2_dev.ser_dev = ser_dev; 119 ps2_dev.notifier.notifier_call = nvec_ps2_notifier; 120 ps2_dev.nvec = nvec; 121 nvec_register_notifier(nvec, &ps2_dev.notifier, 0); 122 123 serio_register_port(ser_dev); 124 125 return 0; 126} 127 128static int nvec_mouse_remove(struct platform_device *pdev) 129{ 130 struct nvec_chip *nvec = dev_get_drvdata(pdev->dev.parent); 131 132 ps2_sendcommand(ps2_dev.ser_dev, DISABLE_MOUSE); 133 ps2_stopstreaming(ps2_dev.ser_dev); 134 nvec_unregister_notifier(nvec, &ps2_dev.notifier); 135 serio_unregister_port(ps2_dev.ser_dev); 136 137 return 0; 138} 139 140#ifdef CONFIG_PM_SLEEP 141static int nvec_mouse_suspend(struct device *dev) 142{ 143 /* disable mouse */ 144 ps2_sendcommand(ps2_dev.ser_dev, DISABLE_MOUSE); 145 146 /* send cancel autoreceive */ 147 ps2_stopstreaming(ps2_dev.ser_dev); 148 149 return 0; 150} 151 152static int nvec_mouse_resume(struct device *dev) 153{ 154 /* start streaming */ 155 ps2_startstreaming(ps2_dev.ser_dev); 156 157 /* enable mouse */ 158 ps2_sendcommand(ps2_dev.ser_dev, ENABLE_MOUSE); 159 160 return 0; 161} 162#endif 163 164static SIMPLE_DEV_PM_OPS(nvec_mouse_pm_ops, nvec_mouse_suspend, 165 nvec_mouse_resume); 166 167static struct platform_driver nvec_mouse_driver = { 168 .probe = nvec_mouse_probe, 169 .remove = nvec_mouse_remove, 170 .driver = { 171 .name = "nvec-mouse", 172 .pm = &nvec_mouse_pm_ops, 173 }, 174}; 175 176module_platform_driver(nvec_mouse_driver); 177 178MODULE_DESCRIPTION("NVEC mouse driver"); 179MODULE_AUTHOR("Marc Dietrich <marvin24@gmx.de>"); 180MODULE_ALIAS("platform:nvec-mouse"); 181MODULE_LICENSE("GPL");