Back to home page

OSCL-LXR

 
 

    


0001 /*
0002  * HID over I2C Open Firmware Subclass
0003  *
0004  * Copyright (c) 2012 Benjamin Tissoires <benjamin.tissoires@gmail.com>
0005  * Copyright (c) 2012 Ecole Nationale de l'Aviation Civile, France
0006  * Copyright (c) 2012 Red Hat, Inc
0007  *
0008  * This code was forked out of the core code, which was partly based on
0009  * "USB HID support for Linux":
0010  *
0011  *  Copyright (c) 1999 Andreas Gal
0012  *  Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz>
0013  *  Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc
0014  *  Copyright (c) 2007-2008 Oliver Neukum
0015  *  Copyright (c) 2006-2010 Jiri Kosina
0016  *
0017  * This file is subject to the terms and conditions of the GNU General Public
0018  * License.  See the file COPYING in the main directory of this archive for
0019  * more details.
0020  */
0021 
0022 #include <linux/delay.h>
0023 #include <linux/device.h>
0024 #include <linux/hid.h>
0025 #include <linux/i2c.h>
0026 #include <linux/kernel.h>
0027 #include <linux/module.h>
0028 #include <linux/of.h>
0029 #include <linux/pm.h>
0030 #include <linux/regulator/consumer.h>
0031 
0032 #include "i2c-hid.h"
0033 
0034 struct i2c_hid_of {
0035     struct i2chid_ops ops;
0036 
0037     struct i2c_client *client;
0038     struct regulator_bulk_data supplies[2];
0039     int post_power_delay_ms;
0040 };
0041 
0042 static int i2c_hid_of_power_up(struct i2chid_ops *ops)
0043 {
0044     struct i2c_hid_of *ihid_of = container_of(ops, struct i2c_hid_of, ops);
0045     struct device *dev = &ihid_of->client->dev;
0046     int ret;
0047 
0048     ret = regulator_bulk_enable(ARRAY_SIZE(ihid_of->supplies),
0049                     ihid_of->supplies);
0050     if (ret) {
0051         dev_warn(dev, "Failed to enable supplies: %d\n", ret);
0052         return ret;
0053     }
0054 
0055     if (ihid_of->post_power_delay_ms)
0056         msleep(ihid_of->post_power_delay_ms);
0057 
0058     return 0;
0059 }
0060 
0061 static void i2c_hid_of_power_down(struct i2chid_ops *ops)
0062 {
0063     struct i2c_hid_of *ihid_of = container_of(ops, struct i2c_hid_of, ops);
0064 
0065     regulator_bulk_disable(ARRAY_SIZE(ihid_of->supplies),
0066                    ihid_of->supplies);
0067 }
0068 
0069 static int i2c_hid_of_probe(struct i2c_client *client,
0070                 const struct i2c_device_id *dev_id)
0071 {
0072     struct device *dev = &client->dev;
0073     struct i2c_hid_of *ihid_of;
0074     u16 hid_descriptor_address;
0075     u32 quirks = 0;
0076     int ret;
0077     u32 val;
0078 
0079     ihid_of = devm_kzalloc(&client->dev, sizeof(*ihid_of), GFP_KERNEL);
0080     if (!ihid_of)
0081         return -ENOMEM;
0082 
0083     ihid_of->ops.power_up = i2c_hid_of_power_up;
0084     ihid_of->ops.power_down = i2c_hid_of_power_down;
0085 
0086     ret = of_property_read_u32(dev->of_node, "hid-descr-addr", &val);
0087     if (ret) {
0088         dev_err(&client->dev, "HID register address not provided\n");
0089         return -ENODEV;
0090     }
0091     if (val >> 16) {
0092         dev_err(&client->dev, "Bad HID register address: 0x%08x\n",
0093             val);
0094         return -EINVAL;
0095     }
0096     hid_descriptor_address = val;
0097 
0098     if (!device_property_read_u32(&client->dev, "post-power-on-delay-ms",
0099                       &val))
0100         ihid_of->post_power_delay_ms = val;
0101 
0102     ihid_of->supplies[0].supply = "vdd";
0103     ihid_of->supplies[1].supply = "vddl";
0104     ret = devm_regulator_bulk_get(&client->dev,
0105                       ARRAY_SIZE(ihid_of->supplies),
0106                       ihid_of->supplies);
0107     if (ret)
0108         return ret;
0109 
0110     if (device_property_read_bool(dev, "touchscreen-inverted-x"))
0111         quirks |= HID_QUIRK_X_INVERT;
0112 
0113     if (device_property_read_bool(dev, "touchscreen-inverted-y"))
0114         quirks |= HID_QUIRK_Y_INVERT;
0115 
0116     return i2c_hid_core_probe(client, &ihid_of->ops,
0117                   hid_descriptor_address, quirks);
0118 }
0119 
0120 static const struct of_device_id i2c_hid_of_match[] = {
0121     { .compatible = "hid-over-i2c" },
0122     {},
0123 };
0124 MODULE_DEVICE_TABLE(of, i2c_hid_of_match);
0125 
0126 static const struct i2c_device_id i2c_hid_of_id_table[] = {
0127     { "hid", 0 },
0128     { "hid-over-i2c", 0 },
0129     { },
0130 };
0131 MODULE_DEVICE_TABLE(i2c, i2c_hid_of_id_table);
0132 
0133 static struct i2c_driver i2c_hid_of_driver = {
0134     .driver = {
0135         .name   = "i2c_hid_of",
0136         .pm = &i2c_hid_core_pm,
0137         .probe_type = PROBE_PREFER_ASYNCHRONOUS,
0138         .of_match_table = of_match_ptr(i2c_hid_of_match),
0139     },
0140 
0141     .probe      = i2c_hid_of_probe,
0142     .remove     = i2c_hid_core_remove,
0143     .shutdown   = i2c_hid_core_shutdown,
0144     .id_table   = i2c_hid_of_id_table,
0145 };
0146 
0147 module_i2c_driver(i2c_hid_of_driver);
0148 
0149 MODULE_DESCRIPTION("HID over I2C OF driver");
0150 MODULE_AUTHOR("Benjamin Tissoires <benjamin.tissoires@gmail.com>");
0151 MODULE_LICENSE("GPL");