1 | /* |
2 | * HID over I2C Open Firmware Subclass |
3 | * |
4 | * Copyright (c) 2012 Benjamin Tissoires <benjamin.tissoires@gmail.com> |
5 | * Copyright (c) 2012 Ecole Nationale de l'Aviation Civile, France |
6 | * Copyright (c) 2012 Red Hat, Inc |
7 | * |
8 | * This code was forked out of the core code, which was partly based on |
9 | * "USB HID support for Linux": |
10 | * |
11 | * Copyright (c) 1999 Andreas Gal |
12 | * Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz> |
13 | * Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc |
14 | * Copyright (c) 2007-2008 Oliver Neukum |
15 | * Copyright (c) 2006-2010 Jiri Kosina |
16 | * |
17 | * This file is subject to the terms and conditions of the GNU General Public |
18 | * License. See the file COPYING in the main directory of this archive for |
19 | * more details. |
20 | */ |
21 | |
22 | #include <linux/delay.h> |
23 | #include <linux/device.h> |
24 | #include <linux/gpio/consumer.h> |
25 | #include <linux/hid.h> |
26 | #include <linux/i2c.h> |
27 | #include <linux/kernel.h> |
28 | #include <linux/module.h> |
29 | #include <linux/of.h> |
30 | #include <linux/pm.h> |
31 | #include <linux/regulator/consumer.h> |
32 | |
33 | #include "i2c-hid.h" |
34 | |
35 | struct i2c_hid_of { |
36 | struct i2chid_ops ops; |
37 | |
38 | struct i2c_client *client; |
39 | struct gpio_desc *reset_gpio; |
40 | struct regulator_bulk_data supplies[2]; |
41 | int post_power_delay_ms; |
42 | int post_reset_delay_ms; |
43 | }; |
44 | |
45 | static int i2c_hid_of_power_up(struct i2chid_ops *ops) |
46 | { |
47 | struct i2c_hid_of *ihid_of = container_of(ops, struct i2c_hid_of, ops); |
48 | struct device *dev = &ihid_of->client->dev; |
49 | int ret; |
50 | |
51 | ret = regulator_bulk_enable(ARRAY_SIZE(ihid_of->supplies), |
52 | consumers: ihid_of->supplies); |
53 | if (ret) { |
54 | dev_warn(dev, "Failed to enable supplies: %d\n" , ret); |
55 | return ret; |
56 | } |
57 | |
58 | if (ihid_of->post_power_delay_ms) |
59 | msleep(msecs: ihid_of->post_power_delay_ms); |
60 | |
61 | gpiod_set_value_cansleep(desc: ihid_of->reset_gpio, value: 0); |
62 | if (ihid_of->post_reset_delay_ms) |
63 | msleep(msecs: ihid_of->post_reset_delay_ms); |
64 | |
65 | return 0; |
66 | } |
67 | |
68 | static void i2c_hid_of_power_down(struct i2chid_ops *ops) |
69 | { |
70 | struct i2c_hid_of *ihid_of = container_of(ops, struct i2c_hid_of, ops); |
71 | |
72 | gpiod_set_value_cansleep(desc: ihid_of->reset_gpio, value: 1); |
73 | regulator_bulk_disable(ARRAY_SIZE(ihid_of->supplies), |
74 | consumers: ihid_of->supplies); |
75 | } |
76 | |
77 | static int i2c_hid_of_probe(struct i2c_client *client) |
78 | { |
79 | struct device *dev = &client->dev; |
80 | struct i2c_hid_of *ihid_of; |
81 | u16 hid_descriptor_address; |
82 | u32 quirks = 0; |
83 | int ret; |
84 | u32 val; |
85 | |
86 | ihid_of = devm_kzalloc(dev, size: sizeof(*ihid_of), GFP_KERNEL); |
87 | if (!ihid_of) |
88 | return -ENOMEM; |
89 | |
90 | ihid_of->client = client; |
91 | ihid_of->ops.power_up = i2c_hid_of_power_up; |
92 | ihid_of->ops.power_down = i2c_hid_of_power_down; |
93 | |
94 | ret = device_property_read_u32(dev, propname: "hid-descr-addr" , val: &val); |
95 | if (ret) { |
96 | dev_err(dev, "HID register address not provided\n" ); |
97 | return -ENODEV; |
98 | } |
99 | if (val >> 16) { |
100 | dev_err(dev, "Bad HID register address: 0x%08x\n" , val); |
101 | return -EINVAL; |
102 | } |
103 | hid_descriptor_address = val; |
104 | |
105 | if (!device_property_read_u32(dev, propname: "post-power-on-delay-ms" , val: &val)) |
106 | ihid_of->post_power_delay_ms = val; |
107 | |
108 | /* |
109 | * Note this is a kernel internal device-property set by x86 platform code, |
110 | * this MUST not be used in devicetree files without first adding it to |
111 | * the DT bindings. |
112 | */ |
113 | if (!device_property_read_u32(dev, propname: "post-reset-deassert-delay-ms" , val: &val)) |
114 | ihid_of->post_reset_delay_ms = val; |
115 | |
116 | /* Start out with reset asserted */ |
117 | ihid_of->reset_gpio = devm_gpiod_get_optional(dev, con_id: "reset" , flags: GPIOD_OUT_HIGH); |
118 | if (IS_ERR(ptr: ihid_of->reset_gpio)) |
119 | return PTR_ERR(ptr: ihid_of->reset_gpio); |
120 | |
121 | ihid_of->supplies[0].supply = "vdd" ; |
122 | ihid_of->supplies[1].supply = "vddl" ; |
123 | ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(ihid_of->supplies), |
124 | consumers: ihid_of->supplies); |
125 | if (ret) |
126 | return ret; |
127 | |
128 | if (device_property_read_bool(dev, propname: "touchscreen-inverted-x" )) |
129 | quirks |= HID_QUIRK_X_INVERT; |
130 | |
131 | if (device_property_read_bool(dev, propname: "touchscreen-inverted-y" )) |
132 | quirks |= HID_QUIRK_Y_INVERT; |
133 | |
134 | return i2c_hid_core_probe(client, ops: &ihid_of->ops, |
135 | hid_descriptor_address, quirks); |
136 | } |
137 | |
138 | #ifdef CONFIG_OF |
139 | static const struct of_device_id i2c_hid_of_match[] = { |
140 | { .compatible = "hid-over-i2c" }, |
141 | {}, |
142 | }; |
143 | MODULE_DEVICE_TABLE(of, i2c_hid_of_match); |
144 | #endif |
145 | |
146 | static const struct i2c_device_id i2c_hid_of_id_table[] = { |
147 | { "hid" , 0 }, |
148 | { "hid-over-i2c" , 0 }, |
149 | { }, |
150 | }; |
151 | MODULE_DEVICE_TABLE(i2c, i2c_hid_of_id_table); |
152 | |
153 | static struct i2c_driver i2c_hid_of_driver = { |
154 | .driver = { |
155 | .name = "i2c_hid_of" , |
156 | .pm = &i2c_hid_core_pm, |
157 | .probe_type = PROBE_PREFER_ASYNCHRONOUS, |
158 | .of_match_table = of_match_ptr(i2c_hid_of_match), |
159 | }, |
160 | |
161 | .probe = i2c_hid_of_probe, |
162 | .remove = i2c_hid_core_remove, |
163 | .shutdown = i2c_hid_core_shutdown, |
164 | .id_table = i2c_hid_of_id_table, |
165 | }; |
166 | |
167 | module_i2c_driver(i2c_hid_of_driver); |
168 | |
169 | MODULE_DESCRIPTION("HID over I2C OF driver" ); |
170 | MODULE_AUTHOR("Benjamin Tissoires <benjamin.tissoires@gmail.com>" ); |
171 | MODULE_LICENSE("GPL" ); |
172 | |