diff options
Diffstat (limited to 'drivers/hid/hid-cp2112.c')
-rw-r--r-- | drivers/hid/hid-cp2112.c | 111 |
1 files changed, 108 insertions, 3 deletions
diff --git a/drivers/hid/hid-cp2112.c b/drivers/hid/hid-cp2112.c index 56be85a9a77c..a822db5a8338 100644 --- a/drivers/hid/hid-cp2112.c +++ b/drivers/hid/hid-cp2112.c | |||
@@ -240,8 +240,6 @@ static int cp2112_gpio_direction_output(struct gpio_chip *chip, | |||
240 | u8 buf[5]; | 240 | u8 buf[5]; |
241 | int ret; | 241 | int ret; |
242 | 242 | ||
243 | cp2112_gpio_set(chip, offset, value); | ||
244 | |||
245 | ret = hid_hw_raw_request(hdev, CP2112_GPIO_CONFIG, buf, | 243 | ret = hid_hw_raw_request(hdev, CP2112_GPIO_CONFIG, buf, |
246 | sizeof(buf), HID_FEATURE_REPORT, | 244 | sizeof(buf), HID_FEATURE_REPORT, |
247 | HID_REQ_GET_REPORT); | 245 | HID_REQ_GET_REPORT); |
@@ -260,6 +258,12 @@ static int cp2112_gpio_direction_output(struct gpio_chip *chip, | |||
260 | return ret; | 258 | return ret; |
261 | } | 259 | } |
262 | 260 | ||
261 | /* | ||
262 | * Set gpio value when output direction is already set, | ||
263 | * as specified in AN495, Rev. 0.2, cpt. 4.4 | ||
264 | */ | ||
265 | cp2112_gpio_set(chip, offset, value); | ||
266 | |||
263 | return 0; | 267 | return 0; |
264 | } | 268 | } |
265 | 269 | ||
@@ -425,6 +429,105 @@ static int cp2112_write_req(void *buf, u8 slave_address, u8 command, u8 *data, | |||
425 | return data_length + 4; | 429 | return data_length + 4; |
426 | } | 430 | } |
427 | 431 | ||
432 | static int cp2112_i2c_write_req(void *buf, u8 slave_address, u8 *data, | ||
433 | u8 data_length) | ||
434 | { | ||
435 | struct cp2112_write_req_report *report = buf; | ||
436 | |||
437 | if (data_length > sizeof(report->data)) | ||
438 | return -EINVAL; | ||
439 | |||
440 | report->report = CP2112_DATA_WRITE_REQUEST; | ||
441 | report->slave_address = slave_address << 1; | ||
442 | report->length = data_length; | ||
443 | memcpy(report->data, data, data_length); | ||
444 | return data_length + 3; | ||
445 | } | ||
446 | |||
447 | static int cp2112_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, | ||
448 | int num) | ||
449 | { | ||
450 | struct cp2112_device *dev = (struct cp2112_device *)adap->algo_data; | ||
451 | struct hid_device *hdev = dev->hdev; | ||
452 | u8 buf[64]; | ||
453 | ssize_t count; | ||
454 | unsigned int retries; | ||
455 | int ret; | ||
456 | |||
457 | hid_dbg(hdev, "I2C %d messages\n", num); | ||
458 | |||
459 | if (num != 1) { | ||
460 | hid_err(hdev, | ||
461 | "Multi-message I2C transactions not supported\n"); | ||
462 | return -EOPNOTSUPP; | ||
463 | } | ||
464 | |||
465 | if (msgs->flags & I2C_M_RD) | ||
466 | count = cp2112_read_req(buf, msgs->addr, msgs->len); | ||
467 | else | ||
468 | count = cp2112_i2c_write_req(buf, msgs->addr, msgs->buf, | ||
469 | msgs->len); | ||
470 | |||
471 | if (count < 0) | ||
472 | return count; | ||
473 | |||
474 | ret = hid_hw_power(hdev, PM_HINT_FULLON); | ||
475 | if (ret < 0) { | ||
476 | hid_err(hdev, "power management error: %d\n", ret); | ||
477 | return ret; | ||
478 | } | ||
479 | |||
480 | ret = cp2112_hid_output(hdev, buf, count, HID_OUTPUT_REPORT); | ||
481 | if (ret < 0) { | ||
482 | hid_warn(hdev, "Error starting transaction: %d\n", ret); | ||
483 | goto power_normal; | ||
484 | } | ||
485 | |||
486 | for (retries = 0; retries < XFER_STATUS_RETRIES; ++retries) { | ||
487 | ret = cp2112_xfer_status(dev); | ||
488 | if (-EBUSY == ret) | ||
489 | continue; | ||
490 | if (ret < 0) | ||
491 | goto power_normal; | ||
492 | break; | ||
493 | } | ||
494 | |||
495 | if (XFER_STATUS_RETRIES <= retries) { | ||
496 | hid_warn(hdev, "Transfer timed out, cancelling.\n"); | ||
497 | buf[0] = CP2112_CANCEL_TRANSFER; | ||
498 | buf[1] = 0x01; | ||
499 | |||
500 | ret = cp2112_hid_output(hdev, buf, 2, HID_OUTPUT_REPORT); | ||
501 | if (ret < 0) | ||
502 | hid_warn(hdev, "Error cancelling transaction: %d\n", | ||
503 | ret); | ||
504 | |||
505 | ret = -ETIMEDOUT; | ||
506 | goto power_normal; | ||
507 | } | ||
508 | |||
509 | if (!(msgs->flags & I2C_M_RD)) | ||
510 | goto finish; | ||
511 | |||
512 | ret = cp2112_read(dev, msgs->buf, msgs->len); | ||
513 | if (ret < 0) | ||
514 | goto power_normal; | ||
515 | if (ret != msgs->len) { | ||
516 | hid_warn(hdev, "short read: %d < %d\n", ret, msgs->len); | ||
517 | ret = -EIO; | ||
518 | goto power_normal; | ||
519 | } | ||
520 | |||
521 | finish: | ||
522 | /* return the number of transferred messages */ | ||
523 | ret = 1; | ||
524 | |||
525 | power_normal: | ||
526 | hid_hw_power(hdev, PM_HINT_NORMAL); | ||
527 | hid_dbg(hdev, "I2C transfer finished: %d\n", ret); | ||
528 | return ret; | ||
529 | } | ||
530 | |||
428 | static int cp2112_xfer(struct i2c_adapter *adap, u16 addr, | 531 | static int cp2112_xfer(struct i2c_adapter *adap, u16 addr, |
429 | unsigned short flags, char read_write, u8 command, | 532 | unsigned short flags, char read_write, u8 command, |
430 | int size, union i2c_smbus_data *data) | 533 | int size, union i2c_smbus_data *data) |
@@ -591,7 +694,8 @@ power_normal: | |||
591 | 694 | ||
592 | static u32 cp2112_functionality(struct i2c_adapter *adap) | 695 | static u32 cp2112_functionality(struct i2c_adapter *adap) |
593 | { | 696 | { |
594 | return I2C_FUNC_SMBUS_BYTE | | 697 | return I2C_FUNC_I2C | |
698 | I2C_FUNC_SMBUS_BYTE | | ||
595 | I2C_FUNC_SMBUS_BYTE_DATA | | 699 | I2C_FUNC_SMBUS_BYTE_DATA | |
596 | I2C_FUNC_SMBUS_WORD_DATA | | 700 | I2C_FUNC_SMBUS_WORD_DATA | |
597 | I2C_FUNC_SMBUS_BLOCK_DATA | | 701 | I2C_FUNC_SMBUS_BLOCK_DATA | |
@@ -601,6 +705,7 @@ static u32 cp2112_functionality(struct i2c_adapter *adap) | |||
601 | } | 705 | } |
602 | 706 | ||
603 | static const struct i2c_algorithm smbus_algorithm = { | 707 | static const struct i2c_algorithm smbus_algorithm = { |
708 | .master_xfer = cp2112_i2c_xfer, | ||
604 | .smbus_xfer = cp2112_xfer, | 709 | .smbus_xfer = cp2112_xfer, |
605 | .functionality = cp2112_functionality, | 710 | .functionality = cp2112_functionality, |
606 | }; | 711 | }; |