diff options
| author | Antonio Borneo <borneo.antonio@gmail.com> | 2014-07-07 19:25:39 -0400 |
|---|---|---|
| committer | Jiri Kosina <jkosina@suse.cz> | 2014-07-29 05:37:38 -0400 |
| commit | b9029345ed6483fcadadc4834b44a5656dd56d70 (patch) | |
| tree | 1ad9c2ef159cf7bd9f5096c9c3124632dbeff087 | |
| parent | beb9d007a846e661cfaa7719cab6b004b3380418 (diff) | |
HID: cp2112: add I2C mode
cp2112 supports single I2C read/write transactions. It can't combine I2C
transactions.
Add master_xfer, using similar code flow as for smbus_xfer.
Signed-off-by: Antonio Borneo <borneo.antonio@gmail.com>
Reviewed-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>
Signed-off-by: Jiri Kosina <jkosina@suse.cz>
| -rw-r--r-- | drivers/hid/hid-cp2112.c | 103 |
1 files changed, 102 insertions, 1 deletions
diff --git a/drivers/hid/hid-cp2112.c b/drivers/hid/hid-cp2112.c index 3952d90723b9..a822db5a8338 100644 --- a/drivers/hid/hid-cp2112.c +++ b/drivers/hid/hid-cp2112.c | |||
| @@ -429,6 +429,105 @@ static int cp2112_write_req(void *buf, u8 slave_address, u8 command, u8 *data, | |||
| 429 | return data_length + 4; | 429 | return data_length + 4; |
| 430 | } | 430 | } |
| 431 | 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 | |||
| 432 | static int cp2112_xfer(struct i2c_adapter *adap, u16 addr, | 531 | static int cp2112_xfer(struct i2c_adapter *adap, u16 addr, |
| 433 | unsigned short flags, char read_write, u8 command, | 532 | unsigned short flags, char read_write, u8 command, |
| 434 | int size, union i2c_smbus_data *data) | 533 | int size, union i2c_smbus_data *data) |
| @@ -595,7 +694,8 @@ power_normal: | |||
| 595 | 694 | ||
| 596 | static u32 cp2112_functionality(struct i2c_adapter *adap) | 695 | static u32 cp2112_functionality(struct i2c_adapter *adap) |
| 597 | { | 696 | { |
| 598 | return I2C_FUNC_SMBUS_BYTE | | 697 | return I2C_FUNC_I2C | |
| 698 | I2C_FUNC_SMBUS_BYTE | | ||
| 599 | I2C_FUNC_SMBUS_BYTE_DATA | | 699 | I2C_FUNC_SMBUS_BYTE_DATA | |
| 600 | I2C_FUNC_SMBUS_WORD_DATA | | 700 | I2C_FUNC_SMBUS_WORD_DATA | |
| 601 | I2C_FUNC_SMBUS_BLOCK_DATA | | 701 | I2C_FUNC_SMBUS_BLOCK_DATA | |
| @@ -605,6 +705,7 @@ static u32 cp2112_functionality(struct i2c_adapter *adap) | |||
| 605 | } | 705 | } |
| 606 | 706 | ||
| 607 | static const struct i2c_algorithm smbus_algorithm = { | 707 | static const struct i2c_algorithm smbus_algorithm = { |
| 708 | .master_xfer = cp2112_i2c_xfer, | ||
| 608 | .smbus_xfer = cp2112_xfer, | 709 | .smbus_xfer = cp2112_xfer, |
| 609 | .functionality = cp2112_functionality, | 710 | .functionality = cp2112_functionality, |
| 610 | }; | 711 | }; |
