diff options
Diffstat (limited to 'drivers/nfc/pn544/i2c.c')
-rw-r--r-- | drivers/nfc/pn544/i2c.c | 194 |
1 files changed, 187 insertions, 7 deletions
diff --git a/drivers/nfc/pn544/i2c.c b/drivers/nfc/pn544/i2c.c index d6185ff2f87b..f2acd85be86e 100644 --- a/drivers/nfc/pn544/i2c.c +++ b/drivers/nfc/pn544/i2c.c | |||
@@ -58,8 +58,19 @@ MODULE_DEVICE_TABLE(i2c, pn544_hci_i2c_id_table); | |||
58 | 58 | ||
59 | #define PN544_HCI_I2C_DRIVER_NAME "pn544_hci_i2c" | 59 | #define PN544_HCI_I2C_DRIVER_NAME "pn544_hci_i2c" |
60 | 60 | ||
61 | /* | ||
62 | * Exposed through the 4 most significant bytes | ||
63 | * from the HCI SW_VERSION first byte, a.k.a. | ||
64 | * SW RomLib. | ||
65 | */ | ||
66 | #define PN544_HW_VARIANT_C2 0xa | ||
67 | #define PN544_HW_VARIANT_C3 0xb | ||
68 | |||
69 | #define PN544_FW_CMD_RESET 0x01 | ||
61 | #define PN544_FW_CMD_WRITE 0x08 | 70 | #define PN544_FW_CMD_WRITE 0x08 |
62 | #define PN544_FW_CMD_CHECK 0x06 | 71 | #define PN544_FW_CMD_CHECK 0x06 |
72 | #define PN544_FW_CMD_SECURE_WRITE 0x0C | ||
73 | #define PN544_FW_CMD_SECURE_CHUNK_WRITE 0x0D | ||
63 | 74 | ||
64 | struct pn544_i2c_fw_frame_write { | 75 | struct pn544_i2c_fw_frame_write { |
65 | u8 cmd; | 76 | u8 cmd; |
@@ -88,13 +99,31 @@ struct pn544_i2c_fw_blob { | |||
88 | u8 data[]; | 99 | u8 data[]; |
89 | }; | 100 | }; |
90 | 101 | ||
102 | struct pn544_i2c_fw_secure_frame { | ||
103 | u8 cmd; | ||
104 | u16 be_datalen; | ||
105 | u8 data[]; | ||
106 | } __packed; | ||
107 | |||
108 | struct pn544_i2c_fw_secure_blob { | ||
109 | u64 header; | ||
110 | u8 data[]; | ||
111 | }; | ||
112 | |||
91 | #define PN544_FW_CMD_RESULT_TIMEOUT 0x01 | 113 | #define PN544_FW_CMD_RESULT_TIMEOUT 0x01 |
92 | #define PN544_FW_CMD_RESULT_BAD_CRC 0x02 | 114 | #define PN544_FW_CMD_RESULT_BAD_CRC 0x02 |
93 | #define PN544_FW_CMD_RESULT_ACCESS_DENIED 0x08 | 115 | #define PN544_FW_CMD_RESULT_ACCESS_DENIED 0x08 |
94 | #define PN544_FW_CMD_RESULT_PROTOCOL_ERROR 0x0B | 116 | #define PN544_FW_CMD_RESULT_PROTOCOL_ERROR 0x0B |
95 | #define PN544_FW_CMD_RESULT_INVALID_PARAMETER 0x11 | 117 | #define PN544_FW_CMD_RESULT_INVALID_PARAMETER 0x11 |
118 | #define PN544_FW_CMD_RESULT_UNSUPPORTED_COMMAND 0x13 | ||
96 | #define PN544_FW_CMD_RESULT_INVALID_LENGTH 0x18 | 119 | #define PN544_FW_CMD_RESULT_INVALID_LENGTH 0x18 |
120 | #define PN544_FW_CMD_RESULT_CRYPTOGRAPHIC_ERROR 0x19 | ||
121 | #define PN544_FW_CMD_RESULT_VERSION_CONDITIONS_ERROR 0x1D | ||
122 | #define PN544_FW_CMD_RESULT_MEMORY_ERROR 0x20 | ||
123 | #define PN544_FW_CMD_RESULT_CHUNK_OK 0x21 | ||
97 | #define PN544_FW_CMD_RESULT_WRITE_FAILED 0x74 | 124 | #define PN544_FW_CMD_RESULT_WRITE_FAILED 0x74 |
125 | #define PN544_FW_CMD_RESULT_COMMAND_REJECTED 0xE0 | ||
126 | #define PN544_FW_CMD_RESULT_CHUNK_ERROR 0xE6 | ||
98 | 127 | ||
99 | #define MIN(X, Y) ((X) < (Y) ? (X) : (Y)) | 128 | #define MIN(X, Y) ((X) < (Y) ? (X) : (Y)) |
100 | 129 | ||
@@ -104,11 +133,17 @@ struct pn544_i2c_fw_blob { | |||
104 | #define PN544_FW_I2C_WRITE_DATA_MAX_LEN MIN((PN544_FW_I2C_MAX_PAYLOAD -\ | 133 | #define PN544_FW_I2C_WRITE_DATA_MAX_LEN MIN((PN544_FW_I2C_MAX_PAYLOAD -\ |
105 | PN544_FW_I2C_WRITE_FRAME_HEADER_LEN),\ | 134 | PN544_FW_I2C_WRITE_FRAME_HEADER_LEN),\ |
106 | PN544_FW_WRITE_BUFFER_MAX_LEN) | 135 | PN544_FW_WRITE_BUFFER_MAX_LEN) |
136 | #define PN544_FW_SECURE_CHUNK_WRITE_HEADER_LEN 3 | ||
137 | #define PN544_FW_SECURE_CHUNK_WRITE_DATA_MAX_LEN (PN544_FW_I2C_MAX_PAYLOAD -\ | ||
138 | PN544_FW_SECURE_CHUNK_WRITE_HEADER_LEN) | ||
139 | #define PN544_FW_SECURE_FRAME_HEADER_LEN 3 | ||
140 | #define PN544_FW_SECURE_BLOB_HEADER_LEN 8 | ||
107 | 141 | ||
108 | #define FW_WORK_STATE_IDLE 1 | 142 | #define FW_WORK_STATE_IDLE 1 |
109 | #define FW_WORK_STATE_START 2 | 143 | #define FW_WORK_STATE_START 2 |
110 | #define FW_WORK_STATE_WAIT_WRITE_ANSWER 3 | 144 | #define FW_WORK_STATE_WAIT_WRITE_ANSWER 3 |
111 | #define FW_WORK_STATE_WAIT_CHECK_ANSWER 4 | 145 | #define FW_WORK_STATE_WAIT_CHECK_ANSWER 4 |
146 | #define FW_WORK_STATE_WAIT_SECURE_WRITE_ANSWER 5 | ||
112 | 147 | ||
113 | struct pn544_i2c_phy { | 148 | struct pn544_i2c_phy { |
114 | struct i2c_client *i2c_dev; | 149 | struct i2c_client *i2c_dev; |
@@ -119,6 +154,8 @@ struct pn544_i2c_phy { | |||
119 | unsigned int gpio_fw; | 154 | unsigned int gpio_fw; |
120 | unsigned int en_polarity; | 155 | unsigned int en_polarity; |
121 | 156 | ||
157 | u8 hw_variant; | ||
158 | |||
122 | struct work_struct fw_work; | 159 | struct work_struct fw_work; |
123 | int fw_work_state; | 160 | int fw_work_state; |
124 | char firmware_name[NFC_FIRMWARE_NAME_MAXSIZE + 1]; | 161 | char firmware_name[NFC_FIRMWARE_NAME_MAXSIZE + 1]; |
@@ -127,6 +164,8 @@ struct pn544_i2c_phy { | |||
127 | size_t fw_blob_size; | 164 | size_t fw_blob_size; |
128 | const u8 *fw_blob_data; | 165 | const u8 *fw_blob_data; |
129 | size_t fw_written; | 166 | size_t fw_written; |
167 | size_t fw_size; | ||
168 | |||
130 | int fw_cmd_result; | 169 | int fw_cmd_result; |
131 | 170 | ||
132 | int powered; | 171 | int powered; |
@@ -390,6 +429,8 @@ static int pn544_hci_i2c_fw_read_status(struct pn544_i2c_phy *phy) | |||
390 | switch (response.status) { | 429 | switch (response.status) { |
391 | case 0: | 430 | case 0: |
392 | return 0; | 431 | return 0; |
432 | case PN544_FW_CMD_RESULT_CHUNK_OK: | ||
433 | return response.status; | ||
393 | case PN544_FW_CMD_RESULT_TIMEOUT: | 434 | case PN544_FW_CMD_RESULT_TIMEOUT: |
394 | return -ETIMEDOUT; | 435 | return -ETIMEDOUT; |
395 | case PN544_FW_CMD_RESULT_BAD_CRC: | 436 | case PN544_FW_CMD_RESULT_BAD_CRC: |
@@ -400,9 +441,20 @@ static int pn544_hci_i2c_fw_read_status(struct pn544_i2c_phy *phy) | |||
400 | return -EPROTO; | 441 | return -EPROTO; |
401 | case PN544_FW_CMD_RESULT_INVALID_PARAMETER: | 442 | case PN544_FW_CMD_RESULT_INVALID_PARAMETER: |
402 | return -EINVAL; | 443 | return -EINVAL; |
444 | case PN544_FW_CMD_RESULT_UNSUPPORTED_COMMAND: | ||
445 | return -ENOTSUPP; | ||
403 | case PN544_FW_CMD_RESULT_INVALID_LENGTH: | 446 | case PN544_FW_CMD_RESULT_INVALID_LENGTH: |
404 | return -EBADMSG; | 447 | return -EBADMSG; |
448 | case PN544_FW_CMD_RESULT_CRYPTOGRAPHIC_ERROR: | ||
449 | return -ENOKEY; | ||
450 | case PN544_FW_CMD_RESULT_VERSION_CONDITIONS_ERROR: | ||
451 | return -EINVAL; | ||
452 | case PN544_FW_CMD_RESULT_MEMORY_ERROR: | ||
453 | return -ENOMEM; | ||
454 | case PN544_FW_CMD_RESULT_COMMAND_REJECTED: | ||
455 | return -EACCES; | ||
405 | case PN544_FW_CMD_RESULT_WRITE_FAILED: | 456 | case PN544_FW_CMD_RESULT_WRITE_FAILED: |
457 | case PN544_FW_CMD_RESULT_CHUNK_ERROR: | ||
406 | return -EIO; | 458 | return -EIO; |
407 | default: | 459 | default: |
408 | return -EIO; | 460 | return -EIO; |
@@ -469,7 +521,8 @@ static struct nfc_phy_ops i2c_phy_ops = { | |||
469 | .disable = pn544_hci_i2c_disable, | 521 | .disable = pn544_hci_i2c_disable, |
470 | }; | 522 | }; |
471 | 523 | ||
472 | static int pn544_hci_i2c_fw_download(void *phy_id, const char *firmware_name) | 524 | static int pn544_hci_i2c_fw_download(void *phy_id, const char *firmware_name, |
525 | u8 hw_variant) | ||
473 | { | 526 | { |
474 | struct pn544_i2c_phy *phy = phy_id; | 527 | struct pn544_i2c_phy *phy = phy_id; |
475 | 528 | ||
@@ -477,6 +530,7 @@ static int pn544_hci_i2c_fw_download(void *phy_id, const char *firmware_name) | |||
477 | 530 | ||
478 | strcpy(phy->firmware_name, firmware_name); | 531 | strcpy(phy->firmware_name, firmware_name); |
479 | 532 | ||
533 | phy->hw_variant = hw_variant; | ||
480 | phy->fw_work_state = FW_WORK_STATE_START; | 534 | phy->fw_work_state = FW_WORK_STATE_START; |
481 | 535 | ||
482 | schedule_work(&phy->fw_work); | 536 | schedule_work(&phy->fw_work); |
@@ -598,12 +652,93 @@ static int pn544_hci_i2c_fw_write_chunk(struct pn544_i2c_phy *phy) | |||
598 | return 0; | 652 | return 0; |
599 | } | 653 | } |
600 | 654 | ||
655 | static int pn544_hci_i2c_fw_secure_write_frame_cmd(struct pn544_i2c_phy *phy, | ||
656 | const u8 *data, u16 datalen) | ||
657 | { | ||
658 | u8 buf[PN544_FW_I2C_MAX_PAYLOAD]; | ||
659 | struct pn544_i2c_fw_secure_frame *chunk; | ||
660 | int chunklen; | ||
661 | int r; | ||
662 | |||
663 | if (datalen > PN544_FW_SECURE_CHUNK_WRITE_DATA_MAX_LEN) | ||
664 | datalen = PN544_FW_SECURE_CHUNK_WRITE_DATA_MAX_LEN; | ||
665 | |||
666 | chunk = (struct pn544_i2c_fw_secure_frame *) buf; | ||
667 | |||
668 | chunk->cmd = PN544_FW_CMD_SECURE_CHUNK_WRITE; | ||
669 | |||
670 | put_unaligned_be16(datalen, &chunk->be_datalen); | ||
671 | |||
672 | memcpy(chunk->data, data, datalen); | ||
673 | |||
674 | chunklen = sizeof(chunk->cmd) + sizeof(chunk->be_datalen) + datalen; | ||
675 | |||
676 | r = i2c_master_send(phy->i2c_dev, buf, chunklen); | ||
677 | |||
678 | if (r == chunklen) | ||
679 | return datalen; | ||
680 | else if (r < 0) | ||
681 | return r; | ||
682 | else | ||
683 | return -EIO; | ||
684 | |||
685 | } | ||
686 | |||
687 | static int pn544_hci_i2c_fw_secure_write_frame(struct pn544_i2c_phy *phy) | ||
688 | { | ||
689 | struct pn544_i2c_fw_secure_frame *framep; | ||
690 | int r; | ||
691 | |||
692 | framep = (struct pn544_i2c_fw_secure_frame *) phy->fw_blob_data; | ||
693 | if (phy->fw_written == 0) | ||
694 | phy->fw_blob_size = get_unaligned_be16(&framep->be_datalen) | ||
695 | + PN544_FW_SECURE_FRAME_HEADER_LEN; | ||
696 | |||
697 | /* Only secure write command can be chunked*/ | ||
698 | if (phy->fw_blob_size > PN544_FW_I2C_MAX_PAYLOAD && | ||
699 | framep->cmd != PN544_FW_CMD_SECURE_WRITE) | ||
700 | return -EINVAL; | ||
701 | |||
702 | /* The firmware also have other commands, we just send them directly */ | ||
703 | if (phy->fw_blob_size < PN544_FW_I2C_MAX_PAYLOAD) { | ||
704 | r = i2c_master_send(phy->i2c_dev, | ||
705 | (const char *) phy->fw_blob_data, phy->fw_blob_size); | ||
706 | |||
707 | if (r == phy->fw_blob_size) | ||
708 | goto exit; | ||
709 | else if (r < 0) | ||
710 | return r; | ||
711 | else | ||
712 | return -EIO; | ||
713 | } | ||
714 | |||
715 | r = pn544_hci_i2c_fw_secure_write_frame_cmd(phy, | ||
716 | phy->fw_blob_data + phy->fw_written, | ||
717 | phy->fw_blob_size - phy->fw_written); | ||
718 | if (r < 0) | ||
719 | return r; | ||
720 | |||
721 | exit: | ||
722 | phy->fw_written += r; | ||
723 | phy->fw_work_state = FW_WORK_STATE_WAIT_SECURE_WRITE_ANSWER; | ||
724 | |||
725 | /* SW reset command will not trig any response from PN544 */ | ||
726 | if (framep->cmd == PN544_FW_CMD_RESET) { | ||
727 | pn544_hci_i2c_enable_mode(phy, PN544_FW_MODE); | ||
728 | phy->fw_cmd_result = 0; | ||
729 | schedule_work(&phy->fw_work); | ||
730 | } | ||
731 | |||
732 | return 0; | ||
733 | } | ||
734 | |||
601 | static void pn544_hci_i2c_fw_work(struct work_struct *work) | 735 | static void pn544_hci_i2c_fw_work(struct work_struct *work) |
602 | { | 736 | { |
603 | struct pn544_i2c_phy *phy = container_of(work, struct pn544_i2c_phy, | 737 | struct pn544_i2c_phy *phy = container_of(work, struct pn544_i2c_phy, |
604 | fw_work); | 738 | fw_work); |
605 | int r; | 739 | int r; |
606 | struct pn544_i2c_fw_blob *blob; | 740 | struct pn544_i2c_fw_blob *blob; |
741 | struct pn544_i2c_fw_secure_blob *secure_blob; | ||
607 | 742 | ||
608 | switch (phy->fw_work_state) { | 743 | switch (phy->fw_work_state) { |
609 | case FW_WORK_STATE_START: | 744 | case FW_WORK_STATE_START: |
@@ -614,13 +749,29 @@ static void pn544_hci_i2c_fw_work(struct work_struct *work) | |||
614 | if (r < 0) | 749 | if (r < 0) |
615 | goto exit_state_start; | 750 | goto exit_state_start; |
616 | 751 | ||
617 | blob = (struct pn544_i2c_fw_blob *) phy->fw->data; | ||
618 | phy->fw_blob_size = get_unaligned_be32(&blob->be_size); | ||
619 | phy->fw_blob_dest_addr = get_unaligned_be32(&blob->be_destaddr); | ||
620 | phy->fw_blob_data = blob->data; | ||
621 | |||
622 | phy->fw_written = 0; | 752 | phy->fw_written = 0; |
623 | r = pn544_hci_i2c_fw_write_chunk(phy); | 753 | |
754 | switch (phy->hw_variant) { | ||
755 | case PN544_HW_VARIANT_C2: | ||
756 | blob = (struct pn544_i2c_fw_blob *) phy->fw->data; | ||
757 | phy->fw_blob_size = get_unaligned_be32(&blob->be_size); | ||
758 | phy->fw_blob_dest_addr = get_unaligned_be32( | ||
759 | &blob->be_destaddr); | ||
760 | phy->fw_blob_data = blob->data; | ||
761 | |||
762 | r = pn544_hci_i2c_fw_write_chunk(phy); | ||
763 | break; | ||
764 | case PN544_HW_VARIANT_C3: | ||
765 | secure_blob = (struct pn544_i2c_fw_secure_blob *) | ||
766 | phy->fw->data; | ||
767 | phy->fw_blob_data = secure_blob->data; | ||
768 | phy->fw_size = phy->fw->size; | ||
769 | r = pn544_hci_i2c_fw_secure_write_frame(phy); | ||
770 | break; | ||
771 | default: | ||
772 | r = -ENOTSUPP; | ||
773 | break; | ||
774 | } | ||
624 | 775 | ||
625 | exit_state_start: | 776 | exit_state_start: |
626 | if (r < 0) | 777 | if (r < 0) |
@@ -672,6 +823,35 @@ exit_state_wait_check_answer: | |||
672 | pn544_hci_i2c_fw_work_complete(phy, r); | 823 | pn544_hci_i2c_fw_work_complete(phy, r); |
673 | break; | 824 | break; |
674 | 825 | ||
826 | case FW_WORK_STATE_WAIT_SECURE_WRITE_ANSWER: | ||
827 | r = phy->fw_cmd_result; | ||
828 | if (r < 0) | ||
829 | goto exit_state_wait_secure_write_answer; | ||
830 | |||
831 | if (r == PN544_FW_CMD_RESULT_CHUNK_OK) { | ||
832 | r = pn544_hci_i2c_fw_secure_write_frame(phy); | ||
833 | goto exit_state_wait_secure_write_answer; | ||
834 | } | ||
835 | |||
836 | if (phy->fw_written == phy->fw_blob_size) { | ||
837 | secure_blob = (struct pn544_i2c_fw_secure_blob *) | ||
838 | (phy->fw_blob_data + phy->fw_blob_size); | ||
839 | phy->fw_size -= phy->fw_blob_size + | ||
840 | PN544_FW_SECURE_BLOB_HEADER_LEN; | ||
841 | if (phy->fw_size >= PN544_FW_SECURE_BLOB_HEADER_LEN | ||
842 | + PN544_FW_SECURE_FRAME_HEADER_LEN) { | ||
843 | phy->fw_blob_data = secure_blob->data; | ||
844 | |||
845 | phy->fw_written = 0; | ||
846 | r = pn544_hci_i2c_fw_secure_write_frame(phy); | ||
847 | } | ||
848 | } | ||
849 | |||
850 | exit_state_wait_secure_write_answer: | ||
851 | if (r < 0 || phy->fw_size == 0) | ||
852 | pn544_hci_i2c_fw_work_complete(phy, r); | ||
853 | break; | ||
854 | |||
675 | default: | 855 | default: |
676 | break; | 856 | break; |
677 | } | 857 | } |