diff options
26 files changed, 1207 insertions, 55 deletions
diff --git a/Documentation/devicetree/bindings/net/nfc/nxp-nci.txt b/Documentation/devicetree/bindings/net/nfc/nxp-nci.txt new file mode 100644 index 000000000000..5b6cd9b3f628 --- /dev/null +++ b/Documentation/devicetree/bindings/net/nfc/nxp-nci.txt | |||
@@ -0,0 +1,35 @@ | |||
1 | * NXP Semiconductors NXP NCI NFC Controllers | ||
2 | |||
3 | Required properties: | ||
4 | - compatible: Should be "nxp,nxp-nci-i2c". | ||
5 | - clock-frequency: I²C work frequency. | ||
6 | - reg: address on the bus | ||
7 | - interrupt-parent: phandle for the interrupt gpio controller | ||
8 | - interrupts: GPIO interrupt to which the chip is connected | ||
9 | - enable-gpios: Output GPIO pin used for enabling/disabling the chip | ||
10 | - firmware-gpios: Output GPIO pin used to enter firmware download mode | ||
11 | |||
12 | Optional SoC Specific Properties: | ||
13 | - pinctrl-names: Contains only one value - "default". | ||
14 | - pintctrl-0: Specifies the pin control groups used for this controller. | ||
15 | |||
16 | Example (for ARM-based BeagleBone with NPC100 NFC controller on I2C2): | ||
17 | |||
18 | &i2c2 { | ||
19 | |||
20 | status = "okay"; | ||
21 | |||
22 | npc100: npc100@29 { | ||
23 | |||
24 | compatible = "nxp,nxp-nci-i2c"; | ||
25 | |||
26 | reg = <0x29>; | ||
27 | clock-frequency = <100000>; | ||
28 | |||
29 | interrupt-parent = <&gpio1>; | ||
30 | interrupts = <29 GPIO_ACTIVE_HIGH>; | ||
31 | |||
32 | enable-gpios = <&gpio0 30 GPIO_ACTIVE_HIGH>; | ||
33 | firmware-gpios = <&gpio0 31 GPIO_ACTIVE_HIGH>; | ||
34 | }; | ||
35 | }; | ||
diff --git a/MAINTAINERS b/MAINTAINERS index 9091b4ad1cc3..dcaa54252479 100644 --- a/MAINTAINERS +++ b/MAINTAINERS | |||
@@ -6944,6 +6944,13 @@ S: Supported | |||
6944 | F: drivers/block/nvme* | 6944 | F: drivers/block/nvme* |
6945 | F: include/linux/nvme.h | 6945 | F: include/linux/nvme.h |
6946 | 6946 | ||
6947 | NXP-NCI NFC DRIVER | ||
6948 | M: Clément Perrochaud <clement.perrochaud@effinnov.com> | ||
6949 | R: Charles Gorand <charles.gorand@effinnov.com> | ||
6950 | L: linux-nfc@lists.01.org (moderated for non-subscribers) | ||
6951 | S: Supported | ||
6952 | F: drivers/nfc/nxp-nci | ||
6953 | |||
6947 | NXP TDA998X DRM DRIVER | 6954 | NXP TDA998X DRM DRIVER |
6948 | M: Russell King <rmk+kernel@arm.linux.org.uk> | 6955 | M: Russell King <rmk+kernel@arm.linux.org.uk> |
6949 | S: Supported | 6956 | 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/microread/i2c.c b/drivers/nfc/microread/i2c.c index df85cd3d9db0..661e2c8143c4 100644 --- a/drivers/nfc/microread/i2c.c +++ b/drivers/nfc/microread/i2c.c | |||
@@ -286,7 +286,7 @@ static int microread_i2c_probe(struct i2c_client *client, | |||
286 | if (r < 0) | 286 | if (r < 0) |
287 | goto err_irq; | 287 | goto err_irq; |
288 | 288 | ||
289 | nfc_info(&client->dev, "Probed"); | 289 | nfc_info(&client->dev, "Probed\n"); |
290 | 290 | ||
291 | return 0; | 291 | return 0; |
292 | 292 | ||
diff --git a/drivers/nfc/nfcmrvl/main.c b/drivers/nfc/nfcmrvl/main.c index 85e8bcf98693..ad4933cefbd1 100644 --- a/drivers/nfc/nfcmrvl/main.c +++ b/drivers/nfc/nfcmrvl/main.c | |||
@@ -111,7 +111,7 @@ struct nfcmrvl_private *nfcmrvl_nci_register_dev(void *drv_data, | |||
111 | 111 | ||
112 | priv->ndev = nci_allocate_device(&nfcmrvl_nci_ops, protocols, 0, 0); | 112 | priv->ndev = nci_allocate_device(&nfcmrvl_nci_ops, protocols, 0, 0); |
113 | if (!priv->ndev) { | 113 | if (!priv->ndev) { |
114 | nfc_err(dev, "nci_allocate_device failed"); | 114 | nfc_err(dev, "nci_allocate_device failed\n"); |
115 | rc = -ENOMEM; | 115 | rc = -ENOMEM; |
116 | goto error; | 116 | goto error; |
117 | } | 117 | } |
@@ -120,7 +120,7 @@ struct nfcmrvl_private *nfcmrvl_nci_register_dev(void *drv_data, | |||
120 | 120 | ||
121 | rc = nci_register_device(priv->ndev); | 121 | rc = nci_register_device(priv->ndev); |
122 | if (rc) { | 122 | if (rc) { |
123 | nfc_err(dev, "nci_register_device failed %d", rc); | 123 | nfc_err(dev, "nci_register_device failed %d\n", rc); |
124 | nci_free_device(priv->ndev); | 124 | nci_free_device(priv->ndev); |
125 | goto error; | 125 | goto error; |
126 | } | 126 | } |
diff --git a/drivers/nfc/nfcmrvl/usb.c b/drivers/nfc/nfcmrvl/usb.c index 3221ca37d6c9..6cf15c1a2618 100644 --- a/drivers/nfc/nfcmrvl/usb.c +++ b/drivers/nfc/nfcmrvl/usb.c | |||
@@ -80,7 +80,7 @@ static void nfcmrvl_bulk_complete(struct urb *urb) | |||
80 | if (!urb->status) { | 80 | if (!urb->status) { |
81 | if (nfcmrvl_nci_recv_frame(drv_data->priv, urb->transfer_buffer, | 81 | if (nfcmrvl_nci_recv_frame(drv_data->priv, urb->transfer_buffer, |
82 | urb->actual_length) < 0) | 82 | urb->actual_length) < 0) |
83 | nfc_err(&drv_data->udev->dev, "corrupted Rx packet"); | 83 | nfc_err(&drv_data->udev->dev, "corrupted Rx packet\n"); |
84 | } | 84 | } |
85 | 85 | ||
86 | if (!test_bit(NFCMRVL_USB_BULK_RUNNING, &drv_data->flags)) | 86 | if (!test_bit(NFCMRVL_USB_BULK_RUNNING, &drv_data->flags)) |
@@ -96,7 +96,7 @@ static void nfcmrvl_bulk_complete(struct urb *urb) | |||
96 | */ | 96 | */ |
97 | if (err != -EPERM && err != -ENODEV) | 97 | if (err != -EPERM && err != -ENODEV) |
98 | nfc_err(&drv_data->udev->dev, | 98 | nfc_err(&drv_data->udev->dev, |
99 | "urb %p failed to resubmit (%d)", urb, -err); | 99 | "urb %p failed to resubmit (%d)\n", urb, -err); |
100 | usb_unanchor_urb(urb); | 100 | usb_unanchor_urb(urb); |
101 | } | 101 | } |
102 | } | 102 | } |
@@ -137,7 +137,7 @@ nfcmrvl_submit_bulk_urb(struct nfcmrvl_usb_drv_data *drv_data, gfp_t mem_flags) | |||
137 | if (err) { | 137 | if (err) { |
138 | if (err != -EPERM && err != -ENODEV) | 138 | if (err != -EPERM && err != -ENODEV) |
139 | nfc_err(&drv_data->udev->dev, | 139 | nfc_err(&drv_data->udev->dev, |
140 | "urb %p submission failed (%d)", urb, -err); | 140 | "urb %p submission failed (%d)\n", urb, -err); |
141 | usb_unanchor_urb(urb); | 141 | usb_unanchor_urb(urb); |
142 | } | 142 | } |
143 | 143 | ||
@@ -153,7 +153,7 @@ static void nfcmrvl_tx_complete(struct urb *urb) | |||
153 | struct nfcmrvl_private *priv = nci_get_drvdata(ndev); | 153 | struct nfcmrvl_private *priv = nci_get_drvdata(ndev); |
154 | struct nfcmrvl_usb_drv_data *drv_data = priv->drv_data; | 154 | struct nfcmrvl_usb_drv_data *drv_data = priv->drv_data; |
155 | 155 | ||
156 | nfc_info(priv->dev, "urb %p status %d count %d", | 156 | nfc_info(priv->dev, "urb %p status %d count %d\n", |
157 | urb, urb->status, urb->actual_length); | 157 | urb, urb->status, urb->actual_length); |
158 | 158 | ||
159 | spin_lock(&drv_data->txlock); | 159 | spin_lock(&drv_data->txlock); |
@@ -253,7 +253,7 @@ static int nfcmrvl_usb_nci_send(struct nfcmrvl_private *priv, | |||
253 | if (err) { | 253 | if (err) { |
254 | if (err != -EPERM && err != -ENODEV) | 254 | if (err != -EPERM && err != -ENODEV) |
255 | nfc_err(&drv_data->udev->dev, | 255 | nfc_err(&drv_data->udev->dev, |
256 | "urb %p submission failed (%d)", urb, -err); | 256 | "urb %p submission failed (%d)\n", urb, -err); |
257 | kfree(urb->setup_packet); | 257 | kfree(urb->setup_packet); |
258 | usb_unanchor_urb(urb); | 258 | usb_unanchor_urb(urb); |
259 | } else { | 259 | } else { |
@@ -293,7 +293,7 @@ static int nfcmrvl_probe(struct usb_interface *intf, | |||
293 | int i; | 293 | int i; |
294 | struct usb_device *udev = interface_to_usbdev(intf); | 294 | struct usb_device *udev = interface_to_usbdev(intf); |
295 | 295 | ||
296 | nfc_info(&udev->dev, "intf %p id %p", intf, id); | 296 | nfc_info(&udev->dev, "intf %p id %p\n", intf, id); |
297 | 297 | ||
298 | drv_data = devm_kzalloc(&intf->dev, sizeof(*drv_data), GFP_KERNEL); | 298 | drv_data = devm_kzalloc(&intf->dev, sizeof(*drv_data), GFP_KERNEL); |
299 | if (!drv_data) | 299 | if (!drv_data) |
@@ -348,7 +348,7 @@ static void nfcmrvl_disconnect(struct usb_interface *intf) | |||
348 | if (!drv_data) | 348 | if (!drv_data) |
349 | return; | 349 | return; |
350 | 350 | ||
351 | nfc_info(&drv_data->udev->dev, "intf %p", intf); | 351 | nfc_info(&drv_data->udev->dev, "intf %p\n", intf); |
352 | 352 | ||
353 | nfcmrvl_nci_unregister_dev(drv_data->priv); | 353 | nfcmrvl_nci_unregister_dev(drv_data->priv); |
354 | 354 | ||
@@ -360,7 +360,7 @@ static int nfcmrvl_suspend(struct usb_interface *intf, pm_message_t message) | |||
360 | { | 360 | { |
361 | struct nfcmrvl_usb_drv_data *drv_data = usb_get_intfdata(intf); | 361 | struct nfcmrvl_usb_drv_data *drv_data = usb_get_intfdata(intf); |
362 | 362 | ||
363 | nfc_info(&drv_data->udev->dev, "intf %p", intf); | 363 | nfc_info(&drv_data->udev->dev, "intf %p\n", intf); |
364 | 364 | ||
365 | if (drv_data->suspend_count++) | 365 | if (drv_data->suspend_count++) |
366 | return 0; | 366 | return 0; |
@@ -401,7 +401,7 @@ static int nfcmrvl_resume(struct usb_interface *intf) | |||
401 | struct nfcmrvl_usb_drv_data *drv_data = usb_get_intfdata(intf); | 401 | struct nfcmrvl_usb_drv_data *drv_data = usb_get_intfdata(intf); |
402 | int err = 0; | 402 | int err = 0; |
403 | 403 | ||
404 | nfc_info(&drv_data->udev->dev, "intf %p", intf); | 404 | nfc_info(&drv_data->udev->dev, "intf %p\n", intf); |
405 | 405 | ||
406 | if (--drv_data->suspend_count) | 406 | if (--drv_data->suspend_count) |
407 | return 0; | 407 | return 0; |
diff --git a/drivers/nfc/nxp-nci/Kconfig b/drivers/nfc/nxp-nci/Kconfig new file mode 100644 index 000000000000..37b40612520d --- /dev/null +++ b/drivers/nfc/nxp-nci/Kconfig | |||
@@ -0,0 +1,25 @@ | |||
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. | ||
14 | |||
15 | config NFC_NXP_NCI_I2C | ||
16 | tristate "NXP-NCI I2C support" | ||
17 | depends on NFC_NXP_NCI && I2C | ||
18 | ---help--- | ||
19 | This module adds support for an I2C interface to the NXP NCI | ||
20 | chips. | ||
21 | Select this if your platform is using the I2C bus. | ||
22 | |||
23 | To compile this driver as a module, choose m here. The module will | ||
24 | be called nxp_nci_i2c. | ||
25 | Say Y if unsure. | ||
diff --git a/drivers/nfc/nxp-nci/Makefile b/drivers/nfc/nxp-nci/Makefile new file mode 100644 index 000000000000..c008be30bb18 --- /dev/null +++ b/drivers/nfc/nxp-nci/Makefile | |||
@@ -0,0 +1,11 @@ | |||
1 | # | ||
2 | # Makefile for NXP-NCI NFC driver | ||
3 | # | ||
4 | |||
5 | nxp-nci-objs = core.o firmware.o | ||
6 | nxp-nci_i2c-objs = i2c.o | ||
7 | |||
8 | obj-$(CONFIG_NFC_NXP_NCI) += nxp-nci.o | ||
9 | obj-$(CONFIG_NFC_NXP_NCI_I2C) += nxp-nci_i2c.o | ||
10 | |||
11 | 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..5291797324ba --- /dev/null +++ b/drivers/nfc/nxp-nci/firmware.c | |||
@@ -0,0 +1,325 @@ | |||
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 | release_firmware(fw_info->fw); | ||
239 | goto fw_download_exit; | ||
240 | } | ||
241 | |||
242 | info->mode = NXP_NCI_MODE_FW; | ||
243 | |||
244 | fw_info->data = fw_info->fw->data; | ||
245 | fw_info->size = fw_info->fw->size; | ||
246 | fw_info->written = 0; | ||
247 | fw_info->frame_size = 0; | ||
248 | fw_info->cmd_result = 0; | ||
249 | |||
250 | schedule_work(&fw_info->work); | ||
251 | |||
252 | fw_download_exit: | ||
253 | mutex_unlock(&info->info_lock); | ||
254 | return r; | ||
255 | } | ||
256 | |||
257 | static int nxp_nci_fw_read_status(u8 stat) | ||
258 | { | ||
259 | switch (stat) { | ||
260 | case NXP_NCI_FW_RESULT_OK: | ||
261 | return 0; | ||
262 | case NXP_NCI_FW_RESULT_INVALID_ADDR: | ||
263 | return -EINVAL; | ||
264 | case NXP_NCI_FW_RESULT_UNKNOWN_CMD: | ||
265 | return -EINVAL; | ||
266 | case NXP_NCI_FW_RESULT_ABORTED_CMD: | ||
267 | return -EMSGSIZE; | ||
268 | case NXP_NCI_FW_RESULT_ADDR_RANGE_OFL_ERROR: | ||
269 | return -EADDRNOTAVAIL; | ||
270 | case NXP_NCI_FW_RESULT_BUFFER_OFL_ERROR: | ||
271 | return -ENOBUFS; | ||
272 | case NXP_NCI_FW_RESULT_MEM_BSY: | ||
273 | return -ENOKEY; | ||
274 | case NXP_NCI_FW_RESULT_SIGNATURE_ERROR: | ||
275 | return -EKEYREJECTED; | ||
276 | case NXP_NCI_FW_RESULT_FIRMWARE_VERSION_ERROR: | ||
277 | return -EALREADY; | ||
278 | case NXP_NCI_FW_RESULT_PROTOCOL_ERROR: | ||
279 | return -EPROTO; | ||
280 | case NXP_NCI_FW_RESULT_SFWU_DEGRADED: | ||
281 | return -EHWPOISON; | ||
282 | case NXP_NCI_FW_RESULT_PH_STATUS_FIRST_CHUNK: | ||
283 | return 0; | ||
284 | case NXP_NCI_FW_RESULT_PH_STATUS_NEXT_CHUNK: | ||
285 | return 0; | ||
286 | case NXP_NCI_FW_RESULT_PH_STATUS_INTERNAL_ERROR_5: | ||
287 | return -EINVAL; | ||
288 | default: | ||
289 | return -EIO; | ||
290 | } | ||
291 | } | ||
292 | |||
293 | static u16 nxp_nci_fw_check_crc(struct sk_buff *skb) | ||
294 | { | ||
295 | u16 crc, frame_crc; | ||
296 | size_t len = skb->len - NXP_NCI_FW_CRC_LEN; | ||
297 | |||
298 | crc = nxp_nci_fw_crc(skb->data, len); | ||
299 | frame_crc = get_unaligned_be16(skb->data + len); | ||
300 | |||
301 | return (crc ^ frame_crc); | ||
302 | } | ||
303 | |||
304 | void nxp_nci_fw_recv_frame(struct nci_dev *ndev, struct sk_buff *skb) | ||
305 | { | ||
306 | struct nxp_nci_info *info = nci_get_drvdata(ndev); | ||
307 | struct nxp_nci_fw_info *fw_info = &info->fw_info; | ||
308 | |||
309 | complete(&fw_info->cmd_completion); | ||
310 | |||
311 | if (skb) { | ||
312 | if (nxp_nci_fw_check_crc(skb) != 0x00) | ||
313 | fw_info->cmd_result = -EBADMSG; | ||
314 | else | ||
315 | fw_info->cmd_result = nxp_nci_fw_read_status( | ||
316 | *skb_pull(skb, NXP_NCI_FW_HDR_LEN)); | ||
317 | kfree_skb(skb); | ||
318 | } else { | ||
319 | fw_info->cmd_result = -EIO; | ||
320 | } | ||
321 | |||
322 | if (fw_info->fw) | ||
323 | schedule_work(&fw_info->work); | ||
324 | } | ||
325 | EXPORT_SYMBOL(nxp_nci_fw_recv_frame); | ||
diff --git a/drivers/nfc/nxp-nci/i2c.c b/drivers/nfc/nxp-nci/i2c.c new file mode 100644 index 000000000000..17bd67dbebf0 --- /dev/null +++ b/drivers/nfc/nxp-nci/i2c.c | |||
@@ -0,0 +1,415 @@ | |||
1 | /* | ||
2 | * I2C link layer for the NXP NCI driver | ||
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 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt | ||
25 | |||
26 | #include <linux/delay.h> | ||
27 | #include <linux/i2c.h> | ||
28 | #include <linux/interrupt.h> | ||
29 | #include <linux/miscdevice.h> | ||
30 | #include <linux/module.h> | ||
31 | #include <linux/nfc.h> | ||
32 | #include <linux/of_gpio.h> | ||
33 | #include <linux/of_irq.h> | ||
34 | #include <linux/platform_data/nxp-nci.h> | ||
35 | #include <linux/unaligned/access_ok.h> | ||
36 | |||
37 | #include <net/nfc/nfc.h> | ||
38 | |||
39 | #include "nxp-nci.h" | ||
40 | |||
41 | #define NXP_NCI_I2C_DRIVER_NAME "nxp-nci_i2c" | ||
42 | |||
43 | #define NXP_NCI_I2C_MAX_PAYLOAD 32 | ||
44 | |||
45 | struct nxp_nci_i2c_phy { | ||
46 | struct i2c_client *i2c_dev; | ||
47 | struct nci_dev *ndev; | ||
48 | |||
49 | unsigned int gpio_en; | ||
50 | unsigned int gpio_fw; | ||
51 | |||
52 | int hard_fault; /* | ||
53 | * < 0 if hardware error occurred (e.g. i2c err) | ||
54 | * and prevents normal operation. | ||
55 | */ | ||
56 | }; | ||
57 | |||
58 | static int nxp_nci_i2c_set_mode(void *phy_id, | ||
59 | enum nxp_nci_mode mode) | ||
60 | { | ||
61 | struct nxp_nci_i2c_phy *phy = (struct nxp_nci_i2c_phy *) phy_id; | ||
62 | |||
63 | gpio_set_value(phy->gpio_fw, (mode == NXP_NCI_MODE_FW) ? 1 : 0); | ||
64 | gpio_set_value(phy->gpio_en, (mode != NXP_NCI_MODE_COLD) ? 1 : 0); | ||
65 | usleep_range(10000, 15000); | ||
66 | |||
67 | if (mode == NXP_NCI_MODE_COLD) | ||
68 | phy->hard_fault = 0; | ||
69 | |||
70 | return 0; | ||
71 | } | ||
72 | |||
73 | static int nxp_nci_i2c_write(void *phy_id, struct sk_buff *skb) | ||
74 | { | ||
75 | int r; | ||
76 | struct nxp_nci_i2c_phy *phy = phy_id; | ||
77 | struct i2c_client *client = phy->i2c_dev; | ||
78 | |||
79 | if (phy->hard_fault != 0) | ||
80 | return phy->hard_fault; | ||
81 | |||
82 | r = i2c_master_send(client, skb->data, skb->len); | ||
83 | if (r == -EREMOTEIO) { | ||
84 | /* Retry, chip was in standby */ | ||
85 | usleep_range(110000, 120000); | ||
86 | r = i2c_master_send(client, skb->data, skb->len); | ||
87 | } | ||
88 | |||
89 | if (r < 0) { | ||
90 | nfc_err(&client->dev, "Error %d on I2C send\n", r); | ||
91 | } else if (r != skb->len) { | ||
92 | nfc_err(&client->dev, | ||
93 | "Invalid length sent: %u (expected %u)\n", | ||
94 | r, skb->len); | ||
95 | r = -EREMOTEIO; | ||
96 | } else { | ||
97 | /* Success but return 0 and not number of bytes */ | ||
98 | r = 0; | ||
99 | } | ||
100 | |||
101 | return r; | ||
102 | } | ||
103 | |||
104 | static struct nxp_nci_phy_ops i2c_phy_ops = { | ||
105 | .set_mode = nxp_nci_i2c_set_mode, | ||
106 | .write = nxp_nci_i2c_write, | ||
107 | }; | ||
108 | |||
109 | static int nxp_nci_i2c_fw_read(struct nxp_nci_i2c_phy *phy, | ||
110 | struct sk_buff **skb) | ||
111 | { | ||
112 | struct i2c_client *client = phy->i2c_dev; | ||
113 | u16 header; | ||
114 | size_t frame_len; | ||
115 | int r; | ||
116 | |||
117 | r = i2c_master_recv(client, (u8 *) &header, NXP_NCI_FW_HDR_LEN); | ||
118 | if (r < 0) { | ||
119 | goto fw_read_exit; | ||
120 | } else if (r != NXP_NCI_FW_HDR_LEN) { | ||
121 | nfc_err(&client->dev, "Incorrect header length: %u\n", r); | ||
122 | r = -EBADMSG; | ||
123 | goto fw_read_exit; | ||
124 | } | ||
125 | |||
126 | frame_len = (get_unaligned_be16(&header) & NXP_NCI_FW_FRAME_LEN_MASK) + | ||
127 | NXP_NCI_FW_CRC_LEN; | ||
128 | |||
129 | *skb = alloc_skb(NXP_NCI_FW_HDR_LEN + frame_len, GFP_KERNEL); | ||
130 | if (*skb == NULL) { | ||
131 | r = -ENOMEM; | ||
132 | goto fw_read_exit; | ||
133 | } | ||
134 | |||
135 | memcpy(skb_put(*skb, NXP_NCI_FW_HDR_LEN), &header, NXP_NCI_FW_HDR_LEN); | ||
136 | |||
137 | r = i2c_master_recv(client, skb_put(*skb, frame_len), frame_len); | ||
138 | if (r != frame_len) { | ||
139 | nfc_err(&client->dev, | ||
140 | "Invalid frame length: %u (expected %zu)\n", | ||
141 | r, frame_len); | ||
142 | r = -EBADMSG; | ||
143 | goto fw_read_exit_free_skb; | ||
144 | } | ||
145 | |||
146 | return 0; | ||
147 | |||
148 | fw_read_exit_free_skb: | ||
149 | kfree_skb(*skb); | ||
150 | fw_read_exit: | ||
151 | return r; | ||
152 | } | ||
153 | |||
154 | static int nxp_nci_i2c_nci_read(struct nxp_nci_i2c_phy *phy, | ||
155 | struct sk_buff **skb) | ||
156 | { | ||
157 | struct nci_ctrl_hdr header; /* May actually be a data header */ | ||
158 | struct i2c_client *client = phy->i2c_dev; | ||
159 | int r; | ||
160 | |||
161 | r = i2c_master_recv(client, (u8 *) &header, NCI_CTRL_HDR_SIZE); | ||
162 | if (r < 0) { | ||
163 | goto nci_read_exit; | ||
164 | } else if (r != NCI_CTRL_HDR_SIZE) { | ||
165 | nfc_err(&client->dev, "Incorrect header length: %u\n", r); | ||
166 | r = -EBADMSG; | ||
167 | goto nci_read_exit; | ||
168 | } | ||
169 | |||
170 | *skb = alloc_skb(NCI_CTRL_HDR_SIZE + header.plen, GFP_KERNEL); | ||
171 | if (*skb == NULL) { | ||
172 | r = -ENOMEM; | ||
173 | goto nci_read_exit; | ||
174 | } | ||
175 | |||
176 | memcpy(skb_put(*skb, NCI_CTRL_HDR_SIZE), (void *) &header, | ||
177 | NCI_CTRL_HDR_SIZE); | ||
178 | |||
179 | r = i2c_master_recv(client, skb_put(*skb, header.plen), header.plen); | ||
180 | if (r != header.plen) { | ||
181 | nfc_err(&client->dev, | ||
182 | "Invalid frame payload length: %u (expected %u)\n", | ||
183 | r, header.plen); | ||
184 | r = -EBADMSG; | ||
185 | goto nci_read_exit_free_skb; | ||
186 | } | ||
187 | |||
188 | return 0; | ||
189 | |||
190 | nci_read_exit_free_skb: | ||
191 | kfree_skb(*skb); | ||
192 | nci_read_exit: | ||
193 | return r; | ||
194 | } | ||
195 | |||
196 | static irqreturn_t nxp_nci_i2c_irq_thread_fn(int irq, void *phy_id) | ||
197 | { | ||
198 | struct nxp_nci_i2c_phy *phy = phy_id; | ||
199 | struct i2c_client *client; | ||
200 | struct nxp_nci_info *info; | ||
201 | |||
202 | struct sk_buff *skb = NULL; | ||
203 | int r = 0; | ||
204 | |||
205 | if (!phy || !phy->ndev) | ||
206 | goto exit_irq_none; | ||
207 | |||
208 | client = phy->i2c_dev; | ||
209 | |||
210 | if (!client || irq != client->irq) | ||
211 | goto exit_irq_none; | ||
212 | |||
213 | info = nci_get_drvdata(phy->ndev); | ||
214 | |||
215 | if (!info) | ||
216 | goto exit_irq_none; | ||
217 | |||
218 | mutex_lock(&info->info_lock); | ||
219 | |||
220 | if (phy->hard_fault != 0) | ||
221 | goto exit_irq_handled; | ||
222 | |||
223 | switch (info->mode) { | ||
224 | case NXP_NCI_MODE_NCI: | ||
225 | r = nxp_nci_i2c_nci_read(phy, &skb); | ||
226 | break; | ||
227 | case NXP_NCI_MODE_FW: | ||
228 | r = nxp_nci_i2c_fw_read(phy, &skb); | ||
229 | break; | ||
230 | case NXP_NCI_MODE_COLD: | ||
231 | r = -EREMOTEIO; | ||
232 | break; | ||
233 | } | ||
234 | |||
235 | if (r == -EREMOTEIO) { | ||
236 | phy->hard_fault = r; | ||
237 | skb = NULL; | ||
238 | } else if (r < 0) { | ||
239 | nfc_err(&client->dev, "Read failed with error %d\n", r); | ||
240 | goto exit_irq_handled; | ||
241 | } | ||
242 | |||
243 | switch (info->mode) { | ||
244 | case NXP_NCI_MODE_NCI: | ||
245 | nci_recv_frame(phy->ndev, skb); | ||
246 | break; | ||
247 | case NXP_NCI_MODE_FW: | ||
248 | nxp_nci_fw_recv_frame(phy->ndev, skb); | ||
249 | break; | ||
250 | case NXP_NCI_MODE_COLD: | ||
251 | break; | ||
252 | } | ||
253 | |||
254 | exit_irq_handled: | ||
255 | mutex_unlock(&info->info_lock); | ||
256 | return IRQ_HANDLED; | ||
257 | exit_irq_none: | ||
258 | WARN_ON_ONCE(1); | ||
259 | return IRQ_NONE; | ||
260 | } | ||
261 | |||
262 | #ifdef CONFIG_OF | ||
263 | |||
264 | static int nxp_nci_i2c_parse_devtree(struct i2c_client *client) | ||
265 | { | ||
266 | struct nxp_nci_i2c_phy *phy = i2c_get_clientdata(client); | ||
267 | struct device_node *pp; | ||
268 | int r; | ||
269 | |||
270 | pp = client->dev.of_node; | ||
271 | if (!pp) | ||
272 | return -ENODEV; | ||
273 | |||
274 | r = of_get_named_gpio(pp, "enable-gpios", 0); | ||
275 | if (r == -EPROBE_DEFER) | ||
276 | r = of_get_named_gpio(pp, "enable-gpios", 0); | ||
277 | if (r < 0) { | ||
278 | nfc_err(&client->dev, "Failed to get EN gpio, error: %d\n", r); | ||
279 | return r; | ||
280 | } | ||
281 | phy->gpio_en = r; | ||
282 | |||
283 | r = of_get_named_gpio(pp, "firmware-gpios", 0); | ||
284 | if (r == -EPROBE_DEFER) | ||
285 | r = of_get_named_gpio(pp, "firmware-gpios", 0); | ||
286 | if (r < 0) { | ||
287 | nfc_err(&client->dev, "Failed to get FW gpio, error: %d\n", r); | ||
288 | return r; | ||
289 | } | ||
290 | phy->gpio_fw = r; | ||
291 | |||
292 | r = irq_of_parse_and_map(pp, 0); | ||
293 | if (r < 0) { | ||
294 | nfc_err(&client->dev, "Unable to get irq, error: %d\n", r); | ||
295 | return r; | ||
296 | } | ||
297 | client->irq = r; | ||
298 | |||
299 | return 0; | ||
300 | } | ||
301 | |||
302 | #else | ||
303 | |||
304 | static int nxp_nci_i2c_parse_devtree(struct i2c_client *client) | ||
305 | { | ||
306 | return -ENODEV; | ||
307 | } | ||
308 | |||
309 | #endif | ||
310 | |||
311 | static int nxp_nci_i2c_probe(struct i2c_client *client, | ||
312 | const struct i2c_device_id *id) | ||
313 | { | ||
314 | struct nxp_nci_i2c_phy *phy; | ||
315 | struct nxp_nci_nfc_platform_data *pdata; | ||
316 | int r; | ||
317 | |||
318 | if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { | ||
319 | nfc_err(&client->dev, "Need I2C_FUNC_I2C\n"); | ||
320 | r = -ENODEV; | ||
321 | goto probe_exit; | ||
322 | } | ||
323 | |||
324 | phy = devm_kzalloc(&client->dev, sizeof(struct nxp_nci_i2c_phy), | ||
325 | GFP_KERNEL); | ||
326 | if (!phy) { | ||
327 | r = -ENOMEM; | ||
328 | goto probe_exit; | ||
329 | } | ||
330 | |||
331 | phy->i2c_dev = client; | ||
332 | i2c_set_clientdata(client, phy); | ||
333 | |||
334 | pdata = client->dev.platform_data; | ||
335 | |||
336 | if (!pdata && client->dev.of_node) { | ||
337 | r = nxp_nci_i2c_parse_devtree(client); | ||
338 | if (r < 0) { | ||
339 | nfc_err(&client->dev, "Failed to get DT data\n"); | ||
340 | goto probe_exit; | ||
341 | } | ||
342 | } else if (pdata) { | ||
343 | phy->gpio_en = pdata->gpio_en; | ||
344 | phy->gpio_fw = pdata->gpio_fw; | ||
345 | client->irq = pdata->irq; | ||
346 | } else { | ||
347 | nfc_err(&client->dev, "No platform data\n"); | ||
348 | r = -EINVAL; | ||
349 | goto probe_exit; | ||
350 | } | ||
351 | |||
352 | r = devm_gpio_request_one(&phy->i2c_dev->dev, phy->gpio_en, | ||
353 | GPIOF_OUT_INIT_LOW, "nxp_nci_en"); | ||
354 | if (r < 0) | ||
355 | goto probe_exit; | ||
356 | |||
357 | r = devm_gpio_request_one(&phy->i2c_dev->dev, phy->gpio_fw, | ||
358 | GPIOF_OUT_INIT_LOW, "nxp_nci_fw"); | ||
359 | if (r < 0) | ||
360 | goto probe_exit; | ||
361 | |||
362 | r = nxp_nci_probe(phy, &client->dev, &i2c_phy_ops, | ||
363 | NXP_NCI_I2C_MAX_PAYLOAD, &phy->ndev); | ||
364 | if (r < 0) | ||
365 | goto probe_exit; | ||
366 | |||
367 | r = request_threaded_irq(client->irq, NULL, | ||
368 | nxp_nci_i2c_irq_thread_fn, | ||
369 | IRQF_TRIGGER_RISING | IRQF_ONESHOT, | ||
370 | NXP_NCI_I2C_DRIVER_NAME, phy); | ||
371 | if (r < 0) | ||
372 | nfc_err(&client->dev, "Unable to register IRQ handler\n"); | ||
373 | |||
374 | probe_exit: | ||
375 | return r; | ||
376 | } | ||
377 | |||
378 | static int nxp_nci_i2c_remove(struct i2c_client *client) | ||
379 | { | ||
380 | struct nxp_nci_i2c_phy *phy = i2c_get_clientdata(client); | ||
381 | |||
382 | nxp_nci_remove(phy->ndev); | ||
383 | free_irq(client->irq, phy); | ||
384 | |||
385 | return 0; | ||
386 | } | ||
387 | |||
388 | static struct i2c_device_id nxp_nci_i2c_id_table[] = { | ||
389 | {"nxp-nci_i2c", 0}, | ||
390 | {} | ||
391 | }; | ||
392 | MODULE_DEVICE_TABLE(i2c, nxp_nci_i2c_id_table); | ||
393 | |||
394 | static const struct of_device_id of_nxp_nci_i2c_match[] = { | ||
395 | { .compatible = "nxp,nxp-nci-i2c", }, | ||
396 | {}, | ||
397 | }; | ||
398 | MODULE_DEVICE_TABLE(of, of_nxp_nci_i2c_match); | ||
399 | |||
400 | static struct i2c_driver nxp_nci_i2c_driver = { | ||
401 | .driver = { | ||
402 | .name = NXP_NCI_I2C_DRIVER_NAME, | ||
403 | .owner = THIS_MODULE, | ||
404 | .of_match_table = of_match_ptr(of_nxp_nci_i2c_match), | ||
405 | }, | ||
406 | .probe = nxp_nci_i2c_probe, | ||
407 | .id_table = nxp_nci_i2c_id_table, | ||
408 | .remove = nxp_nci_i2c_remove, | ||
409 | }; | ||
410 | |||
411 | module_i2c_driver(nxp_nci_i2c_driver); | ||
412 | |||
413 | MODULE_LICENSE("GPL"); | ||
414 | MODULE_DESCRIPTION("I2C driver for NXP NCI NFC controllers"); | ||
415 | MODULE_AUTHOR("Clément Perrochaud <clement.perrochaud@nxp.com>"); | ||
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/drivers/nfc/pn533.c b/drivers/nfc/pn533.c index d46a700a9637..a03e4eb5fe29 100644 --- a/drivers/nfc/pn533.c +++ b/drivers/nfc/pn533.c | |||
@@ -1820,7 +1820,7 @@ static int pn533_rf_complete(struct pn533 *dev, void *arg, | |||
1820 | if (IS_ERR(resp)) { | 1820 | if (IS_ERR(resp)) { |
1821 | rc = PTR_ERR(resp); | 1821 | rc = PTR_ERR(resp); |
1822 | 1822 | ||
1823 | nfc_err(&dev->interface->dev, "RF setting error %d", rc); | 1823 | nfc_err(&dev->interface->dev, "RF setting error %d\n", rc); |
1824 | 1824 | ||
1825 | return rc; | 1825 | return rc; |
1826 | } | 1826 | } |
@@ -2554,8 +2554,10 @@ static int pn533_data_exchange_complete(struct pn533 *dev, void *_arg, | |||
2554 | } | 2554 | } |
2555 | 2555 | ||
2556 | skb = pn533_build_response(dev); | 2556 | skb = pn533_build_response(dev); |
2557 | if (!skb) | 2557 | if (!skb) { |
2558 | rc = -ENOMEM; | ||
2558 | goto error; | 2559 | goto error; |
2560 | } | ||
2559 | 2561 | ||
2560 | arg->cb(arg->cb_context, skb, 0); | 2562 | arg->cb(arg->cb_context, skb, 0); |
2561 | kfree(arg); | 2563 | kfree(arg); |
diff --git a/drivers/nfc/pn544/i2c.c b/drivers/nfc/pn544/i2c.c index cdde745b96bd..6fd986f5ac3e 100644 --- a/drivers/nfc/pn544/i2c.c +++ b/drivers/nfc/pn544/i2c.c | |||
@@ -953,7 +953,7 @@ static int pn544_hci_i2c_acpi_request_resources(struct i2c_client *client) | |||
953 | } | 953 | } |
954 | 954 | ||
955 | nfc_info(dev, "GPIO resource, no:%d irq:%d\n", | 955 | nfc_info(dev, "GPIO resource, no:%d irq:%d\n", |
956 | desc_to_gpio(gpiod_irq), ret); | 956 | desc_to_gpio(gpiod_irq), ret); |
957 | client->irq = ret; | 957 | client->irq = ret; |
958 | 958 | ||
959 | return 0; | 959 | return 0; |
@@ -1062,11 +1062,8 @@ static int pn544_hci_i2c_probe(struct i2c_client *client, | |||
1062 | 1062 | ||
1063 | phy = devm_kzalloc(&client->dev, sizeof(struct pn544_i2c_phy), | 1063 | phy = devm_kzalloc(&client->dev, sizeof(struct pn544_i2c_phy), |
1064 | GFP_KERNEL); | 1064 | GFP_KERNEL); |
1065 | if (!phy) { | 1065 | if (!phy) |
1066 | nfc_err(&client->dev, | ||
1067 | "Cannot allocate memory for pn544 i2c phy.\n"); | ||
1068 | return -ENOMEM; | 1066 | return -ENOMEM; |
1069 | } | ||
1070 | 1067 | ||
1071 | INIT_WORK(&phy->fw_work, pn544_hci_i2c_fw_work); | 1068 | INIT_WORK(&phy->fw_work, pn544_hci_i2c_fw_work); |
1072 | phy->fw_work_state = FW_WORK_STATE_IDLE; | 1069 | phy->fw_work_state = FW_WORK_STATE_IDLE; |
diff --git a/drivers/nfc/port100.c b/drivers/nfc/port100.c index 4ac4d31f6c59..87d509996704 100644 --- a/drivers/nfc/port100.c +++ b/drivers/nfc/port100.c | |||
@@ -604,11 +604,11 @@ static void port100_recv_response(struct urb *urb) | |||
604 | case -ECONNRESET: | 604 | case -ECONNRESET: |
605 | case -ENOENT: | 605 | case -ENOENT: |
606 | nfc_err(&dev->interface->dev, | 606 | nfc_err(&dev->interface->dev, |
607 | "The urb has been canceled (status %d)", urb->status); | 607 | "The urb has been canceled (status %d)\n", urb->status); |
608 | goto sched_wq; | 608 | goto sched_wq; |
609 | case -ESHUTDOWN: | 609 | case -ESHUTDOWN: |
610 | default: | 610 | default: |
611 | nfc_err(&dev->interface->dev, "Urb failure (status %d)", | 611 | nfc_err(&dev->interface->dev, "Urb failure (status %d)\n", |
612 | urb->status); | 612 | urb->status); |
613 | goto sched_wq; | 613 | goto sched_wq; |
614 | } | 614 | } |
@@ -616,7 +616,7 @@ static void port100_recv_response(struct urb *urb) | |||
616 | in_frame = dev->in_urb->transfer_buffer; | 616 | in_frame = dev->in_urb->transfer_buffer; |
617 | 617 | ||
618 | if (!port100_rx_frame_is_valid(in_frame)) { | 618 | if (!port100_rx_frame_is_valid(in_frame)) { |
619 | nfc_err(&dev->interface->dev, "Received an invalid frame"); | 619 | nfc_err(&dev->interface->dev, "Received an invalid frame\n"); |
620 | cmd->status = -EIO; | 620 | cmd->status = -EIO; |
621 | goto sched_wq; | 621 | goto sched_wq; |
622 | } | 622 | } |
@@ -626,7 +626,7 @@ static void port100_recv_response(struct urb *urb) | |||
626 | 626 | ||
627 | if (!port100_rx_frame_is_cmd_response(dev, in_frame)) { | 627 | if (!port100_rx_frame_is_cmd_response(dev, in_frame)) { |
628 | nfc_err(&dev->interface->dev, | 628 | nfc_err(&dev->interface->dev, |
629 | "It's not the response to the last command"); | 629 | "It's not the response to the last command\n"); |
630 | cmd->status = -EIO; | 630 | cmd->status = -EIO; |
631 | goto sched_wq; | 631 | goto sched_wq; |
632 | } | 632 | } |
@@ -657,11 +657,11 @@ static void port100_recv_ack(struct urb *urb) | |||
657 | case -ECONNRESET: | 657 | case -ECONNRESET: |
658 | case -ENOENT: | 658 | case -ENOENT: |
659 | nfc_err(&dev->interface->dev, | 659 | nfc_err(&dev->interface->dev, |
660 | "The urb has been stopped (status %d)", urb->status); | 660 | "The urb has been stopped (status %d)\n", urb->status); |
661 | goto sched_wq; | 661 | goto sched_wq; |
662 | case -ESHUTDOWN: | 662 | case -ESHUTDOWN: |
663 | default: | 663 | default: |
664 | nfc_err(&dev->interface->dev, "Urb failure (status %d)", | 664 | nfc_err(&dev->interface->dev, "Urb failure (status %d)\n", |
665 | urb->status); | 665 | urb->status); |
666 | goto sched_wq; | 666 | goto sched_wq; |
667 | } | 667 | } |
@@ -669,7 +669,7 @@ static void port100_recv_ack(struct urb *urb) | |||
669 | in_frame = dev->in_urb->transfer_buffer; | 669 | in_frame = dev->in_urb->transfer_buffer; |
670 | 670 | ||
671 | if (!port100_rx_frame_is_ack(in_frame)) { | 671 | if (!port100_rx_frame_is_ack(in_frame)) { |
672 | nfc_err(&dev->interface->dev, "Received an invalid ack"); | 672 | nfc_err(&dev->interface->dev, "Received an invalid ack\n"); |
673 | cmd->status = -EIO; | 673 | cmd->status = -EIO; |
674 | goto sched_wq; | 674 | goto sched_wq; |
675 | } | 675 | } |
@@ -677,7 +677,7 @@ static void port100_recv_ack(struct urb *urb) | |||
677 | rc = port100_submit_urb_for_response(dev, GFP_ATOMIC); | 677 | rc = port100_submit_urb_for_response(dev, GFP_ATOMIC); |
678 | if (rc) { | 678 | if (rc) { |
679 | nfc_err(&dev->interface->dev, | 679 | nfc_err(&dev->interface->dev, |
680 | "usb_submit_urb failed with result %d", rc); | 680 | "usb_submit_urb failed with result %d\n", rc); |
681 | cmd->status = rc; | 681 | cmd->status = rc; |
682 | goto sched_wq; | 682 | goto sched_wq; |
683 | } | 683 | } |
@@ -873,11 +873,11 @@ static void port100_send_complete(struct urb *urb) | |||
873 | case -ECONNRESET: | 873 | case -ECONNRESET: |
874 | case -ENOENT: | 874 | case -ENOENT: |
875 | nfc_err(&dev->interface->dev, | 875 | nfc_err(&dev->interface->dev, |
876 | "The urb has been stopped (status %d)", urb->status); | 876 | "The urb has been stopped (status %d)\n", urb->status); |
877 | break; | 877 | break; |
878 | case -ESHUTDOWN: | 878 | case -ESHUTDOWN: |
879 | default: | 879 | default: |
880 | nfc_err(&dev->interface->dev, "Urb failure (status %d)", | 880 | nfc_err(&dev->interface->dev, "Urb failure (status %d)\n", |
881 | urb->status); | 881 | urb->status); |
882 | } | 882 | } |
883 | } | 883 | } |
@@ -1094,7 +1094,7 @@ static void port100_in_comm_rf_complete(struct port100 *dev, void *arg, | |||
1094 | 1094 | ||
1095 | if (resp->len < 4) { | 1095 | if (resp->len < 4) { |
1096 | nfc_err(&dev->interface->dev, | 1096 | nfc_err(&dev->interface->dev, |
1097 | "Invalid packet length received.\n"); | 1097 | "Invalid packet length received\n"); |
1098 | rc = -EIO; | 1098 | rc = -EIO; |
1099 | goto error; | 1099 | goto error; |
1100 | } | 1100 | } |
@@ -1250,7 +1250,7 @@ static bool port100_tg_target_activated(struct port100 *dev, u8 tgt_activated) | |||
1250 | PORT100_MDAA_TGT_WAS_ACTIVATED_MASK; | 1250 | PORT100_MDAA_TGT_WAS_ACTIVATED_MASK; |
1251 | break; | 1251 | break; |
1252 | default: | 1252 | default: |
1253 | nfc_err(&dev->interface->dev, "Unknonwn command type.\n"); | 1253 | nfc_err(&dev->interface->dev, "Unknown command type\n"); |
1254 | return false; | 1254 | return false; |
1255 | } | 1255 | } |
1256 | 1256 | ||
@@ -1481,7 +1481,7 @@ static int port100_probe(struct usb_interface *interface, | |||
1481 | cmd_type_mask = port100_get_command_type_mask(dev); | 1481 | cmd_type_mask = port100_get_command_type_mask(dev); |
1482 | if (!cmd_type_mask) { | 1482 | if (!cmd_type_mask) { |
1483 | nfc_err(&interface->dev, | 1483 | nfc_err(&interface->dev, |
1484 | "Could not get supported command types.\n"); | 1484 | "Could not get supported command types\n"); |
1485 | rc = -ENODEV; | 1485 | rc = -ENODEV; |
1486 | goto error; | 1486 | goto error; |
1487 | } | 1487 | } |
@@ -1494,7 +1494,7 @@ static int port100_probe(struct usb_interface *interface, | |||
1494 | rc = port100_set_command_type(dev, dev->cmd_type); | 1494 | rc = port100_set_command_type(dev, dev->cmd_type); |
1495 | if (rc) { | 1495 | if (rc) { |
1496 | nfc_err(&interface->dev, | 1496 | nfc_err(&interface->dev, |
1497 | "The device does not support command type %u.\n", | 1497 | "The device does not support command type %u\n", |
1498 | dev->cmd_type); | 1498 | dev->cmd_type); |
1499 | goto error; | 1499 | goto error; |
1500 | } | 1500 | } |
@@ -1502,7 +1502,7 @@ static int port100_probe(struct usb_interface *interface, | |||
1502 | fw_version = port100_get_firmware_version(dev); | 1502 | fw_version = port100_get_firmware_version(dev); |
1503 | if (!fw_version) | 1503 | if (!fw_version) |
1504 | nfc_err(&interface->dev, | 1504 | nfc_err(&interface->dev, |
1505 | "Could not get device firmware version.\n"); | 1505 | "Could not get device firmware version\n"); |
1506 | 1506 | ||
1507 | nfc_info(&interface->dev, | 1507 | nfc_info(&interface->dev, |
1508 | "Sony NFC Port-100 Series attached (firmware v%x.%02x)\n", | 1508 | "Sony NFC Port-100 Series attached (firmware v%x.%02x)\n", |
@@ -1515,7 +1515,7 @@ static int port100_probe(struct usb_interface *interface, | |||
1515 | dev->skb_tailroom); | 1515 | dev->skb_tailroom); |
1516 | if (!dev->nfc_digital_dev) { | 1516 | if (!dev->nfc_digital_dev) { |
1517 | nfc_err(&interface->dev, | 1517 | nfc_err(&interface->dev, |
1518 | "Could not allocate nfc_digital_dev.\n"); | 1518 | "Could not allocate nfc_digital_dev\n"); |
1519 | rc = -ENOMEM; | 1519 | rc = -ENOMEM; |
1520 | goto error; | 1520 | goto error; |
1521 | } | 1521 | } |
@@ -1526,7 +1526,7 @@ static int port100_probe(struct usb_interface *interface, | |||
1526 | rc = nfc_digital_register_device(dev->nfc_digital_dev); | 1526 | rc = nfc_digital_register_device(dev->nfc_digital_dev); |
1527 | if (rc) { | 1527 | if (rc) { |
1528 | nfc_err(&interface->dev, | 1528 | nfc_err(&interface->dev, |
1529 | "Could not register digital device.\n"); | 1529 | "Could not register digital device\n"); |
1530 | goto free_nfc_dev; | 1530 | goto free_nfc_dev; |
1531 | } | 1531 | } |
1532 | 1532 | ||
@@ -1562,7 +1562,7 @@ static void port100_disconnect(struct usb_interface *interface) | |||
1562 | 1562 | ||
1563 | kfree(dev->cmd); | 1563 | kfree(dev->cmd); |
1564 | 1564 | ||
1565 | nfc_info(&interface->dev, "Sony Port-100 NFC device disconnected"); | 1565 | nfc_info(&interface->dev, "Sony Port-100 NFC device disconnected\n"); |
1566 | } | 1566 | } |
1567 | 1567 | ||
1568 | static struct usb_driver port100_driver = { | 1568 | static struct usb_driver port100_driver = { |
diff --git a/drivers/nfc/st21nfca/st21nfca.c b/drivers/nfc/st21nfca/st21nfca.c index 24d3d240d5f4..d251f7229c4e 100644 --- a/drivers/nfc/st21nfca/st21nfca.c +++ b/drivers/nfc/st21nfca/st21nfca.c | |||
@@ -572,7 +572,7 @@ exit: | |||
572 | return r; | 572 | return r; |
573 | } | 573 | } |
574 | 574 | ||
575 | static int st21nfca_get_iso14443_3_uid(struct nfc_hci_dev *hdev, u8 *gate, | 575 | static int st21nfca_get_iso14443_3_uid(struct nfc_hci_dev *hdev, u8 *uid, |
576 | int *len) | 576 | int *len) |
577 | { | 577 | { |
578 | int r; | 578 | int r; |
@@ -588,7 +588,7 @@ static int st21nfca_get_iso14443_3_uid(struct nfc_hci_dev *hdev, u8 *gate, | |||
588 | goto exit; | 588 | goto exit; |
589 | } | 589 | } |
590 | 590 | ||
591 | gate = uid_skb->data; | 591 | memcpy(uid, uid_skb->data, uid_skb->len); |
592 | *len = uid_skb->len; | 592 | *len = uid_skb->len; |
593 | exit: | 593 | exit: |
594 | kfree_skb(uid_skb); | 594 | kfree_skb(uid_skb); |
diff --git a/drivers/nfc/st21nfca/st21nfca_se.c b/drivers/nfc/st21nfca/st21nfca_se.c index bd13cac9c66a..3197e9bb66f7 100644 --- a/drivers/nfc/st21nfca/st21nfca_se.c +++ b/drivers/nfc/st21nfca/st21nfca_se.c | |||
@@ -310,6 +310,13 @@ int st21nfca_connectivity_event_received(struct nfc_hci_dev *hdev, u8 host, | |||
310 | case ST21NFCA_EVT_CONNECTIVITY: | 310 | case ST21NFCA_EVT_CONNECTIVITY: |
311 | break; | 311 | break; |
312 | case ST21NFCA_EVT_TRANSACTION: | 312 | case ST21NFCA_EVT_TRANSACTION: |
313 | /* | ||
314 | * According to specification etsi 102 622 | ||
315 | * 11.2.2.4 EVT_TRANSACTION Table 52 | ||
316 | * Description Tag Length | ||
317 | * AID 81 5 to 16 | ||
318 | * PARAMETERS 82 0 to 255 | ||
319 | */ | ||
313 | if (skb->len < NFC_MIN_AID_LENGTH + 2 && | 320 | if (skb->len < NFC_MIN_AID_LENGTH + 2 && |
314 | skb->data[0] != NFC_EVT_TRANSACTION_AID_TAG) | 321 | skb->data[0] != NFC_EVT_TRANSACTION_AID_TAG) |
315 | return -EPROTO; | 322 | return -EPROTO; |
@@ -318,8 +325,10 @@ int st21nfca_connectivity_event_received(struct nfc_hci_dev *hdev, u8 host, | |||
318 | skb->len - 2, GFP_KERNEL); | 325 | skb->len - 2, GFP_KERNEL); |
319 | 326 | ||
320 | transaction->aid_len = skb->data[1]; | 327 | transaction->aid_len = skb->data[1]; |
321 | memcpy(transaction->aid, &skb->data[2], skb->data[1]); | 328 | memcpy(transaction->aid, &skb->data[2], |
329 | transaction->aid_len); | ||
322 | 330 | ||
331 | /* Check next byte is PARAMETERS tag (82) */ | ||
323 | if (skb->data[transaction->aid_len + 2] != | 332 | if (skb->data[transaction->aid_len + 2] != |
324 | NFC_EVT_TRANSACTION_PARAMS_TAG) | 333 | NFC_EVT_TRANSACTION_PARAMS_TAG) |
325 | return -EPROTO; | 334 | return -EPROTO; |
diff --git a/drivers/nfc/st21nfcb/i2c.c b/drivers/nfc/st21nfcb/i2c.c index eb886932d972..76a4cad41cec 100644 --- a/drivers/nfc/st21nfcb/i2c.c +++ b/drivers/nfc/st21nfcb/i2c.c | |||
@@ -109,7 +109,7 @@ static int st21nfcb_nci_i2c_write(void *phy_id, struct sk_buff *skb) | |||
109 | return phy->ndlc->hard_fault; | 109 | return phy->ndlc->hard_fault; |
110 | 110 | ||
111 | r = i2c_master_send(client, skb->data, skb->len); | 111 | r = i2c_master_send(client, skb->data, skb->len); |
112 | if (r == -EREMOTEIO) { /* Retry, chip was in standby */ | 112 | if (r < 0) { /* Retry, chip was in standby */ |
113 | usleep_range(1000, 4000); | 113 | usleep_range(1000, 4000); |
114 | r = i2c_master_send(client, skb->data, skb->len); | 114 | r = i2c_master_send(client, skb->data, skb->len); |
115 | } | 115 | } |
@@ -148,7 +148,7 @@ static int st21nfcb_nci_i2c_read(struct st21nfcb_i2c_phy *phy, | |||
148 | struct i2c_client *client = phy->i2c_dev; | 148 | struct i2c_client *client = phy->i2c_dev; |
149 | 149 | ||
150 | r = i2c_master_recv(client, buf, ST21NFCB_NCI_I2C_MIN_SIZE); | 150 | r = i2c_master_recv(client, buf, ST21NFCB_NCI_I2C_MIN_SIZE); |
151 | if (r == -EREMOTEIO) { /* Retry, chip was in standby */ | 151 | if (r < 0) { /* Retry, chip was in standby */ |
152 | usleep_range(1000, 4000); | 152 | usleep_range(1000, 4000); |
153 | r = i2c_master_recv(client, buf, ST21NFCB_NCI_I2C_MIN_SIZE); | 153 | r = i2c_master_recv(client, buf, ST21NFCB_NCI_I2C_MIN_SIZE); |
154 | } | 154 | } |
@@ -313,11 +313,8 @@ static int st21nfcb_nci_i2c_probe(struct i2c_client *client, | |||
313 | 313 | ||
314 | phy = devm_kzalloc(&client->dev, sizeof(struct st21nfcb_i2c_phy), | 314 | phy = devm_kzalloc(&client->dev, sizeof(struct st21nfcb_i2c_phy), |
315 | GFP_KERNEL); | 315 | GFP_KERNEL); |
316 | if (!phy) { | 316 | if (!phy) |
317 | nfc_err(&client->dev, | ||
318 | "Cannot allocate memory for st21nfcb i2c phy.\n"); | ||
319 | return -ENOMEM; | 317 | return -ENOMEM; |
320 | } | ||
321 | 318 | ||
322 | phy->i2c_dev = client; | 319 | phy->i2c_dev = client; |
323 | 320 | ||
diff --git a/drivers/nfc/st21nfcb/ndlc.c b/drivers/nfc/st21nfcb/ndlc.c index 5fbf59d2138c..6014b5859465 100644 --- a/drivers/nfc/st21nfcb/ndlc.c +++ b/drivers/nfc/st21nfcb/ndlc.c | |||
@@ -256,10 +256,9 @@ int ndlc_probe(void *phy_id, struct nfc_phy_ops *phy_ops, struct device *dev, | |||
256 | struct llt_ndlc *ndlc; | 256 | struct llt_ndlc *ndlc; |
257 | 257 | ||
258 | ndlc = devm_kzalloc(dev, sizeof(struct llt_ndlc), GFP_KERNEL); | 258 | ndlc = devm_kzalloc(dev, sizeof(struct llt_ndlc), GFP_KERNEL); |
259 | if (!ndlc) { | 259 | if (!ndlc) |
260 | nfc_err(dev, "Cannot allocate memory for ndlc.\n"); | ||
261 | return -ENOMEM; | 260 | return -ENOMEM; |
262 | } | 261 | |
263 | ndlc->ops = phy_ops; | 262 | ndlc->ops = phy_ops; |
264 | ndlc->phy_id = phy_id; | 263 | ndlc->phy_id = phy_id; |
265 | ndlc->dev = dev; | 264 | ndlc->dev = dev; |
diff --git a/drivers/nfc/st21nfcb/st21nfcb_se.c b/drivers/nfc/st21nfcb/st21nfcb_se.c index 7c82e9d87a65..24862a525fb5 100644 --- a/drivers/nfc/st21nfcb/st21nfcb_se.c +++ b/drivers/nfc/st21nfcb/st21nfcb_se.c | |||
@@ -321,6 +321,12 @@ static int st21nfcb_hci_connectivity_event_received(struct nci_dev *ndev, | |||
321 | 321 | ||
322 | break; | 322 | break; |
323 | case ST21NFCB_EVT_TRANSACTION: | 323 | case ST21NFCB_EVT_TRANSACTION: |
324 | /* According to specification etsi 102 622 | ||
325 | * 11.2.2.4 EVT_TRANSACTION Table 52 | ||
326 | * Description Tag Length | ||
327 | * AID 81 5 to 16 | ||
328 | * PARAMETERS 82 0 to 255 | ||
329 | */ | ||
324 | if (skb->len < NFC_MIN_AID_LENGTH + 2 && | 330 | if (skb->len < NFC_MIN_AID_LENGTH + 2 && |
325 | skb->data[0] != NFC_EVT_TRANSACTION_AID_TAG) | 331 | skb->data[0] != NFC_EVT_TRANSACTION_AID_TAG) |
326 | return -EPROTO; | 332 | return -EPROTO; |
@@ -329,8 +335,9 @@ static int st21nfcb_hci_connectivity_event_received(struct nci_dev *ndev, | |||
329 | skb->len - 2, GFP_KERNEL); | 335 | skb->len - 2, GFP_KERNEL); |
330 | 336 | ||
331 | transaction->aid_len = skb->data[1]; | 337 | transaction->aid_len = skb->data[1]; |
332 | memcpy(transaction->aid, &skb->data[2], skb->data[1]); | 338 | memcpy(transaction->aid, &skb->data[2], transaction->aid_len); |
333 | 339 | ||
340 | /* Check next byte is PARAMETERS tag (82) */ | ||
334 | if (skb->data[transaction->aid_len + 2] != | 341 | if (skb->data[transaction->aid_len + 2] != |
335 | NFC_EVT_TRANSACTION_PARAMS_TAG) | 342 | NFC_EVT_TRANSACTION_PARAMS_TAG) |
336 | return -EPROTO; | 343 | return -EPROTO; |
@@ -340,6 +347,7 @@ static int st21nfcb_hci_connectivity_event_received(struct nci_dev *ndev, | |||
340 | transaction->aid_len + 4, transaction->params_len); | 347 | transaction->aid_len + 4, transaction->params_len); |
341 | 348 | ||
342 | r = nfc_se_transaction(ndev->nfc_dev, host, transaction); | 349 | r = nfc_se_transaction(ndev->nfc_dev, host, transaction); |
350 | break; | ||
343 | default: | 351 | default: |
344 | return 1; | 352 | return 1; |
345 | } | 353 | } |
@@ -542,14 +550,12 @@ static int st21nfcb_hci_network_init(struct nci_dev *ndev) | |||
542 | 550 | ||
543 | r = nci_hci_dev_session_init(ndev); | 551 | r = nci_hci_dev_session_init(ndev); |
544 | if (r != NCI_HCI_ANY_OK) | 552 | if (r != NCI_HCI_ANY_OK) |
545 | goto exit; | 553 | goto free_dest_params; |
546 | 554 | ||
547 | r = nci_nfcee_mode_set(ndev, ndev->hci_dev->conn_info->id, | 555 | r = nci_nfcee_mode_set(ndev, ndev->hci_dev->conn_info->id, |
548 | NCI_NFCEE_ENABLE); | 556 | NCI_NFCEE_ENABLE); |
549 | if (r != NCI_STATUS_OK) | 557 | if (r != NCI_STATUS_OK) |
550 | goto exit; | 558 | goto free_dest_params; |
551 | |||
552 | return 0; | ||
553 | 559 | ||
554 | free_dest_params: | 560 | free_dest_params: |
555 | kfree(dest_params); | 561 | kfree(dest_params); |
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_ */ | ||
diff --git a/include/net/nfc/hci.h b/include/net/nfc/hci.h index ab672b537dd4..020a814bc8ed 100644 --- a/include/net/nfc/hci.h +++ b/include/net/nfc/hci.h | |||
@@ -83,6 +83,10 @@ struct nfc_hci_pipe { | |||
83 | }; | 83 | }; |
84 | 84 | ||
85 | #define NFC_HCI_MAX_CUSTOM_GATES 50 | 85 | #define NFC_HCI_MAX_CUSTOM_GATES 50 |
86 | /* | ||
87 | * According to specification 102 622 chapter 4.4 Pipes, | ||
88 | * the pipe identifier is 7 bits long. | ||
89 | */ | ||
86 | #define NFC_HCI_MAX_PIPES 127 | 90 | #define NFC_HCI_MAX_PIPES 127 |
87 | struct nfc_hci_init_data { | 91 | struct nfc_hci_init_data { |
88 | u8 gate_count; | 92 | u8 gate_count; |
diff --git a/include/net/nfc/nci_core.h b/include/net/nfc/nci_core.h index ff87f8611fa3..d4dcc7199fd7 100644 --- a/include/net/nfc/nci_core.h +++ b/include/net/nfc/nci_core.h | |||
@@ -71,6 +71,7 @@ struct nci_ops { | |||
71 | int (*close)(struct nci_dev *ndev); | 71 | int (*close)(struct nci_dev *ndev); |
72 | int (*send)(struct nci_dev *ndev, struct sk_buff *skb); | 72 | int (*send)(struct nci_dev *ndev, struct sk_buff *skb); |
73 | int (*setup)(struct nci_dev *ndev); | 73 | int (*setup)(struct nci_dev *ndev); |
74 | int (*fw_download)(struct nci_dev *ndev, const char *firmware_name); | ||
74 | __u32 (*get_rfprotocol)(struct nci_dev *ndev, __u8 rf_protocol); | 75 | __u32 (*get_rfprotocol)(struct nci_dev *ndev, __u8 rf_protocol); |
75 | int (*discover_se)(struct nci_dev *ndev); | 76 | int (*discover_se)(struct nci_dev *ndev); |
76 | int (*disable_se)(struct nci_dev *ndev, u32 se_idx); | 77 | int (*disable_se)(struct nci_dev *ndev, u32 se_idx); |
@@ -137,6 +138,10 @@ struct nci_conn_info { | |||
137 | #define NCI_HCI_INVALID_HOST 0x80 | 138 | #define NCI_HCI_INVALID_HOST 0x80 |
138 | 139 | ||
139 | #define NCI_HCI_MAX_CUSTOM_GATES 50 | 140 | #define NCI_HCI_MAX_CUSTOM_GATES 50 |
141 | /* | ||
142 | * According to specification 102 622 chapter 4.4 Pipes, | ||
143 | * the pipe identifier is 7 bits long. | ||
144 | */ | ||
140 | #define NCI_HCI_MAX_PIPES 127 | 145 | #define NCI_HCI_MAX_PIPES 127 |
141 | 146 | ||
142 | struct nci_hci_gate { | 147 | struct nci_hci_gate { |
diff --git a/include/net/nfc/nfc.h b/include/net/nfc/nfc.h index 73190e65d5c1..7ac029c07546 100644 --- a/include/net/nfc/nfc.h +++ b/include/net/nfc/nfc.h | |||
@@ -157,7 +157,7 @@ struct nfc_evt_transaction { | |||
157 | u32 aid_len; | 157 | u32 aid_len; |
158 | u8 aid[NFC_MAX_AID_LENGTH]; | 158 | u8 aid[NFC_MAX_AID_LENGTH]; |
159 | u8 params_len; | 159 | u8 params_len; |
160 | u8 params[NFC_MAX_PARAMS_LENGTH]; | 160 | u8 params[0]; |
161 | } __packed; | 161 | } __packed; |
162 | 162 | ||
163 | struct nfc_genl_data { | 163 | struct nfc_genl_data { |
diff --git a/net/nfc/nci/core.c b/net/nfc/nci/core.c index 9575a1892607..49ff32106080 100644 --- a/net/nfc/nci/core.c +++ b/net/nfc/nci/core.c | |||
@@ -907,6 +907,16 @@ static int nci_se_io(struct nfc_dev *nfc_dev, u32 se_idx, | |||
907 | return 0; | 907 | return 0; |
908 | } | 908 | } |
909 | 909 | ||
910 | static int nci_fw_download(struct nfc_dev *nfc_dev, const char *firmware_name) | ||
911 | { | ||
912 | struct nci_dev *ndev = nfc_get_drvdata(nfc_dev); | ||
913 | |||
914 | if (!ndev->ops->fw_download) | ||
915 | return -ENOTSUPP; | ||
916 | |||
917 | return ndev->ops->fw_download(ndev, firmware_name); | ||
918 | } | ||
919 | |||
910 | static struct nfc_ops nci_nfc_ops = { | 920 | static struct nfc_ops nci_nfc_ops = { |
911 | .dev_up = nci_dev_up, | 921 | .dev_up = nci_dev_up, |
912 | .dev_down = nci_dev_down, | 922 | .dev_down = nci_dev_down, |
@@ -922,6 +932,7 @@ static struct nfc_ops nci_nfc_ops = { | |||
922 | .disable_se = nci_disable_se, | 932 | .disable_se = nci_disable_se, |
923 | .discover_se = nci_discover_se, | 933 | .discover_se = nci_discover_se, |
924 | .se_io = nci_se_io, | 934 | .se_io = nci_se_io, |
935 | .fw_download = nci_fw_download, | ||
925 | }; | 936 | }; |
926 | 937 | ||
927 | /* ---- Interface to NCI drivers ---- */ | 938 | /* ---- Interface to NCI drivers ---- */ |