12025cf9eSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
2e932d817SDavid Barksdale /*
3e932d817SDavid Barksdale * hid-cp2112.c - Silicon Labs HID USB to SMBus master bridge
4e932d817SDavid Barksdale * Copyright (c) 2013,2014 Uplogix, Inc.
5e932d817SDavid Barksdale * David Barksdale <dbarksdale@uplogix.com>
6e932d817SDavid Barksdale */
7e932d817SDavid Barksdale
8e932d817SDavid Barksdale /*
9e932d817SDavid Barksdale * The Silicon Labs CP2112 chip is a USB HID device which provides an
10e932d817SDavid Barksdale * SMBus controller for talking to slave devices and 8 GPIO pins. The
11e932d817SDavid Barksdale * host communicates with the CP2112 via raw HID reports.
12e932d817SDavid Barksdale *
13e932d817SDavid Barksdale * Data Sheet:
14b1631b84SAlexander A. Klimov * https://www.silabs.com/Support%20Documents/TechnicalDocs/CP2112.pdf
15e932d817SDavid Barksdale * Programming Interface Specification:
16ce4dd820SSébastien Szymanski * https://www.silabs.com/documents/public/application-notes/an495-cp2112-interface-specification.pdf
17e932d817SDavid Barksdale */
18e932d817SDavid Barksdale
19ecb42bb8SAndy Shevchenko #include <linux/bitops.h>
2047513c2fSLinus Walleij #include <linux/gpio/driver.h>
21e932d817SDavid Barksdale #include <linux/hid.h>
2242cb6b35SJaejoong Kim #include <linux/hidraw.h>
23e932d817SDavid Barksdale #include <linux/i2c.h>
24e932d817SDavid Barksdale #include <linux/module.h>
25e932d817SDavid Barksdale #include <linux/nls.h>
264a3983d7SAndy Shevchenko #include <linux/string_choices.h>
27e932d817SDavid Barksdale #include <linux/usb/ch9.h>
28e932d817SDavid Barksdale #include "hid-ids.h"
29e932d817SDavid Barksdale
301ffb3c40SBenjamin Tissoires #define CP2112_REPORT_MAX_LENGTH 64
311ffb3c40SBenjamin Tissoires #define CP2112_GPIO_CONFIG_LENGTH 5
321ffb3c40SBenjamin Tissoires #define CP2112_GPIO_GET_LENGTH 2
331ffb3c40SBenjamin Tissoires #define CP2112_GPIO_SET_LENGTH 3
34e7378e09SAndy Shevchenko #define CP2112_GPIO_MAX_GPIO 8
35ee0682b0SAndy Shevchenko #define CP2112_GPIO_ALL_GPIO_MASK GENMASK(7, 0)
361ffb3c40SBenjamin Tissoires
37e932d817SDavid Barksdale enum {
38e932d817SDavid Barksdale CP2112_GPIO_CONFIG = 0x02,
39e932d817SDavid Barksdale CP2112_GPIO_GET = 0x03,
40e932d817SDavid Barksdale CP2112_GPIO_SET = 0x04,
41e932d817SDavid Barksdale CP2112_GET_VERSION_INFO = 0x05,
42e932d817SDavid Barksdale CP2112_SMBUS_CONFIG = 0x06,
43e932d817SDavid Barksdale CP2112_DATA_READ_REQUEST = 0x10,
44e932d817SDavid Barksdale CP2112_DATA_WRITE_READ_REQUEST = 0x11,
45e932d817SDavid Barksdale CP2112_DATA_READ_FORCE_SEND = 0x12,
46e932d817SDavid Barksdale CP2112_DATA_READ_RESPONSE = 0x13,
47e932d817SDavid Barksdale CP2112_DATA_WRITE_REQUEST = 0x14,
48e932d817SDavid Barksdale CP2112_TRANSFER_STATUS_REQUEST = 0x15,
49e932d817SDavid Barksdale CP2112_TRANSFER_STATUS_RESPONSE = 0x16,
50e932d817SDavid Barksdale CP2112_CANCEL_TRANSFER = 0x17,
51e932d817SDavid Barksdale CP2112_LOCK_BYTE = 0x20,
52e932d817SDavid Barksdale CP2112_USB_CONFIG = 0x21,
53e932d817SDavid Barksdale CP2112_MANUFACTURER_STRING = 0x22,
54e932d817SDavid Barksdale CP2112_PRODUCT_STRING = 0x23,
55e932d817SDavid Barksdale CP2112_SERIAL_STRING = 0x24,
56e932d817SDavid Barksdale };
57e932d817SDavid Barksdale
58e932d817SDavid Barksdale enum {
59e932d817SDavid Barksdale STATUS0_IDLE = 0x00,
60e932d817SDavid Barksdale STATUS0_BUSY = 0x01,
61e932d817SDavid Barksdale STATUS0_COMPLETE = 0x02,
62e932d817SDavid Barksdale STATUS0_ERROR = 0x03,
63e932d817SDavid Barksdale };
64e932d817SDavid Barksdale
65e932d817SDavid Barksdale enum {
66e932d817SDavid Barksdale STATUS1_TIMEOUT_NACK = 0x00,
67e932d817SDavid Barksdale STATUS1_TIMEOUT_BUS = 0x01,
68e932d817SDavid Barksdale STATUS1_ARBITRATION_LOST = 0x02,
69e932d817SDavid Barksdale STATUS1_READ_INCOMPLETE = 0x03,
70e932d817SDavid Barksdale STATUS1_WRITE_INCOMPLETE = 0x04,
71e932d817SDavid Barksdale STATUS1_SUCCESS = 0x05,
72e932d817SDavid Barksdale };
73e932d817SDavid Barksdale
74e932d817SDavid Barksdale struct cp2112_smbus_config_report {
75e932d817SDavid Barksdale u8 report; /* CP2112_SMBUS_CONFIG */
76e932d817SDavid Barksdale __be32 clock_speed; /* Hz */
77e932d817SDavid Barksdale u8 device_address; /* Stored in the upper 7 bits */
78e932d817SDavid Barksdale u8 auto_send_read; /* 1 = enabled, 0 = disabled */
79e932d817SDavid Barksdale __be16 write_timeout; /* ms, 0 = no timeout */
80e932d817SDavid Barksdale __be16 read_timeout; /* ms, 0 = no timeout */
81e932d817SDavid Barksdale u8 scl_low_timeout; /* 1 = enabled, 0 = disabled */
82e932d817SDavid Barksdale __be16 retry_time; /* # of retries, 0 = no limit */
83e932d817SDavid Barksdale } __packed;
84e932d817SDavid Barksdale
85e932d817SDavid Barksdale struct cp2112_usb_config_report {
86e932d817SDavid Barksdale u8 report; /* CP2112_USB_CONFIG */
87e932d817SDavid Barksdale __le16 vid; /* Vendor ID */
88e932d817SDavid Barksdale __le16 pid; /* Product ID */
89e932d817SDavid Barksdale u8 max_power; /* Power requested in 2mA units */
90e932d817SDavid Barksdale u8 power_mode; /* 0x00 = bus powered
91e932d817SDavid Barksdale 0x01 = self powered & regulator off
92e932d817SDavid Barksdale 0x02 = self powered & regulator on */
93e932d817SDavid Barksdale u8 release_major;
94e932d817SDavid Barksdale u8 release_minor;
95e932d817SDavid Barksdale u8 mask; /* What fields to program */
96e932d817SDavid Barksdale } __packed;
97e932d817SDavid Barksdale
98e932d817SDavid Barksdale struct cp2112_read_req_report {
99e932d817SDavid Barksdale u8 report; /* CP2112_DATA_READ_REQUEST */
100e932d817SDavid Barksdale u8 slave_address;
101e932d817SDavid Barksdale __be16 length;
102e932d817SDavid Barksdale } __packed;
103e932d817SDavid Barksdale
104e932d817SDavid Barksdale struct cp2112_write_read_req_report {
105e932d817SDavid Barksdale u8 report; /* CP2112_DATA_WRITE_READ_REQUEST */
106e932d817SDavid Barksdale u8 slave_address;
107e932d817SDavid Barksdale __be16 length;
108e932d817SDavid Barksdale u8 target_address_length;
109e932d817SDavid Barksdale u8 target_address[16];
110e932d817SDavid Barksdale } __packed;
111e932d817SDavid Barksdale
112e932d817SDavid Barksdale struct cp2112_write_req_report {
113e932d817SDavid Barksdale u8 report; /* CP2112_DATA_WRITE_REQUEST */
114e932d817SDavid Barksdale u8 slave_address;
115e932d817SDavid Barksdale u8 length;
116e932d817SDavid Barksdale u8 data[61];
117e932d817SDavid Barksdale } __packed;
118e932d817SDavid Barksdale
119e932d817SDavid Barksdale struct cp2112_force_read_report {
120e932d817SDavid Barksdale u8 report; /* CP2112_DATA_READ_FORCE_SEND */
121e932d817SDavid Barksdale __be16 length;
122e932d817SDavid Barksdale } __packed;
123e932d817SDavid Barksdale
124e932d817SDavid Barksdale struct cp2112_xfer_status_report {
125e932d817SDavid Barksdale u8 report; /* CP2112_TRANSFER_STATUS_RESPONSE */
126e932d817SDavid Barksdale u8 status0; /* STATUS0_* */
127e932d817SDavid Barksdale u8 status1; /* STATUS1_* */
128e932d817SDavid Barksdale __be16 retries;
129e932d817SDavid Barksdale __be16 length;
130e932d817SDavid Barksdale } __packed;
131e932d817SDavid Barksdale
132e932d817SDavid Barksdale struct cp2112_string_report {
133e932d817SDavid Barksdale u8 dummy; /* force .string to be aligned */
1345e423a0cSKees Cook struct_group_attr(contents, __packed,
135e932d817SDavid Barksdale u8 report; /* CP2112_*_STRING */
1365e423a0cSKees Cook u8 length; /* length in bytes of everything after .report */
137e932d817SDavid Barksdale u8 type; /* USB_DT_STRING */
138e932d817SDavid Barksdale wchar_t string[30]; /* UTF16_LITTLE_ENDIAN string */
1395e423a0cSKees Cook );
140e932d817SDavid Barksdale } __packed;
141e932d817SDavid Barksdale
142e932d817SDavid Barksdale /* Number of times to request transfer status before giving up waiting for a
143e932d817SDavid Barksdale transfer to complete. This may need to be changed if SMBUS clock, retries,
144e932d817SDavid Barksdale or read/write/scl_low timeout settings are changed. */
145e932d817SDavid Barksdale static const int XFER_STATUS_RETRIES = 10;
146e932d817SDavid Barksdale
147e932d817SDavid Barksdale /* Time in ms to wait for a CP2112_DATA_READ_RESPONSE or
148e932d817SDavid Barksdale CP2112_TRANSFER_STATUS_RESPONSE. */
149e932d817SDavid Barksdale static const int RESPONSE_TIMEOUT = 50;
150e932d817SDavid Barksdale
151e932d817SDavid Barksdale static const struct hid_device_id cp2112_devices[] = {
152e932d817SDavid Barksdale { HID_USB_DEVICE(USB_VENDOR_ID_CYGNAL, USB_DEVICE_ID_CYGNAL_CP2112) },
153e932d817SDavid Barksdale { }
154e932d817SDavid Barksdale };
155e932d817SDavid Barksdale MODULE_DEVICE_TABLE(hid, cp2112_devices);
156e932d817SDavid Barksdale
157e932d817SDavid Barksdale struct cp2112_device {
158e932d817SDavid Barksdale struct i2c_adapter adap;
159e932d817SDavid Barksdale struct hid_device *hdev;
160e932d817SDavid Barksdale wait_queue_head_t wait;
161e932d817SDavid Barksdale u8 read_data[61];
162e932d817SDavid Barksdale u8 read_length;
16344eda784SEllen Wang u8 hwversion;
164e932d817SDavid Barksdale int xfer_status;
165e932d817SDavid Barksdale atomic_t read_avail;
166e932d817SDavid Barksdale atomic_t xfer_avail;
167e932d817SDavid Barksdale struct gpio_chip gc;
1681ffb3c40SBenjamin Tissoires u8 *in_out_buffer;
1697a7b5df8SJohan Hovold struct mutex lock;
17013de9ccaSBenjamin Tissoires
17113de9ccaSBenjamin Tissoires bool gpio_poll;
17213de9ccaSBenjamin Tissoires struct delayed_work gpio_poll_worker;
17313de9ccaSBenjamin Tissoires unsigned long irq_mask;
17413de9ccaSBenjamin Tissoires u8 gpio_prev_state;
175e932d817SDavid Barksdale };
176e932d817SDavid Barksdale
177ee0682b0SAndy Shevchenko static int gpio_push_pull = CP2112_GPIO_ALL_GPIO_MASK;
178a6a5ecccSAndy Shevchenko module_param(gpio_push_pull, int, 0644);
179e932d817SDavid Barksdale MODULE_PARM_DESC(gpio_push_pull, "GPIO push-pull configuration bitmask");
180e932d817SDavid Barksdale
cp2112_gpio_direction_input(struct gpio_chip * chip,unsigned offset)181e932d817SDavid Barksdale static int cp2112_gpio_direction_input(struct gpio_chip *chip, unsigned offset)
182e932d817SDavid Barksdale {
18347513c2fSLinus Walleij struct cp2112_device *dev = gpiochip_get_data(chip);
184e932d817SDavid Barksdale struct hid_device *hdev = dev->hdev;
1851ffb3c40SBenjamin Tissoires u8 *buf = dev->in_out_buffer;
186e932d817SDavid Barksdale int ret;
187e932d817SDavid Barksdale
1887a7b5df8SJohan Hovold mutex_lock(&dev->lock);
1891ffb3c40SBenjamin Tissoires
190490051adSJiri Kosina ret = hid_hw_raw_request(hdev, CP2112_GPIO_CONFIG, buf,
1911ffb3c40SBenjamin Tissoires CP2112_GPIO_CONFIG_LENGTH, HID_FEATURE_REPORT,
192490051adSJiri Kosina HID_REQ_GET_REPORT);
1931ffb3c40SBenjamin Tissoires if (ret != CP2112_GPIO_CONFIG_LENGTH) {
194e932d817SDavid Barksdale hid_err(hdev, "error requesting GPIO config: %d\n", ret);
1957da85fbfSSébastien Szymanski if (ret >= 0)
1967da85fbfSSébastien Szymanski ret = -EIO;
1971ffb3c40SBenjamin Tissoires goto exit;
198e932d817SDavid Barksdale }
199e932d817SDavid Barksdale
200e19c6bd4SAndy Shevchenko buf[1] &= ~BIT(offset);
201e932d817SDavid Barksdale buf[2] = gpio_push_pull;
202e932d817SDavid Barksdale
2031ffb3c40SBenjamin Tissoires ret = hid_hw_raw_request(hdev, CP2112_GPIO_CONFIG, buf,
2041ffb3c40SBenjamin Tissoires CP2112_GPIO_CONFIG_LENGTH, HID_FEATURE_REPORT,
2051ffb3c40SBenjamin Tissoires HID_REQ_SET_REPORT);
2067da85fbfSSébastien Szymanski if (ret != CP2112_GPIO_CONFIG_LENGTH) {
207e932d817SDavid Barksdale hid_err(hdev, "error setting GPIO config: %d\n", ret);
2087da85fbfSSébastien Szymanski if (ret >= 0)
2097da85fbfSSébastien Szymanski ret = -EIO;
2101ffb3c40SBenjamin Tissoires goto exit;
211e932d817SDavid Barksdale }
212e932d817SDavid Barksdale
2131ffb3c40SBenjamin Tissoires ret = 0;
2141ffb3c40SBenjamin Tissoires
2151ffb3c40SBenjamin Tissoires exit:
2167a7b5df8SJohan Hovold mutex_unlock(&dev->lock);
2177da85fbfSSébastien Szymanski return ret;
218e932d817SDavid Barksdale }
219e932d817SDavid Barksdale
cp2112_gpio_set(struct gpio_chip * chip,unsigned offset,int value)220e932d817SDavid Barksdale static void cp2112_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
221e932d817SDavid Barksdale {
22247513c2fSLinus Walleij struct cp2112_device *dev = gpiochip_get_data(chip);
223e932d817SDavid Barksdale struct hid_device *hdev = dev->hdev;
2241ffb3c40SBenjamin Tissoires u8 *buf = dev->in_out_buffer;
225e932d817SDavid Barksdale int ret;
226e932d817SDavid Barksdale
2277a7b5df8SJohan Hovold mutex_lock(&dev->lock);
2281ffb3c40SBenjamin Tissoires
229e932d817SDavid Barksdale buf[0] = CP2112_GPIO_SET;
230ee0682b0SAndy Shevchenko buf[1] = value ? CP2112_GPIO_ALL_GPIO_MASK : 0;
231e19c6bd4SAndy Shevchenko buf[2] = BIT(offset);
232e932d817SDavid Barksdale
2331ffb3c40SBenjamin Tissoires ret = hid_hw_raw_request(hdev, CP2112_GPIO_SET, buf,
2341ffb3c40SBenjamin Tissoires CP2112_GPIO_SET_LENGTH, HID_FEATURE_REPORT,
2351ffb3c40SBenjamin Tissoires HID_REQ_SET_REPORT);
236e932d817SDavid Barksdale if (ret < 0)
237e932d817SDavid Barksdale hid_err(hdev, "error setting GPIO values: %d\n", ret);
2381ffb3c40SBenjamin Tissoires
2397a7b5df8SJohan Hovold mutex_unlock(&dev->lock);
240e932d817SDavid Barksdale }
241e932d817SDavid Barksdale
cp2112_gpio_get_all(struct gpio_chip * chip)24213de9ccaSBenjamin Tissoires static int cp2112_gpio_get_all(struct gpio_chip *chip)
243e932d817SDavid Barksdale {
24447513c2fSLinus Walleij struct cp2112_device *dev = gpiochip_get_data(chip);
245e932d817SDavid Barksdale struct hid_device *hdev = dev->hdev;
2461ffb3c40SBenjamin Tissoires u8 *buf = dev->in_out_buffer;
247e932d817SDavid Barksdale int ret;
248e932d817SDavid Barksdale
2497a7b5df8SJohan Hovold mutex_lock(&dev->lock);
2501ffb3c40SBenjamin Tissoires
2511ffb3c40SBenjamin Tissoires ret = hid_hw_raw_request(hdev, CP2112_GPIO_GET, buf,
2521ffb3c40SBenjamin Tissoires CP2112_GPIO_GET_LENGTH, HID_FEATURE_REPORT,
2531ffb3c40SBenjamin Tissoires HID_REQ_GET_REPORT);
2541ffb3c40SBenjamin Tissoires if (ret != CP2112_GPIO_GET_LENGTH) {
255e932d817SDavid Barksdale hid_err(hdev, "error requesting GPIO values: %d\n", ret);
2561ffb3c40SBenjamin Tissoires ret = ret < 0 ? ret : -EIO;
2571ffb3c40SBenjamin Tissoires goto exit;
258e932d817SDavid Barksdale }
259e932d817SDavid Barksdale
26013de9ccaSBenjamin Tissoires ret = buf[1];
2611ffb3c40SBenjamin Tissoires
2621ffb3c40SBenjamin Tissoires exit:
2637a7b5df8SJohan Hovold mutex_unlock(&dev->lock);
2641ffb3c40SBenjamin Tissoires
2651ffb3c40SBenjamin Tissoires return ret;
266e932d817SDavid Barksdale }
267e932d817SDavid Barksdale
cp2112_gpio_get(struct gpio_chip * chip,unsigned int offset)26813de9ccaSBenjamin Tissoires static int cp2112_gpio_get(struct gpio_chip *chip, unsigned int offset)
26913de9ccaSBenjamin Tissoires {
27013de9ccaSBenjamin Tissoires int ret;
27113de9ccaSBenjamin Tissoires
27213de9ccaSBenjamin Tissoires ret = cp2112_gpio_get_all(chip);
27313de9ccaSBenjamin Tissoires if (ret < 0)
27413de9ccaSBenjamin Tissoires return ret;
27513de9ccaSBenjamin Tissoires
27613de9ccaSBenjamin Tissoires return (ret >> offset) & 1;
27713de9ccaSBenjamin Tissoires }
27813de9ccaSBenjamin Tissoires
cp2112_gpio_direction_output(struct gpio_chip * chip,unsigned offset,int value)279e932d817SDavid Barksdale static int cp2112_gpio_direction_output(struct gpio_chip *chip,
280e932d817SDavid Barksdale unsigned offset, int value)
281e932d817SDavid Barksdale {
28247513c2fSLinus Walleij struct cp2112_device *dev = gpiochip_get_data(chip);
283e932d817SDavid Barksdale struct hid_device *hdev = dev->hdev;
2841ffb3c40SBenjamin Tissoires u8 *buf = dev->in_out_buffer;
285e932d817SDavid Barksdale int ret;
286e932d817SDavid Barksdale
2877a7b5df8SJohan Hovold mutex_lock(&dev->lock);
2881ffb3c40SBenjamin Tissoires
289490051adSJiri Kosina ret = hid_hw_raw_request(hdev, CP2112_GPIO_CONFIG, buf,
2901ffb3c40SBenjamin Tissoires CP2112_GPIO_CONFIG_LENGTH, HID_FEATURE_REPORT,
291490051adSJiri Kosina HID_REQ_GET_REPORT);
2921ffb3c40SBenjamin Tissoires if (ret != CP2112_GPIO_CONFIG_LENGTH) {
293e932d817SDavid Barksdale hid_err(hdev, "error requesting GPIO config: %d\n", ret);
2941ffb3c40SBenjamin Tissoires goto fail;
295e932d817SDavid Barksdale }
296e932d817SDavid Barksdale
297e932d817SDavid Barksdale buf[1] |= 1 << offset;
298e932d817SDavid Barksdale buf[2] = gpio_push_pull;
299e932d817SDavid Barksdale
3001ffb3c40SBenjamin Tissoires ret = hid_hw_raw_request(hdev, CP2112_GPIO_CONFIG, buf,
3011ffb3c40SBenjamin Tissoires CP2112_GPIO_CONFIG_LENGTH, HID_FEATURE_REPORT,
3021ffb3c40SBenjamin Tissoires HID_REQ_SET_REPORT);
303e932d817SDavid Barksdale if (ret < 0) {
304e932d817SDavid Barksdale hid_err(hdev, "error setting GPIO config: %d\n", ret);
3051ffb3c40SBenjamin Tissoires goto fail;
306e932d817SDavid Barksdale }
307e932d817SDavid Barksdale
3087a7b5df8SJohan Hovold mutex_unlock(&dev->lock);
3091ffb3c40SBenjamin Tissoires
310beb9d007SAntonio Borneo /*
311beb9d007SAntonio Borneo * Set gpio value when output direction is already set,
312beb9d007SAntonio Borneo * as specified in AN495, Rev. 0.2, cpt. 4.4
313beb9d007SAntonio Borneo */
314beb9d007SAntonio Borneo cp2112_gpio_set(chip, offset, value);
315beb9d007SAntonio Borneo
316e932d817SDavid Barksdale return 0;
3171ffb3c40SBenjamin Tissoires
3181ffb3c40SBenjamin Tissoires fail:
3197a7b5df8SJohan Hovold mutex_unlock(&dev->lock);
3201ffb3c40SBenjamin Tissoires return ret < 0 ? ret : -EIO;
321e932d817SDavid Barksdale }
322e932d817SDavid Barksdale
cp2112_hid_get(struct hid_device * hdev,unsigned char report_number,u8 * data,size_t count,unsigned char report_type)323e932d817SDavid Barksdale static int cp2112_hid_get(struct hid_device *hdev, unsigned char report_number,
324e932d817SDavid Barksdale u8 *data, size_t count, unsigned char report_type)
325e932d817SDavid Barksdale {
326e932d817SDavid Barksdale u8 *buf;
327e932d817SDavid Barksdale int ret;
328e932d817SDavid Barksdale
329e932d817SDavid Barksdale buf = kmalloc(count, GFP_KERNEL);
330e932d817SDavid Barksdale if (!buf)
331e932d817SDavid Barksdale return -ENOMEM;
332e932d817SDavid Barksdale
333490051adSJiri Kosina ret = hid_hw_raw_request(hdev, report_number, buf, count,
334490051adSJiri Kosina report_type, HID_REQ_GET_REPORT);
335e932d817SDavid Barksdale memcpy(data, buf, count);
336e932d817SDavid Barksdale kfree(buf);
337e932d817SDavid Barksdale return ret;
338e932d817SDavid Barksdale }
339e932d817SDavid Barksdale
cp2112_hid_output(struct hid_device * hdev,u8 * data,size_t count,unsigned char report_type)340e932d817SDavid Barksdale static int cp2112_hid_output(struct hid_device *hdev, u8 *data, size_t count,
341e932d817SDavid Barksdale unsigned char report_type)
342e932d817SDavid Barksdale {
343e932d817SDavid Barksdale u8 *buf;
344e932d817SDavid Barksdale int ret;
345e932d817SDavid Barksdale
346e932d817SDavid Barksdale buf = kmemdup(data, count, GFP_KERNEL);
347e932d817SDavid Barksdale if (!buf)
348e932d817SDavid Barksdale return -ENOMEM;
349e932d817SDavid Barksdale
350866e4797SBenjamin Tissoires if (report_type == HID_OUTPUT_REPORT)
351866e4797SBenjamin Tissoires ret = hid_hw_output_report(hdev, buf, count);
352866e4797SBenjamin Tissoires else
353866e4797SBenjamin Tissoires ret = hid_hw_raw_request(hdev, buf[0], buf, count, report_type,
354866e4797SBenjamin Tissoires HID_REQ_SET_REPORT);
355866e4797SBenjamin Tissoires
356e932d817SDavid Barksdale kfree(buf);
357e932d817SDavid Barksdale return ret;
358e932d817SDavid Barksdale }
359e932d817SDavid Barksdale
cp2112_wait(struct cp2112_device * dev,atomic_t * avail)360e932d817SDavid Barksdale static int cp2112_wait(struct cp2112_device *dev, atomic_t *avail)
361e932d817SDavid Barksdale {
362e932d817SDavid Barksdale int ret = 0;
363e932d817SDavid Barksdale
364e932d817SDavid Barksdale /* We have sent either a CP2112_TRANSFER_STATUS_REQUEST or a
365e932d817SDavid Barksdale * CP2112_DATA_READ_FORCE_SEND and we are waiting for the response to
366e932d817SDavid Barksdale * come in cp2112_raw_event or timeout. There will only be one of these
367e932d817SDavid Barksdale * in flight at any one time. The timeout is extremely large and is a
368e932d817SDavid Barksdale * last resort if the CP2112 has died. If we do timeout we don't expect
369e932d817SDavid Barksdale * to receive the response which would cause data races, it's not like
370e932d817SDavid Barksdale * we can do anything about it anyway.
371e932d817SDavid Barksdale */
372e932d817SDavid Barksdale ret = wait_event_interruptible_timeout(dev->wait,
373e932d817SDavid Barksdale atomic_read(avail), msecs_to_jiffies(RESPONSE_TIMEOUT));
374e932d817SDavid Barksdale if (-ERESTARTSYS == ret)
375e932d817SDavid Barksdale return ret;
376e932d817SDavid Barksdale if (!ret)
377e932d817SDavid Barksdale return -ETIMEDOUT;
378e932d817SDavid Barksdale
379e932d817SDavid Barksdale atomic_set(avail, 0);
380e932d817SDavid Barksdale return 0;
381e932d817SDavid Barksdale }
382e932d817SDavid Barksdale
cp2112_xfer_status(struct cp2112_device * dev)383e932d817SDavid Barksdale static int cp2112_xfer_status(struct cp2112_device *dev)
384e932d817SDavid Barksdale {
385e932d817SDavid Barksdale struct hid_device *hdev = dev->hdev;
386e932d817SDavid Barksdale u8 buf[2];
387e932d817SDavid Barksdale int ret;
388e932d817SDavid Barksdale
389e932d817SDavid Barksdale buf[0] = CP2112_TRANSFER_STATUS_REQUEST;
390e932d817SDavid Barksdale buf[1] = 0x01;
391e932d817SDavid Barksdale atomic_set(&dev->xfer_avail, 0);
392e932d817SDavid Barksdale
393e932d817SDavid Barksdale ret = cp2112_hid_output(hdev, buf, 2, HID_OUTPUT_REPORT);
394e932d817SDavid Barksdale if (ret < 0) {
395e932d817SDavid Barksdale hid_warn(hdev, "Error requesting status: %d\n", ret);
396e932d817SDavid Barksdale return ret;
397e932d817SDavid Barksdale }
398e932d817SDavid Barksdale
399e932d817SDavid Barksdale ret = cp2112_wait(dev, &dev->xfer_avail);
400e932d817SDavid Barksdale if (ret)
401e932d817SDavid Barksdale return ret;
402e932d817SDavid Barksdale
403e932d817SDavid Barksdale return dev->xfer_status;
404e932d817SDavid Barksdale }
405e932d817SDavid Barksdale
cp2112_read(struct cp2112_device * dev,u8 * data,size_t size)406e932d817SDavid Barksdale static int cp2112_read(struct cp2112_device *dev, u8 *data, size_t size)
407e932d817SDavid Barksdale {
408e932d817SDavid Barksdale struct hid_device *hdev = dev->hdev;
409e932d817SDavid Barksdale struct cp2112_force_read_report report;
410e932d817SDavid Barksdale int ret;
411e932d817SDavid Barksdale
4126debce6fSAntonio Borneo if (size > sizeof(dev->read_data))
4136debce6fSAntonio Borneo size = sizeof(dev->read_data);
414e932d817SDavid Barksdale report.report = CP2112_DATA_READ_FORCE_SEND;
415e932d817SDavid Barksdale report.length = cpu_to_be16(size);
416e932d817SDavid Barksdale
417e932d817SDavid Barksdale atomic_set(&dev->read_avail, 0);
418e932d817SDavid Barksdale
419e932d817SDavid Barksdale ret = cp2112_hid_output(hdev, &report.report, sizeof(report),
420e932d817SDavid Barksdale HID_OUTPUT_REPORT);
421e932d817SDavid Barksdale if (ret < 0) {
422e932d817SDavid Barksdale hid_warn(hdev, "Error requesting data: %d\n", ret);
423e932d817SDavid Barksdale return ret;
424e932d817SDavid Barksdale }
425e932d817SDavid Barksdale
426e932d817SDavid Barksdale ret = cp2112_wait(dev, &dev->read_avail);
427e932d817SDavid Barksdale if (ret)
428e932d817SDavid Barksdale return ret;
429e932d817SDavid Barksdale
4305a673fceSJiri Kosina hid_dbg(hdev, "read %d of %zd bytes requested\n",
431e932d817SDavid Barksdale dev->read_length, size);
432e932d817SDavid Barksdale
433e932d817SDavid Barksdale if (size > dev->read_length)
434e932d817SDavid Barksdale size = dev->read_length;
435e932d817SDavid Barksdale
436e932d817SDavid Barksdale memcpy(data, dev->read_data, size);
437e932d817SDavid Barksdale return dev->read_length;
438e932d817SDavid Barksdale }
439e932d817SDavid Barksdale
cp2112_read_req(void * buf,u8 slave_address,u16 length)440e932d817SDavid Barksdale static int cp2112_read_req(void *buf, u8 slave_address, u16 length)
441e932d817SDavid Barksdale {
442e932d817SDavid Barksdale struct cp2112_read_req_report *report = buf;
443e932d817SDavid Barksdale
444e932d817SDavid Barksdale if (length < 1 || length > 512)
445e932d817SDavid Barksdale return -EINVAL;
446e932d817SDavid Barksdale
447e932d817SDavid Barksdale report->report = CP2112_DATA_READ_REQUEST;
448e932d817SDavid Barksdale report->slave_address = slave_address << 1;
449e932d817SDavid Barksdale report->length = cpu_to_be16(length);
450e932d817SDavid Barksdale return sizeof(*report);
451e932d817SDavid Barksdale }
452e932d817SDavid Barksdale
cp2112_write_read_req(void * buf,u8 slave_address,u16 length,u8 command,u8 * data,u8 data_length)453e932d817SDavid Barksdale static int cp2112_write_read_req(void *buf, u8 slave_address, u16 length,
454e932d817SDavid Barksdale u8 command, u8 *data, u8 data_length)
455e932d817SDavid Barksdale {
456e932d817SDavid Barksdale struct cp2112_write_read_req_report *report = buf;
457e932d817SDavid Barksdale
458e932d817SDavid Barksdale if (length < 1 || length > 512
459e932d817SDavid Barksdale || data_length > sizeof(report->target_address) - 1)
460e932d817SDavid Barksdale return -EINVAL;
461e932d817SDavid Barksdale
462e932d817SDavid Barksdale report->report = CP2112_DATA_WRITE_READ_REQUEST;
463e932d817SDavid Barksdale report->slave_address = slave_address << 1;
464e932d817SDavid Barksdale report->length = cpu_to_be16(length);
465e932d817SDavid Barksdale report->target_address_length = data_length + 1;
466e932d817SDavid Barksdale report->target_address[0] = command;
467e932d817SDavid Barksdale memcpy(&report->target_address[1], data, data_length);
468e932d817SDavid Barksdale return data_length + 6;
469e932d817SDavid Barksdale }
470e932d817SDavid Barksdale
cp2112_write_req(void * buf,u8 slave_address,u8 command,u8 * data,u8 data_length)471e932d817SDavid Barksdale static int cp2112_write_req(void *buf, u8 slave_address, u8 command, u8 *data,
472e932d817SDavid Barksdale u8 data_length)
473e932d817SDavid Barksdale {
474e932d817SDavid Barksdale struct cp2112_write_req_report *report = buf;
475e932d817SDavid Barksdale
476e932d817SDavid Barksdale if (data_length > sizeof(report->data) - 1)
477e932d817SDavid Barksdale return -EINVAL;
478e932d817SDavid Barksdale
479e932d817SDavid Barksdale report->report = CP2112_DATA_WRITE_REQUEST;
480e932d817SDavid Barksdale report->slave_address = slave_address << 1;
481e932d817SDavid Barksdale report->length = data_length + 1;
482e932d817SDavid Barksdale report->data[0] = command;
483e932d817SDavid Barksdale memcpy(&report->data[1], data, data_length);
484e932d817SDavid Barksdale return data_length + 4;
485e932d817SDavid Barksdale }
486e932d817SDavid Barksdale
cp2112_i2c_write_req(void * buf,u8 slave_address,u8 * data,u8 data_length)487b9029345SAntonio Borneo static int cp2112_i2c_write_req(void *buf, u8 slave_address, u8 *data,
488b9029345SAntonio Borneo u8 data_length)
489b9029345SAntonio Borneo {
490b9029345SAntonio Borneo struct cp2112_write_req_report *report = buf;
491b9029345SAntonio Borneo
492b9029345SAntonio Borneo if (data_length > sizeof(report->data))
493b9029345SAntonio Borneo return -EINVAL;
494b9029345SAntonio Borneo
495b9029345SAntonio Borneo report->report = CP2112_DATA_WRITE_REQUEST;
496b9029345SAntonio Borneo report->slave_address = slave_address << 1;
497b9029345SAntonio Borneo report->length = data_length;
498b9029345SAntonio Borneo memcpy(report->data, data, data_length);
499b9029345SAntonio Borneo return data_length + 3;
500b9029345SAntonio Borneo }
501b9029345SAntonio Borneo
cp2112_i2c_write_read_req(void * buf,u8 slave_address,u8 * addr,int addr_length,int read_length)50244eda784SEllen Wang static int cp2112_i2c_write_read_req(void *buf, u8 slave_address,
50344eda784SEllen Wang u8 *addr, int addr_length,
50444eda784SEllen Wang int read_length)
50544eda784SEllen Wang {
50644eda784SEllen Wang struct cp2112_write_read_req_report *report = buf;
50744eda784SEllen Wang
50844eda784SEllen Wang if (read_length < 1 || read_length > 512 ||
50944eda784SEllen Wang addr_length > sizeof(report->target_address))
51044eda784SEllen Wang return -EINVAL;
51144eda784SEllen Wang
51244eda784SEllen Wang report->report = CP2112_DATA_WRITE_READ_REQUEST;
51344eda784SEllen Wang report->slave_address = slave_address << 1;
51444eda784SEllen Wang report->length = cpu_to_be16(read_length);
51544eda784SEllen Wang report->target_address_length = addr_length;
51644eda784SEllen Wang memcpy(report->target_address, addr, addr_length);
51744eda784SEllen Wang return addr_length + 5;
51844eda784SEllen Wang }
51944eda784SEllen Wang
cp2112_i2c_xfer(struct i2c_adapter * adap,struct i2c_msg * msgs,int num)520b9029345SAntonio Borneo static int cp2112_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs,
521b9029345SAntonio Borneo int num)
522b9029345SAntonio Borneo {
523b9029345SAntonio Borneo struct cp2112_device *dev = (struct cp2112_device *)adap->algo_data;
524b9029345SAntonio Borneo struct hid_device *hdev = dev->hdev;
525b9029345SAntonio Borneo u8 buf[64];
526b9029345SAntonio Borneo ssize_t count;
52744eda784SEllen Wang ssize_t read_length = 0;
52844eda784SEllen Wang u8 *read_buf = NULL;
529b9029345SAntonio Borneo unsigned int retries;
530b9029345SAntonio Borneo int ret;
531b9029345SAntonio Borneo
532b9029345SAntonio Borneo hid_dbg(hdev, "I2C %d messages\n", num);
533b9029345SAntonio Borneo
53444eda784SEllen Wang if (num == 1) {
5354a3983d7SAndy Shevchenko hid_dbg(hdev, "I2C %s %#04x len %d\n",
5364a3983d7SAndy Shevchenko str_read_write(msgs->flags & I2C_M_RD), msgs->addr, msgs->len);
53744eda784SEllen Wang if (msgs->flags & I2C_M_RD) {
53844eda784SEllen Wang read_length = msgs->len;
53944eda784SEllen Wang read_buf = msgs->buf;
54044eda784SEllen Wang count = cp2112_read_req(buf, msgs->addr, msgs->len);
54144eda784SEllen Wang } else {
54244eda784SEllen Wang count = cp2112_i2c_write_req(buf, msgs->addr,
54344eda784SEllen Wang msgs->buf, msgs->len);
54444eda784SEllen Wang }
54544eda784SEllen Wang if (count < 0)
54644eda784SEllen Wang return count;
54744eda784SEllen Wang } else if (dev->hwversion > 1 && /* no repeated start in rev 1 */
54844eda784SEllen Wang num == 2 &&
54944eda784SEllen Wang msgs[0].addr == msgs[1].addr &&
55044eda784SEllen Wang !(msgs[0].flags & I2C_M_RD) && (msgs[1].flags & I2C_M_RD)) {
55144eda784SEllen Wang hid_dbg(hdev, "I2C write-read %#04x wlen %d rlen %d\n",
55244eda784SEllen Wang msgs[0].addr, msgs[0].len, msgs[1].len);
55344eda784SEllen Wang read_length = msgs[1].len;
55444eda784SEllen Wang read_buf = msgs[1].buf;
55544eda784SEllen Wang count = cp2112_i2c_write_read_req(buf, msgs[0].addr,
55644eda784SEllen Wang msgs[0].buf, msgs[0].len, msgs[1].len);
55744eda784SEllen Wang if (count < 0)
55844eda784SEllen Wang return count;
55944eda784SEllen Wang } else {
560b9029345SAntonio Borneo hid_err(hdev,
561b9029345SAntonio Borneo "Multi-message I2C transactions not supported\n");
562b9029345SAntonio Borneo return -EOPNOTSUPP;
563b9029345SAntonio Borneo }
564b9029345SAntonio Borneo
565b9029345SAntonio Borneo ret = hid_hw_power(hdev, PM_HINT_FULLON);
566b9029345SAntonio Borneo if (ret < 0) {
567b9029345SAntonio Borneo hid_err(hdev, "power management error: %d\n", ret);
568b9029345SAntonio Borneo return ret;
569b9029345SAntonio Borneo }
570b9029345SAntonio Borneo
571b9029345SAntonio Borneo ret = cp2112_hid_output(hdev, buf, count, HID_OUTPUT_REPORT);
572b9029345SAntonio Borneo if (ret < 0) {
573b9029345SAntonio Borneo hid_warn(hdev, "Error starting transaction: %d\n", ret);
574b9029345SAntonio Borneo goto power_normal;
575b9029345SAntonio Borneo }
576b9029345SAntonio Borneo
577b9029345SAntonio Borneo for (retries = 0; retries < XFER_STATUS_RETRIES; ++retries) {
578b9029345SAntonio Borneo ret = cp2112_xfer_status(dev);
579b9029345SAntonio Borneo if (-EBUSY == ret)
580b9029345SAntonio Borneo continue;
581b9029345SAntonio Borneo if (ret < 0)
582b9029345SAntonio Borneo goto power_normal;
583b9029345SAntonio Borneo break;
584b9029345SAntonio Borneo }
585b9029345SAntonio Borneo
586b9029345SAntonio Borneo if (XFER_STATUS_RETRIES <= retries) {
587b9029345SAntonio Borneo hid_warn(hdev, "Transfer timed out, cancelling.\n");
588b9029345SAntonio Borneo buf[0] = CP2112_CANCEL_TRANSFER;
589b9029345SAntonio Borneo buf[1] = 0x01;
590b9029345SAntonio Borneo
591b9029345SAntonio Borneo ret = cp2112_hid_output(hdev, buf, 2, HID_OUTPUT_REPORT);
592b9029345SAntonio Borneo if (ret < 0)
593b9029345SAntonio Borneo hid_warn(hdev, "Error cancelling transaction: %d\n",
594b9029345SAntonio Borneo ret);
595b9029345SAntonio Borneo
596b9029345SAntonio Borneo ret = -ETIMEDOUT;
597b9029345SAntonio Borneo goto power_normal;
598b9029345SAntonio Borneo }
599b9029345SAntonio Borneo
60044eda784SEllen Wang for (count = 0; count < read_length;) {
60144eda784SEllen Wang ret = cp2112_read(dev, read_buf + count, read_length - count);
602b9029345SAntonio Borneo if (ret < 0)
603b9029345SAntonio Borneo goto power_normal;
6045ddfb12eSEllen Wang if (ret == 0) {
6055ddfb12eSEllen Wang hid_err(hdev, "read returned 0\n");
606b9029345SAntonio Borneo ret = -EIO;
607b9029345SAntonio Borneo goto power_normal;
608b9029345SAntonio Borneo }
6095ddfb12eSEllen Wang count += ret;
61044eda784SEllen Wang if (count > read_length) {
6115ddfb12eSEllen Wang /*
6125ddfb12eSEllen Wang * The hardware returned too much data.
6135ddfb12eSEllen Wang * This is mostly harmless because cp2112_read()
6145ddfb12eSEllen Wang * has a limit check so didn't overrun our
6155ddfb12eSEllen Wang * buffer. Nevertheless, we return an error
6165ddfb12eSEllen Wang * because something is seriously wrong and
6175ddfb12eSEllen Wang * it shouldn't go unnoticed.
6185ddfb12eSEllen Wang */
6195ddfb12eSEllen Wang hid_err(hdev, "long read: %d > %zd\n",
62044eda784SEllen Wang ret, read_length - count + ret);
6215ddfb12eSEllen Wang ret = -EIO;
6225ddfb12eSEllen Wang goto power_normal;
6235ddfb12eSEllen Wang }
6245ddfb12eSEllen Wang }
625b9029345SAntonio Borneo
626b9029345SAntonio Borneo /* return the number of transferred messages */
62744eda784SEllen Wang ret = num;
628b9029345SAntonio Borneo
629b9029345SAntonio Borneo power_normal:
630b9029345SAntonio Borneo hid_hw_power(hdev, PM_HINT_NORMAL);
631b9029345SAntonio Borneo hid_dbg(hdev, "I2C transfer finished: %d\n", ret);
632b9029345SAntonio Borneo return ret;
633b9029345SAntonio Borneo }
634b9029345SAntonio Borneo
cp2112_xfer(struct i2c_adapter * adap,u16 addr,unsigned short flags,char read_write,u8 command,int size,union i2c_smbus_data * data)635e932d817SDavid Barksdale static int cp2112_xfer(struct i2c_adapter *adap, u16 addr,
636e932d817SDavid Barksdale unsigned short flags, char read_write, u8 command,
637e932d817SDavid Barksdale int size, union i2c_smbus_data *data)
638e932d817SDavid Barksdale {
639e932d817SDavid Barksdale struct cp2112_device *dev = (struct cp2112_device *)adap->algo_data;
640e932d817SDavid Barksdale struct hid_device *hdev = dev->hdev;
641e932d817SDavid Barksdale u8 buf[64];
64229e2d6d1SEllen Wang __le16 word;
6430438ee70SJiri Kosina ssize_t count;
644e932d817SDavid Barksdale size_t read_length = 0;
645e932d817SDavid Barksdale unsigned int retries;
646e932d817SDavid Barksdale int ret;
647e932d817SDavid Barksdale
648e932d817SDavid Barksdale hid_dbg(hdev, "%s addr 0x%x flags 0x%x cmd 0x%x size %d\n",
6494a3983d7SAndy Shevchenko str_write_read(read_write == I2C_SMBUS_WRITE),
650e932d817SDavid Barksdale addr, flags, command, size);
651e932d817SDavid Barksdale
652e932d817SDavid Barksdale switch (size) {
653e932d817SDavid Barksdale case I2C_SMBUS_BYTE:
654e932d817SDavid Barksdale read_length = 1;
655e932d817SDavid Barksdale
656e932d817SDavid Barksdale if (I2C_SMBUS_READ == read_write)
657e932d817SDavid Barksdale count = cp2112_read_req(buf, addr, read_length);
658e932d817SDavid Barksdale else
6596d00d153SEllen Wang count = cp2112_write_req(buf, addr, command, NULL,
660e932d817SDavid Barksdale 0);
661e932d817SDavid Barksdale break;
662e932d817SDavid Barksdale case I2C_SMBUS_BYTE_DATA:
663e932d817SDavid Barksdale read_length = 1;
664e932d817SDavid Barksdale
665e932d817SDavid Barksdale if (I2C_SMBUS_READ == read_write)
666e932d817SDavid Barksdale count = cp2112_write_read_req(buf, addr, read_length,
667e932d817SDavid Barksdale command, NULL, 0);
668e932d817SDavid Barksdale else
669e932d817SDavid Barksdale count = cp2112_write_req(buf, addr, command,
670e932d817SDavid Barksdale &data->byte, 1);
671e932d817SDavid Barksdale break;
672e932d817SDavid Barksdale case I2C_SMBUS_WORD_DATA:
673e932d817SDavid Barksdale read_length = 2;
67429e2d6d1SEllen Wang word = cpu_to_le16(data->word);
675e932d817SDavid Barksdale
676e932d817SDavid Barksdale if (I2C_SMBUS_READ == read_write)
677e932d817SDavid Barksdale count = cp2112_write_read_req(buf, addr, read_length,
678e932d817SDavid Barksdale command, NULL, 0);
679e932d817SDavid Barksdale else
680e932d817SDavid Barksdale count = cp2112_write_req(buf, addr, command,
681e932d817SDavid Barksdale (u8 *)&word, 2);
682e932d817SDavid Barksdale break;
683e932d817SDavid Barksdale case I2C_SMBUS_PROC_CALL:
684e932d817SDavid Barksdale size = I2C_SMBUS_WORD_DATA;
685e932d817SDavid Barksdale read_write = I2C_SMBUS_READ;
686e932d817SDavid Barksdale read_length = 2;
68729e2d6d1SEllen Wang word = cpu_to_le16(data->word);
688e932d817SDavid Barksdale
689e932d817SDavid Barksdale count = cp2112_write_read_req(buf, addr, read_length, command,
690e932d817SDavid Barksdale (u8 *)&word, 2);
691e932d817SDavid Barksdale break;
692e932d817SDavid Barksdale case I2C_SMBUS_I2C_BLOCK_DATA:
693542134c0SEudean Sun if (read_write == I2C_SMBUS_READ) {
694542134c0SEudean Sun read_length = data->block[0];
695542134c0SEudean Sun count = cp2112_write_read_req(buf, addr, read_length,
696542134c0SEudean Sun command, NULL, 0);
697542134c0SEudean Sun } else {
698542134c0SEudean Sun count = cp2112_write_req(buf, addr, command,
699542134c0SEudean Sun data->block + 1,
700542134c0SEudean Sun data->block[0]);
701542134c0SEudean Sun }
702542134c0SEudean Sun break;
703e932d817SDavid Barksdale case I2C_SMBUS_BLOCK_DATA:
704e932d817SDavid Barksdale if (I2C_SMBUS_READ == read_write) {
705e932d817SDavid Barksdale count = cp2112_write_read_req(buf, addr,
706e932d817SDavid Barksdale I2C_SMBUS_BLOCK_MAX,
707e932d817SDavid Barksdale command, NULL, 0);
708e932d817SDavid Barksdale } else {
709e932d817SDavid Barksdale count = cp2112_write_req(buf, addr, command,
710e932d817SDavid Barksdale data->block,
711e932d817SDavid Barksdale data->block[0] + 1);
712e932d817SDavid Barksdale }
713e932d817SDavid Barksdale break;
714e932d817SDavid Barksdale case I2C_SMBUS_BLOCK_PROC_CALL:
715e932d817SDavid Barksdale size = I2C_SMBUS_BLOCK_DATA;
716e932d817SDavid Barksdale read_write = I2C_SMBUS_READ;
717e932d817SDavid Barksdale
718e932d817SDavid Barksdale count = cp2112_write_read_req(buf, addr, I2C_SMBUS_BLOCK_MAX,
719e932d817SDavid Barksdale command, data->block,
720e932d817SDavid Barksdale data->block[0] + 1);
721e932d817SDavid Barksdale break;
722e932d817SDavid Barksdale default:
723e932d817SDavid Barksdale hid_warn(hdev, "Unsupported transaction %d\n", size);
724e932d817SDavid Barksdale return -EOPNOTSUPP;
725e932d817SDavid Barksdale }
726e932d817SDavid Barksdale
727e932d817SDavid Barksdale if (count < 0)
728e932d817SDavid Barksdale return count;
729e932d817SDavid Barksdale
730e932d817SDavid Barksdale ret = hid_hw_power(hdev, PM_HINT_FULLON);
731e932d817SDavid Barksdale if (ret < 0) {
732e932d817SDavid Barksdale hid_err(hdev, "power management error: %d\n", ret);
733e932d817SDavid Barksdale return ret;
734e932d817SDavid Barksdale }
735e932d817SDavid Barksdale
736e932d817SDavid Barksdale ret = cp2112_hid_output(hdev, buf, count, HID_OUTPUT_REPORT);
737e932d817SDavid Barksdale if (ret < 0) {
738e932d817SDavid Barksdale hid_warn(hdev, "Error starting transaction: %d\n", ret);
739e932d817SDavid Barksdale goto power_normal;
740e932d817SDavid Barksdale }
741e932d817SDavid Barksdale
742e932d817SDavid Barksdale for (retries = 0; retries < XFER_STATUS_RETRIES; ++retries) {
743e932d817SDavid Barksdale ret = cp2112_xfer_status(dev);
744e932d817SDavid Barksdale if (-EBUSY == ret)
745e932d817SDavid Barksdale continue;
746e932d817SDavid Barksdale if (ret < 0)
747e932d817SDavid Barksdale goto power_normal;
748e932d817SDavid Barksdale break;
749e932d817SDavid Barksdale }
750e932d817SDavid Barksdale
751e932d817SDavid Barksdale if (XFER_STATUS_RETRIES <= retries) {
752e932d817SDavid Barksdale hid_warn(hdev, "Transfer timed out, cancelling.\n");
753e932d817SDavid Barksdale buf[0] = CP2112_CANCEL_TRANSFER;
754e932d817SDavid Barksdale buf[1] = 0x01;
755e932d817SDavid Barksdale
756e932d817SDavid Barksdale ret = cp2112_hid_output(hdev, buf, 2, HID_OUTPUT_REPORT);
757e932d817SDavid Barksdale if (ret < 0)
758e932d817SDavid Barksdale hid_warn(hdev, "Error cancelling transaction: %d\n",
759e932d817SDavid Barksdale ret);
760e932d817SDavid Barksdale
761e932d817SDavid Barksdale ret = -ETIMEDOUT;
762e932d817SDavid Barksdale goto power_normal;
763e932d817SDavid Barksdale }
764e932d817SDavid Barksdale
765e932d817SDavid Barksdale if (I2C_SMBUS_WRITE == read_write) {
766e932d817SDavid Barksdale ret = 0;
767e932d817SDavid Barksdale goto power_normal;
768e932d817SDavid Barksdale }
769e932d817SDavid Barksdale
770e932d817SDavid Barksdale if (I2C_SMBUS_BLOCK_DATA == size)
771e932d817SDavid Barksdale read_length = ret;
772e932d817SDavid Barksdale
773e932d817SDavid Barksdale ret = cp2112_read(dev, buf, read_length);
774e932d817SDavid Barksdale if (ret < 0)
775e932d817SDavid Barksdale goto power_normal;
776e932d817SDavid Barksdale if (ret != read_length) {
7775a673fceSJiri Kosina hid_warn(hdev, "short read: %d < %zd\n", ret, read_length);
778e932d817SDavid Barksdale ret = -EIO;
779e932d817SDavid Barksdale goto power_normal;
780e932d817SDavid Barksdale }
781e932d817SDavid Barksdale
782e932d817SDavid Barksdale switch (size) {
783e932d817SDavid Barksdale case I2C_SMBUS_BYTE:
784e932d817SDavid Barksdale case I2C_SMBUS_BYTE_DATA:
785e932d817SDavid Barksdale data->byte = buf[0];
786e932d817SDavid Barksdale break;
787e932d817SDavid Barksdale case I2C_SMBUS_WORD_DATA:
78829e2d6d1SEllen Wang data->word = le16_to_cpup((__le16 *)buf);
789e932d817SDavid Barksdale break;
790542134c0SEudean Sun case I2C_SMBUS_I2C_BLOCK_DATA:
79138158384SHarshit Mogalapalli if (read_length > I2C_SMBUS_BLOCK_MAX) {
79238158384SHarshit Mogalapalli ret = -EINVAL;
79338158384SHarshit Mogalapalli goto power_normal;
79438158384SHarshit Mogalapalli }
79538158384SHarshit Mogalapalli
796542134c0SEudean Sun memcpy(data->block + 1, buf, read_length);
797542134c0SEudean Sun break;
798e932d817SDavid Barksdale case I2C_SMBUS_BLOCK_DATA:
799e932d817SDavid Barksdale if (read_length > I2C_SMBUS_BLOCK_MAX) {
800e932d817SDavid Barksdale ret = -EPROTO;
801e932d817SDavid Barksdale goto power_normal;
802e932d817SDavid Barksdale }
803e932d817SDavid Barksdale
804e932d817SDavid Barksdale memcpy(data->block, buf, read_length);
805e932d817SDavid Barksdale break;
806e932d817SDavid Barksdale }
807e932d817SDavid Barksdale
808e932d817SDavid Barksdale ret = 0;
809e932d817SDavid Barksdale power_normal:
810e932d817SDavid Barksdale hid_hw_power(hdev, PM_HINT_NORMAL);
811e932d817SDavid Barksdale hid_dbg(hdev, "transfer finished: %d\n", ret);
812e932d817SDavid Barksdale return ret;
813e932d817SDavid Barksdale }
814e932d817SDavid Barksdale
cp2112_functionality(struct i2c_adapter * adap)815e932d817SDavid Barksdale static u32 cp2112_functionality(struct i2c_adapter *adap)
816e932d817SDavid Barksdale {
817b9029345SAntonio Borneo return I2C_FUNC_I2C |
818b9029345SAntonio Borneo I2C_FUNC_SMBUS_BYTE |
819e932d817SDavid Barksdale I2C_FUNC_SMBUS_BYTE_DATA |
820e932d817SDavid Barksdale I2C_FUNC_SMBUS_WORD_DATA |
821e932d817SDavid Barksdale I2C_FUNC_SMBUS_BLOCK_DATA |
822e932d817SDavid Barksdale I2C_FUNC_SMBUS_I2C_BLOCK |
823e932d817SDavid Barksdale I2C_FUNC_SMBUS_PROC_CALL |
824e932d817SDavid Barksdale I2C_FUNC_SMBUS_BLOCK_PROC_CALL;
825e932d817SDavid Barksdale }
826e932d817SDavid Barksdale
827e932d817SDavid Barksdale static const struct i2c_algorithm smbus_algorithm = {
828b9029345SAntonio Borneo .master_xfer = cp2112_i2c_xfer,
829e932d817SDavid Barksdale .smbus_xfer = cp2112_xfer,
830e932d817SDavid Barksdale .functionality = cp2112_functionality,
831e932d817SDavid Barksdale };
832e932d817SDavid Barksdale
cp2112_get_usb_config(struct hid_device * hdev,struct cp2112_usb_config_report * cfg)833e932d817SDavid Barksdale static int cp2112_get_usb_config(struct hid_device *hdev,
834e932d817SDavid Barksdale struct cp2112_usb_config_report *cfg)
835e932d817SDavid Barksdale {
836e932d817SDavid Barksdale int ret;
837e932d817SDavid Barksdale
838e932d817SDavid Barksdale ret = cp2112_hid_get(hdev, CP2112_USB_CONFIG, (u8 *)cfg, sizeof(*cfg),
839e932d817SDavid Barksdale HID_FEATURE_REPORT);
840e932d817SDavid Barksdale if (ret != sizeof(*cfg)) {
841e932d817SDavid Barksdale hid_err(hdev, "error reading usb config: %d\n", ret);
842e932d817SDavid Barksdale if (ret < 0)
843e932d817SDavid Barksdale return ret;
844e932d817SDavid Barksdale return -EIO;
845e932d817SDavid Barksdale }
846e932d817SDavid Barksdale
847e932d817SDavid Barksdale return 0;
848e932d817SDavid Barksdale }
849e932d817SDavid Barksdale
cp2112_set_usb_config(struct hid_device * hdev,struct cp2112_usb_config_report * cfg)850e932d817SDavid Barksdale static int cp2112_set_usb_config(struct hid_device *hdev,
851e932d817SDavid Barksdale struct cp2112_usb_config_report *cfg)
852e932d817SDavid Barksdale {
853e932d817SDavid Barksdale int ret;
854e932d817SDavid Barksdale
855e932d817SDavid Barksdale BUG_ON(cfg->report != CP2112_USB_CONFIG);
856e932d817SDavid Barksdale
857e932d817SDavid Barksdale ret = cp2112_hid_output(hdev, (u8 *)cfg, sizeof(*cfg),
858e932d817SDavid Barksdale HID_FEATURE_REPORT);
859e932d817SDavid Barksdale if (ret != sizeof(*cfg)) {
860e932d817SDavid Barksdale hid_err(hdev, "error writing usb config: %d\n", ret);
861e932d817SDavid Barksdale if (ret < 0)
862e932d817SDavid Barksdale return ret;
863e932d817SDavid Barksdale return -EIO;
864e932d817SDavid Barksdale }
865e932d817SDavid Barksdale
866e932d817SDavid Barksdale return 0;
867e932d817SDavid Barksdale }
868e932d817SDavid Barksdale
869e932d817SDavid Barksdale static void chmod_sysfs_attrs(struct hid_device *hdev);
870e932d817SDavid Barksdale
871e932d817SDavid Barksdale #define CP2112_CONFIG_ATTR(name, store, format, ...) \
872e932d817SDavid Barksdale static ssize_t name##_store(struct device *kdev, \
873e932d817SDavid Barksdale struct device_attribute *attr, const char *buf, \
874e932d817SDavid Barksdale size_t count) \
875e932d817SDavid Barksdale { \
876ee79a8f8SGeliang Tang struct hid_device *hdev = to_hid_device(kdev); \
877e932d817SDavid Barksdale struct cp2112_usb_config_report cfg; \
878e932d817SDavid Barksdale int ret = cp2112_get_usb_config(hdev, &cfg); \
879e932d817SDavid Barksdale if (ret) \
880e932d817SDavid Barksdale return ret; \
881e932d817SDavid Barksdale store; \
882e932d817SDavid Barksdale ret = cp2112_set_usb_config(hdev, &cfg); \
883e932d817SDavid Barksdale if (ret) \
884e932d817SDavid Barksdale return ret; \
885e932d817SDavid Barksdale chmod_sysfs_attrs(hdev); \
886e932d817SDavid Barksdale return count; \
887e932d817SDavid Barksdale } \
888e932d817SDavid Barksdale static ssize_t name##_show(struct device *kdev, \
889e932d817SDavid Barksdale struct device_attribute *attr, char *buf) \
890e932d817SDavid Barksdale { \
891ee79a8f8SGeliang Tang struct hid_device *hdev = to_hid_device(kdev); \
892e932d817SDavid Barksdale struct cp2112_usb_config_report cfg; \
893e932d817SDavid Barksdale int ret = cp2112_get_usb_config(hdev, &cfg); \
894e932d817SDavid Barksdale if (ret) \
895e932d817SDavid Barksdale return ret; \
8965120bf04SAndy Shevchenko return sysfs_emit(buf, format, ##__VA_ARGS__); \
897e932d817SDavid Barksdale } \
898c3c041baSJiri Kosina static DEVICE_ATTR_RW(name);
899e932d817SDavid Barksdale
900e932d817SDavid Barksdale CP2112_CONFIG_ATTR(vendor_id, ({
901e932d817SDavid Barksdale u16 vid;
902e932d817SDavid Barksdale
903e932d817SDavid Barksdale if (sscanf(buf, "%hi", &vid) != 1)
904e932d817SDavid Barksdale return -EINVAL;
905e932d817SDavid Barksdale
906e932d817SDavid Barksdale cfg.vid = cpu_to_le16(vid);
907e932d817SDavid Barksdale cfg.mask = 0x01;
908e932d817SDavid Barksdale }), "0x%04x\n", le16_to_cpu(cfg.vid));
909e932d817SDavid Barksdale
910e932d817SDavid Barksdale CP2112_CONFIG_ATTR(product_id, ({
911e932d817SDavid Barksdale u16 pid;
912e932d817SDavid Barksdale
913e932d817SDavid Barksdale if (sscanf(buf, "%hi", &pid) != 1)
914e932d817SDavid Barksdale return -EINVAL;
915e932d817SDavid Barksdale
916e932d817SDavid Barksdale cfg.pid = cpu_to_le16(pid);
917e932d817SDavid Barksdale cfg.mask = 0x02;
918e932d817SDavid Barksdale }), "0x%04x\n", le16_to_cpu(cfg.pid));
919e932d817SDavid Barksdale
920e932d817SDavid Barksdale CP2112_CONFIG_ATTR(max_power, ({
921e932d817SDavid Barksdale int mA;
922e932d817SDavid Barksdale
923e932d817SDavid Barksdale if (sscanf(buf, "%i", &mA) != 1)
924e932d817SDavid Barksdale return -EINVAL;
925e932d817SDavid Barksdale
926e932d817SDavid Barksdale cfg.max_power = (mA + 1) / 2;
927e932d817SDavid Barksdale cfg.mask = 0x04;
928e932d817SDavid Barksdale }), "%u mA\n", cfg.max_power * 2);
929e932d817SDavid Barksdale
930e932d817SDavid Barksdale CP2112_CONFIG_ATTR(power_mode, ({
931e932d817SDavid Barksdale if (sscanf(buf, "%hhi", &cfg.power_mode) != 1)
932e932d817SDavid Barksdale return -EINVAL;
933e932d817SDavid Barksdale
934e932d817SDavid Barksdale cfg.mask = 0x08;
935e932d817SDavid Barksdale }), "%u\n", cfg.power_mode);
936e932d817SDavid Barksdale
937e932d817SDavid Barksdale CP2112_CONFIG_ATTR(release_version, ({
938e932d817SDavid Barksdale if (sscanf(buf, "%hhi.%hhi", &cfg.release_major, &cfg.release_minor)
939e932d817SDavid Barksdale != 2)
940e932d817SDavid Barksdale return -EINVAL;
941e932d817SDavid Barksdale
942e932d817SDavid Barksdale cfg.mask = 0x10;
943e932d817SDavid Barksdale }), "%u.%u\n", cfg.release_major, cfg.release_minor);
944e932d817SDavid Barksdale
945e932d817SDavid Barksdale #undef CP2112_CONFIG_ATTR
946e932d817SDavid Barksdale
pstr_store(struct device * kdev,struct device_attribute * kattr,const char * buf,size_t count,int number)9477f758125SAndy Shevchenko static ssize_t pstr_store(struct device *kdev, struct device_attribute *kattr,
9487f758125SAndy Shevchenko const char *buf, size_t count, int number)
949e932d817SDavid Barksdale {
950ee79a8f8SGeliang Tang struct hid_device *hdev = to_hid_device(kdev);
951e932d817SDavid Barksdale struct cp2112_string_report report;
952e932d817SDavid Barksdale int ret;
953e932d817SDavid Barksdale
954e932d817SDavid Barksdale memset(&report, 0, sizeof(report));
955e932d817SDavid Barksdale
956e932d817SDavid Barksdale ret = utf8s_to_utf16s(buf, count, UTF16_LITTLE_ENDIAN,
957e932d817SDavid Barksdale report.string, ARRAY_SIZE(report.string));
9587f758125SAndy Shevchenko report.report = number;
959e932d817SDavid Barksdale report.length = ret * sizeof(report.string[0]) + 2;
960e932d817SDavid Barksdale report.type = USB_DT_STRING;
961e932d817SDavid Barksdale
962e932d817SDavid Barksdale ret = cp2112_hid_output(hdev, &report.report, report.length + 1,
963e932d817SDavid Barksdale HID_FEATURE_REPORT);
964e932d817SDavid Barksdale if (ret != report.length + 1) {
965e932d817SDavid Barksdale hid_err(hdev, "error writing %s string: %d\n", kattr->attr.name,
966e932d817SDavid Barksdale ret);
967e932d817SDavid Barksdale if (ret < 0)
968e932d817SDavid Barksdale return ret;
969e932d817SDavid Barksdale return -EIO;
970e932d817SDavid Barksdale }
971e932d817SDavid Barksdale
972e932d817SDavid Barksdale chmod_sysfs_attrs(hdev);
973e932d817SDavid Barksdale return count;
974e932d817SDavid Barksdale }
975e932d817SDavid Barksdale
pstr_show(struct device * kdev,struct device_attribute * kattr,char * buf,int number)9767f758125SAndy Shevchenko static ssize_t pstr_show(struct device *kdev, struct device_attribute *kattr,
9777f758125SAndy Shevchenko char *buf, int number)
978e932d817SDavid Barksdale {
979ee79a8f8SGeliang Tang struct hid_device *hdev = to_hid_device(kdev);
980e932d817SDavid Barksdale struct cp2112_string_report report;
981e932d817SDavid Barksdale u8 length;
982e932d817SDavid Barksdale int ret;
983e932d817SDavid Barksdale
9847f758125SAndy Shevchenko ret = cp2112_hid_get(hdev, number, (u8 *)&report.contents,
9855e423a0cSKees Cook sizeof(report.contents), HID_FEATURE_REPORT);
986e932d817SDavid Barksdale if (ret < 3) {
987e932d817SDavid Barksdale hid_err(hdev, "error reading %s string: %d\n", kattr->attr.name,
988e932d817SDavid Barksdale ret);
989e932d817SDavid Barksdale if (ret < 0)
990e932d817SDavid Barksdale return ret;
991e932d817SDavid Barksdale return -EIO;
992e932d817SDavid Barksdale }
993e932d817SDavid Barksdale
994e932d817SDavid Barksdale if (report.length < 2) {
995e932d817SDavid Barksdale hid_err(hdev, "invalid %s string length: %d\n",
996e932d817SDavid Barksdale kattr->attr.name, report.length);
997e932d817SDavid Barksdale return -EIO;
998e932d817SDavid Barksdale }
999e932d817SDavid Barksdale
1000e932d817SDavid Barksdale length = report.length > ret - 1 ? ret - 1 : report.length;
1001e932d817SDavid Barksdale length = (length - 2) / sizeof(report.string[0]);
1002e932d817SDavid Barksdale ret = utf16s_to_utf8s(report.string, length, UTF16_LITTLE_ENDIAN, buf,
1003e932d817SDavid Barksdale PAGE_SIZE - 1);
1004e932d817SDavid Barksdale buf[ret++] = '\n';
1005e932d817SDavid Barksdale return ret;
1006e932d817SDavid Barksdale }
1007e932d817SDavid Barksdale
1008e932d817SDavid Barksdale #define CP2112_PSTR_ATTR(name, _report) \
10097f758125SAndy Shevchenko static ssize_t name##_store(struct device *kdev, struct device_attribute *kattr, \
10107f758125SAndy Shevchenko const char *buf, size_t count) \
10117f758125SAndy Shevchenko { \
10127f758125SAndy Shevchenko return pstr_store(kdev, kattr, buf, count, _report); \
10137f758125SAndy Shevchenko } \
10147f758125SAndy Shevchenko static ssize_t name##_show(struct device *kdev, struct device_attribute *kattr, char *buf) \
10157f758125SAndy Shevchenko { \
10167f758125SAndy Shevchenko return pstr_show(kdev, kattr, buf, _report); \
10177f758125SAndy Shevchenko } \
10187f758125SAndy Shevchenko static DEVICE_ATTR_RW(name);
1019e932d817SDavid Barksdale
1020e932d817SDavid Barksdale CP2112_PSTR_ATTR(manufacturer, CP2112_MANUFACTURER_STRING);
1021e932d817SDavid Barksdale CP2112_PSTR_ATTR(product, CP2112_PRODUCT_STRING);
1022e932d817SDavid Barksdale CP2112_PSTR_ATTR(serial, CP2112_SERIAL_STRING);
1023e932d817SDavid Barksdale
1024e932d817SDavid Barksdale #undef CP2112_PSTR_ATTR
1025e932d817SDavid Barksdale
1026e932d817SDavid Barksdale static const struct attribute_group cp2112_attr_group = {
1027e932d817SDavid Barksdale .attrs = (struct attribute *[]){
1028e932d817SDavid Barksdale &dev_attr_vendor_id.attr,
1029e932d817SDavid Barksdale &dev_attr_product_id.attr,
1030e932d817SDavid Barksdale &dev_attr_max_power.attr,
1031e932d817SDavid Barksdale &dev_attr_power_mode.attr,
1032e932d817SDavid Barksdale &dev_attr_release_version.attr,
10337f758125SAndy Shevchenko &dev_attr_manufacturer.attr,
10347f758125SAndy Shevchenko &dev_attr_product.attr,
10357f758125SAndy Shevchenko &dev_attr_serial.attr,
1036e932d817SDavid Barksdale NULL
1037e932d817SDavid Barksdale }
1038e932d817SDavid Barksdale };
1039e932d817SDavid Barksdale
1040e932d817SDavid Barksdale /* Chmoding our sysfs attributes is simply a way to expose which fields in the
1041e932d817SDavid Barksdale * PROM have already been programmed. We do not depend on this preventing
1042e932d817SDavid Barksdale * writing to these attributes since the CP2112 will simply ignore writes to
1043e932d817SDavid Barksdale * already-programmed fields. This is why there is no sense in fixing this
1044e932d817SDavid Barksdale * racy behaviour.
1045e932d817SDavid Barksdale */
chmod_sysfs_attrs(struct hid_device * hdev)1046e932d817SDavid Barksdale static void chmod_sysfs_attrs(struct hid_device *hdev)
1047e932d817SDavid Barksdale {
1048e932d817SDavid Barksdale struct attribute **attr;
1049e932d817SDavid Barksdale u8 buf[2];
1050e932d817SDavid Barksdale int ret;
1051e932d817SDavid Barksdale
1052e932d817SDavid Barksdale ret = cp2112_hid_get(hdev, CP2112_LOCK_BYTE, buf, sizeof(buf),
1053e932d817SDavid Barksdale HID_FEATURE_REPORT);
1054e932d817SDavid Barksdale if (ret != sizeof(buf)) {
1055e932d817SDavid Barksdale hid_err(hdev, "error reading lock byte: %d\n", ret);
1056e932d817SDavid Barksdale return;
1057e932d817SDavid Barksdale }
1058e932d817SDavid Barksdale
1059e932d817SDavid Barksdale for (attr = cp2112_attr_group.attrs; *attr; ++attr) {
1060a6a5ecccSAndy Shevchenko umode_t mode = (buf[1] & 1) ? 0644 : 0444;
1061e932d817SDavid Barksdale ret = sysfs_chmod_file(&hdev->dev.kobj, *attr, mode);
1062e932d817SDavid Barksdale if (ret < 0)
1063e932d817SDavid Barksdale hid_err(hdev, "error chmoding sysfs file %s\n",
1064e932d817SDavid Barksdale (*attr)->name);
1065e932d817SDavid Barksdale buf[1] >>= 1;
1066e932d817SDavid Barksdale }
1067e932d817SDavid Barksdale }
1068e932d817SDavid Barksdale
cp2112_gpio_irq_ack(struct irq_data * d)106913de9ccaSBenjamin Tissoires static void cp2112_gpio_irq_ack(struct irq_data *d)
107013de9ccaSBenjamin Tissoires {
107113de9ccaSBenjamin Tissoires }
107213de9ccaSBenjamin Tissoires
cp2112_gpio_irq_mask(struct irq_data * d)107313de9ccaSBenjamin Tissoires static void cp2112_gpio_irq_mask(struct irq_data *d)
107413de9ccaSBenjamin Tissoires {
107513de9ccaSBenjamin Tissoires struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
107613de9ccaSBenjamin Tissoires struct cp2112_device *dev = gpiochip_get_data(gc);
10773e2977c4SAndy Shevchenko irq_hw_number_t hwirq = irqd_to_hwirq(d);
107813de9ccaSBenjamin Tissoires
10793e2977c4SAndy Shevchenko __clear_bit(hwirq, &dev->irq_mask);
10803e2977c4SAndy Shevchenko gpiochip_disable_irq(gc, hwirq);
108113de9ccaSBenjamin Tissoires }
108213de9ccaSBenjamin Tissoires
cp2112_gpio_irq_unmask(struct irq_data * d)108313de9ccaSBenjamin Tissoires static void cp2112_gpio_irq_unmask(struct irq_data *d)
108413de9ccaSBenjamin Tissoires {
108513de9ccaSBenjamin Tissoires struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
108613de9ccaSBenjamin Tissoires struct cp2112_device *dev = gpiochip_get_data(gc);
10873e2977c4SAndy Shevchenko irq_hw_number_t hwirq = irqd_to_hwirq(d);
108813de9ccaSBenjamin Tissoires
10893e2977c4SAndy Shevchenko gpiochip_enable_irq(gc, hwirq);
10903e2977c4SAndy Shevchenko __set_bit(hwirq, &dev->irq_mask);
109113de9ccaSBenjamin Tissoires }
109213de9ccaSBenjamin Tissoires
cp2112_gpio_poll_callback(struct work_struct * work)109313de9ccaSBenjamin Tissoires static void cp2112_gpio_poll_callback(struct work_struct *work)
109413de9ccaSBenjamin Tissoires {
109513de9ccaSBenjamin Tissoires struct cp2112_device *dev = container_of(work, struct cp2112_device,
109613de9ccaSBenjamin Tissoires gpio_poll_worker.work);
109713de9ccaSBenjamin Tissoires struct irq_data *d;
109813de9ccaSBenjamin Tissoires u8 gpio_mask;
109913de9ccaSBenjamin Tissoires u32 irq_type;
110013de9ccaSBenjamin Tissoires int irq, virq, ret;
110113de9ccaSBenjamin Tissoires
110213de9ccaSBenjamin Tissoires ret = cp2112_gpio_get_all(&dev->gc);
110313de9ccaSBenjamin Tissoires if (ret == -ENODEV) /* the hardware has been disconnected */
110413de9ccaSBenjamin Tissoires return;
110513de9ccaSBenjamin Tissoires if (ret < 0)
110613de9ccaSBenjamin Tissoires goto exit;
110713de9ccaSBenjamin Tissoires
110813de9ccaSBenjamin Tissoires gpio_mask = ret;
1109e7378e09SAndy Shevchenko for_each_set_bit(virq, &dev->irq_mask, CP2112_GPIO_MAX_GPIO) {
1110b5ac0088SAndy Shevchenko irq = irq_find_mapping(dev->gc.irq.domain, virq);
1111b5ac0088SAndy Shevchenko if (!irq)
1112b5ac0088SAndy Shevchenko continue;
111313de9ccaSBenjamin Tissoires
111413de9ccaSBenjamin Tissoires d = irq_get_irq_data(irq);
111513de9ccaSBenjamin Tissoires if (!d)
111613de9ccaSBenjamin Tissoires continue;
111713de9ccaSBenjamin Tissoires
111813de9ccaSBenjamin Tissoires irq_type = irqd_get_trigger_type(d);
111913de9ccaSBenjamin Tissoires
112013de9ccaSBenjamin Tissoires if (gpio_mask & BIT(virq)) {
112113de9ccaSBenjamin Tissoires /* Level High */
112213de9ccaSBenjamin Tissoires
112313de9ccaSBenjamin Tissoires if (irq_type & IRQ_TYPE_LEVEL_HIGH)
112413de9ccaSBenjamin Tissoires handle_nested_irq(irq);
112513de9ccaSBenjamin Tissoires
112613de9ccaSBenjamin Tissoires if ((irq_type & IRQ_TYPE_EDGE_RISING) &&
112713de9ccaSBenjamin Tissoires !(dev->gpio_prev_state & BIT(virq)))
112813de9ccaSBenjamin Tissoires handle_nested_irq(irq);
112913de9ccaSBenjamin Tissoires } else {
113013de9ccaSBenjamin Tissoires /* Level Low */
113113de9ccaSBenjamin Tissoires
113213de9ccaSBenjamin Tissoires if (irq_type & IRQ_TYPE_LEVEL_LOW)
113313de9ccaSBenjamin Tissoires handle_nested_irq(irq);
113413de9ccaSBenjamin Tissoires
113513de9ccaSBenjamin Tissoires if ((irq_type & IRQ_TYPE_EDGE_FALLING) &&
113613de9ccaSBenjamin Tissoires (dev->gpio_prev_state & BIT(virq)))
113713de9ccaSBenjamin Tissoires handle_nested_irq(irq);
113813de9ccaSBenjamin Tissoires }
113913de9ccaSBenjamin Tissoires }
114013de9ccaSBenjamin Tissoires
114113de9ccaSBenjamin Tissoires dev->gpio_prev_state = gpio_mask;
114213de9ccaSBenjamin Tissoires
114313de9ccaSBenjamin Tissoires exit:
114413de9ccaSBenjamin Tissoires if (dev->gpio_poll)
114513de9ccaSBenjamin Tissoires schedule_delayed_work(&dev->gpio_poll_worker, 10);
114613de9ccaSBenjamin Tissoires }
114713de9ccaSBenjamin Tissoires
114813de9ccaSBenjamin Tissoires
cp2112_gpio_irq_startup(struct irq_data * d)114913de9ccaSBenjamin Tissoires static unsigned int cp2112_gpio_irq_startup(struct irq_data *d)
115013de9ccaSBenjamin Tissoires {
115113de9ccaSBenjamin Tissoires struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
115213de9ccaSBenjamin Tissoires struct cp2112_device *dev = gpiochip_get_data(gc);
115313de9ccaSBenjamin Tissoires
115413de9ccaSBenjamin Tissoires if (!dev->gpio_poll) {
115513de9ccaSBenjamin Tissoires dev->gpio_poll = true;
115613de9ccaSBenjamin Tissoires schedule_delayed_work(&dev->gpio_poll_worker, 0);
115713de9ccaSBenjamin Tissoires }
115813de9ccaSBenjamin Tissoires
115913de9ccaSBenjamin Tissoires cp2112_gpio_irq_unmask(d);
116013de9ccaSBenjamin Tissoires return 0;
116113de9ccaSBenjamin Tissoires }
116213de9ccaSBenjamin Tissoires
cp2112_gpio_irq_shutdown(struct irq_data * d)116313de9ccaSBenjamin Tissoires static void cp2112_gpio_irq_shutdown(struct irq_data *d)
116413de9ccaSBenjamin Tissoires {
116513de9ccaSBenjamin Tissoires struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
116613de9ccaSBenjamin Tissoires struct cp2112_device *dev = gpiochip_get_data(gc);
116713de9ccaSBenjamin Tissoires
11683e2977c4SAndy Shevchenko cp2112_gpio_irq_mask(d);
1169*e2847849SDanny Kaehn
1170*e2847849SDanny Kaehn if (!dev->irq_mask) {
1171*e2847849SDanny Kaehn dev->gpio_poll = false;
117213de9ccaSBenjamin Tissoires cancel_delayed_work_sync(&dev->gpio_poll_worker);
117313de9ccaSBenjamin Tissoires }
1174*e2847849SDanny Kaehn }
117513de9ccaSBenjamin Tissoires
cp2112_gpio_irq_type(struct irq_data * d,unsigned int type)117613de9ccaSBenjamin Tissoires static int cp2112_gpio_irq_type(struct irq_data *d, unsigned int type)
117713de9ccaSBenjamin Tissoires {
117813de9ccaSBenjamin Tissoires return 0;
117913de9ccaSBenjamin Tissoires }
118013de9ccaSBenjamin Tissoires
11813e2977c4SAndy Shevchenko static const struct irq_chip cp2112_gpio_irqchip = {
11823e2977c4SAndy Shevchenko .name = "cp2112-gpio",
11833e2977c4SAndy Shevchenko .irq_startup = cp2112_gpio_irq_startup,
11843e2977c4SAndy Shevchenko .irq_shutdown = cp2112_gpio_irq_shutdown,
11853e2977c4SAndy Shevchenko .irq_ack = cp2112_gpio_irq_ack,
11863e2977c4SAndy Shevchenko .irq_mask = cp2112_gpio_irq_mask,
11873e2977c4SAndy Shevchenko .irq_unmask = cp2112_gpio_irq_unmask,
11883e2977c4SAndy Shevchenko .irq_set_type = cp2112_gpio_irq_type,
11893e2977c4SAndy Shevchenko .flags = IRQCHIP_MASK_ON_SUSPEND | IRQCHIP_IMMUTABLE,
11903e2977c4SAndy Shevchenko GPIOCHIP_IRQ_RESOURCE_HELPERS,
11913e2977c4SAndy Shevchenko };
11923e2977c4SAndy Shevchenko
cp2112_probe(struct hid_device * hdev,const struct hid_device_id * id)1193e932d817SDavid Barksdale static int cp2112_probe(struct hid_device *hdev, const struct hid_device_id *id)
1194e932d817SDavid Barksdale {
1195e932d817SDavid Barksdale struct cp2112_device *dev;
1196e932d817SDavid Barksdale u8 buf[3];
1197e932d817SDavid Barksdale struct cp2112_smbus_config_report config;
11986bfa3175SLinus Walleij struct gpio_irq_chip *girq;
1199e932d817SDavid Barksdale int ret;
1200e932d817SDavid Barksdale
12011ffb3c40SBenjamin Tissoires dev = devm_kzalloc(&hdev->dev, sizeof(*dev), GFP_KERNEL);
12021ffb3c40SBenjamin Tissoires if (!dev)
12031ffb3c40SBenjamin Tissoires return -ENOMEM;
12041ffb3c40SBenjamin Tissoires
12051ffb3c40SBenjamin Tissoires dev->in_out_buffer = devm_kzalloc(&hdev->dev, CP2112_REPORT_MAX_LENGTH,
12061ffb3c40SBenjamin Tissoires GFP_KERNEL);
12071ffb3c40SBenjamin Tissoires if (!dev->in_out_buffer)
12081ffb3c40SBenjamin Tissoires return -ENOMEM;
12091ffb3c40SBenjamin Tissoires
12107a7b5df8SJohan Hovold mutex_init(&dev->lock);
12111ffb3c40SBenjamin Tissoires
1212e932d817SDavid Barksdale ret = hid_parse(hdev);
1213e932d817SDavid Barksdale if (ret) {
1214e932d817SDavid Barksdale hid_err(hdev, "parse failed\n");
1215e932d817SDavid Barksdale return ret;
1216e932d817SDavid Barksdale }
1217e932d817SDavid Barksdale
1218e932d817SDavid Barksdale ret = hid_hw_start(hdev, HID_CONNECT_HIDRAW);
1219e932d817SDavid Barksdale if (ret) {
1220e932d817SDavid Barksdale hid_err(hdev, "hw start failed\n");
1221e932d817SDavid Barksdale return ret;
1222e932d817SDavid Barksdale }
1223e932d817SDavid Barksdale
1224e932d817SDavid Barksdale ret = hid_hw_open(hdev);
1225e932d817SDavid Barksdale if (ret) {
1226e932d817SDavid Barksdale hid_err(hdev, "hw open failed\n");
1227e932d817SDavid Barksdale goto err_hid_stop;
1228e932d817SDavid Barksdale }
1229e932d817SDavid Barksdale
1230e932d817SDavid Barksdale ret = hid_hw_power(hdev, PM_HINT_FULLON);
1231e932d817SDavid Barksdale if (ret < 0) {
1232e932d817SDavid Barksdale hid_err(hdev, "power management error: %d\n", ret);
1233e932d817SDavid Barksdale goto err_hid_close;
1234e932d817SDavid Barksdale }
1235e932d817SDavid Barksdale
1236e932d817SDavid Barksdale ret = cp2112_hid_get(hdev, CP2112_GET_VERSION_INFO, buf, sizeof(buf),
1237e932d817SDavid Barksdale HID_FEATURE_REPORT);
1238e932d817SDavid Barksdale if (ret != sizeof(buf)) {
1239e932d817SDavid Barksdale hid_err(hdev, "error requesting version\n");
1240e932d817SDavid Barksdale if (ret >= 0)
1241e932d817SDavid Barksdale ret = -EIO;
1242e932d817SDavid Barksdale goto err_power_normal;
1243e932d817SDavid Barksdale }
1244e932d817SDavid Barksdale
1245e932d817SDavid Barksdale hid_info(hdev, "Part Number: 0x%02X Device Version: 0x%02X\n",
1246e932d817SDavid Barksdale buf[1], buf[2]);
1247e932d817SDavid Barksdale
1248e932d817SDavid Barksdale ret = cp2112_hid_get(hdev, CP2112_SMBUS_CONFIG, (u8 *)&config,
1249e932d817SDavid Barksdale sizeof(config), HID_FEATURE_REPORT);
1250e932d817SDavid Barksdale if (ret != sizeof(config)) {
1251e932d817SDavid Barksdale hid_err(hdev, "error requesting SMBus config\n");
1252e932d817SDavid Barksdale if (ret >= 0)
1253e932d817SDavid Barksdale ret = -EIO;
1254e932d817SDavid Barksdale goto err_power_normal;
1255e932d817SDavid Barksdale }
1256e932d817SDavid Barksdale
1257e932d817SDavid Barksdale config.retry_time = cpu_to_be16(1);
1258e932d817SDavid Barksdale
1259e932d817SDavid Barksdale ret = cp2112_hid_output(hdev, (u8 *)&config, sizeof(config),
1260e932d817SDavid Barksdale HID_FEATURE_REPORT);
1261e932d817SDavid Barksdale if (ret != sizeof(config)) {
1262e932d817SDavid Barksdale hid_err(hdev, "error setting SMBus config\n");
1263e932d817SDavid Barksdale if (ret >= 0)
1264e932d817SDavid Barksdale ret = -EIO;
1265e932d817SDavid Barksdale goto err_power_normal;
1266e932d817SDavid Barksdale }
1267e932d817SDavid Barksdale
1268e932d817SDavid Barksdale hid_set_drvdata(hdev, (void *)dev);
1269e932d817SDavid Barksdale dev->hdev = hdev;
1270e932d817SDavid Barksdale dev->adap.owner = THIS_MODULE;
1271e932d817SDavid Barksdale dev->adap.class = I2C_CLASS_HWMON;
1272e932d817SDavid Barksdale dev->adap.algo = &smbus_algorithm;
1273e932d817SDavid Barksdale dev->adap.algo_data = dev;
1274e932d817SDavid Barksdale dev->adap.dev.parent = &hdev->dev;
1275e932d817SDavid Barksdale snprintf(dev->adap.name, sizeof(dev->adap.name),
127642cb6b35SJaejoong Kim "CP2112 SMBus Bridge on hidraw%d",
127742cb6b35SJaejoong Kim ((struct hidraw *)hdev->hidraw)->minor);
127844eda784SEllen Wang dev->hwversion = buf[2];
1279e932d817SDavid Barksdale init_waitqueue_head(&dev->wait);
1280e932d817SDavid Barksdale
1281e932d817SDavid Barksdale hid_device_io_start(hdev);
1282e932d817SDavid Barksdale ret = i2c_add_adapter(&dev->adap);
1283e932d817SDavid Barksdale hid_device_io_stop(hdev);
1284e932d817SDavid Barksdale
1285e932d817SDavid Barksdale if (ret) {
1286e932d817SDavid Barksdale hid_err(hdev, "error registering i2c adapter\n");
12871ffb3c40SBenjamin Tissoires goto err_power_normal;
1288e932d817SDavid Barksdale }
1289e932d817SDavid Barksdale
1290e932d817SDavid Barksdale hid_dbg(hdev, "adapter registered\n");
1291e932d817SDavid Barksdale
1292e932d817SDavid Barksdale dev->gc.label = "cp2112_gpio";
1293e932d817SDavid Barksdale dev->gc.direction_input = cp2112_gpio_direction_input;
1294e932d817SDavid Barksdale dev->gc.direction_output = cp2112_gpio_direction_output;
1295e932d817SDavid Barksdale dev->gc.set = cp2112_gpio_set;
1296e932d817SDavid Barksdale dev->gc.get = cp2112_gpio_get;
1297e932d817SDavid Barksdale dev->gc.base = -1;
1298e7378e09SAndy Shevchenko dev->gc.ngpio = CP2112_GPIO_MAX_GPIO;
1299e932d817SDavid Barksdale dev->gc.can_sleep = 1;
130058383c78SLinus Walleij dev->gc.parent = &hdev->dev;
1301e932d817SDavid Barksdale
13026bfa3175SLinus Walleij girq = &dev->gc.irq;
13033e2977c4SAndy Shevchenko gpio_irq_chip_set_chip(girq, &cp2112_gpio_irqchip);
13046bfa3175SLinus Walleij /* The event comes from the outside so no parent handler */
13056bfa3175SLinus Walleij girq->parent_handler = NULL;
13066bfa3175SLinus Walleij girq->num_parents = 0;
13076bfa3175SLinus Walleij girq->parents = NULL;
13086bfa3175SLinus Walleij girq->default_type = IRQ_TYPE_NONE;
13096bfa3175SLinus Walleij girq->handler = handle_simple_irq;
131037f5b858SDanny Kaehn girq->threaded = true;
13116bfa3175SLinus Walleij
1312eb1121faSDanny Kaehn INIT_DELAYED_WORK(&dev->gpio_poll_worker, cp2112_gpio_poll_callback);
1313eb1121faSDanny Kaehn
131447513c2fSLinus Walleij ret = gpiochip_add_data(&dev->gc, dev);
1315e932d817SDavid Barksdale if (ret < 0) {
1316e932d817SDavid Barksdale hid_err(hdev, "error registering gpio chip\n");
1317e932d817SDavid Barksdale goto err_free_i2c;
1318e932d817SDavid Barksdale }
1319e932d817SDavid Barksdale
1320e932d817SDavid Barksdale ret = sysfs_create_group(&hdev->dev.kobj, &cp2112_attr_group);
1321e932d817SDavid Barksdale if (ret < 0) {
1322e932d817SDavid Barksdale hid_err(hdev, "error creating sysfs attrs\n");
1323e932d817SDavid Barksdale goto err_gpiochip_remove;
1324e932d817SDavid Barksdale }
1325e932d817SDavid Barksdale
1326e932d817SDavid Barksdale chmod_sysfs_attrs(hdev);
1327e932d817SDavid Barksdale hid_hw_power(hdev, PM_HINT_NORMAL);
1328e932d817SDavid Barksdale
1329e932d817SDavid Barksdale return ret;
1330e932d817SDavid Barksdale
1331e932d817SDavid Barksdale err_gpiochip_remove:
133288d5e520Sabdoulaye berthe gpiochip_remove(&dev->gc);
1333e932d817SDavid Barksdale err_free_i2c:
1334e932d817SDavid Barksdale i2c_del_adapter(&dev->adap);
1335e932d817SDavid Barksdale err_power_normal:
1336e932d817SDavid Barksdale hid_hw_power(hdev, PM_HINT_NORMAL);
1337e932d817SDavid Barksdale err_hid_close:
1338e932d817SDavid Barksdale hid_hw_close(hdev);
1339e932d817SDavid Barksdale err_hid_stop:
1340e932d817SDavid Barksdale hid_hw_stop(hdev);
1341e932d817SDavid Barksdale return ret;
1342e932d817SDavid Barksdale }
1343e932d817SDavid Barksdale
cp2112_remove(struct hid_device * hdev)1344e932d817SDavid Barksdale static void cp2112_remove(struct hid_device *hdev)
1345e932d817SDavid Barksdale {
1346e932d817SDavid Barksdale struct cp2112_device *dev = hid_get_drvdata(hdev);
1347e932d817SDavid Barksdale
1348e932d817SDavid Barksdale sysfs_remove_group(&hdev->dev.kobj, &cp2112_attr_group);
1349e932d817SDavid Barksdale i2c_del_adapter(&dev->adap);
135013de9ccaSBenjamin Tissoires
135113de9ccaSBenjamin Tissoires if (dev->gpio_poll) {
135213de9ccaSBenjamin Tissoires dev->gpio_poll = false;
135313de9ccaSBenjamin Tissoires cancel_delayed_work_sync(&dev->gpio_poll_worker);
135413de9ccaSBenjamin Tissoires }
135513de9ccaSBenjamin Tissoires
135613de9ccaSBenjamin Tissoires gpiochip_remove(&dev->gc);
1357e932d817SDavid Barksdale /* i2c_del_adapter has finished removing all i2c devices from our
1358e932d817SDavid Barksdale * adapter. Well behaved devices should no longer call our cp2112_xfer
1359e932d817SDavid Barksdale * and should have waited for any pending calls to finish. It has also
1360e932d817SDavid Barksdale * waited for device_unregister(&adap->dev) to complete. Therefore we
1361e932d817SDavid Barksdale * can safely free our struct cp2112_device.
1362e932d817SDavid Barksdale */
1363e932d817SDavid Barksdale hid_hw_close(hdev);
1364e932d817SDavid Barksdale hid_hw_stop(hdev);
1365e932d817SDavid Barksdale }
1366e932d817SDavid Barksdale
cp2112_raw_event(struct hid_device * hdev,struct hid_report * report,u8 * data,int size)1367e932d817SDavid Barksdale static int cp2112_raw_event(struct hid_device *hdev, struct hid_report *report,
1368e932d817SDavid Barksdale u8 *data, int size)
1369e932d817SDavid Barksdale {
1370e932d817SDavid Barksdale struct cp2112_device *dev = hid_get_drvdata(hdev);
1371e932d817SDavid Barksdale struct cp2112_xfer_status_report *xfer = (void *)data;
1372e932d817SDavid Barksdale
1373e932d817SDavid Barksdale switch (data[0]) {
1374e932d817SDavid Barksdale case CP2112_TRANSFER_STATUS_RESPONSE:
1375e932d817SDavid Barksdale hid_dbg(hdev, "xfer status: %02x %02x %04x %04x\n",
1376e932d817SDavid Barksdale xfer->status0, xfer->status1,
1377e932d817SDavid Barksdale be16_to_cpu(xfer->retries), be16_to_cpu(xfer->length));
1378e932d817SDavid Barksdale
1379e932d817SDavid Barksdale switch (xfer->status0) {
1380e932d817SDavid Barksdale case STATUS0_IDLE:
1381e932d817SDavid Barksdale dev->xfer_status = -EAGAIN;
1382e932d817SDavid Barksdale break;
1383e932d817SDavid Barksdale case STATUS0_BUSY:
1384e932d817SDavid Barksdale dev->xfer_status = -EBUSY;
1385e932d817SDavid Barksdale break;
1386e932d817SDavid Barksdale case STATUS0_COMPLETE:
1387e932d817SDavid Barksdale dev->xfer_status = be16_to_cpu(xfer->length);
1388e932d817SDavid Barksdale break;
1389e932d817SDavid Barksdale case STATUS0_ERROR:
1390e932d817SDavid Barksdale switch (xfer->status1) {
1391e932d817SDavid Barksdale case STATUS1_TIMEOUT_NACK:
1392e932d817SDavid Barksdale case STATUS1_TIMEOUT_BUS:
1393e932d817SDavid Barksdale dev->xfer_status = -ETIMEDOUT;
1394e932d817SDavid Barksdale break;
1395e932d817SDavid Barksdale default:
1396e932d817SDavid Barksdale dev->xfer_status = -EIO;
1397e932d817SDavid Barksdale break;
1398e932d817SDavid Barksdale }
1399e932d817SDavid Barksdale break;
1400e932d817SDavid Barksdale default:
1401e932d817SDavid Barksdale dev->xfer_status = -EINVAL;
1402e932d817SDavid Barksdale break;
1403e932d817SDavid Barksdale }
1404e932d817SDavid Barksdale
1405e932d817SDavid Barksdale atomic_set(&dev->xfer_avail, 1);
1406e932d817SDavid Barksdale break;
1407e932d817SDavid Barksdale case CP2112_DATA_READ_RESPONSE:
1408e932d817SDavid Barksdale hid_dbg(hdev, "read response: %02x %02x\n", data[1], data[2]);
1409e932d817SDavid Barksdale
1410e932d817SDavid Barksdale dev->read_length = data[2];
1411e932d817SDavid Barksdale if (dev->read_length > sizeof(dev->read_data))
1412e932d817SDavid Barksdale dev->read_length = sizeof(dev->read_data);
1413e932d817SDavid Barksdale
1414e932d817SDavid Barksdale memcpy(dev->read_data, &data[3], dev->read_length);
1415e932d817SDavid Barksdale atomic_set(&dev->read_avail, 1);
1416e932d817SDavid Barksdale break;
1417e932d817SDavid Barksdale default:
1418e932d817SDavid Barksdale hid_err(hdev, "unknown report\n");
1419e932d817SDavid Barksdale
1420e932d817SDavid Barksdale return 0;
1421e932d817SDavid Barksdale }
1422e932d817SDavid Barksdale
1423e932d817SDavid Barksdale wake_up_interruptible(&dev->wait);
1424e932d817SDavid Barksdale return 1;
1425e932d817SDavid Barksdale }
1426e932d817SDavid Barksdale
1427e932d817SDavid Barksdale static struct hid_driver cp2112_driver = {
1428e932d817SDavid Barksdale .name = "cp2112",
1429e932d817SDavid Barksdale .id_table = cp2112_devices,
1430e932d817SDavid Barksdale .probe = cp2112_probe,
1431e932d817SDavid Barksdale .remove = cp2112_remove,
1432e932d817SDavid Barksdale .raw_event = cp2112_raw_event,
1433e932d817SDavid Barksdale };
1434e932d817SDavid Barksdale
1435e932d817SDavid Barksdale module_hid_driver(cp2112_driver);
1436e932d817SDavid Barksdale MODULE_DESCRIPTION("Silicon Labs HID USB to SMBus master bridge");
1437e932d817SDavid Barksdale MODULE_AUTHOR("David Barksdale <dbarksdale@uplogix.com>");
1438e932d817SDavid Barksdale MODULE_LICENSE("GPL");
1439e932d817SDavid Barksdale
1440