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 PACKET_SIZE 6 25 26 #define ENABLE_MOUSE 0xf4 27 #define DISABLE_MOUSE 0xf5 28 #define PSMOUSE_RST 0xff 29 30 #ifdef NVEC_PS2_DEBUG 31 #define NVEC_PHD(str, buf, len) \ 32 print_hex_dump(KERN_DEBUG, str, DUMP_PREFIX_NONE, \ 33 16, 1, buf, len, false) 34 #else 35 #define NVEC_PHD(str, buf, len) 36 #endif 37 38 enum ps2_subcmds { 39 SEND_COMMAND = 1, 40 RECEIVE_N, 41 AUTO_RECEIVE_N, 42 CANCEL_AUTO_RECEIVE, 43 }; 44 45 struct nvec_ps2 { 46 struct serio *ser_dev; 47 struct notifier_block notifier; 48 struct nvec_chip *nvec; 49 }; 50 51 static struct nvec_ps2 ps2_dev; 52 53 static int ps2_startstreaming(struct serio *ser_dev) 54 { 55 unsigned char buf[] = { NVEC_PS2, AUTO_RECEIVE_N, PACKET_SIZE }; 56 57 return nvec_write_async(ps2_dev.nvec, buf, sizeof(buf)); 58 } 59 60 static void ps2_stopstreaming(struct serio *ser_dev) 61 { 62 unsigned char buf[] = { NVEC_PS2, CANCEL_AUTO_RECEIVE }; 63 64 nvec_write_async(ps2_dev.nvec, buf, sizeof(buf)); 65 } 66 67 static int ps2_sendcommand(struct serio *ser_dev, unsigned char cmd) 68 { 69 unsigned char buf[] = { NVEC_PS2, SEND_COMMAND, ENABLE_MOUSE, 1 }; 70 71 buf[2] = cmd & 0xff; 72 73 dev_dbg(&ser_dev->dev, "Sending ps2 cmd %02x\n", cmd); 74 return nvec_write_async(ps2_dev.nvec, buf, sizeof(buf)); 75 } 76 77 static int nvec_ps2_notifier(struct notifier_block *nb, 78 unsigned long event_type, void *data) 79 { 80 int i; 81 unsigned char *msg = data; 82 83 switch (event_type) { 84 case NVEC_PS2_EVT: 85 for (i = 0; i < msg[1]; i++) 86 serio_interrupt(ps2_dev.ser_dev, msg[2 + i], 0); 87 NVEC_PHD("ps/2 mouse event: ", &msg[2], msg[1]); 88 return NOTIFY_STOP; 89 90 case NVEC_PS2: 91 if (msg[2] == 1) { 92 for (i = 0; i < (msg[1] - 2); i++) 93 serio_interrupt(ps2_dev.ser_dev, msg[i + 4], 0); 94 NVEC_PHD("ps/2 mouse reply: ", &msg[4], msg[1] - 2); 95 } 96 97 else if (msg[1] != 2) /* !ack */ 98 NVEC_PHD("unhandled mouse event: ", msg, msg[1] + 2); 99 return NOTIFY_STOP; 100 } 101 102 return NOTIFY_DONE; 103 } 104 105 static int nvec_mouse_probe(struct platform_device *pdev) 106 { 107 struct nvec_chip *nvec = dev_get_drvdata(pdev->dev.parent); 108 struct serio *ser_dev; 109 char mouse_reset[] = { NVEC_PS2, SEND_COMMAND, PSMOUSE_RST, 3 }; 110 111 ser_dev = devm_kzalloc(&pdev->dev, sizeof(struct serio), GFP_KERNEL); 112 if (!ser_dev) 113 return -ENOMEM; 114 115 ser_dev->id.type = SERIO_PS_PSTHRU; 116 ser_dev->write = ps2_sendcommand; 117 ser_dev->start = ps2_startstreaming; 118 ser_dev->stop = ps2_stopstreaming; 119 120 strlcpy(ser_dev->name, "nvec mouse", sizeof(ser_dev->name)); 121 strlcpy(ser_dev->phys, "nvec", sizeof(ser_dev->phys)); 122 123 ps2_dev.ser_dev = ser_dev; 124 ps2_dev.notifier.notifier_call = nvec_ps2_notifier; 125 ps2_dev.nvec = nvec; 126 nvec_register_notifier(nvec, &ps2_dev.notifier, 0); 127 128 serio_register_port(ser_dev); 129 130 /* mouse reset */ 131 nvec_write_async(nvec, mouse_reset, sizeof(mouse_reset)); 132 133 return 0; 134 } 135 136 static int nvec_mouse_remove(struct platform_device *pdev) 137 { 138 struct nvec_chip *nvec = dev_get_drvdata(pdev->dev.parent); 139 140 ps2_sendcommand(ps2_dev.ser_dev, DISABLE_MOUSE); 141 ps2_stopstreaming(ps2_dev.ser_dev); 142 nvec_unregister_notifier(nvec, &ps2_dev.notifier); 143 serio_unregister_port(ps2_dev.ser_dev); 144 145 return 0; 146 } 147 148 #ifdef CONFIG_PM_SLEEP 149 static int nvec_mouse_suspend(struct device *dev) 150 { 151 /* disable mouse */ 152 ps2_sendcommand(ps2_dev.ser_dev, DISABLE_MOUSE); 153 154 /* send cancel autoreceive */ 155 ps2_stopstreaming(ps2_dev.ser_dev); 156 157 return 0; 158 } 159 160 static int nvec_mouse_resume(struct device *dev) 161 { 162 /* start streaming */ 163 ps2_startstreaming(ps2_dev.ser_dev); 164 165 /* enable mouse */ 166 ps2_sendcommand(ps2_dev.ser_dev, ENABLE_MOUSE); 167 168 return 0; 169 } 170 #endif 171 172 static SIMPLE_DEV_PM_OPS(nvec_mouse_pm_ops, nvec_mouse_suspend, 173 nvec_mouse_resume); 174 175 static struct platform_driver nvec_mouse_driver = { 176 .probe = nvec_mouse_probe, 177 .remove = nvec_mouse_remove, 178 .driver = { 179 .name = "nvec-mouse", 180 .pm = &nvec_mouse_pm_ops, 181 }, 182 }; 183 184 module_platform_driver(nvec_mouse_driver); 185 186 MODULE_DESCRIPTION("NVEC mouse driver"); 187 MODULE_AUTHOR("Marc Dietrich <marvin24@gmx.de>"); 188 MODULE_ALIAS("platform:nvec-mouse"); 189 MODULE_LICENSE("GPL"); 190