diff options
-rw-r--r-- | MAINTAINERS | 7 | ||||
-rw-r--r-- | drivers/nfc/Kconfig | 1 | ||||
-rw-r--r-- | drivers/nfc/Makefile | 1 | ||||
-rw-r--r-- | drivers/nfc/nxp-nci/Kconfig | 13 | ||||
-rw-r--r-- | drivers/nfc/nxp-nci/Makefile | 9 | ||||
-rw-r--r-- | drivers/nfc/nxp-nci/core.c | 186 | ||||
-rw-r--r-- | drivers/nfc/nxp-nci/firmware.c | 324 | ||||
-rw-r--r-- | drivers/nfc/nxp-nci/nxp-nci.h | 89 | ||||
-rw-r--r-- | include/linux/platform_data/nxp-nci.h | 27 |
9 files changed, 657 insertions, 0 deletions
diff --git a/MAINTAINERS b/MAINTAINERS index c2016557b294..12fdf22a77b0 100644 --- a/MAINTAINERS +++ b/MAINTAINERS | |||
@@ -6937,6 +6937,13 @@ S: Supported | |||
6937 | F: drivers/block/nvme* | 6937 | F: drivers/block/nvme* |
6938 | F: include/linux/nvme.h | 6938 | F: include/linux/nvme.h |
6939 | 6939 | ||
6940 | NXP-NCI NFC DRIVER | ||
6941 | M: Clément Perrochaud <clement.perrochaud@effinnov.com> | ||
6942 | R: Charles Gorand <charles.gorand@effinnov.com> | ||
6943 | L: linux-nfc@lists.01.org (moderated for non-subscribers) | ||
6944 | S: Supported | ||
6945 | F: drivers/nfc/nxp-nci | ||
6946 | |||
6940 | NXP TDA998X DRM DRIVER | 6947 | NXP TDA998X DRM DRIVER |
6941 | M: Russell King <rmk+kernel@arm.linux.org.uk> | 6948 | M: Russell King <rmk+kernel@arm.linux.org.uk> |
6942 | S: Supported | 6949 | S: Supported |
diff --git a/drivers/nfc/Kconfig b/drivers/nfc/Kconfig index 7929fac13e1c..107714e4405f 100644 --- a/drivers/nfc/Kconfig +++ b/drivers/nfc/Kconfig | |||
@@ -73,4 +73,5 @@ source "drivers/nfc/microread/Kconfig" | |||
73 | source "drivers/nfc/nfcmrvl/Kconfig" | 73 | source "drivers/nfc/nfcmrvl/Kconfig" |
74 | source "drivers/nfc/st21nfca/Kconfig" | 74 | source "drivers/nfc/st21nfca/Kconfig" |
75 | source "drivers/nfc/st21nfcb/Kconfig" | 75 | source "drivers/nfc/st21nfcb/Kconfig" |
76 | source "drivers/nfc/nxp-nci/Kconfig" | ||
76 | endmenu | 77 | endmenu |
diff --git a/drivers/nfc/Makefile b/drivers/nfc/Makefile index 6b23a2c6e34a..a4292d790f9b 100644 --- a/drivers/nfc/Makefile +++ b/drivers/nfc/Makefile | |||
@@ -13,5 +13,6 @@ obj-$(CONFIG_NFC_MRVL) += nfcmrvl/ | |||
13 | obj-$(CONFIG_NFC_TRF7970A) += trf7970a.o | 13 | obj-$(CONFIG_NFC_TRF7970A) += trf7970a.o |
14 | obj-$(CONFIG_NFC_ST21NFCA) += st21nfca/ | 14 | obj-$(CONFIG_NFC_ST21NFCA) += st21nfca/ |
15 | obj-$(CONFIG_NFC_ST21NFCB) += st21nfcb/ | 15 | obj-$(CONFIG_NFC_ST21NFCB) += st21nfcb/ |
16 | obj-$(CONFIG_NFC_NXP_NCI) += nxp-nci/ | ||
16 | 17 | ||
17 | ccflags-$(CONFIG_NFC_DEBUG) := -DDEBUG | 18 | ccflags-$(CONFIG_NFC_DEBUG) := -DDEBUG |
diff --git a/drivers/nfc/nxp-nci/Kconfig b/drivers/nfc/nxp-nci/Kconfig new file mode 100644 index 000000000000..5f60c7cf02e8 --- /dev/null +++ b/drivers/nfc/nxp-nci/Kconfig | |||
@@ -0,0 +1,13 @@ | |||
1 | config NFC_NXP_NCI | ||
2 | tristate "NXP-NCI NFC driver" | ||
3 | depends on NFC_NCI | ||
4 | default n | ||
5 | ---help--- | ||
6 | Generic core driver for NXP NCI chips such as the NPC100 | ||
7 | or PN7150 families. | ||
8 | This is a driver based on the NCI NFC kernel layers and | ||
9 | will thus not work with NXP libnfc library. | ||
10 | |||
11 | To compile this driver as a module, choose m here. The module will | ||
12 | be called nxp_nci. | ||
13 | Say N if unsure. | ||
diff --git a/drivers/nfc/nxp-nci/Makefile b/drivers/nfc/nxp-nci/Makefile new file mode 100644 index 000000000000..8f1e32826961 --- /dev/null +++ b/drivers/nfc/nxp-nci/Makefile | |||
@@ -0,0 +1,9 @@ | |||
1 | # | ||
2 | # Makefile for NXP-NCI NFC driver | ||
3 | # | ||
4 | |||
5 | nxp-nci-objs = core.o firmware.o | ||
6 | |||
7 | obj-$(CONFIG_NFC_NXP_NCI) += nxp-nci.o | ||
8 | |||
9 | ccflags-$(CONFIG_NFC_DEBUG) := -DDEBUG | ||
diff --git a/drivers/nfc/nxp-nci/core.c b/drivers/nfc/nxp-nci/core.c new file mode 100644 index 000000000000..8979636d48ea --- /dev/null +++ b/drivers/nfc/nxp-nci/core.c | |||
@@ -0,0 +1,186 @@ | |||
1 | /* | ||
2 | * Generic driver for NXP NCI NFC chips | ||
3 | * | ||
4 | * Copyright (C) 2014 NXP Semiconductors All rights reserved. | ||
5 | * | ||
6 | * Authors: Clément Perrochaud <clement.perrochaud@nxp.com> | ||
7 | * | ||
8 | * Derived from PN544 device driver: | ||
9 | * Copyright (C) 2012 Intel Corporation. All rights reserved. | ||
10 | * | ||
11 | * This program is free software; you can redistribute it and/or modify it | ||
12 | * under the terms and conditions of the GNU General Public License, | ||
13 | * version 2, as published by the Free Software Foundation. | ||
14 | * | ||
15 | * This program is distributed in the hope that it will be useful, | ||
16 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
18 | * GNU General Public License for more details. | ||
19 | * | ||
20 | * You should have received a copy of the GNU General Public License | ||
21 | * along with this program; if not, see <http://www.gnu.org/licenses/>. | ||
22 | */ | ||
23 | |||
24 | #include <linux/delay.h> | ||
25 | #include <linux/gpio.h> | ||
26 | #include <linux/module.h> | ||
27 | #include <linux/nfc.h> | ||
28 | #include <linux/platform_data/nxp-nci.h> | ||
29 | |||
30 | #include <net/nfc/nci_core.h> | ||
31 | |||
32 | #include "nxp-nci.h" | ||
33 | |||
34 | #define NXP_NCI_HDR_LEN 4 | ||
35 | |||
36 | #define NXP_NCI_NFC_PROTOCOLS (NFC_PROTO_JEWEL_MASK | \ | ||
37 | NFC_PROTO_MIFARE_MASK | \ | ||
38 | NFC_PROTO_FELICA_MASK | \ | ||
39 | NFC_PROTO_ISO14443_MASK | \ | ||
40 | NFC_PROTO_ISO14443_B_MASK | \ | ||
41 | NFC_PROTO_NFC_DEP_MASK) | ||
42 | |||
43 | static int nxp_nci_open(struct nci_dev *ndev) | ||
44 | { | ||
45 | struct nxp_nci_info *info = nci_get_drvdata(ndev); | ||
46 | int r = 0; | ||
47 | |||
48 | mutex_lock(&info->info_lock); | ||
49 | |||
50 | if (info->mode != NXP_NCI_MODE_COLD) { | ||
51 | r = -EBUSY; | ||
52 | goto open_exit; | ||
53 | } | ||
54 | |||
55 | if (info->phy_ops->set_mode) | ||
56 | r = info->phy_ops->set_mode(info->phy_id, NXP_NCI_MODE_NCI); | ||
57 | |||
58 | info->mode = NXP_NCI_MODE_NCI; | ||
59 | |||
60 | open_exit: | ||
61 | mutex_unlock(&info->info_lock); | ||
62 | return r; | ||
63 | } | ||
64 | |||
65 | static int nxp_nci_close(struct nci_dev *ndev) | ||
66 | { | ||
67 | struct nxp_nci_info *info = nci_get_drvdata(ndev); | ||
68 | int r = 0; | ||
69 | |||
70 | mutex_lock(&info->info_lock); | ||
71 | |||
72 | if (info->phy_ops->set_mode) | ||
73 | r = info->phy_ops->set_mode(info->phy_id, NXP_NCI_MODE_COLD); | ||
74 | |||
75 | info->mode = NXP_NCI_MODE_COLD; | ||
76 | |||
77 | mutex_unlock(&info->info_lock); | ||
78 | return r; | ||
79 | } | ||
80 | |||
81 | static int nxp_nci_send(struct nci_dev *ndev, struct sk_buff *skb) | ||
82 | { | ||
83 | struct nxp_nci_info *info = nci_get_drvdata(ndev); | ||
84 | int r; | ||
85 | |||
86 | if (!info->phy_ops->write) { | ||
87 | r = -ENOTSUPP; | ||
88 | goto send_exit; | ||
89 | } | ||
90 | |||
91 | if (info->mode != NXP_NCI_MODE_NCI) { | ||
92 | r = -EINVAL; | ||
93 | goto send_exit; | ||
94 | } | ||
95 | |||
96 | r = info->phy_ops->write(info->phy_id, skb); | ||
97 | if (r < 0) | ||
98 | kfree_skb(skb); | ||
99 | |||
100 | send_exit: | ||
101 | return r; | ||
102 | } | ||
103 | |||
104 | static struct nci_ops nxp_nci_ops = { | ||
105 | .open = nxp_nci_open, | ||
106 | .close = nxp_nci_close, | ||
107 | .send = nxp_nci_send, | ||
108 | .fw_download = nxp_nci_fw_download, | ||
109 | }; | ||
110 | |||
111 | int nxp_nci_probe(void *phy_id, struct device *pdev, | ||
112 | struct nxp_nci_phy_ops *phy_ops, unsigned int max_payload, | ||
113 | struct nci_dev **ndev) | ||
114 | { | ||
115 | struct nxp_nci_info *info; | ||
116 | int r; | ||
117 | |||
118 | info = devm_kzalloc(pdev, sizeof(struct nxp_nci_info), GFP_KERNEL); | ||
119 | if (!info) { | ||
120 | r = -ENOMEM; | ||
121 | goto probe_exit; | ||
122 | } | ||
123 | |||
124 | info->phy_id = phy_id; | ||
125 | info->pdev = pdev; | ||
126 | info->phy_ops = phy_ops; | ||
127 | info->max_payload = max_payload; | ||
128 | INIT_WORK(&info->fw_info.work, nxp_nci_fw_work); | ||
129 | init_completion(&info->fw_info.cmd_completion); | ||
130 | mutex_init(&info->info_lock); | ||
131 | |||
132 | if (info->phy_ops->set_mode) { | ||
133 | r = info->phy_ops->set_mode(info->phy_id, NXP_NCI_MODE_COLD); | ||
134 | if (r < 0) | ||
135 | goto probe_exit; | ||
136 | } | ||
137 | |||
138 | info->mode = NXP_NCI_MODE_COLD; | ||
139 | |||
140 | info->ndev = nci_allocate_device(&nxp_nci_ops, NXP_NCI_NFC_PROTOCOLS, | ||
141 | NXP_NCI_HDR_LEN, 0); | ||
142 | if (!info->ndev) { | ||
143 | r = -ENOMEM; | ||
144 | goto probe_exit; | ||
145 | } | ||
146 | |||
147 | nci_set_parent_dev(info->ndev, pdev); | ||
148 | nci_set_drvdata(info->ndev, info); | ||
149 | r = nci_register_device(info->ndev); | ||
150 | if (r < 0) | ||
151 | goto probe_exit_free_nci; | ||
152 | |||
153 | *ndev = info->ndev; | ||
154 | |||
155 | goto probe_exit; | ||
156 | |||
157 | probe_exit_free_nci: | ||
158 | nci_free_device(info->ndev); | ||
159 | probe_exit: | ||
160 | return r; | ||
161 | } | ||
162 | EXPORT_SYMBOL(nxp_nci_probe); | ||
163 | |||
164 | void nxp_nci_remove(struct nci_dev *ndev) | ||
165 | { | ||
166 | struct nxp_nci_info *info = nci_get_drvdata(ndev); | ||
167 | |||
168 | if (info->mode == NXP_NCI_MODE_FW) | ||
169 | nxp_nci_fw_work_complete(info, -ESHUTDOWN); | ||
170 | cancel_work_sync(&info->fw_info.work); | ||
171 | |||
172 | mutex_lock(&info->info_lock); | ||
173 | |||
174 | if (info->phy_ops->set_mode) | ||
175 | info->phy_ops->set_mode(info->phy_id, NXP_NCI_MODE_COLD); | ||
176 | |||
177 | nci_unregister_device(ndev); | ||
178 | nci_free_device(ndev); | ||
179 | |||
180 | mutex_unlock(&info->info_lock); | ||
181 | } | ||
182 | EXPORT_SYMBOL(nxp_nci_remove); | ||
183 | |||
184 | MODULE_LICENSE("GPL"); | ||
185 | MODULE_DESCRIPTION("NXP NCI NFC driver"); | ||
186 | MODULE_AUTHOR("Clément Perrochaud <clement.perrochaud@nxp.com>"); | ||
diff --git a/drivers/nfc/nxp-nci/firmware.c b/drivers/nfc/nxp-nci/firmware.c new file mode 100644 index 000000000000..08573f97975f --- /dev/null +++ b/drivers/nfc/nxp-nci/firmware.c | |||
@@ -0,0 +1,324 @@ | |||
1 | /* | ||
2 | * Generic driver for NXP NCI NFC chips | ||
3 | * | ||
4 | * Copyright (C) 2014 NXP Semiconductors All rights reserved. | ||
5 | * | ||
6 | * Author: Clément Perrochaud <clement.perrochaud@nxp.com> | ||
7 | * | ||
8 | * Derived from PN544 device driver: | ||
9 | * Copyright (C) 2012 Intel Corporation. All rights reserved. | ||
10 | * | ||
11 | * This program is free software; you can redistribute it and/or modify it | ||
12 | * under the terms and conditions of the GNU General Public License, | ||
13 | * version 2, as published by the Free Software Foundation. | ||
14 | * | ||
15 | * This program is distributed in the hope that it will be useful, | ||
16 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
18 | * GNU General Public License for more details. | ||
19 | * | ||
20 | * You should have received a copy of the GNU General Public License | ||
21 | * along with this program; if not, see <http://www.gnu.org/licenses/>. | ||
22 | */ | ||
23 | |||
24 | #include <linux/completion.h> | ||
25 | #include <linux/firmware.h> | ||
26 | #include <linux/nfc.h> | ||
27 | #include <linux/unaligned/access_ok.h> | ||
28 | |||
29 | #include "nxp-nci.h" | ||
30 | |||
31 | /* Crypto operations can take up to 30 seconds */ | ||
32 | #define NXP_NCI_FW_ANSWER_TIMEOUT msecs_to_jiffies(30000) | ||
33 | |||
34 | #define NXP_NCI_FW_CMD_RESET 0xF0 | ||
35 | #define NXP_NCI_FW_CMD_GETVERSION 0xF1 | ||
36 | #define NXP_NCI_FW_CMD_CHECKINTEGRITY 0xE0 | ||
37 | #define NXP_NCI_FW_CMD_WRITE 0xC0 | ||
38 | #define NXP_NCI_FW_CMD_READ 0xA2 | ||
39 | #define NXP_NCI_FW_CMD_GETSESSIONSTATE 0xF2 | ||
40 | #define NXP_NCI_FW_CMD_LOG 0xA7 | ||
41 | #define NXP_NCI_FW_CMD_FORCE 0xD0 | ||
42 | #define NXP_NCI_FW_CMD_GET_DIE_ID 0xF4 | ||
43 | |||
44 | #define NXP_NCI_FW_CHUNK_FLAG 0x0400 | ||
45 | |||
46 | #define NXP_NCI_FW_RESULT_OK 0x00 | ||
47 | #define NXP_NCI_FW_RESULT_INVALID_ADDR 0x01 | ||
48 | #define NXP_NCI_FW_RESULT_GENERIC_ERROR 0x02 | ||
49 | #define NXP_NCI_FW_RESULT_UNKNOWN_CMD 0x0B | ||
50 | #define NXP_NCI_FW_RESULT_ABORTED_CMD 0x0C | ||
51 | #define NXP_NCI_FW_RESULT_PLL_ERROR 0x0D | ||
52 | #define NXP_NCI_FW_RESULT_ADDR_RANGE_OFL_ERROR 0x1E | ||
53 | #define NXP_NCI_FW_RESULT_BUFFER_OFL_ERROR 0x1F | ||
54 | #define NXP_NCI_FW_RESULT_MEM_BSY 0x20 | ||
55 | #define NXP_NCI_FW_RESULT_SIGNATURE_ERROR 0x21 | ||
56 | #define NXP_NCI_FW_RESULT_FIRMWARE_VERSION_ERROR 0x24 | ||
57 | #define NXP_NCI_FW_RESULT_PROTOCOL_ERROR 0x28 | ||
58 | #define NXP_NCI_FW_RESULT_SFWU_DEGRADED 0x2A | ||
59 | #define NXP_NCI_FW_RESULT_PH_STATUS_FIRST_CHUNK 0x2D | ||
60 | #define NXP_NCI_FW_RESULT_PH_STATUS_NEXT_CHUNK 0x2E | ||
61 | #define NXP_NCI_FW_RESULT_PH_STATUS_INTERNAL_ERROR_5 0xC5 | ||
62 | |||
63 | void nxp_nci_fw_work_complete(struct nxp_nci_info *info, int result) | ||
64 | { | ||
65 | struct nxp_nci_fw_info *fw_info = &info->fw_info; | ||
66 | int r; | ||
67 | |||
68 | if (info->phy_ops->set_mode) { | ||
69 | r = info->phy_ops->set_mode(info->phy_id, NXP_NCI_MODE_COLD); | ||
70 | if (r < 0 && result == 0) | ||
71 | result = -r; | ||
72 | } | ||
73 | |||
74 | info->mode = NXP_NCI_MODE_COLD; | ||
75 | |||
76 | if (fw_info->fw) { | ||
77 | release_firmware(fw_info->fw); | ||
78 | fw_info->fw = NULL; | ||
79 | } | ||
80 | |||
81 | nfc_fw_download_done(info->ndev->nfc_dev, fw_info->name, (u32) -result); | ||
82 | } | ||
83 | |||
84 | /* crc_ccitt cannot be used since it is computed MSB first and not LSB first */ | ||
85 | static u16 nxp_nci_fw_crc(u8 const *buffer, size_t len) | ||
86 | { | ||
87 | u16 crc = 0xffff; | ||
88 | |||
89 | while (len--) { | ||
90 | crc = ((crc >> 8) | (crc << 8)) ^ *buffer++; | ||
91 | crc ^= (crc & 0xff) >> 4; | ||
92 | crc ^= (crc & 0xff) << 12; | ||
93 | crc ^= (crc & 0xff) << 5; | ||
94 | } | ||
95 | |||
96 | return crc; | ||
97 | } | ||
98 | |||
99 | static int nxp_nci_fw_send_chunk(struct nxp_nci_info *info) | ||
100 | { | ||
101 | struct nxp_nci_fw_info *fw_info = &info->fw_info; | ||
102 | u16 header, crc; | ||
103 | struct sk_buff *skb; | ||
104 | size_t chunk_len; | ||
105 | size_t remaining_len; | ||
106 | int r; | ||
107 | |||
108 | skb = nci_skb_alloc(info->ndev, info->max_payload, GFP_KERNEL); | ||
109 | if (!skb) { | ||
110 | r = -ENOMEM; | ||
111 | goto chunk_exit; | ||
112 | } | ||
113 | |||
114 | chunk_len = info->max_payload - NXP_NCI_FW_HDR_LEN - NXP_NCI_FW_CRC_LEN; | ||
115 | remaining_len = fw_info->frame_size - fw_info->written; | ||
116 | |||
117 | if (remaining_len > chunk_len) { | ||
118 | header = NXP_NCI_FW_CHUNK_FLAG; | ||
119 | } else { | ||
120 | chunk_len = remaining_len; | ||
121 | header = 0x0000; | ||
122 | } | ||
123 | |||
124 | header |= chunk_len & NXP_NCI_FW_FRAME_LEN_MASK; | ||
125 | put_unaligned_be16(header, skb_put(skb, NXP_NCI_FW_HDR_LEN)); | ||
126 | |||
127 | memcpy(skb_put(skb, chunk_len), fw_info->data + fw_info->written, | ||
128 | chunk_len); | ||
129 | |||
130 | crc = nxp_nci_fw_crc(skb->data, chunk_len + NXP_NCI_FW_HDR_LEN); | ||
131 | put_unaligned_be16(crc, skb_put(skb, NXP_NCI_FW_CRC_LEN)); | ||
132 | |||
133 | r = info->phy_ops->write(info->phy_id, skb); | ||
134 | if (r >= 0) | ||
135 | r = chunk_len; | ||
136 | |||
137 | kfree_skb(skb); | ||
138 | |||
139 | chunk_exit: | ||
140 | return r; | ||
141 | } | ||
142 | |||
143 | static int nxp_nci_fw_send(struct nxp_nci_info *info) | ||
144 | { | ||
145 | struct nxp_nci_fw_info *fw_info = &info->fw_info; | ||
146 | long completion_rc; | ||
147 | int r; | ||
148 | |||
149 | reinit_completion(&fw_info->cmd_completion); | ||
150 | |||
151 | if (fw_info->written == 0) { | ||
152 | fw_info->frame_size = get_unaligned_be16(fw_info->data) & | ||
153 | NXP_NCI_FW_FRAME_LEN_MASK; | ||
154 | fw_info->data += NXP_NCI_FW_HDR_LEN; | ||
155 | fw_info->size -= NXP_NCI_FW_HDR_LEN; | ||
156 | } | ||
157 | |||
158 | if (fw_info->frame_size > fw_info->size) | ||
159 | return -EMSGSIZE; | ||
160 | |||
161 | r = nxp_nci_fw_send_chunk(info); | ||
162 | if (r < 0) | ||
163 | return r; | ||
164 | |||
165 | fw_info->written += r; | ||
166 | |||
167 | if (*fw_info->data == NXP_NCI_FW_CMD_RESET) { | ||
168 | fw_info->cmd_result = 0; | ||
169 | if (fw_info->fw) | ||
170 | schedule_work(&fw_info->work); | ||
171 | } else { | ||
172 | completion_rc = wait_for_completion_interruptible_timeout( | ||
173 | &fw_info->cmd_completion, NXP_NCI_FW_ANSWER_TIMEOUT); | ||
174 | if (completion_rc == 0) | ||
175 | return -ETIMEDOUT; | ||
176 | } | ||
177 | |||
178 | return 0; | ||
179 | } | ||
180 | |||
181 | void nxp_nci_fw_work(struct work_struct *work) | ||
182 | { | ||
183 | struct nxp_nci_info *info; | ||
184 | struct nxp_nci_fw_info *fw_info; | ||
185 | int r; | ||
186 | |||
187 | fw_info = container_of(work, struct nxp_nci_fw_info, work); | ||
188 | info = container_of(fw_info, struct nxp_nci_info, fw_info); | ||
189 | |||
190 | mutex_lock(&info->info_lock); | ||
191 | |||
192 | r = fw_info->cmd_result; | ||
193 | if (r < 0) | ||
194 | goto exit_work; | ||
195 | |||
196 | if (fw_info->written == fw_info->frame_size) { | ||
197 | fw_info->data += fw_info->frame_size; | ||
198 | fw_info->size -= fw_info->frame_size; | ||
199 | fw_info->written = 0; | ||
200 | } | ||
201 | |||
202 | if (fw_info->size > 0) | ||
203 | r = nxp_nci_fw_send(info); | ||
204 | |||
205 | exit_work: | ||
206 | if (r < 0 || fw_info->size == 0) | ||
207 | nxp_nci_fw_work_complete(info, r); | ||
208 | mutex_unlock(&info->info_lock); | ||
209 | } | ||
210 | |||
211 | int nxp_nci_fw_download(struct nci_dev *ndev, const char *firmware_name) | ||
212 | { | ||
213 | struct nxp_nci_info *info = nci_get_drvdata(ndev); | ||
214 | struct nxp_nci_fw_info *fw_info = &info->fw_info; | ||
215 | int r; | ||
216 | |||
217 | mutex_lock(&info->info_lock); | ||
218 | |||
219 | if (!info->phy_ops->set_mode || !info->phy_ops->write) { | ||
220 | r = -ENOTSUPP; | ||
221 | goto fw_download_exit; | ||
222 | } | ||
223 | |||
224 | if (!firmware_name || firmware_name[0] == '\0') { | ||
225 | r = -EINVAL; | ||
226 | goto fw_download_exit; | ||
227 | } | ||
228 | |||
229 | strcpy(fw_info->name, firmware_name); | ||
230 | |||
231 | r = request_firmware(&fw_info->fw, firmware_name, | ||
232 | ndev->nfc_dev->dev.parent); | ||
233 | if (r < 0) | ||
234 | goto fw_download_exit; | ||
235 | |||
236 | r = info->phy_ops->set_mode(info->phy_id, NXP_NCI_MODE_FW); | ||
237 | if (r < 0) | ||
238 | goto fw_download_exit; | ||
239 | |||
240 | info->mode = NXP_NCI_MODE_FW; | ||
241 | |||
242 | fw_info->data = fw_info->fw->data; | ||
243 | fw_info->size = fw_info->fw->size; | ||
244 | fw_info->written = 0; | ||
245 | fw_info->frame_size = 0; | ||
246 | fw_info->cmd_result = 0; | ||
247 | |||
248 | if (fw_info->fw) | ||
249 | schedule_work(&fw_info->work); | ||
250 | |||
251 | fw_download_exit: | ||
252 | mutex_unlock(&info->info_lock); | ||
253 | return r; | ||
254 | } | ||
255 | |||
256 | static int nxp_nci_fw_read_status(u8 stat) | ||
257 | { | ||
258 | switch (stat) { | ||
259 | case NXP_NCI_FW_RESULT_OK: | ||
260 | return 0; | ||
261 | case NXP_NCI_FW_RESULT_INVALID_ADDR: | ||
262 | return -EINVAL; | ||
263 | case NXP_NCI_FW_RESULT_UNKNOWN_CMD: | ||
264 | return -EINVAL; | ||
265 | case NXP_NCI_FW_RESULT_ABORTED_CMD: | ||
266 | return -EMSGSIZE; | ||
267 | case NXP_NCI_FW_RESULT_ADDR_RANGE_OFL_ERROR: | ||
268 | return -EADDRNOTAVAIL; | ||
269 | case NXP_NCI_FW_RESULT_BUFFER_OFL_ERROR: | ||
270 | return -ENOBUFS; | ||
271 | case NXP_NCI_FW_RESULT_MEM_BSY: | ||
272 | return -ENOKEY; | ||
273 | case NXP_NCI_FW_RESULT_SIGNATURE_ERROR: | ||
274 | return -EKEYREJECTED; | ||
275 | case NXP_NCI_FW_RESULT_FIRMWARE_VERSION_ERROR: | ||
276 | return -EALREADY; | ||
277 | case NXP_NCI_FW_RESULT_PROTOCOL_ERROR: | ||
278 | return -EPROTO; | ||
279 | case NXP_NCI_FW_RESULT_SFWU_DEGRADED: | ||
280 | return -EHWPOISON; | ||
281 | case NXP_NCI_FW_RESULT_PH_STATUS_FIRST_CHUNK: | ||
282 | return 0; | ||
283 | case NXP_NCI_FW_RESULT_PH_STATUS_NEXT_CHUNK: | ||
284 | return 0; | ||
285 | case NXP_NCI_FW_RESULT_PH_STATUS_INTERNAL_ERROR_5: | ||
286 | return -EINVAL; | ||
287 | default: | ||
288 | return -EIO; | ||
289 | } | ||
290 | } | ||
291 | |||
292 | static u16 nxp_nci_fw_check_crc(struct sk_buff *skb) | ||
293 | { | ||
294 | u16 crc, frame_crc; | ||
295 | size_t len = skb->len - NXP_NCI_FW_CRC_LEN; | ||
296 | |||
297 | crc = nxp_nci_fw_crc(skb->data, len); | ||
298 | frame_crc = get_unaligned_be16(skb->data + len); | ||
299 | |||
300 | return (crc ^ frame_crc); | ||
301 | } | ||
302 | |||
303 | void nxp_nci_fw_recv_frame(struct nci_dev *ndev, struct sk_buff *skb) | ||
304 | { | ||
305 | struct nxp_nci_info *info = nci_get_drvdata(ndev); | ||
306 | struct nxp_nci_fw_info *fw_info = &info->fw_info; | ||
307 | |||
308 | complete(&fw_info->cmd_completion); | ||
309 | |||
310 | if (skb) { | ||
311 | if (nxp_nci_fw_check_crc(skb) != 0x00) | ||
312 | fw_info->cmd_result = -EBADMSG; | ||
313 | else | ||
314 | fw_info->cmd_result = nxp_nci_fw_read_status( | ||
315 | *skb_pull(skb, NXP_NCI_FW_HDR_LEN)); | ||
316 | kfree_skb(skb); | ||
317 | } else { | ||
318 | fw_info->cmd_result = -EIO; | ||
319 | } | ||
320 | |||
321 | if (fw_info->fw) | ||
322 | schedule_work(&fw_info->work); | ||
323 | } | ||
324 | EXPORT_SYMBOL(nxp_nci_fw_recv_frame); | ||
diff --git a/drivers/nfc/nxp-nci/nxp-nci.h b/drivers/nfc/nxp-nci/nxp-nci.h new file mode 100644 index 000000000000..f1fecc4e2457 --- /dev/null +++ b/drivers/nfc/nxp-nci/nxp-nci.h | |||
@@ -0,0 +1,89 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2014 NXP Semiconductors All rights reserved. | ||
3 | * | ||
4 | * Authors: Clément Perrochaud <clement.perrochaud@nxp.com> | ||
5 | * | ||
6 | * Derived from PN544 device driver: | ||
7 | * Copyright (C) 2012 Intel Corporation. All rights reserved. | ||
8 | * | ||
9 | * This program is free software; you can redistribute it and/or modify it | ||
10 | * under the terms and conditions of the GNU General Public License, | ||
11 | * version 2, as published by the Free Software Foundation. | ||
12 | * | ||
13 | * This program is distributed in the hope that it will be useful, | ||
14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
16 | * GNU General Public License for more details. | ||
17 | * | ||
18 | * You should have received a copy of the GNU General Public License | ||
19 | * along with this program; if not, see <http://www.gnu.org/licenses/>. | ||
20 | */ | ||
21 | |||
22 | #ifndef __LOCAL_NXP_NCI_H_ | ||
23 | #define __LOCAL_NXP_NCI_H_ | ||
24 | |||
25 | #include <linux/completion.h> | ||
26 | #include <linux/firmware.h> | ||
27 | #include <linux/nfc.h> | ||
28 | #include <linux/platform_data/nxp-nci.h> | ||
29 | |||
30 | #include <net/nfc/nci_core.h> | ||
31 | |||
32 | #define NXP_NCI_FW_HDR_LEN 2 | ||
33 | #define NXP_NCI_FW_CRC_LEN 2 | ||
34 | |||
35 | #define NXP_NCI_FW_FRAME_LEN_MASK 0x03FF | ||
36 | |||
37 | enum nxp_nci_mode { | ||
38 | NXP_NCI_MODE_COLD, | ||
39 | NXP_NCI_MODE_NCI, | ||
40 | NXP_NCI_MODE_FW | ||
41 | }; | ||
42 | |||
43 | struct nxp_nci_phy_ops { | ||
44 | int (*set_mode)(void *id, enum nxp_nci_mode mode); | ||
45 | int (*write)(void *id, struct sk_buff *skb); | ||
46 | }; | ||
47 | |||
48 | struct nxp_nci_fw_info { | ||
49 | char name[NFC_FIRMWARE_NAME_MAXSIZE + 1]; | ||
50 | const struct firmware *fw; | ||
51 | |||
52 | size_t size; | ||
53 | size_t written; | ||
54 | |||
55 | const u8 *data; | ||
56 | size_t frame_size; | ||
57 | |||
58 | struct work_struct work; | ||
59 | struct completion cmd_completion; | ||
60 | |||
61 | int cmd_result; | ||
62 | }; | ||
63 | |||
64 | struct nxp_nci_info { | ||
65 | struct nci_dev *ndev; | ||
66 | void *phy_id; | ||
67 | struct device *pdev; | ||
68 | |||
69 | enum nxp_nci_mode mode; | ||
70 | |||
71 | struct nxp_nci_phy_ops *phy_ops; | ||
72 | unsigned int max_payload; | ||
73 | |||
74 | struct mutex info_lock; | ||
75 | |||
76 | struct nxp_nci_fw_info fw_info; | ||
77 | }; | ||
78 | |||
79 | int nxp_nci_fw_download(struct nci_dev *ndev, const char *firmware_name); | ||
80 | void nxp_nci_fw_work(struct work_struct *work); | ||
81 | void nxp_nci_fw_recv_frame(struct nci_dev *ndev, struct sk_buff *skb); | ||
82 | void nxp_nci_fw_work_complete(struct nxp_nci_info *info, int result); | ||
83 | |||
84 | int nxp_nci_probe(void *phy_id, struct device *pdev, | ||
85 | struct nxp_nci_phy_ops *phy_ops, unsigned int max_payload, | ||
86 | struct nci_dev **ndev); | ||
87 | void nxp_nci_remove(struct nci_dev *ndev); | ||
88 | |||
89 | #endif /* __LOCAL_NXP_NCI_H_ */ | ||
diff --git a/include/linux/platform_data/nxp-nci.h b/include/linux/platform_data/nxp-nci.h new file mode 100644 index 000000000000..d6ed28679bb2 --- /dev/null +++ b/include/linux/platform_data/nxp-nci.h | |||
@@ -0,0 +1,27 @@ | |||
1 | /* | ||
2 | * Generic platform data for the NXP NCI NFC chips. | ||
3 | * | ||
4 | * Copyright (C) 2014 NXP Semiconductors All rights reserved. | ||
5 | * | ||
6 | * Authors: Clément Perrochaud <clement.perrochaud@nxp.com> | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify it | ||
9 | * under the terms and conditions of the GNU General Public License, | ||
10 | * version 2, as published by the Free Software Foundation. | ||
11 | * | ||
12 | * This program is distributed in the hope that it will be useful, | ||
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
15 | * GNU General Public License for more details. | ||
16 | */ | ||
17 | |||
18 | #ifndef _NXP_NCI_H_ | ||
19 | #define _NXP_NCI_H_ | ||
20 | |||
21 | struct nxp_nci_nfc_platform_data { | ||
22 | unsigned int gpio_en; | ||
23 | unsigned int gpio_fw; | ||
24 | unsigned int irq; | ||
25 | }; | ||
26 | |||
27 | #endif /* _NXP_NCI_H_ */ | ||