Skip to content

Commit c04c674

Browse files
Robert BaldygaSamuel Ortiz
authored andcommitted
nfc: s3fwrn5: Add driver for Samsung S3FWRN5 NFC Chip
Add driver for Samsung S3FWRN5 NFC controller. S3FWRN5 is using NCI protocol and I2C communication interface. Signed-off-by: Robert Baldyga <[email protected]> Signed-off-by: Samuel Ortiz <[email protected]>
1 parent 025a0cb commit c04c674

File tree

13 files changed

+1565
-0
lines changed

13 files changed

+1565
-0
lines changed
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
* Samsung S3FWRN5 NCI NFC Controller
2+
3+
Required properties:
4+
- compatible: Should be "samsung,s3fwrn5-i2c".
5+
- reg: address on the bus
6+
- interrupt-parent: phandle for the interrupt gpio controller
7+
- interrupts: GPIO interrupt to which the chip is connected
8+
- s3fwrn5,en-gpios: Output GPIO pin used for enabling/disabling the chip
9+
- s3fwrn5,fw-gpios: Output GPIO pin used to enter firmware mode and
10+
sleep/wakeup control
11+
12+
Example:
13+
14+
&hsi2c_4 {
15+
status = "okay";
16+
s3fwrn5@27 {
17+
compatible = "samsung,s3fwrn5-i2c";
18+
19+
reg = <0x27>;
20+
21+
interrupt-parent = <&gpa1>;
22+
interrupts = <3 0 0>;
23+
24+
s3fwrn5,en-gpios = <&gpf1 4 0>;
25+
s3fwrn5,fw-gpios = <&gpj0 2 0>;
26+
};
27+
};

MAINTAINERS

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8871,6 +8871,12 @@ L: [email protected]
88718871
S: Supported
88728872
F: drivers/media/i2c/s5k5baf.c
88738873

8874+
SAMSUNG S3FWRN5 NFC DRIVER
8875+
M: Robert Baldyga <[email protected]>
8876+
L: [email protected] (moderated for non-subscribers)
8877+
S: Supported
8878+
F: drivers/nfc/s3fwrn5
8879+
88748880
SAMSUNG SOC CLOCK DRIVERS
88758881
M: Sylwester Nawrocki <[email protected]>
88768882
M: Tomasz Figa <[email protected]>

drivers/nfc/Kconfig

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,4 +74,5 @@ source "drivers/nfc/nfcmrvl/Kconfig"
7474
source "drivers/nfc/st21nfca/Kconfig"
7575
source "drivers/nfc/st-nci/Kconfig"
7676
source "drivers/nfc/nxp-nci/Kconfig"
77+
source "drivers/nfc/s3fwrn5/Kconfig"
7778
endmenu

drivers/nfc/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,3 +14,4 @@ obj-$(CONFIG_NFC_TRF7970A) += trf7970a.o
1414
obj-$(CONFIG_NFC_ST21NFCA) += st21nfca/
1515
obj-$(CONFIG_NFC_ST_NCI) += st-nci/
1616
obj-$(CONFIG_NFC_NXP_NCI) += nxp-nci/
17+
obj-$(CONFIG_NFC_S3FWRN5) += s3fwrn5/

drivers/nfc/s3fwrn5/Kconfig

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
config NFC_S3FWRN5
2+
tristate
3+
---help---
4+
Core driver for Samsung S3FWRN5 NFC chip. Contains core utilities
5+
of chip. It's intended to be used by PHYs to avoid duplicating lots
6+
of common code.
7+
8+
config NFC_S3FWRN5_I2C
9+
tristate "Samsung S3FWRN5 I2C support"
10+
depends on NFC_NCI && I2C
11+
select NFC_S3FWRN5
12+
default n
13+
---help---
14+
This module adds support for an I2C interface to the S3FWRN5 chip.
15+
Select this if your platform is using the I2C bus.
16+
17+
To compile this driver as a module, choose m here. The module will
18+
be called s3fwrn5_i2c.ko.
19+
Say N if unsure.

drivers/nfc/s3fwrn5/Makefile

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
#
2+
# Makefile for Samsung S3FWRN5 NFC driver
3+
#
4+
5+
s3fwrn5-objs = core.o firmware.o nci.o
6+
s3fwrn5_i2c-objs = i2c.o
7+
8+
obj-$(CONFIG_NFC_S3FWRN5) += s3fwrn5.o
9+
obj-$(CONFIG_NFC_S3FWRN5_I2C) += s3fwrn5_i2c.o
10+
11+
ccflags-$(CONFIG_NFC_DEBUG) := -DDEBUG

