Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 /*
0003  * nvec_ps2: mouse driver for a NVIDIA compliant embedded controller
0004  *
0005  * Copyright (C) 2011 The AC100 Kernel Team <ac100@lists.launchpad.net>
0006  *
0007  * Authors:  Pierre-Hugues Husson <phhusson@free.fr>
0008  *           Ilya Petrov <ilya.muromec@gmail.com>
0009  *           Marc Dietrich <marvin24@gmx.de>
0010  */
0011 
0012 #include <linux/module.h>
0013 #include <linux/slab.h>
0014 #include <linux/serio.h>
0015 #include <linux/delay.h>
0016 #include <linux/platform_device.h>
0017 
0018 #include "nvec.h"
0019 
0020 #define PACKET_SIZE 6
0021 
0022 #define ENABLE_MOUSE    0xf4
0023 #define DISABLE_MOUSE   0xf5
0024 #define PSMOUSE_RST 0xff
0025 
0026 #ifdef NVEC_PS2_DEBUG
0027 #define NVEC_PHD(str, buf, len) \
0028     print_hex_dump(KERN_DEBUG, str, DUMP_PREFIX_NONE, \
0029             16, 1, buf, len, false)
0030 #else
0031 #define NVEC_PHD(str, buf, len) do { } while (0)
0032 #endif
0033 
0034 enum ps2_subcmds {
0035     SEND_COMMAND = 1,
0036     RECEIVE_N,
0037     AUTO_RECEIVE_N,
0038     CANCEL_AUTO_RECEIVE,
0039 };
0040 
0041 struct nvec_ps2 {
0042     struct serio *ser_dev;
0043     struct notifier_block notifier;
0044     struct nvec_chip *nvec;
0045 };
0046 
0047 static struct nvec_ps2 ps2_dev;
0048 
0049 static int ps2_startstreaming(struct serio *ser_dev)
0050 {
0051     unsigned char buf[] = { NVEC_PS2, AUTO_RECEIVE_N, PACKET_SIZE };
0052 
0053     return nvec_write_async(ps2_dev.nvec, buf, sizeof(buf));
0054 }
0055 
0056 static void ps2_stopstreaming(struct serio *ser_dev)
0057 {
0058     unsigned char buf[] = { NVEC_PS2, CANCEL_AUTO_RECEIVE };
0059 
0060     nvec_write_async(ps2_dev.nvec, buf, sizeof(buf));
0061 }
0062 
0063 static int ps2_sendcommand(struct serio *ser_dev, unsigned char cmd)
0064 {
0065     unsigned char buf[] = { NVEC_PS2, SEND_COMMAND, ENABLE_MOUSE, 1 };
0066 
0067     buf[2] = cmd & 0xff;
0068 
0069     dev_dbg(&ser_dev->dev, "Sending ps2 cmd %02x\n", cmd);
0070     return nvec_write_async(ps2_dev.nvec, buf, sizeof(buf));
0071 }
0072 
0073 static int nvec_ps2_notifier(struct notifier_block *nb,
0074                  unsigned long event_type, void *data)
0075 {
0076     int i;
0077     unsigned char *msg = data;
0078 
0079     switch (event_type) {
0080     case NVEC_PS2_EVT:
0081         for (i = 0; i < msg[1]; i++)
0082             serio_interrupt(ps2_dev.ser_dev, msg[2 + i], 0);
0083         NVEC_PHD("ps/2 mouse event: ", &msg[2], msg[1]);
0084         return NOTIFY_STOP;
0085 
0086     case NVEC_PS2:
0087         if (msg[2] == 1) {
0088             for (i = 0; i < (msg[1] - 2); i++)
0089                 serio_interrupt(ps2_dev.ser_dev, msg[i + 4], 0);
0090             NVEC_PHD("ps/2 mouse reply: ", &msg[4], msg[1] - 2);
0091         }
0092 
0093         else if (msg[1] != 2) /* !ack */
0094             NVEC_PHD("unhandled mouse event: ", msg, msg[1] + 2);
0095         return NOTIFY_STOP;
0096     }
0097 
0098     return NOTIFY_DONE;
0099 }
0100 
0101 static int nvec_mouse_probe(struct platform_device *pdev)
0102 {
0103     struct nvec_chip *nvec = dev_get_drvdata(pdev->dev.parent);
0104     struct serio *ser_dev;
0105 
0106     ser_dev = kzalloc(sizeof(*ser_dev), GFP_KERNEL);
0107     if (!ser_dev)
0108         return -ENOMEM;
0109 
0110     ser_dev->id.type = SERIO_8042;
0111     ser_dev->write = ps2_sendcommand;
0112     ser_dev->start = ps2_startstreaming;
0113     ser_dev->stop = ps2_stopstreaming;
0114 
0115     strscpy(ser_dev->name, "nvec mouse", sizeof(ser_dev->name));
0116     strscpy(ser_dev->phys, "nvec", sizeof(ser_dev->phys));
0117 
0118     ps2_dev.ser_dev = ser_dev;
0119     ps2_dev.notifier.notifier_call = nvec_ps2_notifier;
0120     ps2_dev.nvec = nvec;
0121     nvec_register_notifier(nvec, &ps2_dev.notifier, 0);
0122 
0123     serio_register_port(ser_dev);
0124 
0125     return 0;
0126 }
0127 
0128 static int nvec_mouse_remove(struct platform_device *pdev)
0129 {
0130     struct nvec_chip *nvec = dev_get_drvdata(pdev->dev.parent);
0131 
0132     ps2_sendcommand(ps2_dev.ser_dev, DISABLE_MOUSE);
0133     ps2_stopstreaming(ps2_dev.ser_dev);
0134     nvec_unregister_notifier(nvec, &ps2_dev.notifier);
0135     serio_unregister_port(ps2_dev.ser_dev);
0136 
0137     return 0;
0138 }
0139 
0140 #ifdef CONFIG_PM_SLEEP
0141 static int nvec_mouse_suspend(struct device *dev)
0142 {
0143     /* disable mouse */
0144     ps2_sendcommand(ps2_dev.ser_dev, DISABLE_MOUSE);
0145 
0146     /* send cancel autoreceive */
0147     ps2_stopstreaming(ps2_dev.ser_dev);
0148 
0149     return 0;
0150 }
0151 
0152 static int nvec_mouse_resume(struct device *dev)
0153 {
0154     /* start streaming */
0155     ps2_startstreaming(ps2_dev.ser_dev);
0156 
0157     /* enable mouse */
0158     ps2_sendcommand(ps2_dev.ser_dev, ENABLE_MOUSE);
0159 
0160     return 0;
0161 }
0162 #endif
0163 
0164 static SIMPLE_DEV_PM_OPS(nvec_mouse_pm_ops, nvec_mouse_suspend,
0165              nvec_mouse_resume);
0166 
0167 static struct platform_driver nvec_mouse_driver = {
0168     .probe  = nvec_mouse_probe,
0169     .remove = nvec_mouse_remove,
0170     .driver = {
0171         .name = "nvec-mouse",
0172         .pm = &nvec_mouse_pm_ops,
0173     },
0174 };
0175 
0176 module_platform_driver(nvec_mouse_driver);
0177 
0178 MODULE_DESCRIPTION("NVEC mouse driver");
0179 MODULE_AUTHOR("Marc Dietrich <marvin24@gmx.de>");
0180 MODULE_ALIAS("platform:nvec-mouse");
0181 MODULE_LICENSE("GPL");