Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 /* Quanta I2C Touchpad Driver | |
| 2 * | |
| 3 * Copyright (C) 2009 Quanta Computer Inc. | |
| 4 * Author: Hsin Wu <hsin.wu@quantatw.com> | |
| 5 * Author: Austin Lai <austin.lai@quantatw.com> | |
| 6 * | |
| 7 * This software is licensed under the terms of the GNU General Public | |
| 8 * License version 2, as published by the Free Software Foundation, and | |
| 9 * may be copied, distributed, and modified under those terms. | |
| 10 * | |
| 11 * This program is distributed in the hope that it will be useful, | |
| 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
| 14 * GNU General Public License for more details. | |
| 15 * | |
| 16 */ | |
| 17 | |
| 18 /* | |
| 19 * | |
| 20 * The Driver with I/O communications via the I2C Interface for ON2 of AP BU. | |
| 21 * And it is only working on the nuvoTon WPCE775x Embedded Controller. | |
| 22 * | |
| 23 */ | |
| 24 | |
| 25 #include <linux/kernel.h> | |
| 26 #include <linux/init.h> | |
| 27 #include <linux/module.h> | |
| 28 #include <linux/slab.h> | |
| 29 #include <linux/jiffies.h> | |
| 30 #include <linux/i2c.h> | |
| 31 #include <linux/mutex.h> | |
| 32 #include <linux/interrupt.h> | |
| 33 #include <linux/input.h> | |
| 34 #include <linux/keyboard.h> | |
| 35 #include <linux/gpio.h> | |
| 36 #include <linux/delay.h> | |
| 37 | |
| 38 #define TOUCHPAD_ID_NAME "qci-i2cpad" | |
| 39 #define TOUCHPAD_NAME "PS2 Touchpad" | |
| 40 #define TOUCHPAD_DEVICE "/i2c/input1" | |
| 41 #define TOUCHPAD_CMD_ENABLE 0xF4 | |
| 42 | |
| 43 static int __devinit qcitp_probe(struct i2c_client *client, | |
| 44 const struct i2c_device_id *id); | |
| 45 static int __devexit qcitp_remove(struct i2c_client *kbd); | |
| 46 | |
| 47 /* General structure to hold the driver data */ | |
| 48 struct i2ctpad_drv_data { | |
| 49 struct i2c_client *ti2c_client; | |
| 50 struct work_struct work; | |
| 51 struct input_dev *qcitp_dev; | |
| 52 struct kobject *tp_kobj; | |
| 53 unsigned int qcitp_gpio; | |
| 54 unsigned int qcitp_irq; | |
| 55 char ecdata[8]; | |
| 56 }; | |
| 57 | |
| 58 static int tp_sense_val = 10; | |
| 59 static ssize_t tp_sensitive_show(struct kobject *kobj, | |
| 60 struct kobj_attribute *attr, char * buf) | |
| 61 { | |
| 62 return sprintf(buf, "%d\n", tp_sense_val); | |
| 63 } | |
| 64 | |
| 65 static ssize_t tp_sensitive_store(struct kobject *kobj, | |
| 66 struct kobj_attribute *attr, const char* buf, size_t n) | |
| 67 { | |
| 68 unsigned int val = 0; | |
| 69 sscanf(buf, "%d", &val); | |
| 70 | |
| 71 if (val >= 1 && val <= 10) | |
| 72 tp_sense_val = val; | |
| 73 else | |
| 74 return -ENOSYS; | |
| 75 | |
| 76 return sizeof(buf); | |
| 77 } | |
| 78 | |
| 79 static struct kobj_attribute tp_sensitivity = __ATTR(tp_sensitivity , | |
| 80 0644 , | |
| 81 tp_sensitive_show , | |
| 82 tp_sensitive_store); | |
| 83 | |
| 84 static struct attribute *g_tp[] = { | |
| 85 &tp_sensitivity.attr, | |
| 86 NULL, | |
| 87 }; | |
| 88 | |
| 89 static struct attribute_group attr_group = { | |
| 90 .attrs = g_tp, | |
| 91 }; | |
| 92 | |
| 93 /*----------------------------------------------------------------------------- | |
| 94 * Driver functions | |
| 95 *---------------------------------------------------------------------------*/ | |
| 96 | |
| 97 #ifdef CONFIG_PM | |
| 98 static int qcitp_suspend(struct device *dev) | |
| 99 { | |
| 100 return 0; | |
| 101 } | |
| 102 | |
| 103 static int qcitp_resume(struct device *dev) | |
| 104 { | |
| 105 return 0; | |
| 106 } | |
| 107 #endif | |
| 108 | |
| 109 static const struct i2c_device_id qcitp_idtable[] = { | |
| 110 { TOUCHPAD_ID_NAME, 0 }, | |
| 111 { } | |
| 112 }; | |
| 113 | |
| 114 MODULE_DEVICE_TABLE(i2c, qcitp_idtable); | |
| 115 #ifdef CONFIG_PM | |
| 116 static struct dev_pm_ops qcitp_pm_ops = { | |
| 117 .suspend = qcitp_suspend, | |
| 118 .resume = qcitp_resume, | |
| 119 }; | |
| 120 #endif | |
| 121 static struct i2c_driver i2ctp_driver = { | |
| 122 .driver = { | |
| 123 .owner = THIS_MODULE, | |
| 124 .name = TOUCHPAD_ID_NAME, | |
| 125 #ifdef CONFIG_PM | |
| 126 .pm = &qcitp_pm_ops, | |
| 127 #endif | |
| 128 }, | |
| 129 .probe = qcitp_probe, | |
| 130 .remove = __devexit_p(qcitp_remove), | |
| 131 .id_table = qcitp_idtable, | |
| 132 }; | |
| 133 | |
| 134 static void qcitp_fetch_data(struct i2c_client *tpad_client, | |
| 135 char *ec_data) | |
| 136 { | |
| 137 struct i2c_msg tp_msg; | |
| 138 int ret; | |
| 139 tp_msg.addr = tpad_client->addr; | |
| 140 tp_msg.flags = I2C_M_RD; | |
| 141 tp_msg.len = 3; | |
| 142 tp_msg.buf = (char *)&ec_data[0]; | |
| 143 ret = i2c_transfer(tpad_client->adapter, &tp_msg, 1); | |
| 144 } | |
| 145 | |
| 146 static void qcitp_report_key(struct input_dev *tpad_dev, char *ec_data) | |
| 147 { | |
| 148 int dx = 0; | |
| 149 int dy = 0; | |
| 150 | |
| 151 if (ec_data[1]) | |
| 152 dx = (int) ec_data[1] - | |
| 153 (int) ((ec_data[0] << 4) & 0x100); | |
| 154 | |
| 155 if (ec_data[2]) | |
| 156 dy = (int) ((ec_data[0] << 3) & 0x100) - | |
| 157 (int) ec_data[2]; | |
| 158 | |
| 159 dx = (dx * tp_sense_val)/10; | |
| 160 dy = (dy * tp_sense_val)/10; | |
| 161 | |
| 162 input_report_key(tpad_dev, BTN_LEFT, ec_data[0] & 0x01); | |
| 163 input_report_key(tpad_dev, BTN_RIGHT, ec_data[0] & 0x02); | |
| 164 input_report_key(tpad_dev, BTN_MIDDLE, ec_data[0] & 0x04); | |
| 165 input_report_rel(tpad_dev, REL_X, dx); | |
| 166 input_report_rel(tpad_dev, REL_Y, dy); | |
| 167 input_sync(tpad_dev); | |
| 168 } | |
| 169 | |
| 170 static void qcitp_work_handler(struct work_struct *_work) | |
| 171 { | |
| 172 struct i2ctpad_drv_data *itpad_drv_data = | |
| 173 container_of(_work, struct i2ctpad_drv_data, work); | |
| 174 | |
| 175 struct i2c_client *itpad_client = itpad_drv_data->ti2c_client; | |
| 176 struct input_dev *itpad_dev = itpad_drv_data->qcitp_dev; | |
| 177 | |
| 178 qcitp_fetch_data(itpad_client, itpad_drv_data->ecdata); | |
| 179 qcitp_report_key(itpad_dev, itpad_drv_data->ecdata); | |
| 180 } | |
| 181 | |
| 182 static irqreturn_t qcitp_interrupt(int irq, void *dev_id) | |
| 183 { | |
| 184 struct i2ctpad_drv_data *itpad_drv_data = dev_id; | |
| 185 schedule_work(&itpad_drv_data->work); | |
| 186 return IRQ_HANDLED; | |
| 187 } | |
| 188 | |
| 189 static int __devinit qcitp_probe(struct i2c_client *client, | |
| 190 const struct i2c_device_id *id) | |
| 191 { | |
| 192 int err = -ENOMEM; | |
| 193 struct i2ctpad_drv_data *context = 0; | |
| 194 | |
| 195 context = kzalloc(sizeof(struct i2ctpad_drv_data), GFP_KERNEL); | |
| 196 if (!context) | |
| 197 return err; | |
| 198 i2c_set_clientdata(client, context); | |
| 199 context->ti2c_client = client; | |
| 200 context->qcitp_gpio = client->irq; | |
| 201 | |
| 202 /* Enable mouse */ | |
| 203 i2c_smbus_write_byte(client, TOUCHPAD_CMD_ENABLE); | |
| 204 mdelay(100); | |
| 205 i2c_smbus_read_byte(client); | |
| 206 /*allocate and register input device*/ | |
| 207 context->qcitp_dev = input_allocate_device(); | |
| 208 if (!context->qcitp_dev) { | |
| 209 pr_err("[TouchPad] allocting memory fail\n"); | |
| 210 err = -ENOMEM; | |
| 211 goto allocate_fail; | |
| 212 } | |
| 213 context->qcitp_dev->name = TOUCHPAD_NAME; | |
| 214 context->qcitp_dev->phys = TOUCHPAD_DEVICE; | |
| 215 context->qcitp_dev->id.bustype = BUS_I2C; | |
| 216 context->qcitp_dev->id.vendor = 0x1050; | |
| 217 context->qcitp_dev->id.product = 0x1; | |
| 218 context->qcitp_dev->id.version = 0x1; | |
| 219 context->qcitp_dev->evbit[0] = BIT_MASK(EV_KEY) | | |
| 220 BIT_MASK(EV_REL); | |
| 221 context->qcitp_dev->relbit[0] = BIT_MASK(REL_X) | | |
| 222 BIT_MASK(REL_Y); | |
| 223 context->qcitp_dev->keybit[BIT_WORD(BTN_LEFT)] = BIT_MASK(BTN_LEFT) | | |
| 224 BIT_MASK(BTN_MIDDLE) | | |
| 225 BIT_MASK(BTN_RIGHT); | |
| 226 | |
| 227 input_set_drvdata(context->qcitp_dev, context); | |
| 228 err = input_register_device(context->qcitp_dev); | |
| 229 if (err) { | |
| 230 pr_err("[TouchPad] register device fail\n"); | |
| 231 goto register_fail; | |
| 232 } | |
| 233 | |
| 234 /*request intterrupt*/ | |
|
Mandeep Singh Baines
2010/03/23 18:06:03
request interrupt
| |
| 235 INIT_WORK(&context->work, qcitp_work_handler); | |
| 236 | |
| 237 err = gpio_request(context->qcitp_gpio, "qci-pad"); | |
| 238 if (err) { | |
| 239 pr_err("[TouchPad]err gpio request\n"); | |
| 240 goto gpio_request_fail; | |
| 241 } | |
| 242 | |
| 243 context->qcitp_irq = gpio_to_irq(context->qcitp_gpio); | |
| 244 err = request_irq(context->qcitp_irq, | |
| 245 qcitp_interrupt, | |
| 246 IRQF_TRIGGER_FALLING, | |
| 247 TOUCHPAD_ID_NAME, | |
| 248 context); | |
| 249 if (err) { | |
| 250 pr_err("[TouchPad] unable to get IRQ\n"); | |
| 251 goto request_irq_fail; | |
| 252 } | |
| 253 /*create touchpad kobject*/ | |
| 254 context->tp_kobj = kobject_create_and_add("touchpad", NULL); | |
| 255 | |
| 256 err = sysfs_create_group(context->tp_kobj, &attr_group); | |
| 257 if (err) | |
| 258 pr_warning("[TouchPad] sysfs create fail\n"); | |
| 259 | |
| 260 tp_sense_val = 10; | |
| 261 | |
| 262 return 0; | |
| 263 | |
| 264 request_irq_fail: | |
| 265 gpio_free(context->qcitp_gpio); | |
| 266 | |
| 267 gpio_request_fail: | |
| 268 input_unregister_device(context->qcitp_dev); | |
| 269 | |
| 270 register_fail: | |
| 271 input_free_device(context->qcitp_dev); | |
| 272 | |
| 273 allocate_fail: | |
| 274 i2c_set_clientdata(client, NULL); | |
| 275 kfree(context); | |
| 276 return err; | |
| 277 } | |
| 278 | |
| 279 static int __devexit qcitp_remove(struct i2c_client *dev) | |
| 280 { | |
| 281 struct i2ctpad_drv_data *context = i2c_get_clientdata(dev); | |
| 282 | |
| 283 free_irq(context->qcitp_irq, context); | |
| 284 gpio_free(context->qcitp_gpio); | |
| 285 input_free_device(context->qcitp_dev); | |
| 286 input_unregister_device(context->qcitp_dev); | |
| 287 kfree(context); | |
| 288 | |
| 289 return 0; | |
| 290 } | |
| 291 | |
| 292 static int __init qcitp_init(void) | |
| 293 { | |
| 294 return i2c_add_driver(&i2ctp_driver); | |
| 295 } | |
| 296 | |
| 297 | |
| 298 static void __exit qcitp_exit(void) | |
| 299 { | |
| 300 i2c_del_driver(&i2ctp_driver); | |
| 301 } | |
| 302 | |
| 303 module_init(qcitp_init); | |
| 304 module_exit(qcitp_exit); | |
| 305 | |
| 306 MODULE_AUTHOR("Quanta Computer Inc."); | |
| 307 MODULE_DESCRIPTION("Quanta Embedded Controller I2C Touch Pad Driver"); | |
| 308 MODULE_LICENSE("GPL v2"); | |
| OLD | NEW |