drivers/nfc/s3fwrn5/core.c

Lines changed: 219 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,219 @@
1+
/*
2+
* NCI based driver for Samsung S3FWRN5 NFC chip
3+
*
4+
* Copyright (C) 2015 Samsung Electrnoics
5+
* Robert Baldyga <[email protected]>
6+
*
7+
* This program is free software; you can redistribute it and/or modify it
8+
* under the terms and conditions of the GNU General Public License,
9+
* version 2 or later, as published by the Free Software Foundation.
10+
*
11+
* This program is distributed in the hope that it will be useful,
12+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
13+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14+
* GNU General Public License for more details.
15+
*
16+
* You should have received a copy of the GNU General Public License
17+
* along with this program; if not, see <http://www.gnu.org/licenses/>.
18+
*/
19+
20+
#include <linux/module.h>
21+
#include <net/nfc/nci_core.h>
22+
23+
#include "s3fwrn5.h"
24+
#include "firmware.h"
25+
#include "nci.h"
26+
27+
#define S3FWRN5_NFC_PROTOCOLS (NFC_PROTO_JEWEL_MASK | \
28+
NFC_PROTO_MIFARE_MASK | \
29+
NFC_PROTO_FELICA_MASK | \
30+
NFC_PROTO_ISO14443_MASK | \
31+
NFC_PROTO_ISO14443_B_MASK | \
32+
NFC_PROTO_ISO15693_MASK)
33+
34+
static int s3fwrn5_firmware_update(struct s3fwrn5_info *info)
35+
{
36+
bool need_update;
37+
int ret;
38+
39+
s3fwrn5_fw_init(&info->fw_info, "sec_s3fwrn5_firmware.bin");
40+
41+
/* Update firmware */
42+
43+
s3fwrn5_set_wake(info, false);
44+
s3fwrn5_set_mode(info, S3FWRN5_MODE_FW);
45+
46+
ret = s3fwrn5_fw_setup(&info->fw_info);
47+
if (ret < 0)
48+
return ret;
49+
50+
need_update = s3fwrn5_fw_check_version(&info->fw_info,
51+
info->ndev->manufact_specific_info);
52+
if (!need_update)
53+
goto out;
54+
55+
dev_info(&info->ndev->nfc_dev->dev, "Detected new firmware version\n");
56+
57+
ret = s3fwrn5_fw_download(&info->fw_info);
58+
if (ret < 0)
59+
goto out;
60+
61+
/* Update RF configuration */
62+
63+
s3fwrn5_set_mode(info, S3FWRN5_MODE_NCI);
64+
65+
s3fwrn5_set_wake(info, true);
66+
ret = s3fwrn5_nci_rf_configure(info, "sec_s3fwrn5_rfreg.bin");
67+
s3fwrn5_set_wake(info, false);
68+
69+
out:
70+
s3fwrn5_set_mode(info, S3FWRN5_MODE_COLD);
71+
s3fwrn5_fw_cleanup(&info->fw_info);
72+
return ret;
73+
}
74+
75+
static int s3fwrn5_nci_open(struct nci_dev *ndev)
76+
{
77+
struct s3fwrn5_info *info = nci_get_drvdata(ndev);
78+
79+
if (s3fwrn5_get_mode(info) != S3FWRN5_MODE_COLD)
80+
return -EBUSY;
81+
82+
s3fwrn5_set_mode(info, S3FWRN5_MODE_NCI);
83+
s3fwrn5_set_wake(info, true);
84+
85+
return 0;
86+
}
87+
88+
static int s3fwrn5_nci_close(struct nci_dev *ndev)
89+
{
90+
struct s3fwrn5_info *info = nci_get_drvdata(ndev);
91+
92+
s3fwrn5_set_wake(info, false);
93+
s3fwrn5_set_mode(info, S3FWRN5_MODE_COLD);
94+
95+
return 0;
96+
}
97+
98+
static int s3fwrn5_nci_send(struct nci_dev *ndev, struct sk_buff *skb)
99+
{
100+
struct s3fwrn5_info *info = nci_get_drvdata(ndev);
101+
int ret;
102+
103+
mutex_lock(&info->mutex);
104+
105+
if (s3fwrn5_get_mode(info) != S3FWRN5_MODE_NCI) {
106+
mutex_unlock(&info->mutex);
107+
return -EINVAL;
108+
}
109+
110+
ret = s3fwrn5_write(info, skb);
111+
if (ret < 0)
112+
kfree_skb(skb);
113+
114+
mutex_unlock(&info->mutex);
115+
return ret;
116+
}
117+
118+
static int s3fwrn5_nci_post_setup(struct nci_dev *ndev)
119+
{
120+
struct s3fwrn5_info *info = nci_get_drvdata(ndev);
121+
int ret;
122+
123+
ret = s3fwrn5_firmware_update(info);
124+
if (ret < 0)
125+
goto out;
126+
127+
/* NCI core reset */
128+
129+
s3fwrn5_set_mode(info, S3FWRN5_MODE_NCI);
130+
s3fwrn5_set_wake(info, true);
131+
132+
ret = nci_core_reset(info->ndev);
133+
if (ret < 0)
134+
goto out;
135+
136+
ret = nci_core_init(info->ndev);
137+
138+
out:
139+
return ret;
140+
}
141+
142+
static struct nci_ops s3fwrn5_nci_ops = {
143+
.open = s3fwrn5_nci_open,
144+
.close = s3fwrn5_nci_close,
145+
.send = s3fwrn5_nci_send,
146+
.post_setup = s3fwrn5_nci_post_setup,
147+
};
148+
149+
int s3fwrn5_probe(struct nci_dev **ndev, void *phy_id, struct device *pdev,
150+
struct s3fwrn5_phy_ops *phy_ops, unsigned int max_payload)
151+
{
152+
struct s3fwrn5_info *info;
153+
int ret;
154+
155+
info = devm_kzalloc(pdev, sizeof(*info), GFP_KERNEL);
156+
if (!info)
157+
return -ENOMEM;
158+
159+
info->phy_id = phy_id;
160+
info->pdev = pdev;
161+
info->phy_ops = phy_ops;
162+
info->max_payload = max_payload;
163+
mutex_init(&info->mutex);
164+
165+
s3fwrn5_set_mode(info, S3FWRN5_MODE_COLD);
166+
167+
s3fwrn5_nci_get_prop_ops(&s3fwrn5_nci_ops.prop_ops,
168+
&s3fwrn5_nci_ops.n_prop_ops);
169+
170+
info->ndev = nci_allocate_device(&s3fwrn5_nci_ops,
171+
S3FWRN5_NFC_PROTOCOLS, 0, 0);
172+
if (!info->ndev)
173+
return -ENOMEM;
174+
175+
nci_set_parent_dev(info->ndev, pdev);
176+
nci_set_drvdata(info->ndev, info);
177+
178+
ret = nci_register_device(info->ndev);
179+
if (ret < 0) {
180+
nci_free_device(info->ndev);
181+
return ret;
182+
}
183+
184+
info->fw_info.ndev = info->ndev;
185+
186+
*ndev = info->ndev;
187+
188+
return ret;
189+
}
190+
EXPORT_SYMBOL(s3fwrn5_probe);
191+
192+
void s3fwrn5_remove(struct nci_dev *ndev)
193+
{
194+
struct s3fwrn5_info *info = nci_get_drvdata(ndev);
195+
196+
s3fwrn5_set_mode(info, S3FWRN5_MODE_COLD);
197+
198+
nci_unregister_device(ndev);
199+
nci_free_device(ndev);
200+
}
201+
EXPORT_SYMBOL(s3fwrn5_remove);
202+
203+
int s3fwrn5_recv_frame(struct nci_dev *ndev, struct sk_buff *skb,
204+
enum s3fwrn5_mode mode)
205+
{
206+
switch (mode) {
207+
case S3FWRN5_MODE_NCI:
208+
return nci_recv_frame(ndev, skb);
209+
case S3FWRN5_MODE_FW:
210+
return s3fwrn5_fw_recv_frame(ndev, skb);
211+
default:
212+
return -ENODEV;
213+
}
214+
}
215+
EXPORT_SYMBOL(s3fwrn5_recv_frame);
216+
217+
MODULE_LICENSE("GPL");
218+
MODULE_DESCRIPTION("Samsung S3FWRN5 NFC driver");
219+
MODULE_AUTHOR("Robert Baldyga <[email protected]>");

0 commit comments

Comments
 (0)